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的。
参考: