Chinaunix首页 | 论坛 | 博客
  • 博客访问: 68768
  • 博文数量: 11
  • 博客积分: 10
  • 博客等级: 民兵
  • 技术积分: 120
  • 用 户 组: 普通用户
  • 注册时间: 2011-07-06 15:04
文章分类
文章存档

2018年(11)

我的朋友

分类: LINUX

2018-04-08 14:41:24

connectfd = accept(sockfd, (struct sockaddr *)&client_addr, &addrlen);
接收新连接将产生一个新的socket描述符
内核系统调用

点击(此处)折叠或打开

  1. SYSCALL_DEFINE3(accept, int, fd, struct sockaddr __user *, upeer_sockaddr,
  2.         int __user *, upeer_addrlen)
  3. {
  4.     return sys_accept4(fd, upeer_sockaddr, upeer_addrlen, 0);
  5. }


点击(此处)折叠或打开

  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.     if (flags & ~(SOCK_CLOEXEC | SOCK_NONBLOCK))
  9.         return -EINVAL;

  10.     if (SOCK_NONBLOCK != O_NONBLOCK && (flags & SOCK_NONBLOCK))
  11.         flags = (flags & ~SOCK_NONBLOCK) | O_NONBLOCK;

  12.     sock = sockfd_lookup_light(fd, &err, &fput_needed);    //根据socket描述符找到socket接口
  13.     if (!sock)
  14.         goto out;

  15.     err = -ENFILE;
  16.     newsock = sock_alloc();    //分配一个新的socket接口
  17.     if (!newsock)
  18.         goto out_put;

  19.     newsock->type = sock->type;
  20.     newsock->ops = sock->ops;

  21.     /*
  22.      * We don't need try_module_get here, as the listening socket (sock)
  23.      * has the protocol module (sock->ops->owner) held.
  24.      */
  25.     __module_get(newsock->ops->owner);

  26.     newfd = get_unused_fd_flags(flags);
  27.     if (unlikely(newfd < 0)) {
  28.         err = newfd;
  29.         sock_release(newsock);
  30.         goto out_put;
  31.     }
  32.     newfile = sock_alloc_file(newsock, flags, sock->sk->sk_prot_creator->name);    //为新的socket接口分配文件描述符,文件结构
  33.     if (IS_ERR(newfile)) {
  34.         err = PTR_ERR(newfile);
  35.         put_unused_fd(newfd);
  36.         sock_release(newsock);
  37.         goto out_put;
  38.     }

  39.     err = security_socket_accept(sock, newsock);
  40.     if (err)
  41.         goto out_fd;
  42.     // 调用各个协议族的accept函数,本地socket是unix_accept
  1.     err = sock->ops->accept(sock, newsock, sock->file->f_flags, false);
  2.     if (err < 0)
  3.         goto out_fd;

  4.     if (upeer_sockaddr) {
  5.         if (newsock->ops->getname(newsock, (struct sockaddr *)&address,
  6.                      &len, 2) < 0) {
  7.             err = -ECONNABORTED;
  8.             goto out_fd;
  9.         }
  10.         err = move_addr_to_user(&address,
  11.                     len, upeer_sockaddr, upeer_addrlen);
  12.         if (err < 0)
  13.             goto out_fd;
  14.     }

  15.     /* File flags are not inherited via accept() unlike another OSes. */

  16.     fd_install(newfd, newfile);
  17.     err = newfd;

  18. out_put:
  19.     fput_light(sock->file, fput_needed);
  20. out:
  21.     return err;
  22. out_fd:
  23.     fput(newfile);
  24.     put_unused_fd(newfd);
  25.     goto out_put;
  26. }
accept()是在一个套接口接受的一个连接。accept()是c语言中网络编程的重要的函数,本函数从s的等待连接队列中抽取第一个连接,创建一个与s同类的新的套接口并返回句柄。如果队列中无等待连接,且套接口为阻塞方式,则accept()阻塞调用进程直至新的连接出现。如果套接口为非阻塞方式且队列中无等待连接,则accept()返回一错误代码。已接受连接的套接口不能用于接受新的连接,原套接口仍保持开放。 accept会新生成一个socket接口,相当于新生成了一条数据管道,这条管道上的数据与其他连接到服务器的socket是互不影响的,而SOCK_DGRAM类型的socket,都是发送到一条通道的就会相互影响,后续会讨论这个问题.

