问题描述
我正在学习用于编写 linux 设备驱动程序的阻塞 I/O 函数,我想知道 ERESTARTSYS
的用途是什么.考虑以下几点:
I'm learning about the blocking I/O functions for writing linux device driver and I'm wondering what is the usage of ERESTARTSYS
. Consider the following:
全局变量:
wait_queue_head_t my_wait_q_head;
int read_avail = 0;
设备初始化():
device_init() :
init_waitqueue_head(&my_wait_q_head);
device_read():
device_read():
printk("I'm inside driver read!
");
wait_event_interruptible(&my_wait_q_head, read_avail != 0);
printk("I'm awaken!
");
device_write():
device_write():
read_avail = 1;
wake_up_interruptible(&my_wait_q_head);
当我从用户空间调用 read()
时,命令提示符会挂起,直到我按预期调用 write()
.printk
消息也相应地出现在 dmesg
中.但是,我看到一些驱动程序是这样写的:
When I call the read()
from user space, the command prompt hang until I call the write()
as expected. The printk
messages appear accordingly as well in dmesg
. However, I'm seeing some of the drivers written like this :
另一个版本的 device_read():
Another version of device_read():
printk("I'm inside driver read!
");
if(wait_event_interruptible(&my_wait_q_head, read_avail != 0))
{return -ERESTARTSYS;}
printk("I'm awaken!
");
我在用户空间用同样的方法测试了第二个版本的device_read()
,结果完全一样,那么,ERESTARTSYS有什么用呢?
I tested the second version of device_read()
using the same method in user space, and the result is exactly the same, so, what's the use of ERESTARTSYS?
p/s:我读过关于这本书的 Linux 设备驱动程序,但我不明白,有人可以举个例子来说明一下吗?:
p/s: I've read the book Linux Device Driver on this but I don't get it, can someone give an example to eleborate?:
一旦我们过了那个电话,有什么东西把我们吵醒了,但我们没有知道什么.一种可能性是进程收到了信号.这包含 wait_event_interruptible 调用检查的 if 语句对于这种情况.此声明可确保正确和预期的反应到信号,这可能是负责唤醒过程(因为我们处于可中断的睡眠状态).如果一个信号有到达并且没有被进程阻塞,正确的行为是让内核的上层处理事件.到为此,驱动程序将 -ERESTARTSYS 返回给调用者;这个值是由虚拟文件系统 (VFS) 层内部使用,它要么重新启动系统调用或将 -EINTR 返回到用户空间.我们使用处理每次读取的信号处理的相同类型的检查和编写实现.
来源:http://www.makelinux.net/ldd3/chp-6-sect-2
推荐答案
-ERESTARTSYS
与可重启系统调用的概念有关.可重启的系统调用是一种在出现中断时可以由内核透明地重新执行的系统调用.
-ERESTARTSYS
is connected to the concept of a restartable system call. A restartable system call is one that can be transparently re-executed by the kernel when there is some interruption.
例如,在系统调用中休眠的用户空间进程可以获得一个信号,执行一个处理程序,然后当处理程序返回时,它似乎回到内核并继续在原始系统调用上休眠.
For instance the user space process which is sleeping in a system call can get a signal, execute a handler, and then when the handler returns, it appears to go back into the kernel and keeps sleeping on the original system call.
使用 POSIX sigaction
APISA_RESTART
标志,进程可以安排与信号关联的重启行为.
Using the POSIX sigaction
API's SA_RESTART
flag, processes can arrange the restart behavior associated with signals.
在Linux内核中,当系统调用上下文中阻塞的驱动程序或其他模块检测到任务因信号而被唤醒时,它可以返回-EINTR
.但是 -EINTR
会冒泡到用户空间并导致系统调用返回 -1
并将 errno
设置为 EINTR
>.
In the Linux kernel, when a driver or other module blocking in the context of a system call detects that a task has been woken because of a signal, it can return -EINTR
. But -EINTR
will bubble up to user space and cause the system call to return -1
with errno
set to EINTR
.
如果您返回 -ERESTARTSYS
,则表示您的系统调用是可重新启动的.ERESTARTSYS
代码不一定会在用户空间中看到.它要么被转换为 -1
返回值并将 errno
设置为 EINTR
(然后,显然,在用户空间中看到),或者被转换进入系统调用重新启动行为,这意味着您的系统调用将使用相同的参数再次调用(对用户空间进程的一部分不采取任何行动:内核通过将信息隐藏在特殊的重新启动块中来做到这一点).
If you return -ERESTARTSYS
instead, it means that your system call is restartable. The ERESTARTSYS
code will not necessarily be seen in user space. It either gets translated to a -1
return and errno
set to EINTR
(then, obviously, seen in user space), or it is translated into a system call restart behavior, which means that your syscall is called again with the same arguments (by no action on part of the user space process: the kernel does this by stashing the info in a special restart block).
注意相同参数"的明显问题;上一段:有些系统调用不能用相同的参数重新启动,因为它们不是幂等的!例如,假设有一个像 nanosleep 这样的睡眠调用,持续 5.3 秒.它会在 5 秒后中断.如果它天真地重新启动,它将再休眠 5.3 秒.它必须仅在剩余的 0.3 秒内将新参数传递给重新启动的睡眠调用;即改变重启块的内容.有一种方法可以做到这一点:将不同的参数填充到任务的重启块中并使用 -ERESTART_RESTARTBLOCK
返回值.
Note the obvious problem with "same arguments" in the previous paragraph: some system calls can't be restarted with the same parameters, because they are not idempotent! For instance, suppose there is a sleep call like nanosleep, for 5.3 seconds. It gets interrupted after 5 seconds. If it restarts naively, it will sleep for another 5.3 seconds. It has to pass new parameters to the restarted call to sleep for only the remaining 0.3 seconds; i.e. alter the contents of the restart block. There is a way to do that: you stuff different arguments into the task's restart block and use the -ERESTART_RESTARTBLOCK
return value.
解决第二个问题:有什么区别?为什么不直接编写读取例程而不检查返回值并返回-ERESTARTSYS
?好吧,因为在唤醒是由于信号的情况下这是不正确的!您是否希望在信号到达时返回读取的 0 个字节?这可能会被用户空间误解为数据结束.这种问题不会出现在不使用信号的测试用例中.
To address the second question: what's the difference? Why not just write the read routine without checking the return value and returning -ERESTARTSYS
? Well, because that is incorrect in the case that the wakeup is due to a signal! Do you want a read to return 0 bytes read whenever a signal arrives? That could be misinterpreted by user space as end of data. This kind of problem won't show up in test cases that don't use signals.
这篇关于ERESTARTSYS 在编写 linux 驱动程序时使用了什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!