Linux Kernel 2.6.9 source code analysis -- accept

Keywords: socket Linux

Linux Kernel 2.6.9 source code analysis – accept

Let's take a look at the prototype: int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
sockfd: this socket is used to listen to a port. When a client connects with the server, it uses this port number, which is associated with the socket at this time. Of course, the client doesn't know the details of the socket. It only knows an address and a port number.
addr: This is a result parameter. It is used to accept a return value. The return value specifies the address of the client. Of course, this address is described by an address structure. The user should know the address structure. If you are not interested in the address of the customer, you can set this value to NULL.
len: as you think, it is also a result parameter. It is used to accept the size of the above addr structure. It indicates the number of bytes occupied by the addr structure. Again, it can be set to NULL.
Let's take a look at the SYSU accept system call

asmlinkage long sys_accept(int fd, struct sockaddr __user *upeer_sockaddr, int __user *upeer_addrlen)
{
	struct socket *sock, *newsock;
	int err, len;
	char address[MAX_SOCK_ADDR];
	sock = sockfd_lookup(fd, &err);
	if (!sock)
		goto out;
	err = -EMFILE;
	if (!(newsock = sock_alloc())) 
		goto out_put;
	newsock->type = sock->type;
	newsock->ops = sock->ops;
	............................
	err = sock->ops->accept(sock, newsock, sock->file->f_flags);
	if (err < 0)
		goto out_release;
    ..................
	if ((err = sock_map_fd(newsock)) < 0)
		goto out_release;
    .............................
}

From the above code,

  1. Find the struct socket structure previously created through sys_socket through the fd passed in by the user
  2. Assign a new struct socket structure, and then assign the type and ops of the old socket to the new socket
  3. Sock - > Ops - > accept hang a struct sock corresponding to the client request. See the detailed implementation later
  4. Through socket map FD, allocate struct file and mount the new socket, then mount it in the current process descriptor
  5. The address information of the client is returned to the user through the parameter upeer ﹣ SOCKADDR, and the file descriptor corresponding to the new socket is returned to the user through the return value

The functions corresponding to sock - > Ops - > accept are:

int inet_accept(struct socket *sock, struct socket *newsock, int flags)
{
	struct sock *sk1 = sock->sk;
	int err = -EINVAL;
	struct sock *sk2 = sk1->sk_prot->accept(sk1, flags, &err);
	if (!sk2)
		goto do_err;
	lock_sock(sk2);
	BUG_TRAP((1 << sk2->sk_state) &
		 (TCPF_ESTABLISHED | TCPF_CLOSE_WAIT | TCPF_CLOSE));
	sock_graft(sk2, newsock);
	newsock->state = SS_CONNECTED;
	err = 0;
	release_sock(sk2);
do_err:
	return err;
}

TCP type socket will eventually call to:

struct sock *tcp_accept(struct sock *sk, int flags, int *err)
{
	struct tcp_opt *tp = tcp_sk(sk);
	struct open_request *req;
	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;
	if (sk->sk_state != TCP_LISTEN)
		goto out;
	/* Find already established connection */
	if (!tp->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;
		error = wait_for_connect(sk, timeo);
		if (error)
			goto out;
	}
	req = tp->accept_queue;
	if ((tp->accept_queue = req->dl_next) == NULL)
		tp->accept_queue_tail = NULL;
 	newsk = req->sk;
	sk_acceptq_removed(sk);
	tcp_openreq_fastfree(req);
	BUG_TRAP(newsk->sk_state != TCP_SYN_RECV);
	release_sock(sk);
	return newsk;
out:
	release_sock(sk);
	*err = error;
	return NULL;
}

1. If the TP - > accept queue queue is empty, wait for connect will be called to put the current process into sleep. When a connection arrives, the current process will be woken up
2. If the TP - > accept queue queue is not empty, take out the TP - > accept queue header element, struct open request, and return the struct sock structure
3. Bind the struct socket structure in the previous step to the newly allocated struct socket structure
As for how the elements in the TP - > accept queue come from, the process is quite complicated. I will discuss it in detail later
The following brief flow chart can be summarized by integrating sys create / sys bind / sys connect / sys accept
It can be seen that every time a client's connection request is received by the server's main socket, a new struct sock et will be added to the accept queue. When the user calls sys accept, a new socket will be returned and a socket in the queue head on the street will be hung. Then a new struct file will be created and its fd will be returned to the user of sys accept. The main socket will continue to receive from other clients Connection request

Published 9 original articles, won praise 0, visited 125
Private letter follow

Posted by ldoozer on Thu, 30 Jan 2020 04:18:10 -0800