⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ch06s06.html

📁 完整的Linux 设备驱动第3版
💻 HTML
📖 第 1 页 / 共 2 页
字号:
<html xmlns:cf="http://docbook.sourceforge.net/xmlns/chunkfast/1.0"><head><meta http-equiv="Content-Type" content="text/html; charset=gb2312"><title>6.6.&#160;在一个设备文件上的存取控制</title><link rel="stylesheet" href="docbook.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets V1.69.0"><link rel="start" href="index.html" title="Linux 设备驱动 Edition 3"><link rel="up" href="ch06.html" title="第&#160;6&#160;章&#160;高级字符驱动操作"><link rel="prev" href="ch06s05.html" title="6.5.&#160;移位一个设备"><link rel="next" href="ch06s07.html" title="6.7.&#160;快速参考"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">6.6.&#160;在一个设备文件上的存取控制</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="ch06s05.html">上一页</a>&#160;</td><th width="60%" align="center">第&#160;6&#160;章&#160;高级字符驱动操作</th><td width="20%" align="right">&#160;<a accesskey="n" href="ch06s07.html">下一页</a></td></tr></table><hr></div><div class="sect1" lang="zh-cn"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="AccessControlonaDeviceFile.sect1"></a>6.6.&#160;在一个设备文件上的存取控制</h2></div></div></div><p>提供存取控制对于一个设备节点来说有时是至关重要的. 不仅是非授权用户不能使用设备(由文件系统许可位所强加的限制), 而且有时只有授权用户才应当被允许来打开设备一次.</p><p>这个问题类似于使用 ttys 的问题. 在那个情况下, login 进程改变设备节点的所有权, 无论何时一个用户登录到系统, 为了阻止其他的用户打扰或者偷听这个 tty 的数据流. 但是, 仅仅为了保证对它的唯一读写而使用一个特权程序在每次打开它时改变一个设备的拥有权是不实际的. </p><p>迄今所显示的代码没有实现任何的存取控制, 除了文件系统许可位. 如果系统调用 open 将请求递交给驱动, open 就成功了. 我们现在介绍几个新技术来实现一些额外的检查.</p><p>每个在本节中展示的设备有和空的  scull 设备有相同的行为(即, 它实现一个持久的内存区)但是在存取控制方面和 scull 不同, 这个实现在 open 和 release 操作中.</p><div class="sect2" lang="zh-cn"><div class="titlepage"><div><div><h3 class="title"><a name="SingleOpenDevices.sect2"></a>6.6.1.&#160;单 open 设备</h3></div></div></div><p>提供存取控制的强力方式是只允许一个设备一次被一个进程打开(单次打开). 这个技术最好是避免因为它限制了用户的灵活性. 一个用户可能想运行不同的进程在一个设备上, 一个读状态信息而另一个写数据. 在某些情况下, 用户通过一个外壳脚本运行几个简单的程序可做很多事情, 只要它们可并发存取设备. 换句话说, 实现一个单 open 行为实际是在创建策略, 这样可能会介入你的用户要做的范围.</p><p>只允许单个进程打开设备有不期望的特性, 但是它也是一个设备驱动最简单实现的存取控制, 因此它在这里被展示. 这个源码是从一个称为 scullsingle 的设备中提取的.</p><p>scullsingle 设备维护一个 atiomic_t 变量, 称为 scull_s_available; 这个变量被初始化为值 1, 表示设备确实可用. open 调用递减并测试 scull_s_available 并拒绝存取如果其他人已经使设备打开.</p><pre class="programlisting">static atomic_t scull_s_available = ATOMIC_INIT(1);static int scull_s_open(struct inode *inode, struct file *filp){        struct scull_dev *dev = &amp;scull_s_device; /* device information */        if (! atomic_dec_and_test (&amp;scull_s_available))        {                atomic_inc(&amp;scull_s_available);                return -EBUSY; /* already open */        }        /* then, everything else is copied from the bare scull device */        if ( (filp-&gt;f_flags &amp; O_ACCMODE) == O_WRONLY)                scull_trim(dev);        filp-&gt;private_data = dev;        return 0; /* success */}</pre><p>release 调用, 另一方面, 标识设备为不再忙:</p><pre class="programlisting">static int scull_s_release(struct inode *inode, struct file *filp){        atomic_inc(&amp;scull_s_available); /* release the device */        return 0;}</pre><p>正常地, 我们建议你将 open 标志 scul_s_available 放在设备结构中( scull_dev 这里), 因为, 从概念上, 它属于这个设备. scull 驱动, 但是, 使用独立的变量来保持这个标志, 因此它可使用和空 scull 设备同样的设备结构和方法, 并且最少的代码复制.</p></div><div class="sect2" lang="zh-cn"><div class="titlepage"><div><div><h3 class="title"><a name="RestrictingAccesstoaSingleUserataTime.sect2"></a>6.6.2.&#160;一次对一个用户限制存取</h3></div></div></div><p>单打开设备之外的下一步是使一个用户在多个进程中打开一个设备, 但是一次只允许一个用户打开设备. 这个解决方案使得容易测试设备, 因为用户一次可从几个进程读写, 但是假定这个用户负责维护在多次存取中的数据完整性. 这通过在 open 方法中添加检查来实现; 这样的检查在通常的许可检查后进行, 并且只能使存取更加严格, 比由拥有者和组许可位所指定的限制. 这是和 ttys 所用的存取策略是相同的, 但是它不依赖于外部的特权程序.</p><p>这些存取策略实现地有些比单打开策略要奇怪. 在这个情况下, 需要 2 项: 一个打开计数和设备拥有者 uid. 再一次, 给这个项的最好的地方是在设备结构中; 我们的例子使用全局变量代替, 是因为之前为 scullsingle 所解释的的原因. 这个设备的名子是 sculluid.</p><p>open 调用在第一次打开时同意了存取但是记住了设备拥有者. 这意味着一个用户可打开设备多次, 因此允许协调多个进程对设备并发操作. 同时, 没有其他用户可打开它, 这样避免了外部干扰. 因为这个函数版本几乎和之前的一致, 这样相关的部分在这里被复制:</p><pre class="programlisting">spin_lock(&amp;scull_u_lock);if (scull_u_count &amp;&amp;                (scull_u_owner != current-&gt;uid) &amp;&amp; /* allow user */                (scull_u_owner != current-&gt;euid) &amp;&amp; /* allow whoever did su */                !capable(CAP_DAC_OVERRIDE)){ /* still allow root */        spin_unlock(&amp;scull_u_lock);        return -EBUSY; /* -EPERM would confuse the user */}if (scull_u_count == 0)        scull_u_owner = current-&gt;uid; /* grab it */scull_u_count++;spin_unlock(&amp;scull_u_lock);</pre><p>注意 sculluid 代码有 2 个变量 ( scull_u_owner 和 scull_u_count)来控制对设备的存取, 并且这样可被多个进程并发地存取. 为使这些变量安全, 我们使用一个自旋锁控制对它们的存取( scull_u_lock ). 没有这个锁, 2 个(或多个)进程可同时测试 scull_u_count , 并且都可能认为它们拥有设备的拥有权. 这里使用一个自旋锁, 是因为这个锁被持有极短的时间, 并且驱动在持有这个锁时不做任何可睡眠的事情.</p><p>我们选择返回 -EBUSY 而不是 -EPERM, 即便这个代码在进行许可检测, 为了给一个被拒绝存取的用户指出正确的方向. 对于"许可拒绝"的反应常常是检查 /dev 文件的模式和拥有者, 而"设备忙"正确地建议用户应当寻找一个已经在使用设备的进程.</p><p>这个代码也检查来看是否正在试图打开的进程有能力来覆盖文件存取许可; 如果是这样, open 被允许即便打开进程不是设备的拥有者. CAP_DAC_OVERRIDE 能力在这个情况中适合这个任务.</p><p>release 方法看来如下:</p><pre class="programlisting">static int scull_u_release(struct inode *inode, struct file *filp){        spin_lock(&amp;scull_u_lock);        scull_u_count--; /* nothing else */        spin_unlock(&amp;scull_u_lock);        return 0;}</pre><p>再次, 我们在修改计数之前必须获得锁, 来确保我们没有和另一个进程竞争.</p></div><div class="sect2" lang="zh-cn"><div class="titlepage"><div><div><h3 class="title"><a name="BlockingopenasanAlternativetoEBUSY.sect2"></a>6.6.3.&#160;阻塞 open 作为对 EBUSY 的替代</h3></div></div></div><p>当设备不可存取, 返回一个错误常常是最合理的方法, 但是有些情况用户可能更愿意等待设备.</p><p>例如, 如果一个数据通讯通道既用于规律地预期地传送报告(使用 crontab), 也用于根据用户的需要偶尔地使用, 对于被安排的操作最好是稍微延迟, 而不是只是因为通道当前忙而失败.</p><p>这是程序员在设计一个设备驱动时必须做的一个选择之一, 并且正确的答案依赖正被解决的实际问题.</p><p>对 EBUSY 的替代, 如同你可能已经想到的, 是实现阻塞 open. scullwuid 设备是一个在打开时等待设备而不是返回 -EBUSY 的 sculluid 版本. 它不同于 sculluid 只在下面的打开操作部分:</p><pre class="programlisting">spin_lock(&amp;scull_w_lock);while (! scull_w_available()){        spin_unlock(&amp;scull_w_lock);        if (filp-&gt;f_flags &amp; O_NONBLOCK)                return -EAGAIN;        if (wait_event_interruptible (scull_w_wait, scull_w_available()))                return -ERESTARTSYS; /* tell the fs layer to handle it */        spin_lock(&amp;scull_w_lock);}if (scull_w_count == 0)        scull_w_owner = current-&gt;uid; /* grab it */scull_w_count++;spin_unlock(&amp;scull_w_lock);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -