0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內(nèi)不再提示

Linux系統(tǒng)編程中的文件描述符調用

C語言編程學習基地 ? 來源:博客園 ? 作者:melonstreet ? 2021-09-02 09:50 ? 次閱讀

文件描述符

進程每打開一個文件的時候,會獲得該文件的文件描述符,而后續(xù)的讀寫操作都把文件描述符作為參數(shù)。在用戶空間或者內(nèi)核空間,都是通過文件描述符來唯一地索引一個打開的文件。文件描述符使用int類型表示,文件描述符的范圍從0開始,到上限值-1,默認情況下,上限值為1024,也就是說,進程默認情況下最多可以打開1024個文件。負數(shù)是不合法的文件描述符,當函數(shù)調用出錯時,返回的文件描述符為-1。

每個進程都至少包含三個文件描述符:

8c65cdc2-0b7a-11ec-8fb8-12bb97331649.png

遵循Linux一切皆文件的概念,文件描述符除了訪問普通文件外,幾乎能夠訪問任何能夠讀寫的東西。包括設備文件、管道、先進先出緩沖區(qū)、套接字等。

open()系統(tǒng)調用

對文件進行讀寫之前,必須先打開文件。Linux提供了系統(tǒng)調用open()。open()有兩個函數(shù)原型:

8c6f5fc2-0b7a-11ec-8fb8-12bb97331649.png

兩個函數(shù)均可用來打開文件,第二個函數(shù)比第一個多了參數(shù)mode,mode指定文件的權限---當創(chuàng)建新文件的時候才需要。如果文件打開成功,則返回文件描述符,指向pathname所指定的文件。flags參數(shù)用于指定打開的方式,它支持三種訪問模式:

8c7b926a-0b7a-11ec-8fb8-12bb97331649.png

flags參數(shù)還可以與下面的值進行按位或運算,修改打開文件的行為:

8c8c4a4c-0b7a-11ec-8fb8-12bb97331649.png

舉個例子,下面的句子表示:以寫的方式打開文件,如果文件不存在,則創(chuàng)建新的文件,并且文件的內(nèi)容為空:

int fd ;

fd = open("file.txt",O_WRONLY|O_CREAT|O_TRUNC,0644);

這里的0644指定了新創(chuàng)建的文件訪問權限,參數(shù)mode的取值如下:

8c97b33c-0b7a-11ec-8fb8-12bb97331649.png

實際上最終寫入磁盤的文件訪問權限是由mode參數(shù)和用戶的文件創(chuàng)建掩碼(umask)執(zhí)行按位與操作得到的。舉個例子:

8ca9a132-0b7a-11ec-8fb8-12bb97331649.png

按理來說,創(chuàng)建出來的文件的訪問權限應該是-rwxrwxrwx,而查看后發(fā)現(xiàn)其實不是:

ls -l TEST.txt

-rwxrwxr-x 1 huanzhewu huanzhewu 0 8月 30 21:29 TEST.txt【權限為0775】

查看當前的掩碼:

$ umask

0002

可以發(fā)現(xiàn) 0775 = 0777 ^ (~0002) ,所以0775才是最后的文件訪問權限。umask是進程級屬性,通過調用umask()函數(shù)來修改,支持用戶修改新創(chuàng)建的文件和目錄的權限。

總結起來可以得到這樣一條公式:

newmode = mode ^ (~ umask)

總結一下:至此,我們了解了文件打開所提供的兩個系統(tǒng)調用函數(shù)open(),了解了打開文件的方式、新建文件的訪問權限設置。如果文件打開成功,那么將返回一個文件描述符,這是一個非零整數(shù)(因為0,1,2是進行已經(jīng)擁有的文件描述符),否則函數(shù)將返回-1

creat()系統(tǒng)調用

顧名思義,creat函數(shù)用來創(chuàng)建一個文件,不過我們可能產(chǎn)生疑問:前面的open函數(shù)使用一些選項后,不是也可以創(chuàng)建新文件嗎?沒錯,creat函數(shù)完全等價與下面的open語句:

8cb24800-0b7a-11ec-8fb8-12bb97331649.png

由于選項O_WRONLY|O_CREAT|O_TRUNC組合經(jīng)常使用,因而系統(tǒng)調用專門使用creat函數(shù)來提供這個功能。creat函數(shù)的原型如下:

8cbdfdb2-0b7a-11ec-8fb8-12bb97331649.png

其中參數(shù)的描述與open的參數(shù)一致,這里不再贅述。

read()系統(tǒng)調用

文件打開后,就能夠讀文件了。read()是最基礎、最常見的讀取文件的機制。read的函數(shù)原型為:

8cd2fe10-0b7a-11ec-8fb8-12bb97331649.png

fd 為文件描述符。每次調用read函數(shù)時,會從fd指向的文件的當前偏移(或稱文件位置)開始讀取count字節(jié)到buf所指的的內(nèi)存中。隨著文件的讀取,fd的文件位置指針會向前移動。關于read的讀取,會出現(xiàn)很多需要思考的問題:

