首页 > 代码库 > accept系统调用内核实现
accept系统调用内核实现
用户态对accept的标准用法: if ((client_fd = accept(sockfd, (struct sockaddr *)&remote_addr, &sin_size)) == -1) { //accept()函数让服务器接收客户的连接请求 perror("accept Error\n"); continue; } sockfd是通过socket系统调用,并且经过listen过的套接字: sockfd = socket(AF_INET, SOCK_STREAM, 0) listen(sockfd, 128) remote_addr将会存储远端设备的地址信息。 /* * For accept, we attempt to create a new socket, set up the link * with the client, wake up the client, then return the new * connected fd. We collect the address of the connector in kernel * space and move it to user at the very end. This is unclean because * we open the socket then return an error. * * 1003.1g adds the ability to recvmsg() to query connection pending * status to recvmsg. We need to add that support in a way thats * clean when we restucture accept also. */ asmlinkage long sys_accept(int fd, struct sockaddr __user *upeer_sockaddr, int __user *upeer_addrlen) { struct socket *sock, *newsock; struct file *newfile; int err, len, newfd, fput_needed; char address[MAX_SOCK_ADDR]; //通过监听套接字的描述符fd,找到监听套接字 sock = sockfd_lookup_light(fd, &err, &fput_needed); if (!sock) goto out; err = -ENFILE; //创建新的socket,即心的套接字,它将被client_fd 描述,用于传输数据, //也就是accept系统调用返回值client_fd 所对应的套接口 if (!(newsock = sock_alloc())) goto out_put; //继承listen_fd对应的的一些属性,包括套接字类型,和操作。 //不难理解,listen_fd和client_fd 对应的套接口都是tcp,这些不用一一赋值,直接用listen_fd的属性即可。 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); //创建新的file,然后返回newfd ,这个fd就是待会被返回的client_fd //到现在为止,这个newfd和newfile是有关联的。 newfd = sock_alloc_fd(&newfile); if (unlikely(newfd < 0)) { err = newfd; sock_release(newsock); goto out_put; } //使得这个newsock绑定刚才新建的newfile //即现在为止,newfd newfile newsock之间是有关联的 err = sock_attach_fd(newsock, newfile); if (err < 0) goto out_fd_simple; err = security_socket_accept(sock, newsock); if (err) goto out_fd; //newsock是socket结构体,sock->ops->accept的目的是为newsock关联一个sock结构体 //即三次握手结束后,新建的sock传输控制块,它等待用户accept系统调用“领养”它。 //sock->ops->accept对应的函数是inet_accept err = sock->ops->accept(sock, newsock, sock->file->f_flags); 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; } ......................................... } /* * Accept a pending connection. The TCP layer now gives BSD semantics. */ int inet_accept(struct socket *sock, struct socket *newsock, int flags) { //第一个参数sock是监听套接字代表的套接字 //newsock是刚才我们新建的套接字,用以描述数据传输 //显然,此函数的目的,是找到通过套接字,找到套接字上挂着的以及完成三次握手的sk //这个被找到的sk,将被关联到newsock //sk1 是监听套接字对应的传输控制块 struct sock *sk1 = sock->sk; int err = -EINVAL; //sk2(三次握手后建立的传输控制块) 是挂在sk1(监听套接字的传输控制块) 中的完成三次握手后的sk // sk1->sk_prot->accept 对应的函数是inet_csk_accept struct sock *sk2 = sk1->sk_prot->accept(sk1, flags, &err); if (!sk2) goto do_err; lock_sock(sk2); WARN_ON(!((1 << sk2->sk_state) & (TCPF_ESTABLISHED | TCPF_CLOSE_WAIT | TCPF_CLOSE))); //这个被找到的sk,将被关联到newsock //三次握手后建立的传输控制块sk(sock 结构体),是不对应任何socket结构体的,所以我们关联上 //这样三次握手后建立的传输控制块sk,就和文件系统有关联了 //关联上后,我们就能对其调用send,recvfrom等系统调用了 sock_graft(sk2, newsock); newsock->state = SS_CONNECTED; err = 0; release_sock(sk2); do_err: return err; } /* * This will accept the next outstanding connection. */ //inet_csk_accept的功能时获取建立3次握手后的sk,一次调用返回一个sk struct sock *inet_csk_accept(struct sock *sk, int flags, int *err) { //第一个参数是监听套接字对应的传输控制块。 struct inet_connection_sock *icsk = inet_csk(sk); struct sock *newsk; int error; lock_sock(sk); /* We need to make sure that this socket is listening, * and that it has something pending. */ error = -EINVAL; //如果你传进的参数正确,监听套接字对应的传输控制块的状态,肯定是TCP_LISTEN if (sk->sk_state != TCP_LISTEN) goto out_err; /* Find already established connection */ //icsk->icsk_accept_queue挂的是request_sock,request_sock上挂的就是三次握手后新建的sk //reqsk_queue_empty(&icsk->icsk_accept_queue)判断是否空,就进入if,如何设置为阻塞,则休眠 if (reqsk_queue_empty(&icsk->icsk_accept_queue)) { 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; error = inet_csk_wait_for_connect(sk, timeo); if (error) goto out_err; } //reqsk_queue_get_child的逻辑是: //1:取出icsk_accept_queue的request_sock,然后取出request_sock中的sk //2:listen的sk中,sk_ack_backlog计数减一,sk->sk_ack_backlog--,因为sk_ack_backlog有上限。 //3:删除request_sock //4:return取出的sk newsk = reqsk_queue_get_child(&icsk->icsk_accept_queue, sk); WARN_ON(newsk->sk_state == TCP_SYN_RECV); out: release_sock(sk); return newsk; out_err: newsk = NULL; *err = error; goto out; }
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。