生活随笔
收集整理的這篇文章主要介紹了
linux内核之accept实现
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
用戶態對accept的標準用法:??if?((client_fd?=?accept(sockfd,?(struct?sockaddr?*)&remote_addr,?&sin_size))?==?-1)????{??????????perror("accept?Error\n");?????continue;????}??sockfd是通過socket系統調用,并且經過listen過的套接字:??sockfd?=?socket(AF_INET,?SOCK_STREAM,?0)??listen(sockfd,?128)????remote_addr將會存儲遠端設備的地址信息。?? SYSCALL_DEFINE3(accept,int, fd,struct sockaddr __user *, upeer_sockaddr,int __user *, upeer_addrlen){return sys_accept4(fd, upeer_sockaddr, upeer_addrlen,0);} SYSCALL_DEFINE4(accept4,int, fd,struct sockaddr __user *, upeer_sockaddr,int __user *, upeer_addrlen,int, flags){struct socket *sock,*newsock;struct file *newfile;int err, len, newfd, fput_needed;struct sockaddr_storage address;if(flags &~(SOCK_CLOEXEC | SOCK_NONBLOCK)){return-EINVAL;}if(SOCK_NONBLOCK != O_NONBLOCK &&(flags & SOCK_NONBLOCK)){ flags =(flags &~SOCK_NONBLOCK)| O_NONBLOCK;} sock = sockfd_lookup_light(fd,&err,&fput_needed);if(!sock){goto out;} err =-ENFILE; newsock = sock_alloc();/*! 1.創建新的sock給新的連接 */if(!newsock){goto out_put;} newsock->type = sock->type; newsock->ops = sock->ops;/* * We don't need try_module_get here, as the listening socket (sock) * has the protocol module (sock->ops->owner) held. */ __module_get(newsock->ops->owner); newfd = get_unused_fd_flags(flags);/*! 2.分配一個fd給新的連接 */if(unlikely(newfd <0)){ err = newfd; sock_release(newsock);goto out_put;} newfile = sock_alloc_file(newsock, flags, sock->sk->sk_prot_creator->name);/*! 3.為newsock創建一個對應的file結構 */if(unlikely(IS_ERR(newfile))){ err = PTR_ERR(newfile); put_unused_fd(newfd); sock_release(newsock);goto out_put;} err = security_socket_accept(sock, newsock);if(err){goto out_fd;} err = sock->ops->accept(sock, newsock, sock->file->f_flags);/*! 4.調用Socket層操作函數inet_accept() */if(err <0){goto out_fd;}if(upeer_sockaddr){if(newsock->ops->getname(newsock,(struct sockaddr *)&address,&len,2)<0){ err =-ECONNABORTED;goto out_fd;} err = move_addr_to_user(&address, len, upeer_sockaddr, upeer_addrlen);if(err <0){goto out_fd;}}/* File flags are not inherited via accept() unlike another OSes. */ fd_install(newfd, newfile); err = newfd;out_put: fput_light(sock->file, fput_needed);out:return err;out_fd: fput(newfile); put_unused_fd(newfd);goto out_put;} 3、sock_alloc_file()
struct file *sock_alloc_file(struct socket *sock,int flags,constchar*dname){struct qstr name ={.name =""};struct path path;struct file *file;if(dname){ name.name = dname; name.len = strlen(name.name);}elseif(sock->sk){ name.name = sock->sk->sk_prot_creator->name; name.len = strlen(name.name);} path.dentry = d_alloc_pseudo(sock_mnt->mnt_sb,&name);if(unlikely(!path.dentry)){return ERR_PTR(-ENOMEM);} path.mnt = mntget(sock_mnt); d_instantiate(path.dentry, SOCK_INODE(sock)); file = alloc_file(&path, FMODE_READ | FMODE_WRITE,&socket_file_ops);if(unlikely(IS_ERR(file))){/* drop dentry, keep inode */ ihold(path.dentry->d_inode); path_put(&path);return file;}/*! 注意這里的屬性設置 */ sock->file = file; file->f_flags = O_RDWR |(flags & O_NONBLOCK); file->private_data = sock;return file;} 4、inet_accept()
/* * Accept a pending connection. The TCP layer now gives BSD semantics. */// <net/ipv4/af_inet.c>int inet_accept(struct socket *sock,struct socket *newsock,int flags){struct sock *sk1 = sock->sk;int err =-EINVAL;/** * 如果使用的是TCP,則sk_prot為tcp_prot,accept為inet_csk_accept() * 獲取新連接的sock。 */struct sock *sk2 = sk1->sk_prot->accept(sk1, flags,&err);/*! 4.1.獲取新連接的sock */if(!sk2){goto do_err;} lock_sock(sk2); sock_rps_record_flow(sk2); WARN_ON(!((1<< sk2->sk_state)&(TCPF_ESTABLISHED | TCPF_SYN_RECV | TCPF_CLOSE_WAIT | TCPF_CLOSE))); sock_graft(sk2, newsock);/*! 4.2.把sock和socket嫁接起來,讓它們能相互索引 */ newsock->state = SS_CONNECTED;/*! 4.3.把新socket的狀態設為已連接 */ err =0; release_sock(sk2);do_err:return err;} 4.2、sock_graft()
// <net/Sock.h>staticinlinevoid sock_graft(struct sock *sk,struct socket *parent){ write_lock_bh(&sk->sk_callback_lock); sk->sk_wq = parent->wq; parent->sk = sk; /*! INET層的socket使用下層的sock服務 */ sk_set_socket(sk, parent); security_sock_graft(sk, parent); write_unlock_bh(&sk->sk_callback_lock);} 4.1、inet_csk_accept() /** * inet_csk_accept()用于從backlog隊列(全連接隊列)中取出一個ESTABLISHED狀態的連接請求塊,返回它所對應的連接sock。 * 1. 非阻塞的,且當前沒有已建立的連接,則直接退出,返回-EAGAIN。 * 2. 阻塞的,且當前沒有已建立的連接: * ? ? 2.1 用戶沒有設置超時時間,則無限期阻塞。 * ? ? 2.2 用戶設置了超時時間,超時后會退出。 */
// <net/ipv4/Inet_connection_sock.c>/* * This will accept the next outstanding connection. */struct sock *inet_csk_accept(struct sock *sk,int flags,int*err){struct inet_connection_sock *icsk = inet_csk(sk);struct request_sock_queue *queue=&icsk->icsk_accept_queue;struct sock *newsk;struct request_sock *req;int error; lock_sock(sk);/* We need to make sure that this socket is listening, * and that it has something pending. */ error =-EINVAL;if(sk->sk_state != TCP_LISTEN){goto out_err;}/* Find already established connection */if(reqsk_queue_empty(queue))// 沒有ESTABLISHED狀態的連接請求塊{long timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK);/* If this is a non blocking socket don't sleep */ error =-EAGAIN;if(!timeo){goto out_err;}/*! 4.1.1 阻塞等待,直到有全連接。如果用戶設置有等待時間,超時后會退出 */ error = inet_csk_wait_for_connect(sk, timeo);if(error){goto out_err;}}/*! 從全連接隊列中取出第一個established狀態的連接請求塊 */ req = reqsk_queue_remove(queue); newsk = req->sk; sk_acceptq_removed(sk);if(sk->sk_protocol == IPPROTO_TCP &&queue->fastopenq != NULL){ spin_lock_bh(&queue->fastopenq->lock);if(tcp_rsk(req)->listener){/* We are still waiting for the final ACK from 3WHS * so can't free req now. Instead, we set req->sk to * NULL to signify that the child socket is taken * so reqsk_fastopen_remove() will free the req * when 3WHS finishes (or is aborted). */ req->sk = NULL; req = NULL;} spin_unlock_bh(&queue->fastopenq->lock);}out: release_sock(sk);if(req){ __reqsk_free(req);}return newsk;out_err: newsk = NULL; req = NULL;*err = error;goto out;} 4.1.1?inet_csk_wait_for_connect()
// <net/ipv4/Inet_connection_sock.c>/* * Wait for an incoming connection, avoid race conditions. This must be called * with the socket locked. */staticint inet_csk_wait_for_connect(struct sock *sk,long timeo){struct inet_connection_sock *icsk = inet_csk(sk); DEFINE_WAIT(wait);int err;/* * True wake-one mechanism for incoming connections: only * one process gets woken up, not the 'whole herd'. * Since we do not 'race & poll' for established sockets * anymore, the common case will execute the loop only once. * * Subtle issue: "add_wait_queue_exclusive()" will be added * after any current non-exclusive waiters, and we know that * it will always _stay_ after any new non-exclusive waiters * because all non-exclusive waiters are added at the * beginning of the wait-queue. As such, it's ok to "drop" * our exclusiveness temporarily when we get woken up without * having to remove and re-insert us on the wait queue. */for(;;){/*! 把自己加入到等待隊列,并且設置自己的狀態是可中斷的 */ prepare_to_wait_exclusive(sk_sleep(sk),&wait, TASK_INTERRUPTIBLE); release_sock(sk);if(reqsk_queue_empty(&icsk->icsk_accept_queue)){/** * 用戶發起的accept操作就停schedule_timeout中 * switch (timeout) * { * case MAX_SCHEDULE_TIMEOUT: * schedule(); * goto out; * default: * } * 根據其實現代碼,由于我們一般沒有設置timeout值,所以是MAX_SCHEDULE_TIMEOUT的情況,這表示立即進入重新調度, * 而當前的進程可以處于睡眠,直到被其它事件喚醒。 */ timeo = schedule_timeout(timeo);} sched_annotate_sleep(); lock_sock(sk); err =0;if(!reqsk_queue_empty(&icsk->icsk_accept_queue)){break;} err =-EINVAL;if(sk->sk_state != TCP_LISTEN){break;} err = sock_intr_errno(timeo);if(signal_pending(current)){break;} err =-EAGAIN;if(!timeo){break;}}/*! 下面把任務設置成TASK_RUNNING狀態,然后把當前sock從等待隊列中刪除 */ finish_wait(sk_sleep(sk),&wait);return err;}
來自為知筆記(Wiz)
轉載于:https://www.cnblogs.com/fengkang1008/p/4688633.html
《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀
總結
以上是生活随笔為你收集整理的linux内核之accept实现的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。