socket层---accept的实现

来源:互联网 发布:网络爬虫 demo .net 编辑:程序博客网 时间:2024/06/05 17:40

Socket层实现系列 — accept()的实现(一)

分类: Socket 2832人阅读 评论(0) 收藏 举报
socketTCPIP

目录(?)[+]

本文主要介绍了accept()的系统调用、Socket层实现,以及TCP层实现。

内核版本:3.6

Author:zhangskd @ csdn blog

 

应用层

 

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

It extracts the first connection request on the queue of pending connections (backlog), creates a new

connected socket, and returns a new file descriptor referring to that socket.

 

If no pending connections are present on the queue, and the socket is not marked as non-blocking,

accept() blocks the caller until a connection is present. If the socket is marked non-blocking and no

pending connections are present on the queue, accept() fails with the error EAGAIN.

 

在建立好接收队列以后,服务器就调用accept(),然后睡眠直到有客户端的连接请求到达。

addr用于保存客户端的地址。

 

系统调用

 

accept()是由glibc提供的,声明位于include/sys/socket.h中,实现位于sysdeps/mach/hurd/accept.c中,

主要是用来从用户空间进入名为sys_socketcall的系统调用,并传递参数。sys_socketcall()实际上是所有

socket函数进入内核空间的共同入口。

 

在sys_socketcall()中会调用sys_accept4()。

[java] view plaincopy
  1. SYSCALL_DEFINE2(socketcall, int, call, unsigned long __user *, args)  
  2. {  
  3.     ...  
  4.     switch(call) {  
  5.         ...  
  6.         case SYS_ACCEPT:  
  7.             err = sys_accpet4(a0, (struct sockaddr __user *)a1, (int __user *)a[2], 0);  
  8.             break;  
  9.         ...  
  10.     }  
  11.     return err;  
  12. }  

 

经过了socket层的总入口sys_socketcall(),现在进入sys_accpet4()。

[java] view plaincopy
  1. SYSCALL_DEFINE4(accept4, int, fd, struct sockaddr __user *, upeer_sockaddr,  
  2.                 int __user *, upeer_addrlen, int flags)  
  3. {  
  4.     struct socket *sock, *newsock;  
  5.     struct file *newfile;  
  6.     int err, len, newfd, fput_needed;  
  7.     struct sockaddr_storage address;  
  8.   
  9.     /* 只允许使用这两个标志 */  
  10.     if (flags & ~(SOCK_CLOSEXEC | SOCK_NONBLOCK))  
  11.         return -EINVAL;  
  12.       
  13.     if (SOCK_NONBLOCK != O_NONBLOCK && (flags & SOCK_NONBLOCK))  
  14.         flags = (flags & ~SOCK_NONBLOCK) | O_NONBLOCK;  
  15.   
  16.     /* 通过文件描述符fd,找到对应的socket。 
  17.      * 以fd为索引从当前进程的文件描述符表files_struct中找到对应的file实例, 
  18.      * 然后从file实例的private_data成员中获取socket实例。 
  19.      */  
  20.     sock = sockfd_lookup_light(fd, &err, &fput_needed);  
  21.     if (! sock)  
  22.         goto out;  
  23.   
  24.     err = -ENFILE; /* File table overflow */  
  25.     newsock = sock_alloc(); /* 创建一个新的inode和socket */  
  26.     if (! newsock)  
  27.         goto out_put;  
  28.   
  29.     newsock->type = sock->type; /* 新socket的类型 */  
  30.     newsock->ops = sock->ops; /* 新socket的socket层操作 */  
  31.   
  32.     /* We don't need try_module_get here, as the listening socket (sock) 
  33.      * has the protocol module (sock->ops->owner) held. 
  34.      * Socekt层协议,对SOCK_STREAM来说是inet_stream_ops,它的引用计数加一。 
  35.      */  
  36.     __module_get(newsock->ops->owner);      
  37.   
  38.     /* 为socket创建一个对应的file结构sock->file,返回fd */  
  39.     newfd = sock_alloc_file(newsock, &newfile, flags);  
  40.     if (unlikely(newfd < 0)) {  
  41.         err = newfd;  
  42.         sock_release(newsock);  
  43.         goto out_put;  
  44.     }  
  45.   
  46.     err = security_socket_accept(sock, newsock); /* SELinux相关 */  
  47.     if (err)  
  48.         goto out_fd;  
  49.   
  50.     /* SOCKET层的操作函数,如果是SOCK_STREAM,proto_ops为inet_stream_ops, 
  51.      * 接下来调用inet_accept()。 
  52.      */  
  53.     err = sock->ops->accept(sock, newsock, sock->file->f_flags);  
  54.     if (err < 0)  
  55.         goto out_fd;  
  56.   
  57.     if (upeer_sockaddr) { /* 如果要保存对端地址 */  
  58.         /* 获取对端的地址,以及地址的长度 */  
  59.         if (newsock->ops->getname(newsock, (struct sockaddr *)&address, &len, 2) < 0) {  
  60.             err = -ECONNABORTED; /* Software caused connection abort */  
  61.             goto out_fd;  
  62.         }  
  63.   
  64.         /* 把内核空间的socket地址复制到用户空间 */  
  65.         err = move_addr_to_user(&address, len, upeer_sockaddr, upeer_addrlen);  
  66.         if (err < 0)  
  67.             goto out_fd;  
  68.     }  
  69.   
  70.     /* File flags are not inherited via accept() unlike another OSes. 
  71.      * 以newfd为索引,把newfile加入当前进程的文件描述符表files_struct中。 
  72.      */  
  73.     fd_install(newfd, newfile);  
  74.     err = newfd;  
  75.   
  76. out_put:  
  77.     fput_light(sock->file, fput_needed);  
  78.   
  79. out:  
  80.     return err;  
  81.   
  82. out_fd:  
  83.     fput(newfile);  
  84.     put_unused_fd(newfd);  
  85.     goto out_put;  
  86. }  

 

