?? 10.html
字號:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"><HTML><HEAD> <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=gb2312"> <META NAME="GENERATOR" CONTENT="《良友》v2.1, 作者:安富國,http://winking.126.com"> <TITLE>文件和設備編程</TITLE></HEAD><BODY style="font-family: 宋體; font-size: 9pt"> <CENTER><TABLE CELLSPACING=10 CELLPADDING=10 WIDTH="60%" BGCOLOR="#FFB693" ><TR><TD ALIGN=CENTER><FONT SIZE=+2><!--標題由此開始-->文件和設備編程</TD></TR></TABLE></CENTER><p><h3>目 錄</h3><!--目錄由此開始--><A NAME="Content" ID="Content"></A><OL><LI><A HREF="#I318">文件和設備編程</A></LI><OL><LI><A HREF="#I319">文件訪問原語</A></LI><LI><A HREF="#I320">網卡驅動編寫</A></LI><OL><LI><A HREF="#I321">概述</A></LI><LI><A HREF="#I322">設備驅動</A></LI><LI><A HREF="#I323">需要注意</A></LI><LI><A HREF="#I324">參考</A></LI></OL><LI><A HREF="#I325">設備驅動</A></LI><OL><LI><A HREF="#I326">概述</A></LI><LI><A HREF="#I327">數據結構</A></LI><LI><A HREF="#I328">初始化</A></LI><LI><A HREF="#I329">管理流程</A></LI><LI><A HREF="#I330">添加字符設備</A></LI><LI><A HREF="#I331">添加塊設備</A></LI><LI><A HREF="#I332">一個虛擬的字符設備驅動程序</A></LI><LI><A HREF="#I333">代碼范例</A></LI><OL><LI><A HREF="#I334">header.c</A></LI><LI><A HREF="#I335">init.c</A></LI><LI><A HREF="#I336">ioctl.c</A></LI><LI><A HREF="#I337">open.c</A></LI><LI><A HREF="#I338">read.c</A></LI><LI><A HREF="#I339">release.c</A></LI><LI><A HREF="#I340">tdd.c</A></LI><LI><A HREF="#I341">write.c</A></LI></OL></OL><LI><A HREF="#I316">讀寫音頻</A></LI><OL><LI><A HREF="#I732">錄音</A></LI><LI><A HREF="#I733">調節音量</A></LI></OL><LI><A HREF="#I342">pro結構</A></LI></OL></OL><hr><br><A NAME="I318" ID="I318"></A><center><b><font size=+2>文件和設備編程</font></b></center><br><center><A HREF="#Content">[目錄]</A></center><hr><br><A NAME="I319" ID="I319"></A><center><b><font size=+2>文件訪問原語</font></b></center><br> POSIX API 最重要的一個抽象概念就是文件。盡管幾乎所有的操作系統都將文件用于永久性存儲器,但所有 Unix 版本通過文件抽象概念提供對大多數系統資源的訪問。<p> 更具體地說,這意味著 Linux 使用相同的一組系統調用來提供對設備(例如軟盤和磁帶設備)、網絡資源(最常見的是 TCP/IP 連接)、系統終端,甚至內核狀態信息的訪問。感謝無所不在的系統調用,嫻熟地使用與文件相關的調用對于每個 Linux 程序員來說都很重要。讓我們仔細查看一下文件 API 背后的一些基本概念,并描述最重要的文件相關系統調用。<p> Linux 提供許多不同種類的文件。最常見的類型就簡稱為常規文件,它存儲大量用于以后訪問的信息。您所使用的絕大部分文件 -- 例如可執行文件(如 /bin/vi)、數據文件(如 /etc/passwd)和系統二進制文件(如 /lib/libc.so.6)-- 都是常規文件。它們通常駐留在磁盤上的某處,但我們稍后會發現,并不一定都是這種情況。<p> 另一種文件類型是目錄,它包含了一個其它文件及其位置的列表。使用 ls 命令列出目錄中的文件時,它打開該目錄的文件,并打印出它所包含的所有文件的信息。<p> 其它文件類型包括塊設備(表示文件系統高速緩存的設備,例如硬盤驅動器)、字符設備(表示非高速緩存的設備,例如磁帶驅動器、鼠標和系統終端)、管道和套接字(允許進程相互之間對話),以及符號鏈接(允許文件在目錄層次結構中有多個名稱)。<p> 大多數文件都有一個或多個引用它們的符號名。這些符號名是一組由 / 字符定界的字符串,并向內核標識文件。它們是 Linux 用戶所熟悉的路徑名;例如,路徑名 /home/ewt/article 引用的是我手提電腦中包含這篇文章文本的文件。沒有兩個文件可以共享相同的名稱(但單一文件可以有多個名稱),因此路徑名唯一地標識單一文件。<p> 進程可以訪問的每個文件都由一個小的非負整數標識,稱為“文件描述符”。文件描述符由打開文件的系統調用創建,并由從當前進程創建的新子進程繼承。就是說,當進程啟動了一個新程序時,原始進程的打開文件通常是由新程序繼承的。<p> 按照約定,大多數程序保留前三個文件描述符(0、1 和 2)用于特殊目的 -- 訪問所謂的標準輸出、標準輸出和標準錯誤流。文件描述符 0 是標準輸入,這里許多程序都將從外部世界接收輸入。文件描述符 1 是標準輸出。大多數程序在這里顯示正常的輸出。對于與錯誤情況相關的輸出,使用文件描述符 2(標準錯誤)。<p> 任何習慣使用 Linux shell 的人都曾看到過標準輸入、輸出和錯誤文件描述符的使用。通常,shell 運行命令時帶文件描述符 0、1 和 2,都是指 shell 的終端。當使用 > 字符指示 shell 將一個程序的輸出發送給另一個程序時,shell 在調用新程序之前打開該文件作為文件描述符 1。這將導致程序將它的輸出發送給指定的文件而不是用戶終端;其妙處是,對于程序本身,這是透明的!<p> 與之類似,"<" 字符指示 shell 使用特定的文件作為文件描述符 0。這樣就強迫程序從該文件中讀取它的輸入;這兩種情況下,任何來自程序的錯誤仍將出現在終端上,如同它們在文件描述符 2 的情況下發送給標準錯誤一樣。(在 "bash" shell 中,可以使用 2> 而不是 > 將標準錯誤重定向)。這種類型的文件重定向是 Linux 命令行最強大的特性之一。<p> 使用任何與文件相關的系統調用之前,程序應該包括 <fcntl.h> 和 <unistd.h>;它們為最普遍的文件例程提供了函數原型和常數。在下面的示例代碼中,我們假設每個程序開始處都有<p>#include <fcntl.h><br>#include <unistd.h><p> 首先,讓我們了解如何讀寫文件。憑直覺就可以知道,read() 和 write() 系統調用是執行這些操作的最常用方法。這兩種系統調用將有三個自變量:要訪問的文件描述符、指向要讀寫的信息的指針以及應該讀寫的字符數。返回成功讀寫的字符數。清單 1 說明了一個簡單的程序,它從標準輸入(文件描述符 0)中讀取一行,并將它寫入標準輸出(文件描述符 1):<p>清單 1:<p> void main(void) {<br> char buf[100];<br> int num;<p> num = read(0, buf, sizeof(buf));<br> write(1, "I got: ", 7); /* Length of "I got: " is 7! */<br> write(1, buf, num);<br> }<p> 關于這個處理有兩個值得注意的問題。首先,我們要求 read() 返回 100 個字符,但如果我們運行這個程序,只有在用戶按下了 "enter" 鍵以后才能獲得輸入。許多文件操作都根據最佳效果工作:它們嘗試返回程序要求的所有信息,但只有部分能夠成功。缺省情況下,終端配置成一旦存在 "\n" 或新行符(通過按 "enter" 鍵產生)時,就從 read() 調用返回。這實際上非常方便,因為大多數用戶都希望程序無論如何都是面向行的。但常規數據文件并非如此,如果依靠它就可能產生不可預料的結果。<p> 另一個要注意的問題是我們不必在顯示輸出后寫一個 \n。read() 調用給了我們來自用戶的 \n,只將那個 \n 通過 write() 寫回標準輸出。如果您希望在沒有新行符的情況下看到發生的事件,嘗試將最后一行改為<p>write(1, buf, num - 1);<p> 有關這個簡單示例的最后一點:buf 絕對不包含實際的 C 字符串。C 字符串由標記字符串結束的單一 \0 字符終止。因為 read() 不將 \0 添加到緩沖區的結尾,在 read() 上使用 strlen()(或任何其它 C 字符串函數)將可能鑄成大錯!這種行為可以讓 read() 和 write() 對包括 \0 字符的數據處理,而這對于一般字符串函數來說是不可能的。<p> read() 和 write() 系統調用可以對絕大多數文件起作用。但它們不對目錄起作用,目錄應該通過特殊函數(例如 readdir())來訪問。另外,read() 和 write() 對于某些類型的套接字也不起作用。<p> 某些文件,例如常規文件和塊設備文件,使用文件指針的概念。它指定在文件中,下一個 read() 調用從哪里讀取,下一個 write() 調用從哪里寫入。read() 或 write() 后,文件指針隨著已處理的字符數(在內部,通過內核)增加。這樣,使用單一循環就可以方便地讀取文件中的所有數據。清單 2 就是示例:<p>清單 2:<p> char buffer[1024];<br> while ((num = read(0, buffer, 1024))) {<br> printf("got some data\n");<br> }<p><p><br> 這個循環將讀取標準輸入上的所有數據,自動在每次讀取后增加內核的內部文件指針。當文件指針處于文件結尾時,read() 將返回 0 并退出循環。某些文件(例如字符設備 -- 終端就是很好的一例)本身沒有文件指針,所以對于這一點,該程序將繼續運行,直到用戶提供文件結束標記(通過按 "Ctrl-D")為止。<p> 到現在為止,我們已經知道如何讀寫文件了,下一步要學習如何打開一個新文件。打開不同類型的文件有不同方法;我們將在這里討論的方法是通過路徑名打開在文件系統中表示的文件;包括常規文件、目錄、設備文件和指定的管道。某些套接字文件有路徑名,那些必須通過替代方法打開。<p> 撇開放棄權利的,open() 系統調用可以讓程序訪問大多數系統文件。open() 是個不尋常的系統調用,因為它獲取兩個或者三個自變量:<p>int open(const char *<br> pathname,<br> int flags);<p>或者,<br>int open(const char *<br> pathname,<br> int flags,<br> int perm);<p> 第一種形式更普遍一些;它打開一個已存在的文件。第二種格式應該在需要創建文件時使用。第三個自變量指定應該給予新文件的訪問權限。<p>
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -