第6章:字符设备驱动的高级操作4:The Return Value And The Predefined Commands

张开发
2026/4/21 17:12:41 15 分钟阅读
第6章:字符设备驱动的高级操作4:The Return Value And The Predefined Commands
The Return ValueThe implementation of ioctl is usually a switch statement based on the command number. But what should the default selectionbe when the command number doesn’t match a valid operation? The question is controversial. Several kernel functions return -EINVAL (“Invalid argument”), which makes sense because the command argument is indeed not a valid one. The POSIX standard, however, states that if an inappropriate ioctl command has been issued, then -ENOTTY should be returned.This error code is interpreted by the C library as “inappropriate ioctl for device,” which is usually exactly what the programmer needs to hear. It’s still pretty common, though, to return -EINVAL in response to an invalid ioctl command.ioctl 的实现通常是一个基于命令号的 switch 语句。但当命令号与合法操作不匹配时default 分支应当返回什么值这个问题一直存在争议。有不少内核函数会返回 -EINVAL“无效参数”这一做法看似合理因为传入的命令参数确实不合法。然而POSIX 标准规定若发起了不适用的 ioctl 命令则应返回 -ENOTTY。C 库会将该错误码解释为 “对设备执行了不恰当的 ioctl 操作”这通常正是程序员需要获知的提示信息。尽管如此在遇到非法 ioctl 命令时返回 -EINVAL 的做法依然十分常见。The Predefined CommandsAlthough the ioctl system call is most often used to act on devices, a few commands are recognized by the kernel. Note that these commands, when applied to your device, are decoded before your own file operations are called. Thus, if you choosethe same number for one of your ioctl commands, you won’t ever see any request for that command, and the application gets something unexpected because of the conflict between the ioctl numbers. The predefined commands are divided into three groups:• Those that can be issued on any file (regular, device, FIFO, or socket)• Those that are issued only on regular files• Those specific to the filesystem type尽管 ioctl 系统调用最常用于对设备进行操作但内核本身也能识别一部分命令。需要注意的是当这些命令作用于你的设备时会在你自己的文件操作回调被调用之前就被内核解析处理。因此如果你为自定义的 ioctl 命令选用了与内核预定义命令相同的命令号你的驱动将永远收不到该命令的请求而应用程序也会因 ioctl 命令号冲突得到非预期的结果。这些预定义命令分为三类可作用于任意文件的命令普通文件、设备文件、FIFO 或套接字仅作用于普通文件的命令特定文件系统类型专用的命令Commands in the last group are executed by the implementation of the hosting filesystem (this is how the chattr command works). Device driver writers are interested only in the first group of commands, whose magic number is “T.” Looking at theworkings of the other groups is left to the reader as an exercise; ext2_ioctl is a most interesting function (and easier to understand than one might expect), because it implements the append-only flag and the immutable flag.最后一类命令由所在文件系统的实现执行chattr 命令就是以此方式工作的。设备驱动开发者只需关注第一类命令这类命令的幻数为 T。其余两类命令的实现细节留给读者自行研究ext2_ioctl 是一个非常值得学习的函数且比预想中更容易理解因为它实现了文件的仅追加标记和不可变标记。The following ioctl commands are predefined for any file, including device-special files:FIOCLEXSet the close-on-exec flag (File IOctl CLose on EXec). Setting this flag causes the file descriptor to be closed when the calling process executes a new program.FIONCLEXClear the close-on-exec flag (File IOctl Not CLos on EXec). The command restores the common file behavior, undoing what FIOCLEX above does.FIOASYNCSet or reset asynchronous notification for the file (as discussed in the section “Asynchronous Notification,” later in this chapter). Note that kernel versions up to Linux 2.2.4 incorrectly used this command to modify the O_SYNC flag. Sinceboth actions can be accomplished through fcntl, nobody actually uses the FIOASYNC command, which is reported here only for completeness.FIOQSIZEThis command returns the size of a file or directory; when applied to a device file, however, it yields an ENOTTY error return.FIONBIO“File IOctl Non-Blocking I/O” (described in the section “Blocking and Nonblocking Operations”). This call modifies the O_NONBLOCK flag in filp-f_flags. The third argument to the system call is used to indicate whether the flag is to be set or cleared. (We’ll look at the role of the flag later in this chapter.) Note that the usual way to change this flag is with the fcntl system call, using the F_SETFL command.以下是为任意文件包括设备专用文件预定义的 ioctl 命令FIOCLEX设置执行时关闭标志File IOctl CLose on EXec。设置该标志后当调用进程执行新程序时对应的文件描述符会被自动关闭。FIONCLEX清除执行时关闭标志File IOctl Not CLose on EXec。该命令会恢复文件的常规行为撤销上述 FIOCLEX 命令的效果。FIOASYNC为文件设置或取消异步通知机制详见本章后续 “异步通知” 小节。注意Linux 2.2.4 及更早的内核版本曾错误地用该命令修改 O_SYNC 标志。由于这两种操作都可以通过 fcntl 完成实际上没人会使用 FIOASYNC 命令此处仅为完整性而提及。FIOQSIZE该命令用于返回文件或目录的大小但作用于设备文件时会返回 ENOTTY 错误。FIONBIO“文件 I/O 控制非阻塞 I/O”详见 “阻塞与非阻塞操作” 小节。该调用会修改 filp-f_flags 中的 O_NONBLOCK 标志。系统调用的第三个参数用于指定该标志是被设置还是清除。本章后续会讲解该标志的作用。注意修改该标志的标准做法是使用 fcntl 系统调用并搭配 F_SETFL 命令。The last item in the list introduced a new system call, fcntl, which looks like ioctl. In fact, the fcntl call is very similar to ioctl in that it gets a command argument and an extra (optional) argument. It is kept separate from ioctl mainly for historical reasons:when Unix developers faced the problem of controlling I/O operations, they decided that files and devices were different. At the time, the only devices with ioctl implementations were ttys, which explains why -ENOTTY is the standard reply for an incorrect ioctl command. Things have changed, but fcntl remains a separate system call.列表中的最后一项引入了一个新的系统调用 fcntl它看起来与 ioctl 非常相似。事实上fcntl 与 ioctl 高度相似 —— 都接收一个命令参数和一个额外的可选参数。它与 ioctl 保持独立主要是历史原因当年 Unix 开发者需要解决 I/O 操作控制的问题时认为文件和设备是两类不同的事物。那时只有终端设备tty实现了 ioctl这也正是非法 ioctl 命令标准返回 -ENOTTY 的原因。尽管情况早已改变fcntl 依然作为独立的系统调用保留了下来。补充说明FIOCLEX / FIONCLEX本质是对文件描述符设置 FD_CLOEXEC 标志现在更推荐直接在 open 时带 O_CLOEXEC 标志比事后用 ioctl/fcntl 更安全、更原子。FIONBIO 与非阻塞模式驱动中处理非阻塞 I/O 一般直接检查 filp-f_flags O_NONBLOCK不建议自己实现 FIONBIO内核已有通用处理驱动只需响应标志即可。FIOASYNC 已基本废弃异步通知信号驱动 I/O现在统一用 fcntl(F_SETOWN) fcntl(F_SETFL, O_ASYNC)不再使用 ioctl(FIOASYNC)。ENOTTY 的历史梗原文提到的 ENOTTY 错误字面上是 Not a typewriter不是 “无效参数”正是因为早期 ioctl 只给 tty 用后来扩展到所有设备后错误码含义被解释为 “Inappropriate ioctl for device” 并沿用至今。ioctl 与 fcntl 的分工fcntl用于通用文件描述符属性阻塞、异步、close-on-exec、文件状态标志等ioctl用于设备专属操作协议、硬件控制等高度特定的功能

更多文章