?? 文件和設備編程.htm
字號:
<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> open() 的第一個參數是以正常 C 字符串表示的全路徑名(即以 \0
終止)。第二個參數指定文件應該如何打開,并包括邏輯“與”操作的一個或多個以下標志:
<P> O_RDONLY:文件可以只讀<BR>
O_RDWR:文件可以讀寫<BR> O_APPEND:文件可以讀或附加<BR>
O_CREAT:如果文件還不存在則應該創建<BR> O_EXCL:如果文件已存在,失敗而不是創建它(只應該使用
O_CREAT)<BR> O_TRUNC:如果文件已存在,從中除去所有數據(與創建新文件類似)
<P> open() 的第三個參數只在使用 O_CREAT 時需要;它指定了以數字表示的文件許可權(格式與 chown
命令的數值許可權自變量的格式相同。為 open() 指定的許可權受用戶的 umask
影響,后者允許用戶指定一系列新文件應該獲得的缺省許可權。大多數創建文件的程序都使用第三個自變量 0666 調用 open(),可以讓用戶通過 umask
來控制程序的缺省許可權。(大多數 shell 的 umask 命令都可以更改它。)
<P> 例如,清單 3 顯示了如何為進行讀寫打開文件、如果它不存在則創建,以及廢棄其中的數據:
<P>清單 3:
<P>int fd;<BR>fd = open("myfile", O_RDWR | O_CREAT | O_TRUNC, 0666)<BR>if (fd
< 0) {<BR> /* Some error occurred */<BR> /* ...
*/<BR>}
<P> open() 返回引用文件的文件描述符。回憶一下,文件描述符總是 >= 0。如果 open()
返回了一個負值,就表示發生了錯誤,全局變量錯誤號包含了描述問題的 Unix 錯誤代碼。open() 總盡量返回最小數,如果沒有使用文件描述符 0,open()
將總返回 0。
<P> 進程帶文件結束時,它應該通過 close() 系統調用關閉它,該系統調用的格式為:
<P>int close(int fd);
<P> close 的文件描述符是傳遞給 close() 的唯一自變量,在成功情況下返回 0。盡管 close()
失敗的情況比較少見,但如果文件描述符引用的是遠程服務器上的文件,系統無法正確清空它的高速緩存,close()
就可能真的失敗。進程終止時,內核自動關閉所有還在打開的文件。
<P>
最后的一個常見文件操作是移動文件指針。這(自然)只對帶文件指針的文件有意義,如果嘗試在不恰當的文件上嘗試該操作就會返回錯誤。lseek()
系統調用用于以下目的:
<P>off_t lseek(int fd, off_t pos, int whence);
<P> off_t 類型是表達 longint (long 就是 lseek 中 "l"
的來歷)的一種別致方法。lseek() 返回相對于文件開始處文件指針的最終位置,如果有錯誤,則返回
-1。這個系統調用希望被移動的文件指針所屬的文件描述符作為第一個自變量,將它移動到文件中的位置作為第二個自變量。最后一個自變量描述文件指針的移動方式。
<P> SEEK_SET 將它移動到從文件開始算起的 pos 字節。<BR>
SEEK_END 將它移動到從文件結尾算起的 pos 字節。<BR> SEEK_CUR 從它當前位置開始向文件結尾移動
pos 字節。
<P> open()、close()、write()、read() 和 lseek() 的組合為 Linux
提供了基本的文件訪問 API。雖然還有許多其它操縱文件的函數,但這里描述的是最常用的。
<P> 大多數程序員都使用熟悉的 ANSI C 庫文件函數,例如 fopen() 和
fread(),而不是在此描述的低級系統調用。可以預見到,fopen() 和 fread()
是在用戶級別庫中這些系統調用的基礎上實現的。仍然會經常看到低級系統調用的使用,特別是在更復雜的程序中。通過熟悉這些例程和接口,您就可以成為一個真正的 Unix
黑客了。
<P>關于作者<BR> Erik Troan 是 Red Hat Software 的開發者,Linux
Application Development 一書的作者之一。可以通過 ewt@redhat.com 與他聯系。
<P><BR>
<CENTER><A
href="file:///E:/linux資料/linux筆記及源碼/joyfire-0206.htm/system/10.html#Content">[目錄]</A></CENTER>
<HR>
<BR><A id=I320 name=I320></A>
<CENTER><B><FONT size=+2>網卡驅動編寫</FONT></B></CENTER><BR>
工作需要寫了我們公司一塊網卡的Linux驅動程序。經歷一個從無到有的過程,深感技術交流的重要。Linux作為挑戰微軟壟斷的強有力武器,日益受到大家的喜愛。真希望她能在中國迅速成長。把程序文檔貼出來,希望和大家探討Linux技術和應用,促進Linux在中國的普及。<BR>
本文可隨意轉載,但請不要在盈利性出版物上刊登。
<P>------------------ Linux操作系統網絡驅動程序編寫 -------------------<BR>------------
Contact the author by mailto:bordi@bordi.dhs.org ------
<P>Linux操作系統網絡驅動程序編寫
<P>一.Linux系統設備驅動程序概述<BR> 1.1 Linux設備驅動程序分類<BR> 1.2
編寫驅動程序的一些基本概念<BR>二.Linux系統網絡設備驅動程序<BR> 2.1 網絡驅動程序的結構<BR> 2.2
網絡驅動程序的基本方法<BR> 2.3 網絡驅動程序中用到的數據結構<BR> 2.4
常用的系統支持<BR>三.編寫Linux網絡驅動程序中可能遇到的問題<BR> 3.1 中斷共享<BR> 3.2
硬件發送忙時的處理<BR> 3.3 流量控制(flow control)<BR> 3.4 調試<BR>四.進一步的閱讀<BR>五.雜項
<CENTER><A
href="file:///E:/linux資料/linux筆記及源碼/joyfire-0206.htm/system/10.html#Content">[目錄]</A></CENTER>
<HR>
<BR><A id=I321 name=I321></A>
<CENTER><B><FONT size=+2>概述</FONT></B></CENTER><BR>一.Linux系統設備驅動程序概述<BR>1.1
Linux設備驅動程序分類<BR>
Linux設備驅動程序在Linux的內核源代碼中占有很大的比例,源代碼的長度日益增加,主要是驅動程序的增加。在Linux內核的不斷升級過程中,驅動程序的結構還是相對穩定。在2.0.xx到2.2.xx的變動里,驅動程序的編寫做了一些改變,但是從2.0.xx的驅動到2.2.xx的移植只需做少量的工作。
<P> Linux系統的設備分為字符設備(char device),塊設備(block
device)和網絡設備(network
device)三種。字符設備是指存取時沒有緩存的設備。塊設備的讀寫都有緩存來支持,并且塊設備必須能夠隨機存取(random
access),字符設備則沒有這個要求。典型的字符設備包括鼠標,鍵盤,串行口等。塊設備主要包括硬盤軟盤設備,CD-ROM等。一個文件系統要安裝進入操作系統必須在塊設備上。
<P> 網絡設備在Linux里做專門的處理。Linux的網絡系統主要是基于BSD
unix的socket機制。在系統和驅動程序之間定義有專門的數據結構(sk_buff)進行數據的傳遞。系統里支持對發送數據和接收數據的緩存,提供流量控制機制,提供對多協議的支持。
<P>1.2 編寫驅動程序的一些基本概念<BR>
無論是什么操作系統的驅動程序,都有一些通用的概念。操作系統提供給驅動程序的支持也大致相同。下面簡單介紹一下網絡設備驅動程序的一些基本要求。
<P>1.2.1 發送和接收<BR>
這是一個網絡設備最基本的功能。一塊網卡所做的無非就是收發工作。所以驅動程序里要告訴系統你的發送函數在哪里,系統在有數據要發送時就會調用你的發送程序。還有驅動程序由于是直接操縱硬件的,所以網絡硬件有數據收到最先能得到這個數據的也就是驅動程序,它負責把這些原始數據進行必要的處理然后送給系統。這里,操作系統必須要提供兩個機制,一個是找到驅動程序的發送函數,一個是驅動程序把收到的數據送給系統。
<P>1.2.2 中斷<BR>
中斷在現代計算機結構中有重要的地位。操作系統必須提供驅動程序響應中斷的能力。一般是把一個中斷處理程序注冊到系統中去。操作系統在硬件中斷發生后調用驅動程序的處理程序。Linux支持中斷的共享,即多個設備共享一個中斷。
<P>1.2.3 時鐘<BR>
在實現驅動程序時,很多地方會用到時鐘。如某些協議里的超時處理,沒有中斷機制的硬件的輪詢等。操作系統應為驅動程序提供定時機制。一般是在預定的時間過了以后回調注冊的時鐘函數。在網絡驅動程序中,如果硬件沒有中斷功能,定時器可以提供輪詢(poll)方式對硬件進行存取。或者是實現某些協議時需要的超時重傳等。
<P>
<P><BR>
<CENTER><A
href="file:///E:/linux資料/linux筆記及源碼/joyfire-0206.htm/system/10.html#Content">[目錄]</A></CENTER>
<HR>
<BR><A id=I322 name=I322></A>
<CENTER><B><FONT size=+2>設備驅動</FONT></B></CENTER><BR>二.Linux系統網絡設備驅動程序
<P>2.1 網絡驅動程序的結構<BR>
所有的Linux網絡驅動程序遵循通用的接口。設計時采用的是面向對象的方法。一個設備就是一個對象(device
結構),它內部有自己的數據和方法。每一個設備的方法被調用時的第一個參數都是這個設備對象本身。這樣這個方法就可以存取自身的數據(類似面向對象程序設計時的this引用)。<BR>
一個網絡設備最基本的方法有初始化、發送和接收。
<P>
-------------------
---------------------<BR> |deliver packets
| |receive packets
queue|<BR> |(dev_queue_xmit())
|
|them(netif_rx()) |<BR>
-------------------
---------------------<BR>
|
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -