事件驱动IO之信号驱动IO
接着上一篇讨论的 五种 IO 模型中 关于 事件驱动的 IO模型介绍,我们已经知道了 IO 多路复用是事件驱动的一种,接下来将继续介绍 事件驱动模型的 另外一种:信号驱动 IO
可以看到该模型中,只有 IO 执行到第二阶段阻塞了用户进程,而在第一阶段是没有阻塞的。
首先允许套接字进行信号驱动IO,这就意味着要设置套接字属性为 非阻塞并且异步,设置信号处理函数,进程继续运行并不阻塞。当数据准备好时,进程会收到一个 SIGIO 信号,可以在处理函数中调用 IO 操作函数处理数据。这种模型的优势在于等待数据到达(第一阶段)期间,进程可以继续执行,不被阻塞,免去了 select/epoll等多路复用方式的阻塞与轮询, 当有活跃套接字时,由注册的 hander 处理。
一、想要搞清楚 信号IO 的使用,我们需要回答好如下三个问题:
- 什么时候会产生信号呢?
- 产生的信号发给谁处理呢?
- 收到信号后如何处理呢?
(1.1)首先回答一个问题,这里缩小下讨论范围,只讨论网络IO过程中信号的是啥时候产生的?
》对于 UDP 协议而言,通常会在如下 两种 情况下内核会给用户空间的用户程序发送信号:
- 套接字收到了一个完整的数据包;
- 套接字发生了异步错误;
当我们在使用 UDP 套接字 异步 I/O 的时候,我们使用 recvfrom() 函数来读取数据报数据或者异步IO 错误信息。
》对于 TCP 协议而言,产生信息的地方就多啦:
在 TCP 连接中, SIGIO 信号将会在这个时候产生:
-
在一个监听某个端口的套接字上成功的建立了一个新连接;
-
一个断线的请求被成功的初始化;
-
一个断线的请求成功的结束;
-
套接字的某一个通道(发送通道或是接收通道)被关闭;
-
套接字接收到新数据;
-
套接字将数据发送出去;
-
发生了一个异步 I/O 的错误;
通过对比,我们可以发现,异步 I/O 几乎对 TCP 套接字而言没有什么作用,因为对于一个 TCP 套接字来说, SIGIO 信号发生的几率太高了,所以 SIGIO 信号并不能告诉我们
究竟发生了什么事情。
(1.2)产生的信号发给谁处理呢?
发给谁呢?当然要发给关心 SIGIO 信号的进程啦,发给张三李四王二麻子等不相关的进程可就不对咯。这里可以通过 fcntl 系统调用的 F_SETOWN 来设置。
F_SETOWN 的含义是:设置将要在文件描述符fd 上接收 SIGIO 或 SIGURG 事件信号的进程或进程组标识。一般产生该信号后由当前进程来处理,那么我们可以如下设置:
fcntl(sockfd, F_SETOWN, getpid()); // getpid() to get current process id
(1.3)收到信号如何处理呢?
收到信号后,我们注册的信号处理函数就会去处理。这里有 signal 和 sigaction 两种方式来处理,but 强烈不推荐 signal方式(!!!)。下面我们就来看看使用 sigaction方式
是如何注册信号处理函数的。
struct sigaction sigio_action;
sigio_action.sa_flag = 0;
sigio_action.sa_handler = do_sigio; // signal hander, the same to signal's param handler
sigaction(SIGIO, &sigio_action,