网络函数accept源码分析

static int sock_accept(int fd, struct sockaddr *upeer_sockaddr, int *upeer_addrlen)
{
	struct file *file;
	struct socket *sock, *newsock;
	int i;
	char address[MAX_SOCK_ADDR];
	int len;

	if (fd < 0 || fd >= NR_OPEN || ((file = current->files->fd[fd]) == NULL))
		return(-EBADF);
	// 根据文件描述符找到对应的file结构体和socket结构
  	if (!(sock = sockfd_lookup(fd, &file))) 
		return(-ENOTSOCK);
	if (sock->state != SS_UNCONNECTED) 
	{
		return(-EINVAL);
	}
	// socket没有调用过listen,报错,该标记位在listen中设置
	if (!(sock->flags & SO_ACCEPTCON)) 
	{
		return(-EINVAL);
	}
	// 分配一个新的socket结构体
	if (!(newsock = sock_alloc())) 
	{
		printk("NET: sock_accept: no more sockets\n");
		return(-ENOSR);	/* Was: EAGAIN, but we are out of system
				   resources! */
	}
	newsock->type = sock->type;
	newsock->ops = sock->ops;
	// 创建一个底层的sock结构体和socket结构体互相关联
	if ((i = sock->ops->dup(newsock, sock)) < 0) 
	{
		sock_release(newsock);
		return(i);
	}

	i = newsock->ops->accept(sock, newsock, file->f_flags);
	if ( i < 0) 
	{
		sock_release(newsock);
		return(i);
	}
	// 返回一个新的文件描述符
	if ((fd = get_fd(SOCK_INODE(newsock))) < 0) 
	{
		sock_release(newsock);
		return(-EINVAL);
	}
	// 是否需要获取socket对应的地址
	if (upeer_sockaddr)
	{
		newsock->ops->getname(newsock, (struct sockaddr *)address, &len, 1);
		move_addr_to_user(address,len, upeer_sockaddr, upeer_addrlen);
	}
	return(fd);
}

accept函数主要的两个操作是
1 sock->ops->dup,该函数的是inet_create函数的封装,就是新建一个sock结构体并且和socket结构体互相关联,前面的文章已经分析过。
2 sock->ops->accept,该函数底层是inet_accept函数

static int inet_accept(struct socket *sock, struct socket *newsock, int flags)
{
	struct sock *sk1, *sk2;
	int err;

	sk1 = (struct sock *) sock->data;

	/*
	 * We've been passed an extra socket.
	 * We need to free it up because the tcp module creates
	 * its own when it accepts one.
	 */
	// 销毁新socket结构体中的socke结构体,d调用底层的accept会返回一个新的sock
	if (newsock->data)
	{
	  	struct sock *sk=(struct sock *)newsock->data;
	  	newsock->data=NULL;
	  	sk->dead = 1;
	  	destroy_sock(sk);
	}
  
	if (sk1->prot->accept == NULL) 
		return(-EOPNOTSUPP);

	/* Restore the state if we have been interrupted, and then returned. */
	if (sk1->pair != NULL ) 
	{
		sk2 = sk1->pair;
		sk1->pair = NULL;
	} 
	else
	{	// 返回一个新的sock结构体
		sk2 = sk1->prot->accept(sk1,flags);
		if (sk2 == NULL) 
		{
			if (sk1->err <= 0)
				printk("Warning sock.c:sk1->err <= 0.  Returning non-error.\n");
			err=sk1->err;
			sk1->err=0;
			return(-err);
		}
	}
	// 互相关联
	newsock->data = (void *)sk2;
	// 复制socket结构的wait字段,用于控制进程的阻塞和唤醒
	sk2->sleep = newsock->wait;
	sk2->socket = newsock;
	newsock->conn = NULL;
	if (flags & O_NONBLOCK) 
		return(0);

	cli(); /* avoid the race. */
	// sock是接收syn状态则阻塞当前进程
	while(sk2->state == TCP_SYN_RECV) 
	{
		interruptible_sleep_on(sk2->sleep);
		if (current->signal & ~current->blocked) 
		{
			sti();
			sk1->pair = sk2;
			sk2->sleep = NULL;
			sk2->socket=NULL;
			newsock->data = NULL;
			return(-ERESTARTSYS);
		}
	}
	sti();
	
	if (sk2->state != TCP_ESTABLISHED && sk2->err > 0) 
	{
		err = -sk2->err;
		sk2->err=0;
		sk2->dead=1;	/* ANK */
		destroy_sock(sk2);
		newsock->data = NULL;
		return(err);
	}
	// 设置sock为已经建立连接状态
	newsock->state = SS_CONNECTED;
	return(0);
}

