《电子技术应用》
您所在的位置:首页 > 嵌入式技术 > 其他 > Linux教学—— 3 分钟快速了解信号驱动式 IO

Linux教学—— 3 分钟快速了解信号驱动式 IO

2022-08-12
来源:FPGA之家
关键词: Linux 驱动式IO

  以下是正文:

  一、Linux 的 5 种 IO 模型

  二、如何使用信号驱动式 I/O?

  三、内核何时会发送 "IO 就绪" 信号?

  四、最简单的示例

  五、扩展知识

  一、Linux 的 5 种 IO 模型

  阻塞式 I/O:

  系统调用可能因为无法立即完成而被操作系统挂起,直到等待的事件发生为止。

微信图片_20220812143203.png

  非阻塞式 I/O (O_NONBLOCK):

  系统调用则总是立即返回,而不管事件是否已经发生。

微信图片_20220812143228.png

  I/O 复用 (select、poll、epoll):

  通过 I/O 复用函数向内核注册一组事件,内核通过 I/O 复用函数把其中就绪的事件通知给应用程序。

微信图片_20220812143308.png

  信号驱动式 I/O (SIGIO):

  为一个目标文件描述符指定宿主进程,当文件描述符上有事件发生时,SIGIO 的信号处理函数将被触发,然后便可对目标文件描述符执行 I/O 操作。

  微信图片_20220812143329.png

  异步 I/O (POSIX 的 aio_ 系列函数):

  异步 I/O 的读写操作总是立即返回,而不论 I/O 是否是阻塞的,真正的读写操作由内核接管。

  微信图片_20220812143450.png

  思考一下,什么时候应该选择何种 I/O 模型?为何要这么选择?

  下面重点关注信号驱动式 I/O 这一模型,其他模型可查阅文末参考书籍。

  二、如何使用信号驱动式 I/O?

  一般通过如下 6 个步骤来使用信号驱动式 I/O 模型。

  1> 为通知信号安装处理函数。

  通过 sigaction() 来完成:

  int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);

  默认情况下,这个通知信号为 SIGIO。

  2> 为文件描述符的设置属主。

  通过 fcntl() 的 F_SETOWN 操作来完成:

  fcntl(fd, F_SETOWN, pid)

  属主是当文件描述符上可执行 I/O 时,会接收到通知信号的进程或进程组。

  pid 为正整数时,代表了进程 ID 号。

  pid 为负整数时,它的绝对值就代表了进程组 ID 号。

  3> 使能非阻塞 I/O。

  通过 fcntl() 的 F_SETFL 操作来完成:

  flags = fcntl(fd, F_GETFL);

  fcntl(fd, F_SETFL, flags | O_NONBLOCK);

  4> 使能信号驱动 I/O。

  通过 fcntl() 的 F_SETFL 操作来完成:

  flags = fcntl(fd, F_GETFL);

  fcntl(fd, F_SETFL, flags | O_ASYNC);

  5> 进程等待 "IO 就绪" 信号的到来。

  当 I/O 操作就绪时,内核会给进程发送一个信号,然后调用在第 1 步中安装好的信号处理函数。

  6> 进程尽可能多地执行 I/O 操作。

  循环执行 I/O 系统调用直到失败为止,此时错误码为 EAGAIN 或 EWOULDBLOCK。

  原因:

  信号驱动 I/O 提供的是边缘触发通知,即只有当 I/O 事件发生时我们才会收到通知,

  且当文件描述符收到 I/O 事件通知时,并不知道要处理多少 I/O 数据。

  三、内核何时会发送 "IO 就绪" 信号?

  对于不同类型的文件描述符,情况不一样。

  1> 终端

  对于终端,当有新的输入时会会产生信号。

  2> 管道和 FIFO

  对于读端,下列情况会产生信号:

  数据写入到管道中;管道的写端关闭;

  对于写端,下列情况会产生信号:

  对管道的读操作增加了管道中的空余空间大小。管道的读端关闭;

  3> 套接字

  对于 UDP 套接字,下列情况会产生信号:

  数据报到达套接字;套接字上发生异步错误;

  对于 TCP 套接字,信号驱动式 I/O 近乎无用。

  太多情况都会产生信号,而我们又无法得知事件类型,因此这里就不再列举其产生信号的情况。

  四、最简单的示例

  信号处理函数:

  static volatile sig_atomic_t gotSigio = 0;

  static void handler(int sig)

  {

  gotSigio = 1;

  }

  主程序:

  int main(int argc, char *argv[])

  {

  int flags, j, cnt;

  struct termios origTermios;

  char ch;

  struct sigaction sa;

  int done;

  /* Establish handler */

  sigemptyset(&sa.sa_mask);

  sa.sa_flags = SA_RESTART;

  sa.sa_handler = handler;

  if (sigaction(SIGIO, &sa, NULL) == -1) {

  perror("sigaction()\n");

  exit(1);

  }

  /* Set owner process */

  if (fcntl(STDIN_FILENO, F_SETOWN, getpid()) == -1) {

  perror("fcntl() / F_SETOWN\n");

  exit(1);

  }

  /* Enable "I/O possible" signaling and make I/O nonblocking */

  flags = fcntl(STDIN_FILENO, F_GETFL);

  if (fcntl(STDIN_FILENO, F_SETFL, flags | O_ASYNC | O_NONBLOCK) == -1) {

  perror("fcntl() / F_SETFL\n");

  exit(1);

  }

  for (done = 0, cnt = 0; !done ; cnt++) {

  sleep(1);

  if (gotSigio) {

  gotSigio = 0;

  /* Read all available input until error (probably EAGAIN)

  or EOF */

  while (read(STDIN_FILENO, &ch, 1) > 0 && !done) {

  printf("cnt=%d; read %c\n", cnt, ch);

  done = ch == '#';

  }

  }

  }

  exit(0);

  }

  运行效果:

  ./build/sigio

  a

  cnt=0; read a

  cnt=0; read

  abc

  cnt=4; read a

  cnt=4; read b

  cnt=4; read c

  cnt=4; read

  #

  cnt=7; read #

  该程序会先使能信号驱动 IO,然后循环执行计数操作。

  当有 IO 就绪信号到来时,会去终端读取数据并打印出来,然后继续执行计数操作。

  五、扩展知识

  I/O 多路复用 、信号驱动 I/O 以及 epoll 机制可用于监视多个文件描述符。

  它们并不实际执行 I/O 操作,当某个文件描述符处于就绪态,仍需采用传统的 I/O 系统调用来完成 I/O 操作。

  相比 I/O 多路复用,当监视大量的文件描述符时信号驱动 I/O 有着显著的性能优势,原因是内核能够帮进程记录了正在监视的文件描述符列表。

  信号驱动 I/O 的缺点:

  信号的处理流程较为复杂;

  无法指定需要监控的事件类型。

  Linux 特有的 epoll 是一个更好的选择。

  六、相关参考

  UNIX 网络编程卷1

  6.2 I/O模型25 信号驱动式I/O

  Linux-UNIX 系统编程手册

  63 其他备选的I/O模型

  Linux 高性能服务器编程

  8.3 I/O 模型

  Linux 多线程服务端编程_使用muduo C++网络库

  7.4.1 muduo的IO模型

  更多信息可以来这里获取==>>电子技术应用-AET<<

微信图片_20210517164139.jpg

本站内容除特别声明的原创文章之外,转载内容只为传递更多信息,并不代表本网站赞同其观点。转载的所有的文章、图片、音/视频文件等资料的版权归版权所有权人所有。本站采用的非本站原创文章及图片等内容无法一一联系确认版权者。如涉及作品内容、版权和其它问题,请及时通过电子邮件或电话通知我们,以便迅速采取适当措施,避免给双方造成不必要的经济损失。联系电话:010-82306118;邮箱:aet@chinaaet.com。