我了解硬件中断的工作原理。我有一个关于 I/O 的基本问题,并将使用键盘和按键产生的字符作为问题的背景。
一般来说,进程是否倾向于使用轮询(进程轮询内核)或信号(内核通知进程)来从内核中临时存储字符的缓冲区访问字符?
我了解如何通过简单的循环进行轮询。
举一个更具体的例子,当我输入这个问题时,浏览器是否正在轮询内核(并且,当我单击文本字段时轮询处于活动状态),或者内核是否在管理此浏览器窗口文本输入字段的过程中使用信号并触发信号处理程序?
答案1
这实际上取决于操作系统,但据我所知,Windows 和所有类 Unix 系统主要使用轮询(即进程休眠等待阻塞系统调用返回的轮询 - 而不是进程不断重复检查的轮询)。信号使用很少见。
在最基本的情况下,如果进程想要从单个文件句柄同步读取,它将调用 read() 或 ReadFile(),并且系统调用将阻塞,直到数据可用。(它可能会创建多个线程,每个线程都在等待不同文件句柄上的 read(),但这种情况并不常见。)
对于多个源,进程将调用 poll() 或 WaitForMultipleObjects(),这也会阻塞直到发生某个事件并最终返回进程可以从中 read() 的给定文件句柄之一。
处理大量源时,首选 epoll 或 kqueue - 我不确定 Windows 上的等效方案;可能是 IOCP 或 Overlapped I/O。(另请参阅:Linux 上的新 io_uring API 和 Windows 上几乎相同的 IoRing。)
大多数大型程序都是围绕一个中央“事件循环”构建的,该循环将轮询所有源,处理一些事件,然后再次轮询,重复。例如,当您在 Linux 上使用 Web 浏览器时,会涉及两个程序:
显示服务器(例如 X11 的 Xorg 或 Wayland 的 GNOME Shell)具有一个事件循环,它通过 /dev/input/* 设备(来自内核)轮询用户输入以及通过套接字轮询应用程序 X11 或 Wayland 事件(来自图形应用程序的绘制命令);
浏览器有一个事件循环,它通过其 X11 或 Wayland 套接字(来自显示服务器)以及来自开放套接字的网络流量(例如活动 HTTP 下载)轮询用户输入。
确实存在一些旧的“异步 I/O”API 使用信号(我相信是来自 4.2BSD 的那个?);设置 FASYNC 标志将导致发送 SIGIO;但总体而言,这种情况似乎非常罕见。(如果一个进程什么都不做,只是等待接收信号,那么它不妨只等待阻塞 poll() 返回,而不需要任何信号处理程序的麻烦——尤其是不用担心重入或信号队列溢出……)
事实上,在 Linux 和 FreeBSD 上,事件循环框架通常选择通过轮询接收信号– 程序屏蔽所有信号的正常传递,然后使用 signalfd() 创建可以轮询的文件句柄,当出现信号时,struct siginfo
进程可以在空闲时从 signalfd 句柄读取信号(事件循环负责调用信号处理程序)。同样,计时器事件可以通过 timerfd 传递,而无需依赖 SIGALRM 等。