生活随笔
收集整理的這篇文章主要介紹了
linux内核之accept实现
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
用戶態(tài)對(duì)accept的標(biāo)準(zhǔn)用法:??if?((client_fd?=?accept(sockfd,?(struct?sockaddr?*)&remote_addr,?&sin_size))?==?-1)????{??????????perror("accept?Error\n");?????continue;????}??sockfd是通過(guò)socket系統(tǒng)調(diào)用,并且經(jīng)過(guò)listen過(guò)的套接字:??sockfd?=?socket(AF_INET,?SOCK_STREAM,?0)??listen(sockfd,?128)????remote_addr將會(huì)存儲(chǔ)遠(yuǎn)端設(shè)備的地址信息。?? 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.創(chuàng)建新的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.分配一個(gè)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創(chuàng)建一個(gè)對(duì)應(yīng)的file結(jié)構(gòu) */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.調(diào)用Socket層操作函數(shù)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;}/*! 注意這里的屬性設(shè)置 */ 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嫁接起來(lái),讓它們能相互索引 */ newsock->state = SS_CONNECTED;/*! 4.3.把新socket的狀態(tài)設(shè)為已連接 */ 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服務(wù) */ 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隊(duì)列(全連接隊(duì)列)中取出一個(gè)ESTABLISHED狀態(tài)的連接請(qǐng)求塊,返回它所對(duì)應(yīng)的連接sock。 * 1. 非阻塞的,且當(dāng)前沒(méi)有已建立的連接,則直接退出,返回-EAGAIN。 * 2. 阻塞的,且當(dāng)前沒(méi)有已建立的連接: * ? ? 2.1 用戶沒(méi)有設(shè)置超時(shí)時(shí)間,則無(wú)限期阻塞。 * ? ? 2.2 用戶設(shè)置了超時(shí)時(shí)間,超時(shí)后會(huì)退出。 */
// <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))// 沒(méi)有ESTABLISHED狀態(tài)的連接請(qǐng)求塊{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 阻塞等待,直到有全連接。如果用戶設(shè)置有等待時(shí)間,超時(shí)后會(huì)退出 */ error = inet_csk_wait_for_connect(sk, timeo);if(error){goto out_err;}}/*! 從全連接隊(duì)列中取出第一個(gè)established狀態(tài)的連接請(qǐng)求塊 */ 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(;;){/*! 把自己加入到等待隊(duì)列,并且設(shè)置自己的狀態(tài)是可中斷的 */ prepare_to_wait_exclusive(sk_sleep(sk),&wait, TASK_INTERRUPTIBLE); release_sock(sk);if(reqsk_queue_empty(&icsk->icsk_accept_queue)){/** * 用戶發(fā)起的accept操作就停schedule_timeout中 * switch (timeout) * { * case MAX_SCHEDULE_TIMEOUT: * schedule(); * goto out; * default: * } * 根據(jù)其實(shí)現(xiàn)代碼,由于我們一般沒(méi)有設(shè)置timeout值,所以是MAX_SCHEDULE_TIMEOUT的情況,這表示立即進(jìn)入重新調(diào)度, * 而當(dāng)前的進(jìn)程可以處于睡眠,直到被其它事件喚醒。 */ 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;}}/*! 下面把任務(wù)設(shè)置成TASK_RUNNING狀態(tài),然后把當(dāng)前sock從等待隊(duì)列中刪除 */ finish_wait(sk_sleep(sk),&wait);return err;}
來(lái)自為知筆記(Wiz)
轉(zhuǎn)載于:https://www.cnblogs.com/fengkang1008/p/4688633.html
《新程序員》:云原生和全面數(shù)字化實(shí)踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀
總結(jié)
以上是生活随笔為你收集整理的linux内核之accept实现的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。