sys_accept4()主要做了:

1. 创建了一个新的socket和inode,以及它所对应的fd、file。

2. 调用Socket层操作函数inet_accept()。

3. 保存对端地址到指定的用户空间地址。

 

Socket层

 

SOCK_STREAM套接口的Socket层操作函数集实例为inet_stream_ops,连接接收函数为inet_accept()。

[java] view plaincopy
  1. const struct proto_ops inet_stream_ops = {  
  2.     .family = PF_INET,  
  3.     .owner = THIS_MODULE,  
  4.     ...  
  5.     .accept = inet_accept,  
  6.     ...  
  7. };  

 

inet_accept()主要做了:

1. 调用TCP层的操作函数,获取已建立的连接sock。

2. 把新socket和sock关联起来。

3. 把新socket的状态设为SS_CONNECTED。

至此,新socket的各个字段都赋值完毕了。

[java] view plaincopy
  1. /* Accept a pending connection.  
  2.  * The TCP layer now gives BSD semantics. 
  3.  */  
  4. int inet_accept(struct socket *sock, struct socket *newsock, int flags)  
  5. {  
  6.   
  7.     struct sock *sk1 = sock->sk;  
  8.     int err = -EINVAL;  
  9.   
  10.     /* 如果使用的是TCP,则sk_prot为tcp_prot,accept为inet_csk_accept() 
  11.      * 获取新连接的sock。 
  12.      */  
  13.     struct sock *sk2 = sk1->sk_prot->accept(sk1, flags, &err);  
  14.     if (! sk2)  
  15.         goto do_err;  
  16.   
  17.     lock_sock(sk2);  
  18.   
  19.     sock_rps_record_flow(sk2); /* RPS补丁 */  
  20.     WARN_ON(! ((1 << sk2->sk_state) & (TCPF_ESTABLISHED | TCPF_CLOSE_WAIT | TCPF_CLOSE)));  
  21.   
  22.     sock_graft(sk2, newsock); /* 把sock和socket嫁接起来,让它们能相互索引 */  
  23.     newsock->state = SS_CONNECTED; /* 把新socket的状态设为已连接 */  
  24.   
  25.     err = 0;  
  26.     release_sock(sk2);  
  27.   
  28. do_err:  
  29.     return err;  
  30. }  
[java] view plaincopy
  1. /* 
  2.  * struct callback_head - callback structure for use with RCU and task_work 
  3.  * @next: next update requests in a list 
  4.  * @func: actual update function to call after the grace period. 
  5.  */  
  6. struct callback_head {  
  7.     struct callback_head *next;  
  8.     void (*func) (struct callback_head *head);  
  9. };  
  10. #define rcu_head callback_head  
  11.   
  12. /* 把sock和socket嫁接起来。*/  
  13. static inline void sock_graft(struct sock *sk, struct socket *parent)  
  14. {  
  15.     write_lock_bh(&sk->sk_callback_lock);  
  16.   
  17.     sk->sk_wq = parent->wq; /* 等待队列 */  
  18.     parent->sk = sk;  
  19.     sk_set_socket(sk, parent);  
  20.     security_sock_graft(sk, parent);  
  21.   
  22.     write_unlock_bh(&sk->sk_callback_lock);  
  23. }  
  24.   
  25. static inline void sk_set_socket(struct sock *sk, struct socket *sock)  
  26. {  
  27.     sk_tx_queue_clear(sk);  
  28.     sk->sk_socket = sock;  
  29. }  

 

TCP层实现

 

SOCK_STREAM套接口的TCP层操作函数集实例为tcp_prot,其中连接接收函数为inet_csk_accept()。

[java] view plaincopy
  1. struct proto tcp_prot = {  
  2.     .name = "TCP",  
  3.     .owner = THIS_MODULE,  
  4.     ...  
  5.     .accept = inet_csk_accept,  
  6.     ...  
  7. };  

 