可以看到inet_accept函数的两个核心逻辑是
1 sk1->prot->accept
2 interruptible_sleep_on

 
static struct sock *tcp_accept(struct sock *sk, int flags)
{
	struct sock *newsk;
	struct sk_buff *skb;
  
  /*
   * We need to make sure that this socket is listening,
   * and that it has something pending.
   */

	if (sk->state != TCP_LISTEN) 
	{
		sk->err = EINVAL;
		return(NULL); 
	}

	/* Avoid the race. */
	cli();
	sk->inuse = 1;
	// 从sock的receive_queue队列摘取已建立连接的节点,
	while((skb = tcp_dequeue_established(sk)) == NULL) 
	{	
		// 没有已经建立连接的节点,但是设置了非阻塞模式,直接返回
		if (flags & O_NONBLOCK) 
		{
			sti();
			release_sock(sk);
			sk->err = EAGAIN;
			return(NULL);
		}

		release_sock(sk);
		//阻塞进程,如果后续建立了连接,则进程被唤醒的时候,就会跳出while循环
		interruptible_sleep_on(sk->sleep);
		if (current->signal & ~current->blocked) 
		{
			sti();
			sk->err = ERESTARTSYS;
			return(NULL);
		}
		sk->inuse = 1;
  	}
	sti();

	/*
	 *	Now all we need to do is return skb->sk. 
	 */
	// 拿到一个新的sock结构,由建立连接的时候创建的
	newsk = skb->sk;

	kfree_skb(skb, FREE_READ);
	// 队列节点数减一
	sk->ack_backlog--;
	release_sock(sk);
	// 返回新的sock结构体
	return(newsk);
}

interruptible_sleep_on的核心代码是

// struct wait_queue wait = { current, NULL };p就是socket中的wait,下面的代码就是把当前进程挂到wait队列里
if (!*p) {
	wait->next = wait;
	*p = wait;
} else {
	wait->next = (*p)->next;
	(*p)->next = wait;
}

上面的全部说明了如果调用accept的时候,如果已经有已经完成了连接的节点,则直接返回一个节点,否则就进入阻塞。
接下分析一下什么会被唤醒,在tcp_rcv中有一个分支

if(sk->state==TCP_SYN_SENT){
		if(th->ack)
		{
			if(!tcp_ack(sk,th,saddr,len))
			{
				//...
			}
		}
	}

tcp_ack中有一个分支

if(sk->state==TCP_SYN_RECV)
	{
		tcp_set_state(sk, TCP_ESTABLISHED);
		tcp_options(sk,th);
		sk->dummy_th.dest=th->source;
		sk->copied_seq = sk->acked_seq;
		if(!sk->dead)
			sk->state_change(sk);
		if(sk->max_window==0)
		{
			sk->max_window=32;	/* Sanity check */
			sk->mss=min(sk->max_window,sk->mtu);
		}
	}

其中state_change对应的代码是

static void def_callback1(struct sock *sk)
{
	if(!sk->dead)
		wake_up_interruptible(sk->sleep);
	// 获取sleep队列中的所有进程,sleep即socket的wait,即inode的wait
}

最后accept函数返回。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值