点击(此处)折叠或打开

  1. static int unix_accept(struct socket *sock, struct socket *newsock, int flags,
  2.          bool kern)
  3. {
  4.     struct sock *sk = sock->sk;
  5.     struct sock *tsk;//注意这里是两个sock结构
  6.     struct sk_buff *skb;
  7.     int err;

  8.     err = -EOPNOTSUPP;
  9.     if (sock->type != SOCK_STREAM && sock->type != SOCK_SEQPACKET)
  10.         goto out;

  11.     err = -EINVAL;
  12.     if (sk->sk_state != TCP_LISTEN)
  13.         goto out;

  14.     /* If socket state is TCP_LISTEN it cannot change (for now...),
  15.      * so that no locks are necessary.
  16.      */
  1.     skb = skb_recv_datagram(sk, 0, flags&O_NONBLOCK, &err);//尝试接收数据
  2.     if (!skb) {
  3.         /* This means receive shutdown. */
  4.         if (err == 0)
  5.             err = -EINVAL;
  6.         goto out;
  7.     }

  8.     tsk = skb->sk;
  9.     skb_free_datagram(sk, skb);
  10.     wake_up_interruptible(&unix_sk(sk)->peer_wait);

  11.     /* attach accepted sock to socket */
  12.     unix_state_lock(tsk);
  13.     newsock->state = SS_CONNECTED;   
  14.     unix_sock_inherit_flags(sock, newsock);
  15.     sock_graft(tsk, newsock);    //新生成的socket与接收到的skb->sk传输控制块做关联
  16.     unix_state_unlock(tsk);
  17.     return 0;

  18. out:
  19.     return err;
  20. }
accept调用skb_recv_datagram()从连接队列中取数据,

点击(此处)折叠或打开

  1. struct sk_buff *skb_recv_datagram(struct sock *sk, unsigned int flags,
  2.                  int noblock, int *err)
  3. {
  4.     int peeked, off = 0;

  5.     return __skb_recv_datagram(sk, flags | (noblock ? MSG_DONTWAIT : 0),
  6.                  &peeked, &off, err);
  7. }
如果flags设置socket是非阻塞的,__skb_recv_datagram设置MSG_DONTWAIT,在没有新连接到达时直接返回,并不阻塞

点击(此处)折叠或打开

  1. struct sk_buff *__skb_recv_datagram(struct sock *sk, unsigned int flags,
  2.                  int *peeked, int *off, int *err)
  3. {
  4.     struct sk_buff_head *queue = &sk->sk_receive_queue; //sock接收队列
  5.     struct sk_buff *skb, *last;
  6.     unsigned long cpu_flags;
  7.     long timeo;
  8.     /*
  9.      * Caller is allowed not to check sk->sk_err before skb_recv_datagram()
  10.      */
  11.     int error = sock_error(sk);

  12. #ifdef DHLI_DEBUG
  13.     if (error) {
  14.         if (strstr(current->comm, "TDA")) {
  15.             printk("sock error occur\n");
  16.         }
  17.     }
  18. #endif

  19.     if (error)
  20.         goto no_packet;

  21.     timeo = sock_rcvtimeo(sk, flags & MSG_DONTWAIT); //获取超时时间,如果设置了MSG_DONTWAIT则返回0

  22.     do {
  23.         /* Again only user level code calls this function, so nothing
  24.          * interrupt level will suddenly eat the receive_queue.
  25.          *
  26.          * Look at current nfs client by the way...
  27.          * However, this function was correct in any case. 8)
  28.          */
  29.         int _off = *off;

  30.         last = (struct sk_buff *)queue;
  31.         spin_lock_irqsave(&queue->lock, cpu_flags);
  32.         skb_queue_walk(queue, skb) {
  33.             last = skb;
  34.             *peeked = skb->peeked;
  35.             if (flags & MSG_PEEK) {
  36.                 if (_off >= skb->len && (skb->len || _off ||
  37.                              skb->peeked)) {
  38.                     _off -= skb->len;
  39.                     continue;
  40.                 }

  41.                 skb = skb_set_peeked(skb);
  42.                 error = PTR_ERR(skb);
  43.                 if (IS_ERR(skb))
  44.                     goto unlock_err;

  45.                 atomic_inc(&skb->users);
  46.             } else
  47.                 __skb_unlink(skb, queue);

  48.             spin_unlock_irqrestore(&queue->lock, cpu_flags);
  49.             *off = _off;
  50.             return skb;
  51.         }
  52.         spin_unlock_irqrestore(&queue->lock, cpu_flags);
  53. //如果接收队列中没有消息存在,根据是否设置CONFIG_NET_RX_BUSY_POLL选项运行两种等待方案,忙等待;如果没有设置该选项,调用wait_for_more_packets,它会让进程睡眠
  54.         if (sk_can_busy_loop(sk) &&
  55.          sk_busy_loop(sk, flags & MSG_DONTWAIT))
  56.             continue;

  57.         /* User doesn't want to wait */
  58.         error = -EAGAIN;
  59.         if (!timeo)
  60.             goto no_packet;

  61.     } while (!wait_for_more_packets(sk, err, &timeo, last));

  62.     return NULL;

  63. unlock_err:
  64.     spin_unlock_irqrestore(&queue->lock, cpu_flags);
  65. no_packet:
  66.     *err = error;
  67.     return NULL;
  68. }

执行wait_for_more_packets,进程将睡眠


阅读(2118) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~