inet_csk_accept()用于从backlog队列(全连接队列)中取出一个ESTABLISHED状态的连接请求块,返回它所对应的连接sock。

1. 非阻塞的,且当前没有已建立的连接,则直接退出,返回-EAGAIN。

2. 阻塞的,且当前没有已建立的连接:

    2.1 用户没有设置超时时间,则无限期阻塞。

    2.2 用户设置了超时时间,超时后会退出。

[java] view plaincopy
  1. /* This will accept the next outstanding connection. */  
  2. struct sock *inet_csk_accept(struct sock *sk, int flags, int *err)  
  3. {  
  4.     struct inet_connection_sock *icsk = inet_csk(sk);  
  5.     struct sock *newsk;  
  6.     int error;  
  7.   
  8.     lock_sock(sk);  
  9.   
  10.     /* We need to make sure that this socket is listening, 
  11.      * and that it has something pending. 
  12.      */  
  13.     error = -EINVAL;  
  14.   
  15.     if (sk->sk_state != TCP_LISTEN) /* socket必须处于监听状态 */  
  16.         goto out_err;  
  17.   
  18.     /* Find already established connection. 
  19.      * 发没有现ESTABLISHED状态的连接请求块。 
  20.      */  
  21.     if (reqsk_queue_empty(&icsk->icsk_accept_queue)) {  
  22.   
  23.         /* 等待超时时间,如果是非阻塞则为0 */  
  24.         long timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK);   
  25.   
  26.         /* If this is a non blocking socket don't sleep */  
  27.         error = -EAGAIN; /* Try again */  
  28.   
  29.         if (! timeo) /* 如果是非阻塞的,则直接退出 */  
  30.             goto out_err;  
  31.   
  32.         /* 阻塞等待,直到有全连接。如果用户有设置等待超时时间,超时后会退出 */  
  33.         error = inet_csk_wait_for_connect(sk, timeo);  
  34.   
  35.         if (error)  
  36.             goto out_err;  
  37.     }  
  38.   
  39.     /* 获取新连接的sock,释放连接控制块 */  
  40.     newsk = reqsk_queue_get_child(&icsk->icsk_accept_queue, sk);  
  41.     WARN_ON(newsk->sk_state == TCP_SYN_RECV);  
  42.   
  43. out:  
  44.     release_sock(sk);  
  45.     return newsk;  
  46.   
  47. out err:  
  48.     newsk = NULL;  
  49.     *err = error;  
  50.     goto out;      
  51. }  

 

检查ESTABLISHED状态的连接请求块队列是否为空。

[java] view plaincopy
  1. static inline int reqsk_queue_empty(struct request_sock_queue *queue)  
  2. {  
  3.     return queue->rskq_accept_head == NULL;  
  4. }   
  5.   
  6. static inline long sock_rcvtimeo(const struct sock *sk, bool noblock)  
  7. {  
  8.     return noblock ? 0 : sk->sk_rcvtimeo; /* accept()超时时间 */  
  9. }  

 

从backlog队列(全连接队列)中取出一个ESTABLISHED状态的连接请求块,返回它所对应的连接sock。

同时更新backlog队列的全连接数,释放取出的连接控制块。

[java] view plaincopy
  1. static inline struct sock *reqsk_queue_get_child(struct request_sock_queue *queue,   
  2.                                                  struct sock *parent)  
  3. {  
  4.     /* 从全连接队列中,取出第一个ESTABLISHED状态的连接请求块 */  
  5.     struct request_sock *req = reqsk_queue_remove(queue);  
  6.     struct sock *child = req->sk; /* 一个已建立的连接 */  
  7.       
  8.     WARN_ON(child == NULL);  
  9.   
  10.     sk_acceptq_removed(parent); /* 当前backlog队列的全连接数减一 */  
  11.     __reqsk_free(req); /* 释放取出的连接请求控制块 */  
  12.   
  13.     return child;  
  14. }  
  15.   
  16. static inline struct request_sock *reqsk_queue_remove(struct request_sock_queue *queue)  
  17. {  
  18.     struct request_sock *req = queue->rskq_accept_head; /* 第一个ESTABLISHED状态的连接请求块 */  
  19.     WARN_ON(req == NULL);  
  20.   
  21.     queue->rskq_accept_head = req->dl_next;  
  22.   
  23.     if (queue->rskq_accept_head == NULL)  
  24.         queue->rskq_accept_tail = NULL;  
  25.   
  26.     return req;  
  27. }  
  28.   
  29. static inline void sk_acceptq_removed(struct sock *sk)  
  30. {  
  31.     sk->sk_ack_backlog--; /* 全连接的数量减一 */  
  32. }  
  33.   
  34. /* 释放连接请求控制块 */  
  35. static inline void __reqsk_free(struct request_sock *req)  
  36. {  
  37.     kmem_cache_free(req->rsk_ops->slab, req);  
  38. }  
0 0
原创粉丝点击