accept4()的一些东西
nginx中有用到accept4()
,但有什么用呢?
accept4()
不是标准的Linux扩展,在Linux 2.6.28之后才被支持,跟accept()
的区别仅仅在于对文件描述符的行为控制上。下面两个函数的原型:
1 | #include <sys/types.h> /* See NOTES */ |
可以看到两者的区别仅仅在于accept4()
有第四个参数flags,这个参数如果为0,就跟accept()
一样;下面的两个参数可以用按位OR来获取不同的行为:
SOCK_NONBLOCK
:为新打开的文件描述符设置O_NONBLOCK
标志位,这跟用fcntl()
设置的效果是一样的,区别就是用fcntl()
的话需要多调用个函数。
SOCK_CLOEXEC
: 为新打开的文件描述符设置FD_CLOEXEC
标志位,该标志位的作用是在进程使用fork()
加上execve()
的时候自动关闭打开的文件描述符。其实使用fcntl()
设置FD_CLOEXEC
标志位(也就是用open()
的时候设置的O_CLOEXEC
标志位)也能达到同样的效果,但跟fcntl()
有什么不同呢?在多线程环境中,如果使用fcntl()
会多出一步操作,这样就可能形成竞争;而使用accept4()
就可以直接在打开的文件描述符上设置,可以消除竞争的问题。(原则上该竞争在那些新建文件描述符的调用中都存在,所以很多linux的系统调用都做了类似的处理。)
可以知道两个函数最主要的区别其实是在SOCK_CLOEXEC
上,现在就可以知道nginx的accept4()
是干嘛用的了。其实nginx主要是多进程的,加上该函数并没有做多余的处理:
1 | #if (NGX_HAVE_ACCEPT4) |
只是设置了SOCK_NONBLOCK
标志位,而在其后又调用fcntl()
去设置O_NONBLOCK
标志位。另外,对新建的TCP套接字压根没用SOCK_CLOEXEC
,也是因为这些套接字都只是在一个进程中创建、使用和销毁,并未涉及到多进程。当然,nginx对那些打开的文件还是应用了FD_CLOEXEC
的。
参考: