connectfd = accept(sockfd, (struct sockaddr *)&client_addr, &addrlen);
接收新连接将产生一个新的socket描述符
内核系统调用
-
SYSCALL_DEFINE3(accept, int, fd, struct sockaddr __user *, upeer_sockaddr,
-
int __user *, upeer_addrlen)
-
{
-
return sys_accept4(fd, upeer_sockaddr, upeer_addrlen, 0);
-
}
-
SYSCALL_DEFINE4(accept4, int, fd, struct sockaddr __user *, upeer_sockaddr,
-
int __user *, upeer_addrlen, int, flags)
-
{
-
struct socket *sock, *newsock;
-
struct file *newfile;
-
int err, len, newfd, fput_needed;
-
struct sockaddr_storage address;
-
-
if (flags & ~(SOCK_CLOEXEC | SOCK_NONBLOCK))
-
return -EINVAL;
-
-
if (SOCK_NONBLOCK != O_NONBLOCK && (flags & SOCK_NONBLOCK))
-
flags = (flags & ~SOCK_NONBLOCK) | O_NONBLOCK;
-
-
sock = sockfd_lookup_light(fd, &err, &fput_needed); //根据socket描述符找到socket接口
-
if (!sock)
-
goto out;
-
-
err = -ENFILE;
-
newsock = sock_alloc(); //分配一个新的socket接口
-
if (!newsock)
-
goto out_put;
-
-
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);
-
-
newfd = get_unused_fd_flags(flags);
-
if (unlikely(newfd < 0)) {
-
err = newfd;
-
sock_release(newsock);
-
goto out_put;
-
}
-
newfile = sock_alloc_file(newsock, flags, sock->sk->sk_prot_creator->name); //为新的socket接口分配文件描述符,文件结构
-
if (IS_ERR(newfile)) {
-
err = PTR_ERR(newfile);
-
put_unused_fd(newfd);
-
sock_release(newsock);
-
goto out_put;
-
}
-
-
err = security_socket_accept(sock, newsock);
-
if (err)
-
goto out_fd;
-
// 调用各个协议族的accept函数,本地socket是unix_accept
-
err = sock->ops->accept(sock, newsock, sock->file->f_flags, false);
-
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;
-
}
-
-
/* File flags are not inherited via accept() unlike another OSes. */
-
-
fd_install(newfd, newfile);
-
err = newfd;
-
-
out_put:
-
fput_light(sock->file, fput_needed);
-
out:
-
return err;
-
out_fd:
-
fput(newfile);
-
put_unused_fd(newfd);
-
goto out_put;
-
}
accept()是在一个套接口接受的一个连接。accept()是c语言中网络编程的重要的函数,本函数从s的等待连接队列中抽取第一个连接,创建一个与s同类的新的套接口并返回句柄。如果队列中无等待连接,且套接口为阻塞方式,则accept()阻塞调用进程直至新的连接出现。如果套接口为非阻塞方式且队列中无等待连接,则accept()返回一错误代码。已接受连接的套接口不能用于接受新的连接,原套接口仍保持开放。 accept会新生成一个socket接口,相当于新生成了一条数据管道,这条管道上的数据与其他连接到服务器的socket是互不影响的,而SOCK_DGRAM类型的socket,都是发送到一条通道的就会相互影响,后续会讨论这个问题.
-
static int unix_accept(struct socket *sock, struct socket *newsock, int flags,
-
bool kern)
-
{
-
struct sock *sk = sock->sk;
-
struct sock *tsk;//注意这里是两个sock结构
-
struct sk_buff *skb;
-
int err;
-
-
err = -EOPNOTSUPP;
-
if (sock->type != SOCK_STREAM && sock->type != SOCK_SEQPACKET)
-
goto out;
-
-
err = -EINVAL;
-
if (sk->sk_state != TCP_LISTEN)
-
goto out;
-
-
/* If socket state is TCP_LISTEN it cannot change (for now...),
-
* so that no locks are necessary.
-
*/
-
skb = skb_recv_datagram(sk, 0, flags&O_NONBLOCK, &err);//尝试接收数据
-
if (!skb) {
-
/* This means receive shutdown. */
-
if (err == 0)
-
err = -EINVAL;
-
goto out;
-
}
-
-
tsk = skb->sk;
-
skb_free_datagram(sk, skb);
-
wake_up_interruptible(&unix_sk(sk)->peer_wait);
-
-
/* attach accepted sock to socket */
-
unix_state_lock(tsk);
-
newsock->state = SS_CONNECTED;
-
unix_sock_inherit_flags(sock, newsock);
-
sock_graft(tsk, newsock); //新生成的socket与接收到的skb->sk传输控制块做关联
-
unix_state_unlock(tsk);
-
return 0;
-
-
out:
-
return err;
-
}
accept调用skb_recv_datagram()从连接队列中取数据,
-
struct sk_buff *skb_recv_datagram(struct sock *sk, unsigned int flags,
-
int noblock, int *err)
-
{
-
int peeked, off = 0;
-
-
return __skb_recv_datagram(sk, flags | (noblock ? MSG_DONTWAIT : 0),
-
&peeked, &off, err);
-
}
如果flags设置socket是非阻塞的,__skb_recv_datagram设置MSG_DONTWAIT,在没有新连接到达时直接返回,并不阻塞
-
struct sk_buff *__skb_recv_datagram(struct sock *sk, unsigned int flags,
-
int *peeked, int *off, int *err)
-
{
-
struct sk_buff_head *queue = &sk->sk_receive_queue; //sock接收队列
-
struct sk_buff *skb, *last;
-
unsigned long cpu_flags;
-
long timeo;
-
/*
-
* Caller is allowed not to check sk->sk_err before skb_recv_datagram()
-
*/
-
int error = sock_error(sk);
-
-
#ifdef DHLI_DEBUG
-
if (error) {
-
if (strstr(current->comm, "TDA")) {
-
printk("sock error occur\n");
-
}
-
}
-
#endif
-
-
if (error)
-
goto no_packet;
-
-
timeo = sock_rcvtimeo(sk, flags & MSG_DONTWAIT); //获取超时时间,如果设置了MSG_DONTWAIT则返回0
-
-
do {
-
/* Again only user level code calls this function, so nothing
-
* interrupt level will suddenly eat the receive_queue.
-
*
-
* Look at current nfs client by the way...
-
* However, this function was correct in any case. 8)
-
*/
-
int _off = *off;
-
-
last = (struct sk_buff *)queue;
-
spin_lock_irqsave(&queue->lock, cpu_flags);
-
skb_queue_walk(queue, skb) {
-
last = skb;
-
*peeked = skb->peeked;
-
if (flags & MSG_PEEK) {
-
if (_off >= skb->len && (skb->len || _off ||
-
skb->peeked)) {
-
_off -= skb->len;
-
continue;
-
}
-
-
skb = skb_set_peeked(skb);
-
error = PTR_ERR(skb);
-
if (IS_ERR(skb))
-
goto unlock_err;
-
-
atomic_inc(&skb->users);
-
} else
-
__skb_unlink(skb, queue);
-
-
spin_unlock_irqrestore(&queue->lock, cpu_flags);
-
*off = _off;
-
return skb;
-
}
-
spin_unlock_irqrestore(&queue->lock, cpu_flags);
-
//如果接收队列中没有消息存在,根据是否设置CONFIG_NET_RX_BUSY_POLL选项运行两种等待方案,忙等待;如果没有设置该选项,调用wait_for_more_packets,它会让进程睡眠
-
if (sk_can_busy_loop(sk) &&
-
sk_busy_loop(sk, flags & MSG_DONTWAIT))
-
continue;
-
-
/* User doesn't want to wait */
-
error = -EAGAIN;
-
if (!timeo)
-
goto no_packet;
-
-
} while (!wait_for_more_packets(sk, err, &timeo, last));
-
-
return NULL;
-
-
unlock_err:
-
spin_unlock_irqrestore(&queue->lock, cpu_flags);
-
no_packet:
-
*err = error;
-
return NULL;
-
}
执行wait_for_more_packets,进程将睡眠
阅读(2118) | 评论(0) | 转发(0) |