問題一:如果文件長度為0

問題二:如果文件長度不夠count長度

問題三:如果讀取時,read被信號中斷了

我們一一來看:

問題1屬于“沒有數(shù)據(jù)可讀”,此時read調用會阻塞,直到有數(shù)據(jù)可讀;

問題2屬于到達數(shù)據(jù)結尾(EOF),此時read調用返回0;

問題三,read調用返回大于0小于count的值;如果在讀取任何數(shù)據(jù)之前被信號中斷,則返回-1,同時把errno設置為EINTR。

8fdd37ba-0b7a-11ec-8fb8-12bb97331649.png

再來看看問題1,當文件沒有數(shù)據(jù)可以讀時(一開始就沒有),read調用會被阻塞,直到文件有數(shù)據(jù)可以讀,這是一種阻塞I/O。如果文件以O_NONBLOCK模式打開,則文件為非阻塞模式,當文件沒有數(shù)據(jù)可以讀時,read系統(tǒng)調用返回-1,并把errno設置為EAGAIN。

8fe9f2f2-0b7a-11ec-8fb8-12bb97331649.png

除了errno被設置為EINTR與EAGAIN,其他情況下都是出現(xiàn)嚴重的文件讀取錯誤,重新執(zhí)行讀操作不會成功。

write() 系統(tǒng)調用

write的函數(shù)原型為:

9000f858-0b7a-11ec-8fb8-12bb97331649.png

write的返回值比較簡單:

寫入失敗返回-1 ,同時設置errno的值

寫入成功返回成功寫入的字節(jié)數(shù)。

返回0時沒有特殊含義,僅表示寫入了0個字節(jié)的內(nèi)容。

對于普通文件,write基本能保證每次執(zhí)行調用能夠寫入全部的內(nèi)容。對于其他文件如socket,需要進行循環(huán)寫,保證所有的字節(jié)都寫入了文件中:

9014a24a-0b7a-11ec-8fb8-12bb97331649.png

同樣的,當以非阻塞的模式打開文件時(O_NONBLOCK),系統(tǒng)調用write()會返回-1,并把errno設置為EAGAIN。

系統(tǒng)調用write()時,數(shù)據(jù)從用戶空間的緩沖區(qū)中拷貝到了內(nèi)核空間的緩沖區(qū),但并沒有立即把數(shù)據(jù)寫入磁盤中,這稱為延遲寫。延遲寫的問題在于,如果在數(shù)據(jù)真正寫入磁盤之前系統(tǒng)崩潰了,則數(shù)據(jù)可能丟失。內(nèi)核設置了一個時間,在該時間內(nèi)將內(nèi)核空間緩沖區(qū)上的數(shù)據(jù)寫入磁盤,該時間稱為"最大存放時效"。Linux系統(tǒng)也支持強制文件立即寫入磁盤上,這在后面介紹。

close()系統(tǒng)調用

程序完成文件的讀寫后,調用close函數(shù)關閉文件描述符與文件之間的連接,使得文件描述符可以被重用。close的函數(shù)原型為:

#incldue

int close(int fd);

文件關閉成功返回0,出錯返回-1,并設置相應的errno。文件成功關閉并不以為著該文件的數(shù)據(jù)已經(jīng)被寫入磁盤。

總結:本文簡單介紹了文件的打開、創(chuàng)建、讀寫、關閉操作,介紹了一些常用的open參數(shù)選項,creat與open的等價性,循環(huán)讀、循環(huán)寫的必要性,也關注了各個系統(tǒng)調用的返回值含義,了解如何設置非阻塞讀寫,并簡單提到了延遲寫的問題,在后續(xù)的文件中將介紹同步I/O的內(nèi)容。

責任編輯:haq

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權轉載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學習之用,如有內(nèi)容侵權或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • Linux
    +關注

    關注

    87

    文章

    11204

    瀏覽量

    208699
  • 編程
    +關注

    關注

    88

    文章

    3563

    瀏覽量

    93535

原文標題:Linux系統(tǒng)編程:基本 I/O 系統(tǒng)調用

