先看总体的流程
可以看到在系统调用中可以看到两个 一个accept一个accept4两个的原型如下:
int accept (int sockfd, struct sockaddr *addr, socklen_t *addrlen);
int accept4(int sockfd, struct sockaddr *addr,socklen_t *addrlen, int flags);
两个的区别在于accept4多了个flags参数那么它可以设置如下值:
SOCK_NONBLOCK -->在file描述符设置非阻塞模式
SOCK_CLOEXEC -->在file描述符设置执行close-on-exec标志
在2.6.32此版本上就这两个值,传入其他值会出错,若flag设置为0则accept4和accept的效果是一样的
对于sys_accep则直接调用sys_accept4传入的flag参数为0
SYSCALL_DEFINE3(accept, int, fd, struct sockaddr __user *, upeer_sockaddr,
int __user *, upeer_addrlen)
{
return sys_accept4(fd, upeer_sockaddr, upeer_addrlen, 0);
}
下面着重介绍下sys_accept4的实现
大致所做的工作为:
(1)首选根据fd超找对应的监听套接字socket{}结构
(2)根据监听socket{}创建一个通信用的新socket{}
(3)给新通信socket{}分配一个虚拟文件系统使用的file{}结构体和一个文件描述符fd
(4)将新的socket{}与新分配的file{}进行绑定
(5)调用inet层的inet_accept
(6)若应用层想获得对端IP将对端IP拷贝到用户空间传出
(7)最后将file{}和文件描述符fd进行绑定
(8)返回用于通信的文件描述符fd
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;
//只有在使用accept4的时候才会使用flag标志
/*
flags 是 0,那么 accept4() 与 accept() 功能一样
SOCK_NONBLOCK 在新打开的文件描述符设置 O_NONBLOCK 标记 非阻塞socket
SOCK_CLOEXEC 在新打开的文件描述符里设置 close-on-exec (FD_CLOEXEC) 标记
*/
//检测是否使用了除SOCK_CLOEXEC和SOCK_NONBLOCK以外的标记
if (flags & ~(SOCK_CLOEXEC | SOCK_NONBLOCK))
return -EINVAL;
//传入的flag设置O_NONBLOCK标志非组撒模式
if (SOCK_NONBLOCK != O_NONBLOCK && (flags & SOCK_NONBLOCK))
flags = (flags & ~SOCK_NONBLOCK) | O_NONBLOCK;
//根据fd查找socket{}结构体
sock = sockfd_lookup_light(fd, &err, &fput_needed);
if (!sock)
goto out;
err = -ENFILE;
//新建一个用于数据通信的socket{}结构,从这可知道,通信用的socket和监听用的socket不是同一个
if (!(newsock = sock_alloc()))
goto out_put;
//将流类型和inet层的操作集复制给新的socket{}
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 = sock_alloc_fd(&newfile, flags & O_CLOEXEC);
if (unlikely(newfd < 0))//分配失败在下面释放新创建的socket{}
{
err = newfd;
sock_release(newsock);
goto out_put;
}
//将新创建的socket{}和file{}进行关联 即将file->private_data=newsock
//socket->file=file
err = sock_attach_fd(newsock, newfile, flags & O_NONBLOCK);
if (err < 0)
goto out_fd_simple;
//与LSM安全模块相关,提供开发者一个机会可以来对accept系统调用进行控制
err = security_socket_accept(sock, newsock);
if (err)
goto out_fd;
//调用inet层的inet_stream_ops{}->inet_accept
err = sock->ops->accept(sock, newsock, sock->file->f_flags);
if (err < 0)
goto out_fd;
if (upeer_sockaddr)//应用层想获得对端的IP地址
{
//调用inet层的inet_stream_ops{}->inet_getname
if (newsock->ops->getname(newsock, (struct sockaddr *)&address,
&len, 2) < 0) {
err = -ECONNABORTED;
goto out_fd;
}
//将获得的地址复制到用户地址空间
err = move_addr_to_user((struct sockaddr *)&address,
len, upeer_sockaddr, upeer_addrlen);
if (err < 0)
goto out_fd;
}
/* File flags are not inherited via accept() unlike another OSes. */
//将新的fd与file{}进行关联
fd_install(newfd, newfile);
err = newfd;
out_put:
fput_light(sock->file, fput_needed);
out:
return err;
out_fd_simple:
sock_release(newsock);
put_filp(newfile);
put_unused_fd(newfd);
goto out_put;
out_fd:
fput(newfile);
put_unused_fd(newfd);
goto out_put;
}