?? ch06s06.html
字號(hào):
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<title>6.6. 在一個(gè)設(shè)備文件上的存取控制-Linux設(shè)備驅(qū)動(dòng)第三版(中文版)-開發(fā)頻道-華星在線</title>
<meta name="description" content="驅(qū)動(dòng)開發(fā)-開發(fā)頻道-華星在線" />
<meta name="keywords" content="Linux設(shè)備驅(qū)動(dòng),中文版,第三版,ldd,linux device driver,驅(qū)動(dòng)開發(fā),電子版,程序設(shè)計(jì),軟件開發(fā),開發(fā)頻道" />
<meta name="author" content="華星在線 www.21cstar.com QQ:610061171" />
<meta name="verify-v1" content="5asbXwkS/Vv5OdJbK3Ix0X8osxBUX9hutPyUxoubhes=" />
<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 設(shè)備驅(qū)動(dòng) Edition 3">
<link rel="up" href="ch06.html" title="第 6 章 高級(jí)字符驅(qū)動(dòng)操作">
<link rel="prev" href="ch06s05.html" title="6.5. 移位一個(gè)設(shè)備">
<link rel="next" href="ch06s07.html" title="6.7. 快速參考">
</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. 在一個(gè)設(shè)備文件上的存取控制</th></tr>
<tr>
<td width="20%" align="left">
<a accesskey="p" href="ch06s05.html">上一頁(yè)</a> </td>
<th width="60%" align="center">第 6 章 高級(jí)字符驅(qū)動(dòng)操作</th>
<td width="20%" align="right"> <a accesskey="n" href="ch06s07.html">下一頁(yè)</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. 在一個(gè)設(shè)備文件上的存取控制</h2></div></div></div>
<p>提供存取控制對(duì)于一個(gè)設(shè)備節(jié)點(diǎn)來說有時(shí)是至關(guān)重要的. 不僅是非授權(quán)用戶不能使用設(shè)備(由文件系統(tǒng)許可位所強(qiáng)加的限制), 而且有時(shí)只有授權(quán)用戶才應(yīng)當(dāng)被允許來打開設(shè)備一次.</p>
<p>這個(gè)問題類似于使用 ttys 的問題. 在那個(gè)情況下, login 進(jìn)程改變?cè)O(shè)備節(jié)點(diǎn)的所有權(quán), 無論何時(shí)一個(gè)用戶登錄到系統(tǒng), 為了阻止其他的用戶打擾或者偷聽這個(gè) tty 的數(shù)據(jù)流. 但是, 僅僅為了保證對(duì)它的唯一讀寫而使用一個(gè)特權(quán)程序在每次打開它時(shí)改變一個(gè)設(shè)備的擁有權(quán)是不實(shí)際的. </p>
<p>迄今所顯示的代碼沒有實(shí)現(xiàn)任何的存取控制, 除了文件系統(tǒng)許可位. 如果系統(tǒng)調(diào)用 open 將請(qǐng)求遞交給驅(qū)動(dòng), open 就成功了. 我們現(xiàn)在介紹幾個(gè)新技術(shù)來實(shí)現(xiàn)一些額外的檢查.</p>
<p>每個(gè)在本節(jié)中展示的設(shè)備有和空的 scull 設(shè)備有相同的行為(即, 它實(shí)現(xiàn)一個(gè)持久的內(nèi)存區(qū))但是在存取控制方面和 scull 不同, 這個(gè)實(shí)現(xiàn)在 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. 單 open 設(shè)備</h3></div></div></div>
<p>提供存取控制的強(qiáng)力方式是只允許一個(gè)設(shè)備一次被一個(gè)進(jìn)程打開(單次打開). 這個(gè)技術(shù)最好是避免因?yàn)樗拗屏擞脩舻撵`活性. 一個(gè)用戶可能想運(yùn)行不同的進(jìn)程在一個(gè)設(shè)備上, 一個(gè)讀狀態(tài)信息而另一個(gè)寫數(shù)據(jù). 在某些情況下, 用戶通過一個(gè)外殼腳本運(yùn)行幾個(gè)簡(jiǎn)單的程序可做很多事情, 只要它們可并發(fā)存取設(shè)備. 換句話說, 實(shí)現(xiàn)一個(gè)單 open 行為實(shí)際是在創(chuàng)建策略, 這樣可能會(huì)介入你的用戶要做的范圍.</p>
<p>只允許單個(gè)進(jìn)程打開設(shè)備有不期望的特性, 但是它也是一個(gè)設(shè)備驅(qū)動(dòng)最簡(jiǎn)單實(shí)現(xiàn)的存取控制, 因此它在這里被展示. 這個(gè)源碼是從一個(gè)稱為 scullsingle 的設(shè)備中提取的.</p>
<p>scullsingle 設(shè)備維護(hù)一個(gè) atiomic_t 變量, 稱為 scull_s_available; 這個(gè)變量被初始化為值 1, 表示設(shè)備確實(shí)可用. open 調(diào)用遞減并測(cè)試 scull_s_available 并拒絕存取如果其他人已經(jīng)使設(shè)備打開.</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 = &scull_s_device; /* device information */
if (! atomic_dec_and_test (&scull_s_available))
{
atomic_inc(&scull_s_available);
return -EBUSY; /* already open */
}
/* then, everything else is copied from the bare scull device */
if ( (filp->f_flags & O_ACCMODE) == O_WRONLY)
scull_trim(dev);
filp->private_data = dev;
return 0; /* success */
}
</pre>
<p>release 調(diào)用, 另一方面, 標(biāo)識(shí)設(shè)備為不再忙:</p>
<pre class="programlisting">
static int scull_s_release(struct inode *inode, struct file *filp)
{
atomic_inc(&scull_s_available); /* release the device */
return 0;
}
</pre>
<p>正常地, 我們建議你將 open 標(biāo)志 scul_s_available 放在設(shè)備結(jié)構(gòu)中( scull_dev 這里), 因?yàn)? 從概念上, 它屬于這個(gè)設(shè)備. scull 驅(qū)動(dòng), 但是, 使用獨(dú)立的變量來保持這個(gè)標(biāo)志, 因此它可使用和空 scull 設(shè)備同樣的設(shè)備結(jié)構(gòu)和方法, 并且最少的代碼復(fù)制.</p>
</div>
<div class="sect2" lang="zh-cn">
<div class="titlepage"><div><div><h3 class="title">
<a name="RestrictingAccesstoaSingleUserataTime.sect2"></a>6.6.2. 一次對(duì)一個(gè)用戶限制存取</h3></div></div></div>
<p>單打開設(shè)備之外的下一步是使一個(gè)用戶在多個(gè)進(jìn)程中打開一個(gè)設(shè)備, 但是一次只允許一個(gè)用戶打開設(shè)備. 這個(gè)解決方案使得容易測(cè)試設(shè)備, 因?yàn)橛脩粢淮慰蓮膸讉€(gè)進(jìn)程讀寫, 但是假定這個(gè)用戶負(fù)責(zé)維護(hù)在多次存取中的數(shù)據(jù)完整性. 這通過在 open 方法中添加檢查來實(shí)現(xiàn); 這樣的檢查在通常的許可檢查后進(jìn)行, 并且只能使存取更加嚴(yán)格, 比由擁有者和組許可位所指定的限制. 這是和 ttys 所用的存取策略是相同的, 但是它不依賴于外部的特權(quán)程序.</p>
<p>這些存取策略實(shí)現(xiàn)地有些比單打開策略要奇怪. 在這個(gè)情況下, 需要 2 項(xiàng): 一個(gè)打開計(jì)數(shù)和設(shè)備擁有者 uid. 再一次, 給這個(gè)項(xiàng)的最好的地方是在設(shè)備結(jié)構(gòu)中; 我們的例子使用全局變量代替, 是因?yàn)橹盀?scullsingle 所解釋的的原因. 這個(gè)設(shè)備的名子是 sculluid.</p>
<p>open 調(diào)用在第一次打開時(shí)同意了存取但是記住了設(shè)備擁有者. 這意味著一個(gè)用戶可打開設(shè)備多次, 因此允許協(xié)調(diào)多個(gè)進(jìn)程對(duì)設(shè)備并發(fā)操作. 同時(shí), 沒有其他用戶可打開它, 這樣避免了外部干擾. 因?yàn)檫@個(gè)函數(shù)版本幾乎和之前的一致, 這樣相關(guān)的部分在這里被復(fù)制:</p>
<pre class="programlisting">
spin_lock(&scull_u_lock);
if (scull_u_count &&
(scull_u_owner != current->uid) && /* allow user */
(scull_u_owner != current->euid) && /* allow whoever did su */
!capable(CAP_DAC_OVERRIDE))
{ /* still allow root */
spin_unlock(&scull_u_lock);
return -EBUSY; /* -EPERM would confuse the user */
}
if (scull_u_count == 0)
scull_u_owner = current->uid; /* grab it */
scull_u_count++;
spin_unlock(&scull_u_lock);
</pre>
<p>注意 sculluid 代碼有 2 個(gè)變量 ( scull_u_owner 和 scull_u_count)來控制對(duì)設(shè)備的存取, 并且這樣可被多個(gè)進(jìn)程并發(fā)地存取. 為使這些變量安全, 我們使用一個(gè)自旋鎖控制對(duì)它們的存取( scull_u_lock ). 沒有這個(gè)鎖, 2 個(gè)(或多個(gè))進(jìn)程可同時(shí)測(cè)試 scull_u_count , 并且都可能認(rèn)為它們擁有設(shè)備的擁有權(quán). 這里使用一個(gè)自旋鎖, 是因?yàn)檫@個(gè)鎖被持有極短的時(shí)間, 并且驅(qū)動(dòng)在持有這個(gè)鎖時(shí)不做任何可睡眠的事情.</p>
<p>我們選擇返回 -EBUSY 而不是 -EPERM, 即便這個(gè)代碼在進(jìn)行許可檢測(cè), 為了給一個(gè)被拒絕存取的用戶指出正確的方向. 對(duì)于"許可拒絕"的反應(yīng)常常是檢查 /dev 文件的模式和擁有者, 而"設(shè)備忙"正確地建議用戶應(yīng)當(dāng)尋找一個(gè)已經(jīng)在使用設(shè)備的進(jìn)程.</p>
<p>這個(gè)代碼也檢查來看是否正在試圖打開的進(jìn)程有能力來覆蓋文件存取許可; 如果是這樣, open 被允許即便打開進(jìn)程不是設(shè)備的擁有者. CAP_DAC_OVERRIDE 能力在這個(gè)情況中適合這個(gè)任務(wù).</p>
<p>release 方法看來如下:</p>
<pre class="programlisting">
static int scull_u_release(struct inode *inode, struct file *filp)
{
spin_lock(&scull_u_lock);
scull_u_count--; /* nothing else */
spin_unlock(&scull_u_lock);
return 0;
}
</pre>
<p>再次, 我們?cè)谛薷挠?jì)數(shù)之前必須獲得鎖, 來確保我們沒有和另一個(gè)進(jìn)程競(jìng)爭(zhēng).</p>
</div>
<div class="sect2" lang="zh-cn">
<div class="titlepage"><div><div><h3 class="title">
<a name="BlockingopenasanAlternativetoEBUSY.sect2"></a>6.6.3. 阻塞 open 作為對(duì) EBUSY 的替代</h3></div></div></div>
<p>當(dāng)設(shè)備不可存取, 返回一個(gè)錯(cuò)誤常常是最合理的方法, 但是有些情況用戶可能更愿意等待設(shè)備.</p>
<p>例如, 如果一個(gè)數(shù)據(jù)通訊通道既用于規(guī)律地預(yù)期地傳送報(bào)告(使用 crontab), 也用于根據(jù)用戶的需要偶爾地使用, 對(duì)于被安排的操作最好是稍微延遲, 而不是只是因?yàn)橥ǖ喇?dāng)前忙而失敗.</p>
<p>這是程序員在設(shè)計(jì)一個(gè)設(shè)備驅(qū)動(dòng)時(shí)必須做的一個(gè)選擇之一, 并且正確的答案依賴正被解決的實(shí)際問題.</p>
<p>對(duì) EBUSY 的替代, 如同你可能已經(jīng)想到的, 是實(shí)現(xiàn)阻塞 open. scullwuid 設(shè)備是一個(gè)在打開時(shí)等待設(shè)備而不是返回 -EBUSY 的 sculluid 版本. 它不同于 sculluid 只在下面的打開操作部分:</p>
<pre class="programlisting">
spin_lock(&scull_w_lock);
while (! scull_w_available())
{
spin_unlock(&scull_w_lock);
if (filp->f_flags & 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(&scull_w_lock);
}
if (scull_w_count == 0)
scull_w_owner = current->uid; /* grab it */
scull_w_count++;
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號(hào)
Ctrl + =
減小字號(hào)
Ctrl + -