2.6.32系统调用accept内核执行流程(1)

先看总体的流程

可以看到在系统调用中可以看到两个 一个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;
}

猜你喜欢

转载自blog.csdn.net/yldfree/article/details/81542658