文章出處:【微信號:cyuyanxuexi,微信公眾號:C語言編程學習基地】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    Linux--IO多路復用(select,poll,epoll)

    導致的資源浪費和低效率問題。它通過將多個IO操作合并到一個系統(tǒng)調用,允許程序同時等待多個文件描述符(如sockets、
    的頭像 發(fā)表于 11-06 16:13 ?61次閱讀

    嵌入式學習-飛凌嵌入式ElfBoard ELF 1板卡-Linux C接口編程入門之文件I/O

    程之間的通信。管道和套接字也被視為文件,并通過文件描述符進行訪問。虛擬文件系統(tǒng)(Virtual File System):虛擬文件系統(tǒng)
    發(fā)表于 10-10 09:11

    飛凌嵌入式ElfBoard ELF 1板卡-Linux C接口編程入門之文件I/O

    程之間的通信。管道和套接字也被視為文件,并通過文件描述符進行訪問。虛擬文件系統(tǒng)(Virtual File System):虛擬文件系統(tǒng)
    發(fā)表于 10-09 15:38

    esp32-s2-soala-v1.2如何獲取攝像頭描述符?

    descriptors from PC side,eg. run `lsusb -v` in linux, 請問我該如何獲得所需usb描述符?
    發(fā)表于 06-27 06:48

    二維PDMA可以使用描述符鏈嗎?

    我正在嘗試使用二維描述符連鎖。 編寫了一些二維描述符鏈的代碼。 但我有一些疑問,比如 1.二維 PDMA 可以使用描述符鏈嗎? 2.如果 1 是,請附上一些代碼 我們是否可以使用 2 個結構或只使用 1 個結構即可。
    發(fā)表于 05-31 08:16

    FX3無法設置最小/最大比特率描述符值,為什么?

    我正試圖通過 FX3 以 5200*3900 分辨率、15fps 的速度從我的 fpga 傳輸視頻數(shù)據(jù)流,但無法設置最小/最大比特率描述符值,因為描述符大小只有 32 位。 描述符的預期值應該是
    發(fā)表于 05-21 06:36

    STM32 USB的字符串描述符不能顯示是哪里的問題?

    最近用STM32做了一個USB 的HID類設備,設備描述符中有指定廠商字符串索引、產(chǎn)品字符串索引、序列號索引。在獲取字符串的代碼部分也發(fā)現(xiàn)確實執(zhí)行了,但是插上電腦后卻沒有我想要的字符串,有哪位高手能指點一下。
    發(fā)表于 04-30 08:13

    USB OTG發(fā)送設備描述符失敗是什么原因呢?

    能收到SETUP包,并解析數(shù)據(jù)跑到發(fā)送設備描述符,給FIFO寫數(shù)據(jù)也正確,但波形上就發(fā)了一個數(shù)據(jù)出去,這會是什么原因呢?
    發(fā)表于 03-11 06:29

    為FX3應用程序實現(xiàn)可變USB配置描述符,開始編譯程序時報錯怎么解決?

    __heap_start” 和 “extern char __heap_end” 的錯誤。 我該如何解決這個 _sbrk 錯誤? 有沒有更好的方法來處理可變大小的 USB 描述符? 使用固定大小的數(shù)組,我 CAN 創(chuàng)建一個正確的描述符,但在設備管理器
    發(fā)表于 02-26 06:58

    CYUSB3KIT-003開發(fā)板cyfxusbuart例程,配置描述符無效枚舉失敗的原因?

    系統(tǒng):win11 驅動已正常安裝,出廠默認例程在I2C啟動及USB啟動下均可正常運行。 選擇USB啟動,下載cyfxusbuart固件后,設備管理器顯示配置描述符無效。 請問是否為驅動問題?
    發(fā)表于 02-23 06:02

    Traveo II上的P-DMA描述符總線錯誤是為什么?

    DMA,我就會收到\"描述符總線錯誤\"中斷,由 CH_STATUS 寄存器識別,但我不確定為什么。 如果出現(xiàn)這個錯誤,也許你可以給我更多關于背景的信息? 我正在按照應用手冊的說明
    發(fā)表于 01-31 07:03

    USB字符串描述符里面的序列號字符串到底是什么東西?

    在設備描述符里面,有一個表示序列號字符串描述符的編號,請問這個序列號字符串的描述符是什么東西呢? 廠商字符串和設備字符串都能理解,而且在電腦的“設備與打印機”里面都找到了。但請問這個序列號字符串有什么用呢?在哪里能看到呢?
    發(fā)表于 01-24 08:06

    在Win下,如何獲取對應插入的USB設備描述符呢?

    各位大佬,請問在Win下,如何獲取對應插入的USB設備描述符呢?
    發(fā)表于 01-24 07:33

    請問SPI DMA描述符列表如何單次觸發(fā)?

    要求如下: 定義兩個個描述符類型的數(shù)組,如 SpiTxDesptr[3]= {{ SpiTxDesptr[1], X, X,X,X},{ SpiTxDesptr[2], X, X,X,X
    發(fā)表于 01-12 08:07

    Linux文件系統(tǒng)層的主要結構

    描述符讀取一定數(shù)量的字節(jié)。 read 函數(shù)不了解文件系統(tǒng)的類型,比如 ext3 或 NFS。它也不了解文件系統(tǒng)所在的存儲媒體,比如 AT Attachment Packet Interface(ATAPI
    的頭像 發(fā)表于 11-10 10:37 ?529次閱讀
    <b class='flag-5'>Linux</b><b class='flag-5'>文件系統(tǒng)</b>層的主要結構