当我阅读一些有关 Nginx 的材料时,我注意到在单个套接字上接受传入连接的两种传统方法之一是:
在单个端口上运行的多线程服务器使用的第二种传统方法是拥有所有线程(或进程)
accept()
在以下形式的简单事件循环中对单个侦听套接字执行调用:while (1) { new_fd = accept(...); process_connection(new_fd); }
然后我注意到 Nginx 似乎也使用了这种方法。
如图所示,当
SO_REUSEPORT
未启用该选项时,单个侦听套接字会通知工作线程有关传入连接的信息,并且每个工作线程都会尝试获取连接。
阿金,在开源应用架构(第二卷):nginx,用关键字搜索页面accept
,那里写着:
如前所述,nginx 不会为每个连接生成进程或线程。反而,工作进程接受来自共享“监听”套接字的新请求,并在每个工作进程内执行高效的运行循环,以处理每个工作进程的数千个连接。nginx 中没有专门的仲裁或连接分配给工作人员;这项工作是由操作系统内核机制完成的。
所以我真的很震惊因为没有人告诉我在各个进程或线程之间接受侦听套接字是可以的并且不会导致竞争条件。
因为当提到使用共享资源时,我首先想到的是“该函数调用线程安全吗“?所以我谷歌了一下,在 StackOverflow 上发现了一个相关的问题。
公认的答案再次证明了这种行为,但它根本没有提供参考,并且评论下的人们仍在争论官方文件的定义。
那时,我在想给出的属性thread-safe
是不够的,因为它说的是多线程或进程 accept
在单个侦听套接字上。我需要比这更强大的东西。
于是我就来看看书Linux 编程接口, 在§5.1 原子性和竞争条件,它写道:
原子性是我们在讨论系统调用操作时会反复遇到的一个概念。各种系统调用操作都是原子执行的。我们的意思是内核保证操作中的所有步骤都完成而不会被其他进程或线程中断。
原子性对于成功完成某些操作至关重要。特别是,它使我们能够避免竞争条件(有时称为竞争危险)。竞争条件是指在共享资源上运行的两个进程(或线程)产生的结果以意想不到的方式取决于进程访问 CPU 的相对顺序的情况。
所以我需要的词/属性是大气的或者原子性。
所以我的问题是:
有没有权威的地方说多进程或多线程接受监听套接字是原子操作?
经过几个小时的搜索后,我在网上找不到任何参考资料。
答案1
https://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html
accept()
被列为“异步信号安全”功能。下面解释了所有异步信号安全函数都是线程安全的。为了线程安全列出了一些特定的异常,例如未锁定的 stdio 函数,并且accept()
是不是提及。
这里请注意,这accept
是一个系统调用。系统调用不是线程安全的,这是没有任何意义的,因为这意味着内核不是线程安全的!
内核必须是线程安全的,因此系统调用也必须是线程安全的。