📄 ch06s06.html
字号:
spin_unlock(&scull_w_lock);
</pre>
<p>这个实现再次基于一个等待队列. 如果设备当前不可用, 试图打开它的进程被放置到等待队列直到拥有进程关闭设备.</p>
<p>release 方法, 接着, 负责唤醒任何挂起的进程:</p>
<pre class="programlisting">
static int scull_w_release(struct inode *inode, struct file *filp)
{
int temp;
spin_lock(&scull_w_lock);
scull_w_count--;
temp = scull_w_count;
spin_unlock(&scull_w_lock);
if (temp == 0)
wake_up_interruptible_sync(&scull_w_wait); /* awake other uid's */
return 0;
}
</pre>
<p>这是一个例子, 这里调用 wake_up_interruptible_sync 是有意义的. 当我们做这个唤醒, 我们只是要返回到用户空间, 这对于系统是一个自然的调度点. 当我们做这个唤醒时不是潜在地重新调度, 最好只是调用 "sync" 版本并且完成我们的工作.</p>
<p>阻塞式打开实现的问题是对于交互式用户真的不好, 他们不得不猜想哪里出错了. 交互式用户常常调用标准命令, 例如 cp 和 tar, 并且不能增加 O_NONBLOCK 到 open 调用. 有些使用磁带驱动器做备份的人可能喜欢有一个简单的"设备或者资源忙"消息, 来替代被扔在一边猜为什么今天的硬盘驱动器这么安静, 此时 tar 应当在扫描它.</p>
<p>这类的问题(需要一个不同的, 不兼容的策略对于同一个设备)最好通过为每个存取策略实现一个设备节点来实现. 这个做法的一个例子可在 linux 磁带驱动中找到, 它提供了多个设备文件给同一个设备. 例如, 不同的设备文件将使驱动器使用或者不用压缩记录, 或者自动回绕磁带当设备被关闭时.</p>
</div>
<div class="sect2" lang="zh-cn">
<div class="titlepage"><div><div><h3 class="title">
<a name="CloningtheDeviceonopen.sect2"></a>6.6.4. 在 open 时复制设备</h3></div></div></div>
<p>管理存取控制的另一个技术是创建设备的不同的私有拷贝, 根据打开它的进程.</p>
<p>明显地, 这只当设备没有绑定到一个硬件实体时有可能; scull 是一个这样的"软件"设备的例子. /dev/tty 的内部使用类似的技术来给它的进程一个不同的 /dev 入口点呈现的视图. 当设备的拷贝被软件驱动创建, 我们称它们为虚拟设备--就象虚拟控制台使用一个物理 tty 设备.</p>
<p>结构这类的存取控制很少需要, 这个实现可说明内核代码是多么容易改变应用程序的对周围世界的看法(即, 计算机).</p>
<p>/dev/scullpriv 设备节点在 scull 软件包只实现虚拟设备. scullpriv 实现使用了进程的控制 tty 的设备号作为对存取虚拟设备的钥匙. 但是, 你可以轻易地改变代码来使用任何整数值作为钥匙; 每个选择都导致一个不同的策略. 例如, 使用 uid 导致一个不同地虚拟设备给每个用户, 而使用一个 pid 钥匙创建一个新设备为每个存取它的进程.</p>
<p>使用控制终端的决定打算用在易于使用 I/O 重定向测试设备: 设备被所有的在同一个虚拟终端运行的命令所共享, 并且保持独立于在另一个终端上运行的命令所见到的.</p>
<p>open 方法看来象下面的代码. 它必须寻找正确的虚拟设备并且可能创建一个. 这个函数的最后部分没有展示, 因为它拷贝自空的 scull, 我们已经见到过.</p>
<pre class="programlisting">
/* The clone-specific data structure includes a key field */
struct scull_listitem
{
struct scull_dev device;
dev_t key;
struct list_head list;
};
/* The list of devices, and a lock to protect it */
static LIST_HEAD(scull_c_list);
static spinlock_t scull_c_lock = SPIN_LOCK_UNLOCKED;
/* Look for a device or create one if missing */
static struct scull_dev *scull_c_lookfor_device(dev_t key)
{
struct scull_listitem *lptr;
list_for_each_entry(lptr, &scull_c_list, list)
{
if (lptr->key == key)
return &(lptr->device);
}
/* not found */
lptr = kmalloc(sizeof(struct scull_listitem), GFP_KERNEL);
if (!lptr)
return NULL;
/* initialize the device */
memset(lptr, 0, sizeof(struct scull_listitem));
lptr->key = key;
scull_trim(&(lptr->device)); /* initialize it */
init_MUTEX(&(lptr->device.sem));
/* place it in the list */
list_add(&lptr->list, &scull_c_list);
return &(lptr->device);
}
static int scull_c_open(struct inode *inode, struct file *filp)
{
struct scull_dev *dev;
dev_t key;
if (!current->signal->tty)
{
PDEBUG("Process \"%s\" has no ctl tty\n", current->comm);
return -EINVAL;
}
key = tty_devnum(current->signal->tty);
/* look for a scullc device in the list */
spin_lock(&scull_c_lock);
dev = scull_c_lookfor_device(key);
spin_unlock(&scull_c_lock);
if (!dev)
return -ENOMEM;
/* then, everything else is copied from the bare scull device */
</pre>
<p>这个 release 方法没有做特殊的事情. 它将在最后的关闭时正常地释放设备, 但是我们不选择来维护一个 open 计数而来简化对驱动的测试. 如果设备在最后的关闭被释放, 你将不能读相同的数据在写入设备之后, 除非一个后台进程将保持它打开. 例子驱动采用了简单的方法来保持数据, 以便在下一次打开时, 你会发现它在那里. 设备在 scull_cleanup 被调用时释放.</p>
<p>这个代码使用通用的 linux 链表机制, 而不是从头开始实现相同的功能. linux 链表在第 11 章中讨论.</p>
<p>这里是 /dev/scullpriv 的 release 实现, 它结束了对设备方法的讨论.</p>
<pre class="programlisting">
static int scull_c_release(struct inode *inode, struct file *filp)
{
/*
*Nothing to do, because the device is persistent.
*A `real' cloned device should be freed on last close */
return 0;
}
</pre>
</div>
</div>
<div class="navfooter">
<hr>
<table width="100%" summary="Navigation footer">
<tr>
<td width="40%" align="left">
<a accesskey="p" href="ch06s05.html">上一页</a> </td>
<td width="20%" align="center"><a accesskey="u" href="ch06.html">上一级</a></td>
<td width="40%" align="right"> <a accesskey="n" href="ch06s07.html">下一页</a>
</td>
</tr>
<tr>
<td width="40%" align="left" valign="top">6.5. 移位一个设备 </td>
<td width="20%" align="center"><a accesskey="h" href="index.html">起始页</a></td>
<td width="40%" align="right" valign="top"> 6.7. 快速参考</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 + -