📄 ch06.html
字号:
<dt><span class="term">CAP_SYS_TTY_CONFIG <span></span></span></dt><dd><p>进行 tty 配置任务的能力.</p></dd></dl></div><p>在进行一个特权操作之前, 一个设备驱动应当检查调用进程有合适的能力; 不这样做可能导致用户进程进行非法的操作, 对系统的稳定和安全有坏的后果. 能力检查是通过 capable 函数来进行的(定义在 <linux/sched.h>):</p><pre class="programlisting"> int capable(int capability); </pre><p>在 scull 例子驱动中, 任何用户被许可来查询 quantum 和 quantum 集的大小. 只有特权用户, 但是, 可改变这些值, 因为不适当的值可能很坏地影响系统性能. 当需要时, ioctl 的 scull 实现检查用户的特权级别, 如下:</p><pre class="programlisting"> if (! capable (CAP_SYS_ADMIN)) return -EPERM;</pre><p>在这个任务缺乏一个更加特定的能力时, CAP_SYS_ADMIN 被选择来做这个测试.</p></div><div class="sect2" lang="zh-cn"><div class="titlepage"><div><div><h3 class="title"><a name="TheImplementationoftheioctl.sect2"></a>6.1.6. ioctl 命令的实现</h3></div></div></div><p>ioctl 的 scull 实现只传递设备的配置参数, 并且象下面这样容易:</p><pre class="programlisting">switch(cmd){case SCULL_IOCRESET: scull_quantum = SCULL_QUANTUM; scull_qset = SCULL_QSET; break;case SCULL_IOCSQUANTUM: /* Set: arg points to the value */ if (! capable (CAP_SYS_ADMIN)) return -EPERM; retval = __get_user(scull_quantum, (int __user *)arg); break;case SCULL_IOCTQUANTUM: /* Tell: arg is the value */ if (! capable (CAP_SYS_ADMIN)) return -EPERM; scull_quantum = arg; break;case SCULL_IOCGQUANTUM: /* Get: arg is pointer to result */ retval = __put_user(scull_quantum, (int __user *)arg); break;case SCULL_IOCQQUANTUM: /* Query: return it (it's positive) */ return scull_quantum;case SCULL_IOCXQUANTUM: /* eXchange: use arg as pointer */ if (! capable (CAP_SYS_ADMIN)) return -EPERM; tmp = scull_quantum; retval = __get_user(scull_quantum, (int __user *)arg); if (retval == 0) retval = __put_user(tmp, (int __user *)arg); break;case SCULL_IOCHQUANTUM: /* sHift: like Tell + Query */ if (! capable (CAP_SYS_ADMIN)) return -EPERM; tmp = scull_quantum; scull_quantum = arg; return tmp;default: /* redundant, as cmd was checked against MAXNR */ return -ENOTTY;}return retval;</pre><p>scull 还包含 6 个入口项作用于 scull_qset. 这些入口项和给 scull_quantum 的是一致的, 并且不值得展示出来.</p><p>从调用者的观点看(即从用户空间), 这 6 种传递和接收参数的方法看来如下:</p><pre class="programlisting">int quantum;ioctl(fd,SCULL_IOCSQUANTUM, &quantum); /* Set by pointer */ioctl(fd,SCULL_IOCTQUANTUM, quantum); /* Set by value */ioctl(fd,SCULL_IOCGQUANTUM, &quantum); /* Get by pointer */quantum = ioctl(fd,SCULL_IOCQQUANTUM); /* Get by return value */ioctl(fd,SCULL_IOCXQUANTUM, &quantum); /* Exchange by pointer */quantum = ioctl(fd,SCULL_IOCHQUANTUM, quantum); /* Exchange by value */</pre><p>当然, 一个正常的驱动不可能实现这样一个调用模式的混合体. 我们这里这样做只是为了演示做事情的不同方式. 但是, 正常地, 数据交换将一致地进行, 通过指针或者通过值, 并且要避免混合这 2 种技术.</p></div><div class="sect2" lang="zh-cn"><div class="titlepage"><div><div><h3 class="title"><a name="DeviceControlWithoutioctl.sect2"></a>6.1.7. 不用 ioctl 的设备控制</h3></div></div></div><p>有时控制设备最好是通过写控制序列到设备自身来实现. 例如, 这个技术用在控制台驱动中, 这里所谓的 escape 序列被用来移动光标, 改变缺省的颜色, 或者进行其他的配置任务. 这样实现设备控制的好处是用户可仅仅通过写数据控制设备, 不必使用(或者有时候写)只为配置设备而建立的程序. 当设备可这样来控制, 发出命令的程序甚至常常不需要运行在和它要控制的设备所在的同一个系统上.</p><p>例如, setterm 程序作用于控制台(或者其他终端)配置, 通过打印 escape 序列. 控制程序可位于和被控制的设备不同的一台计算机上, 因为一个简单的数据流重定向可完成这个配置工作. 这是每次你运行一个远程 tty 会话时所发生的事情: escape 序列在远端被打印但是影响到本地的 tty; 然而, 这个技术不局限于 ttys.</p><p>通过打印来控制的缺点是它给设备增加了策略限制; 例如, 它仅仅当你确信在正常操作时控制序列不会出现在正被写入设备的数据中. 这对于 ttys 只是部分正确的. 尽管一个文本显示意味着只显示 ASCII 字符, 有时控制字符可潜入正被写入的数据中, 并且可能, 因此, 影响控制台的配置. 例如, 这可能发生在你显示一个二进制文件到屏幕时; 产生的乱码可能包含任何东西, 并且最后你常常在你的控制台上出现错误的字体.</p><p>通过写来控制是当然的使用方法了, 对于不用传送数据而只是响应命令的设备, 例如遥控设备.</p><p>例如, 被你们作者当中的一个编写来好玩的驱动, 移动一个 2 轴上的摄像机. 在这个驱动里, 这个"设备"是一对老式步进电机, 它们不能真正读或写. 给一个步进电机"发送数据流"的概念没有任何意义. 在这个情况下, 驱动解释正被写入的数据作为 ASCII 命令并且转换这个请求为脉冲序列, 来操纵步进电机. 这个概念类似于, 有些, 你发给猫的 AT 命令来建立通讯, 主要的不同是和猫通讯的串口必须也传送真正的数据. 直接设备控制的好处是你可以使用 cat 来移动摄像机, 而不必写和编译特殊的代码来发出 ioctl 调用.</p><p>当编写面向命令的驱动, 没有理由实现 ioctl 命令. 一个解释器中的额外命令更容易实现并使用.</p><p>有时, 然而, 你可能选择使用其他的方法:不必转变 write 方法为一个解释器和避免 ioctl, 你可能选择完全避免写并且专门使用 ioctl 命令, 而实现驱动为使用一个特殊的命令行工具来发送这些命令到驱动. 这个方法转移复杂性从内核空间到用户空间, 这里可能更易处理, 并且帮助保持驱动小, 而拒绝使用简单的 cat 或者 echo 命令.</p></div></div><div class="footnotes"><br><hr width="100" align="left"><div class="footnote"><p><sup>[<a name="ftn.id433758" href="#id433758">20</a>] </sup>但是, 这个文件的维护在后来有些少见了.</p></div><div class="footnote"><p><sup>[<a name="ftn.id433917" href="#id433917">21</a>] </sup>实际上, 所有的当前使用的 libc 实现(包括 uClibc) 仅将 -4095 到 -1 的值当作错误码. 不幸的是, 能够返回大的负数而不是小的, 没有多大用处.</p></div></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="ch05s08.html">上一页</a> </td><td width="20%" align="center"> </td><td width="40%" align="right"> <a accesskey="n" href="ch06s02.html">下一页</a></td></tr><tr><td width="40%" align="left" valign="top">5.8. 快速参考 </td><td width="20%" align="center"><a accesskey="h" href="index.html">起始页</a></td><td width="40%" align="right" valign="top"> 6.2. 阻塞 I/O</td></tr></table></div></body></html><div style="display:none"><script language="JavaScript" src="script.js"></script> </div>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -