3.7. 讀和寫

2018-02-24 15:49 更新

3.7.?讀和寫

讀和寫方法都進(jìn)行類似的任務(wù), 就是, 從和到應(yīng)用程序代碼拷貝數(shù)據(jù). 因此, 它們的原型相當(dāng)相似, 可以同時(shí)介紹它們:


ssize_t read(struct file *filp, char __user *buff, size_t count, loff_t *offp);
ssize_t write(struct file *filp, const char __user *buff, size_t count, loff_t *offp);

對(duì)于 2 個(gè)方法, filp 是文件指針, count 是請(qǐng)求的傳輸數(shù)據(jù)大小. buff 參數(shù)指向持有被寫入數(shù)據(jù)的緩存, 或者放入新數(shù)據(jù)的空緩存. 最后, offp 是一個(gè)指針指向一個(gè)"long offset type"對(duì)象, 它指出用戶正在存取的文件位置. 返回值是一個(gè)"signed size type"; 它的使用在后面討論.

讓我們重復(fù)一下, read 和 write 方法的 buff 參數(shù)是用戶空間指針. 因此, 它不能被內(nèi)核代碼直接解引用. 這個(gè)限制有幾個(gè)理由:

  • 依賴于你的驅(qū)動(dòng)運(yùn)行的體系, 以及內(nèi)核被如何配置的, 用戶空間指針當(dāng)運(yùn)行于內(nèi)核模式可能根本是無效的. 可能沒有那個(gè)地址的映射, 或者它可能指向一些其他的隨機(jī)數(shù)據(jù).

  • 就算這個(gè)指針在內(nèi)核空間是同樣的東西, 用戶空間內(nèi)存是分頁(yè)的, 在做系統(tǒng)調(diào)用時(shí)這個(gè)內(nèi)存可能沒有在 RAM 中. 試圖直接引用用戶空間內(nèi)存可能產(chǎn)生一個(gè)頁(yè)面錯(cuò), 這是內(nèi)核代碼不允許做的事情. 結(jié)果可能是一個(gè)"oops", 導(dǎo)致進(jìn)行系統(tǒng)調(diào)用的進(jìn)程死亡.

  • 置疑中的指針由一個(gè)用戶程序提供, 它可能是錯(cuò)誤的或者惡意的. 如果你的驅(qū)動(dòng)盲目地解引用一個(gè)用戶提供的指針, 它提供了一個(gè)打開的門路使用戶空間程序存取或覆蓋系統(tǒng)任何地方的內(nèi)存. 如果你不想負(fù)責(zé)你的用戶的系統(tǒng)的安全危險(xiǎn), 你就不能直接解引用用戶空間指針.

顯然, 你的驅(qū)動(dòng)必須能夠存取用戶空間緩存以完成它的工作. 但是, 為安全起見這個(gè)存取必須使用特殊的, 內(nèi)核提供的函數(shù). 我們介紹幾個(gè)這樣的函數(shù)(定義于 <asm/uaccess.h>), 剩下的在第一章"使用 ioctl 參數(shù)"一節(jié)中. 它們使用一些特殊的, 依賴體系的技巧來確保內(nèi)核和用戶空間的數(shù)據(jù)傳輸安全和正確.

scull 中的讀寫代碼需要拷貝一整段數(shù)據(jù)到或者從用戶地址空間. 這個(gè)能力由下列內(nèi)核函數(shù)提供, 它們拷貝一個(gè)任意的字節(jié)數(shù)組, 并且位于大部分讀寫實(shí)現(xiàn)的核心中.


unsigned long copy_to_user(void __user *to,const void *from,unsigned long count); 
unsigned long copy_from_user(void *to,const void __user *from,unsigned long count); 

盡管這些函數(shù)表現(xiàn)象正常的 memcpy 函數(shù), 必須加一點(diǎn)小心在從內(nèi)核代碼中存取用戶空間. 尋址的用戶也當(dāng)前可能不在內(nèi)存, 虛擬內(nèi)存子系統(tǒng)會(huì)使進(jìn)程睡眠在這個(gè)頁(yè)被傳送到位時(shí). 例如, 這發(fā)生在必須從交換空間獲取頁(yè)的時(shí)候. 對(duì)于驅(qū)動(dòng)編寫者來說, 最終結(jié)果是任何存取用戶空間的函數(shù)必須是可重入的, 必須能夠和其他驅(qū)動(dòng)函數(shù)并行執(zhí)行, 并且, 特別的, 必須在一個(gè)它能夠合法地睡眠的位置. 我們?cè)诘?5 章再回到這個(gè)主題.

這 2 個(gè)函數(shù)的角色不限于拷貝數(shù)據(jù)到和從用戶空間: 它們還檢查用戶空間指針是否有效. 如果指針無效, 不進(jìn)行拷貝; 如果在拷貝中遇到一個(gè)無效地址, 另一方面, 只拷貝部分?jǐn)?shù)據(jù). 在 2 種情況下, 返回值是還要拷貝的數(shù)據(jù)量. scull 代碼查看這個(gè)錯(cuò)誤返回, 并且如果它不是 0 就返回 -EFAULT 給用戶.

用戶空間存取和無效用戶空間指針的主題有些高級(jí), 在第 6 章討論. 然而, 值得注意的是如果你不需要檢查用戶空間指針, 你可以調(diào)用 copy_to_user 和 copy_from_user 來代替. 這是有用處的, 例如, 如果你知道你已經(jīng)檢查了這些參數(shù). 但是, 要小心; 事實(shí)上, 如果你不檢查你傳遞給這些函數(shù)的用戶空間指針, 那么你可能造成內(nèi)核崩潰和/或安全漏洞.

至于實(shí)際的設(shè)備方法, read 方法的任務(wù)是從設(shè)備拷貝數(shù)據(jù)到用戶空間(使用 copy_to_user), 而 write 方法必須從用戶空間拷貝數(shù)據(jù)到設(shè)備(使用 copy_from_user). 每個(gè) read 或 write 系統(tǒng)調(diào)用請(qǐng)求一個(gè)特定數(shù)目字節(jié)的傳送, 但是驅(qū)動(dòng)可自由傳送較少數(shù)據(jù) -- 對(duì)讀和寫這確切的規(guī)則稍微不同, 在本章后面描述.

不管這些方法傳送多少數(shù)據(jù), 它們通常應(yīng)當(dāng)更新 *offp 中的文件位置來表示在系統(tǒng)調(diào)用成功完成后當(dāng)前的文件位置. 內(nèi)核接著在適當(dāng)時(shí)候傳播文件位置的改變到文件結(jié)構(gòu). pread 和 pwrite 系統(tǒng)調(diào)用有不同的語(yǔ)義; 它們從一個(gè)給定的文件偏移操作, 并且不改變其他的系統(tǒng)調(diào)用看到的文件位置. 這些調(diào)用傳遞一個(gè)指向用戶提供的位置的指針, 并且放棄你的驅(qū)動(dòng)所做的改變.

給 read 的參數(shù)表示了一個(gè)典型讀實(shí)現(xiàn)是如何使用它的參數(shù).

圖?3.2.?給 read 的參數(shù)

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)