Linux 阻塞 I/O 实际上是如何工作的?

发布于 2024-08-31 07:13:07 字数 246 浏览 11 评论 0原文

在 Linux 中,当您进行阻塞 i/o 调用(例如 read 或accept)时,实际上会发生什么?

我的想法:进程从运行队列中取出,在某个等待队列上进入等待或阻塞状态。然后,当建立 TCP 连接(用于接受)或硬盘驱动器准备就绪或可以读取文件时,会引发硬件中断,让那些等待唤醒并运行的进程(在读取文件的情况下,如何linux 知道要唤醒哪些进程,因为可能有很多进程在等待不同的文件?)。或者,也许不是硬件中断,而是单个进程本身轮询以检查可用性。不确定,帮忙吗?

In Linux, when you make a blocking i/o call like read or accept, what actually happens?

My thoughts: the process get taken out of the run queue, put into a waiting or blocking state on some wait queue. Then when a tcp connection is made (for accept) or the hard drive is ready or something for a file read, a hardware interrupt is raised which lets those processes waiting to wake up and run (in the case of a file read, how does linux know what processes to awaken, as there could be lots of processes waiting on different files?). Or perhaps instead of hardware interrupts, the individual process itself polls to check availability. Not sure, help?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

很快妥协 2024-09-07 07:13:08

每个 Linux 设备的实现似乎都略有不同,并且随着添加了更安全/更快的内核功能,首选方式似乎每隔几个 Linux 版本都会有所不同,但通常:

  1. 设备驱动程序创建读取和读取
    为设备写入等待队列。
  2. 任何想要等待的进程线程
    对于 I/O 放置在适当的位置
    等待队列。当中断发生时
    处理程序唤醒一个或多个
    等待线程。 (显然
    线程不会立即运行,因为我们处于中断状态
    上下文,但被添加到
    内核的调度队列)。
  3. 当由内核调度时
    线程检查是否条件
    是正确的继续进行 - 如果不是
    它返回等待队列。

一个典型的例子(稍微简化):

在初始化时的驱动程序中:

    init_waitqueue_head(&readers_wait_q);

在驱动程序的读取函数中:

    if (filp->f_flags & O_NONBLOCK)
    {
        return -EAGAIN;
    }
    if (wait_event_interruptible(&readers_wait_q, read_avail != 0))
    {
        /* signal interrupted the wait, return */
        return -ERESTARTSYS;
    }
    to_copy = min(user_max_read, read_avail);
    copy_to_user(user_buf, read_ptr, to_copy);

然后中断处理程序只发出:

    wake_up_interruptible(&readers_wait_q);

请注意 wait_event_interruptible() 是一个宏,它隐藏了一个检查条件的循环 - read_avail != 0 在这种情况下 - 如果在条件不成立时被唤醒,则再次重复添加到等待队列。

如前所述,存在多种变体 - 主要的一个是,如果中断处理程序可能需要执行大量工作,那么它本身会执行最少的工作,并将其余部分推迟到工作队列或微线程(通常称为“下半部分”),正是这个会唤醒等待的线程。

有关更多详细信息,请参阅 Linux 设备驱动程序书籍 - pdf 版本可在此处获取:
http://lwn.net/Kernel/LDD3

Each Linux device seems to be implemented slightly differently, and the preferred way seems to vary every few Linux releases as safer/faster kernel features are added, but generally:

  1. The device driver creates read and
    write wait queues for a device.
  2. Any process thread wanting to wait
    for i/o is put on the appropriate
    wait queue. When an interrupt occurs
    the handler wakes up one or more
    waiting threads. (Obviously the
    threads don't run immediately as we are in interrupt
    context, but are added to the
    kernel's scheduling queue).
  3. When scheduled by the kernel the
    thread checks to see if conditions
    are right for it to proceed - if not
    it goes back on the wait queue.

A typical example (slightly simplified):

In the driver at initialisation:

    init_waitqueue_head(&readers_wait_q);

In the read function of a driver:

    if (filp->f_flags & O_NONBLOCK)
    {
        return -EAGAIN;
    }
    if (wait_event_interruptible(&readers_wait_q, read_avail != 0))
    {
        /* signal interrupted the wait, return */
        return -ERESTARTSYS;
    }
    to_copy = min(user_max_read, read_avail);
    copy_to_user(user_buf, read_ptr, to_copy);

Then the interrupt handler just issues:

    wake_up_interruptible(&readers_wait_q);

Note that wait_event_interruptible() is a macro that hides a loop that checks for a condition - read_avail != 0 in this case - and repeatedly adds to the wait queue again if woken when the condition is not true.

As mentioned there are a number of variations - the main one is that if there is potentially a lot of work for the interrupt handler to do then it does the bare minimum itself and defers the rest to a work queue or tasklet (generally known as the "bottom half") and it is this that would wake the waiting threads.

See Linux Device Driver book for more details - pdf available here:
http://lwn.net/Kernel/LDD3

久伴你 2024-09-07 07:13:08

实际上,该方法只会在文件准备好读取、数据位于套接字上、连接到达时返回...

为了确保它可以立即返回,您可能需要使用 Select 系统调用来查找准备好的文件描述符。

Effectivly the method will only returns when the file is ready to read, when data is on a socket, when a connection has arrived...

To make sure it can return immediatly you probably want to use the Select system call to find a ready file descriptor.

等数载,海棠开 2024-09-07 07:13:08

阅读以下内容: http://www.minix3.org/doc/

这是一个非常清晰的,非常容易理解的解释。它通常也适用于 Linux。

Read this: http://www.minix3.org/doc/

It's a very, clear, very easy to understand explanation. It generally applies to Linux, also.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文