linux檔案IO操作

2021-07-25 08:44:02 字數 2484 閱讀 7432

說起linux程式設計來,甚至包括其他系統的程式設計,很大一部分都是處理io操作了(另乙個重要的部分是程序process)。特別是在linux系統上,由於一切都是檔案(fd)的思想,更是擴大了檔案io的範疇。比如磁碟上檔案的io,網路io,終端io,管道io等等。這些io操作的共同點在於都是對檔案描述符的操作,而且在最底層都可能是系統呼叫。當然如果我們考慮最多的兩類io,即檔案io和網路io,它們的區別也很大。

檔案io雖然也是系統呼叫,執行時會進入核心操作,但是往往速度是很快的,我們可以當成是乙個普通函式的呼叫。對檔案io的操作其實我們很容易理解,全部就涉及到開啟,讀,寫,關閉,定位到指定偏移量。對應的函式是open,read,write,close,lseek。值得注意的是這些操作都是系統呼叫,所以它們都是原子操作的,比如說write一段內容到某個檔案,我們不必擔心在此過程備中斷而另乙個任務去覆蓋write。但是這些操作的組合確不是原子的,比如先lseek到某個位置然後write一段內容,這兩個函式之間是有時間窗的,是有可能被中斷的。

檔案io在linux上是很底層的,所以linux又在上面做了一層封裝,提出了標準io的概念。標準io引入了流(file物件),這些讀寫檔案不再直接使用檔案描述符(fd)了,而用乙個file指標來操作檔案,這有個好處是file物件是自帶快取區的,可以不必多次的進行系統呼叫。在c語言裡,基本對檔案的操縱都是通過file物件在操作的,在c++裡更有檔案io的更高層封裝,比如說fstream。思想上都是使用了流的概念。

總體上說來,檔案io的複雜度是不高的,基本認為讀寫都可以成功,而且是同步的阻塞式的讀寫,遠遠沒有網路io的複雜度高。網路io我們這裡只討論tcp的操作,和檔案io類似,在應用層,對網路io也是通過乙個檔案描述符來操作的,函式也很類似,比如也有open,read,write,close,但沒有lseek,同時也多了一些操作函式,比如ioctl,fctnl等,由於得考慮雙工通道的單向關閉,又多了shutdown這樣的函式。說到底網路io和檔案io的最大區別在於它是個「低速」的系統,是有可能阻塞呼叫程序的。正是有了這個區別,才導致網路io異常的複雜,包括了同步,非同步,阻塞,非阻塞等模式。為了提高效率,核心又有了select和epoll這樣的系統呼叫來幫助我們。

網路程式設計的操作物件也是檔案描述符(fd),有一套稱之為socket函式的操作來圍繞這fd來處理網路io。比如要連線對方的程序,那麼必須本地先建立乙個

int fd = socket(af_inet,sock_stream,0);

還要去連線對端位址,對端的位址表示由ip+port來決定

sockaddr_in serv;

serv.sin_famiy = af_inet;

serv.sin_port = ????;

serv.sin_addr = ???;

connect(fd, (sockaddr*)&serv,sizeof(serv));

這裡都沒有處理錯誤。光這些就很複雜了。connect不成功的話,socket還要重新生成。connect也是會阻塞的,因為connect在tcp協議層是會先發syn包的,這期間是阻塞的(當然也可以設定成非阻塞模式,由epoll來監控),如果收到服務返回的ack+syn包,connect才會返回成功。如果不成功,協議層還會重試連線,這些都是應用層看不到的。所有這些都是因為網路是不可靠的,tcp是複雜的,所以網路io隨之也變得複雜起來。不像檔案io,可以像使用函式一樣來使用,而網路io必須處理失敗或者超時的情況。

目前來說主流的網路io都是用的io多路復用+非阻塞socket模式。注意,這也是同步的io,非同步的io必須是一些特殊的api,我們主流的方式都是同步方式。io多路復用就是select或epoll方式,更多的是epoll方式,我們在之前的文章有介紹過epoll(這裡不討論細節。epoll的思想是不是阻塞於某個socket的讀寫,而是阻塞於乙個新的系統呼叫,epoll_wait上,而這個系統呼叫的返回可以告訴我們有乙個或者多個fd可讀或可寫,注意,epoll可以wait的不僅僅是socket的fd了,各種fd都可以被它來監控。由於epoll的高效,於是one loop per thead這個程式設計概念備提出來了,就是在乙個執行緒上完成對成千上萬設定上幾十萬的fd的活動來監控。然後由一組處理執行緒來實現業務操作(比如技術,資料庫操作等)。這就是現代網路服務的基本模型,大同小異。

網路io的複雜性還在於資料是按流的方式來傳輸的,所以每次socket從核心提交給應用層的資料,你應用層不能立刻丟棄,因為可能不是你要的全部,你得儲存下來,等到若干次read後,可能才是乙個完整的包,所以一定要有應用層的buffer。這樣就不能像udp那樣單次無記憶的處理乙個請求來的容易。

網路io的複雜性還有一點是**於tcp協議的複雜性。比如三次握手,4次揮手,流量控制,no_delay設定,time_wait狀態(先發fin包惹來的麻煩),其他眾多的socket選項等,要全面理解可能才能寫出比較能把控的網路io程式來。

目前檔案io和網路io都是很成熟的技術了。通用高效的解決方案都有,我們自然也不必重新早輪子,找一些開源的專案就可以。但是我們若是理解了其中的原理和思想,對我們寫應用層也是很有幫助的。

Linux檔案I O操作

可以呼叫l s e e k顯式地定位乙個開啟檔案。include include off t lseek int filesdes,off t offset,int whence 返回 若成功為新的檔案位移,若出錯為 1。對引數offset 的解釋與引數w h e n c e 的值有關。若whenc...

Linux操作GPIO(檔案IO方式)

首先,看看系統中有沒有 sys class gpio 這個資料夾。如果沒有請在編譯核心的時候加入 device drivers gpio support sys class gpio sysfs inte ce sys class gpio 的使用說明 gpio operation 通過 sys 檔...

Linux系統IO目錄檔案操作

linux目錄檔案 維護著乙個鍊錶的資料結構,目錄檔案的結構和單向鍊錶的節點的類似,可利用鍊錶的遍歷方法來實現目錄檔案遍歷.但是直接使用目錄流物件不會得到乙個鍊錶的節點 因為是流物件 需要配合readdir指令來移動流的當前位置指標,並獲得相應的鍊錶節點 目錄下的檔案的資訊 include incl...