本文主要记录服务器的 IO 模型的类型(从多路复用,异步 IO 讲到 Proactor Reactor 模型),包括 Real World nginx 和 apache ,kafka 等分析,配备自洽的所有知识点方便自己复习。读者应该具备一些 linux 系统知识。
先把 APUE 第八章进程控制复习一遍吧
启动与复制
After a successful return, the old and new file descriptors may
be used interchangeably. Since the two file descriptors refer to
the same open file description, they share file offset and file
status flags;From
login 原理
守护进程脱离控制终端
守护进程 daemon process
The daemon() function is for programs wishing to detach themselves from the controlling terminal and run in the background as system daemons.
1、终端关闭时,该信号被发送到session首进程以及作为job提交的进程(即用 & 符号提交的进程);
2、session首进程退出时,该信号被发送到该session中的前台进程组中的每一个进程;
3、若父进程退出导致进程组成为孤儿进程组,且该进程组中有进程处于停止状态(收到SIGSTOP或SIGTSTP信号),该信号会被发送到该进程组中的每一个进程。
自启动
rc: /etc/rc5.d /etc/rc4.d /etc/rc6.d /etc/rc0.d /etc/rc3.d /etc/rc1.d /etc/rc2.d
25 章了讲 SIGIO 的用法的。现在快速过了他。
不同 IO 辨析(这个是理解 proactor 的关键)
UDP 事件:
同步问题
Any signals specified in act->sa_mask when registering the
handler with sigprocmask(2) are added to the thread's
signal mask. The signal being delivered is also added to
the signal mask, unless SA_NODEFER was specified when
registering the handler. These signals are thus blocked
while the handler executes.From
每个线程都有自己独立的signal mask,但所有线程共享进程的signal action。这意味着,你可以在线程中调用pthread_sigmask(不是sigmask)来决定本线程阻塞哪些信号。但你不能调用sigaction来指定单个线程的信号处理方式。如果在某个线程中调用了sigaction处理某个信号,那么这个进程中的未阻塞这个信号的线程在收到这个信号都会按同一种方式处理这个信号。另外,注意子线程的mask是会从主线程继承而来的。
然后下面是一些总结。
时间关系对 raw socket DL 层的没时间看了,虽然我的确很感兴趣那些可以用来抓包和做 arp 欺诈等功能。书本还讲了 ping 和 traceroute 怎么写,这下知道面试问这两个原理哪来的了。。。
首先总结我们前面做了些什么工作,(这个说的是服务器还是客户端呢?好像都有)
最开始 127.0.0.1也是走 tcp协议栈的,很多冗余的东西,而 AF_UNIX 更像跨进程管道,因此会快很多。但是后面 Linux持续优化,给 127.0.0.1加了很多 fast path。使得本机内部的tcp性能和 AF_UNIX差不多,也和管道一个数量级了,所以今天 Linux下,保证代码的简单性,没有非要用 AF_UNIX的必要。
链接:https://www.zhihu.com/question/29910140/answer/46640164
区分一下这两种方案:
因为 Epoll 原理线索另一篇博客讲了(没有 epoll 的上下文信息下面的内容应该看不懂)
这里因为只能看游双那本,这部分记得有点像 specification 的笔记了,但是没办法,这个东西实在是我觉得就是一个花哨名字而已。结合上面 CS 架构小节的内容,对于 preforking 方案已经很了解了。
这里 Reactor 就是简单的 preforking 方案(或者 threading 也行)然后让主线程 epoll listen descriptor 和已经连接的 descriptor,如果有连接到达或者 fd 有活动,就引发一个调度(回想消息队列和 epoll wait,这里做思想实验是我们只需要唤醒一个 worker 来处理就行了,如果是已经连接的,那么唤醒那个特定的 worker),并且把 fd 交给线程进行业务逻辑。使用消息队列调度工作线程。其实就是说让 epoll 来管理所有的阻塞事件,listend 会阻塞,交给 epoll wait!cond 会有阻塞,交给 epoll_wait ! (包括可读了,读完之后要阻塞写怎么办?交给 epoll_wait !)。其实就是通过避免 OS 的 epoll 唤醒链太复杂,所以都把阻塞唤醒放在 master 线程(进程)上,从而得到高性能。
而 Proactor 就是说让 worker 只进行业务逻辑,所有 IO 都通过主线程调用异步 IO 或者直接调用异步 IO 实现,主线程(进程)只需要 epoll listen descriptor(因为已连接的 d 委托给了异步 IO,自然就不需要阻塞了)。我认为为了更好地理解 Proactor,应该先看下面的同步模拟 Proactor(IO 全部在 master 上)。其实就是说让 kernel 的 asynchronous IO 来管理所有的阻塞事件,要读东西可能阻塞怎么办?让内核读完了再递交上来! 要写东西可能阻塞怎么办?让内核写完了再通知上来!(然而对于 listen d 我们必须要 accept 他才能进行 r/w ,所以这个必须要让 master 线程来阻塞等待!)
下面再详细分析一下:
反应堆模型,master 负责 IO 复用,epoll 套接字,如果有事件来了就加入消息队列通知 worker。worker 会注册写事件等待,然后同样事件到达了就加入消息队列等待工作线程响应。下面的流程图特别的清楚。