代码语言:c复制
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 socketsn");
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函数
代码语言:javascript复制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
代码语言:javascript复制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的核心代码是
代码语言:javascript复制// 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中有一个分支
代码语言:javascript复制if(sk->state==TCP_SYN_SENT){
if(th->ack)
{
if(!tcp_ack(sk,th,saddr,len))
{
//...
}
}
}
tcp_ack中有一个分支
代码语言:javascript复制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对应的代码是
代码语言:javascript复制static void def_callback1(struct sock *sk)
{
if(!sk->dead)
wake_up_interruptible(sk->sleep);
// 获取sleep队列中的所有进程,sleep即socket的wait,即inode的wait
}
最后accept函数返回。