① linux下的read系統調用,什麼時候,會返回0或者負數
傳輸層tcp收到fin,通知read,read返回0
傳輸層tcp收到fin,通知read,read返回0
② Linux 中的read系統調用到底是阻塞還是非阻
所謂阻塞,即當內核發現請求條件不滿足時(可能需要產生IO)將調用進程掛起,讓出CPU給需要的進程執行,提高效率,調用者進程被阻塞至條件滿足時再被喚醒。
我們來深入跟蹤read/write系統調用,因為Linux內核中對文件的讀寫採用了緩存,文件數據按照頁面(默認大小為4096位元組)為單位緩存在內存中,對於read系統調用,內核會根據應用程序發出的讀偏移在緩存中查找所讀位置對應的緩存頁面是否存在,如果存在,那麼萬事大吉,只需將數據從緩存頁面至用戶緩沖區即可,但如果此頁面尚未被緩存,那麼沒有別的辦法,只能從磁碟上讀出該頁面數據並緩存在內存中,所謂的讀過程,其實文件系統所需做的只是鎖定頁面,然後構造一個讀請求,並將請求發送給底層的IO子系統即可。文件系統發送完請求並不代表該頁面已經從磁碟中讀出,如果此時read系統調用返回,那就意味著該調用是非阻塞,不等IO完成即返回至調用者,但閱讀內核代碼發現,文件系統在發送完IO請求後並不立即返回,而是在接下來的流程中去嘗試鎖定該讀頁面,因為在前面文件系統發IO請求時頁面已經被鎖定,因此,如果頁面尚未被讀出的話,此時鎖定的話必然會阻塞,至此,我們就清楚了Linux內核中的read系統調用默認實現是阻塞方式。
③ 標准C的文件操作和Linux的系統調用open、read、write等的使用區別。
說說庫函數和系統調用的聯系和區別吧:
相同的,當然都是以C函數形式出現,呵,正因為這一點,才搞的有點混,不過還是有區別的,如下:
1) 系統調用時linux內核的對外介面,是用戶程序和內核只見唯一的介面,也是最小的介面,位於程序手冊(man)第二節
庫函數依賴於系統調用,提供交高級和復雜的介面,位於程序手冊第三節。
所以,標准C的文件操作也是依賴於如open,read,wite之類的系統調用,不過在較高層次上應用,增加緩沖區空值等。
④ linux下系統調用函數read()
open系統調用
open函數的三個參數:
(1)path是已經存在的文件的路徑;
(2)oflags參數:若值為 O_RDONLY ,就以只讀方式打開文件;
若值為 O_WDONLY,就以只寫方式打開文件;
若值為 O_RDWR,就以讀寫方式打開文件;
(3)參數mode:文件的許可權,對於一個已經存在的文件,參數mode是沒有用的,通常將其省略,因此這種情況下open調用只需兩個參數。
創建新文件:
前面已經說到,當文件不存在時,open會創建一個新文件(僅能是普通文件),我們只需要用 or操作向open的 oflags參數中加入標志O_CREAT即可。這樣可以創建一個新的只讀文件,但是這沒有任何意義,因為所創建的新文件沒有任何可讀內容。因此一般需要 O_CREAT與 O_WRONLY或 O_RDWR一起使用,此時就需要mode參數了。
例如:
int fd = open("/home/LY/newfile",O_RDWR | O_CREAT,MODE_FILE)
參數mode僅在創建新文件時有效,對於一個已經存在的文件,它沒有任何作用。
關於open的flags參數:
除了以上介紹的 open標志外,open還有許多標志,具體的如下表所示:
標志
解釋
O_RDONLY
只讀方式打開
O_WRONLY
只寫方式打開
O_RDWR
讀寫方式打開
O_APPEND
每次寫都追加到文件的尾端
O_CREAT
若文件不存在則創建文件
O_DSYNC
設置同步I/O方式
O_EXCL
如果文件已存在,則出錯;必須與O_CREAT一起使用
O_NOCTTY
不將此設備作為控制終端
O_NONBLOCK
不等待命名管道或特殊文件准備好
O_RSYNC
設置同步I/O方式
O_SYNC
設置同步I/O方式
O_TRUNC
將其長度截短為0
write系統調用
write函數的三個參數:
(1)fildes: 文件描述符
(2)buf:指定寫入數據的數據緩沖區
(3)nbytes:指定寫入的位元組數
函數返回值:
成功:已寫的位元組數
-1 :出錯
0:未寫入任何數據
例子:
運行結果:
read系統調用
read函數的三個參數:
(1)fildes:文件描述符
(2)buf:指定讀入數據的數據緩沖區
(3)nbytes:指定讀入的位元組數
函數返回值:
成功:已讀的位元組數
0:未讀入任何數據
-1:出錯
例子:
運行結果:
close系統調用
通過對 close進行分析,我們會發現close並沒有做什麼實質工作,它沒有刷新任何內核緩沖區,而僅僅是使文件描述符可以重用。
⑤ 如何在Linux內核里增加一個系統調用
一、Linux0.11下添加系統調用:x0dx0ax0dx0a我在bochs2.2.1中對linux0.11內核添加了一個新的系統調用,步驟如下: x0dx0a1./usr/src/linux/include/unistd.h中添加:#define __NR_mytest 87 x0dx0a然後在下面聲明函數原型:int mytest(); x0dx0a2./usr/src/linux/include/linux/sys.h中添加:extern int sys_mytest(); x0dx0a然後在sys_call_table中最後加上sys_mytest; x0dx0a3.在/usr/src/linux/kernel/sys.c中添加函數實現如下: x0dx0aint sys_mytest(){ x0dx0aprintk("This is a test!"); x0dx0areturn 123; x0dx0a} x0dx0a4.在/usr/src/linux/kernel/system_call.s中對系統調用號加1(原來是86改成了87) x0dx0a5.然後到/usr/src/linux目錄下編譯內核make clean; make Image x0dx0a6. cp /usr/src/linux/include/unistd.h /usr/include/unistd.h x0dx0a7. reset bochs x0dx0a8. 在/usr/root中生成test.c文件如下: x0dx0a#define __LIBRARY__ x0dx0a#include
⑥ linux read/write和fread/fwrite有什麼區別
read/write函數是Linux「系統調用」,Linux中系統調用相當於Windows平台API的概念,而fread/fwrite則是標准函數庫中提供的函數。相對於fread/fwrite庫函數,read/write系統調用是屬於更加底層的文件訪問,而與庫函數相比,系統調用的資源開銷要大些,這是因為系統調用更加底層而沒有緩沖機制,而且執行系統調用會馬上進行內核代碼和用戶代碼之間的切換。通常使用系統調用是讀寫大量的數據,盡量避免一次讀寫一個字元這樣的使用情況。而fread/fwrite庫函數是屬於更高層的介面,比如fwrite就提供輸出緩沖功能,所以使用fwrite函數時可以寫任意長度的數據。這就是它們的區別。
⑦ 如何解決linux文件系統read
解決方法
:使用fsck手動修復,具體操作如下:
使用root進入單用戶模式,運行
fsck.ext3
-y
/dev/vda3
說明:ext3的文件系統使用fsck.ext3,ext4文件系統使用fsck.etx4。/dev/vda3是系統/根分區。運行完畢後,reboot重啟系統就恢復正常。
⑧ Linux系統I/O操作與零拷貝
Linux中傳統的I/O操作是一種緩存I/O,I/O過程中產生的數據傳輸通常需要在緩沖區中進行多次拷貝。當應用程序需要訪問某個數據(read()操作)時,操作系統會先判斷這塊數據是否在內核緩沖區中,如果在內核緩沖區中找不到這塊數據,內核會先將這塊數據從磁碟中讀出來放到內核緩沖區中,應用程序再從緩沖區中讀取。當應用程序需要將數據輸出(write())時,同樣需要先將數據拷貝到輸出堆棧相關的內核緩沖區,再從內核緩沖區拷貝到輸出設備中。
以一次網路請求為例,如下圖。對於一次數據讀取,用戶應用程序只需要調用read()及write()兩個系統調用就可以完成一次數據傳輸,但這個過程中數據經過了四次拷貝,且數據拷貝需要由CPU來調控。在某些情況下,這些數據拷貝會極大地降低系統數據傳輸的性能,比如文件伺服器中,一個文件從磁碟讀取後不加修改地回傳給調用方,那麼這佔用CPU時間去處理這四次數據拷貝的性價比是極低的。
一次處理網路調用的系統I/O的流程:
以上可以發現,傳統的Linux系統I/O 操作要進行4次內核空間與應用程序空間的上下文切換,以及4次數據拷貝。
直接內存訪問(Direct Memory Access,DMA)是計算機科學中的一種內存訪問技術,允許某些電腦內部的硬體子系統獨立地讀取系統內存,而不需要中央處理器(CPU)的介入。在同等程度的處理器負擔下,DMA是一種快速的數據傳送方式。這類子系統包括硬碟控制器、顯卡、網卡和音效卡。
在Linux系統中,當應用程序需要讀取文件中的數據時,操作系統先分配一些內存,將數據從存儲設備讀入到這些內存中,然後再將數據傳遞應用進程;當需要往文件中寫數據時,操作系統先分配內存接收用戶數據,然後再將數據從內存寫入磁碟。文件cache管理就是對這些由操作系統分配並用開存儲文件數據的內存的管理。
在Linux系統中,文件cache分為兩個層面,page cache 與 Buffer cache,每個page cache包含若干個buffer cache。操作系統中,磁碟文件都是由一系列的數據塊(Block)組成,buffer cache也叫塊緩存,是對磁碟一個數據塊的緩存,目的是為了在程序多次訪問同一個磁碟塊時減少訪問時間;而文件系統對數據的組織形式為頁,page cache為頁緩存,是由多個塊緩存構成,其對應的緩存數據塊在磁碟上不一定是連續的。也就是說buffer cache緩存文件的具體內容--物理磁碟上的磁碟塊,加速對磁碟的訪問,而page cache緩存文件的邏輯內容,加速對文件內容的訪問。
buffer cache的大小一般為1k,page cache在32位系統上一般為4k,在64位系統上一般為8k。磁碟數據塊、buffer cache、page cache及文件的關系如下圖:
文件cache的目的是加快對數據文件的訪問,同時會有一個預讀過程。對於每個文件的第一次讀請求,系統會讀入所請求的頁面並讀入緊隨其後的幾個頁面;對於第二次讀請求,如果所讀頁面在cache中,則會直接返回,同時又一個非同步預讀的過程(將讀取頁面的下幾頁讀入cache中),如果不在cache中,說明讀請求不是順序讀,則會從磁碟中讀取文件內容並刷新cache。因此在順序讀取情況下,讀取數據的性能近乎內存讀取。
DMA允許硬體子系統直接將數據從磁碟讀取到內核緩沖區,那麼在一次數據傳輸中,磁碟與內核緩沖區,輸出設備與內核緩沖區之間的兩次數據拷貝就不需要CPU進行調度,CPU只需要進行緩沖區管理、以及創建和處理DMA。而Page Cache/Buffer Cache的預讀取機制則加快了數據的訪問效率。如下圖所示,還是以文件伺服器請求為例,此時CPU負責的數據拷貝次數減少了兩次,數據傳輸性能有了較大的提高。
使用DMA的系統I/O操作要進行4次內核空間與應用程序空間的上下文切換,2次CPU數據拷貝及2次DMA數據拷貝。
Mmap內存映射與標准I/O操作的區別在於當應用程序需要訪問數據時,不需要進行內核緩沖區到應用程序緩沖區之間的數據拷貝。Mmap使得應用程序和操作系統共享內核緩沖區,應用程序直接對內核緩沖區進行讀寫操作,不需要進行數據拷貝。Linux系統中通過調用mmap()替代read()操作。
同樣以文件伺服器獲取文件(不加修改)為例,通過mmap操作的一次系統I/O過程如下:
通過以上流程可以看到,數據拷貝從原來的4次變為3次,2次DMA拷貝1次內核空間數據拷貝,CPU只需要調控1次內核空間之間的數據拷貝,CPU花費在數據拷貝上的時間進一步減少(4次上下文切換沒有改變)。對於大容量文件讀寫,採用mmap的方式其讀寫效率和性能都比較高。(數據頁較多,需要多次拷貝)
註:mmap()是讓應用程序空間與內核空間共享DMA從磁碟中讀取的文件緩沖,也就是應用程序能直接讀寫這部分PageCache,至於上圖中從頁緩存到socket緩沖區的數據拷貝只是文件伺服器的處理,根據應用程序的不同會有不同的處理,應用程序也可以讀取數據後進行修改。重點是虛擬內存映射,內核緩存共享。
djk中nio包下的MappedByteBuffer,官方注釋為 A direct byte buffer whose content is a memory-mapped region of a file,即直接位元組緩沖區,其內容是文件的內存映射區域。 FileChannel是是nio操作文件的類,其map()方法在在實現類中調用native map0()本地方法,該方法通過mmap()實現,因此是將文件從磁碟讀取到內核緩沖區,用戶應用程序空間直接操作內核空間共享的緩沖區,Java程序通過MappedByteBuffer的get()方法獲取內存數據。
MappedByteBuffer允許Java程序直接從內存訪問文件,可以將整個文件或文件的一部分映射到內存中,由操作系統進行相關的請求並將內存中的修改寫入到磁碟中。
FileChannel map有三種模式
MappedByteBuffer的應用,以rocketMQ為例(簡單介紹)。
procer端發送消息最終會被寫入到commitLog文件中,consumer端消費時先從訂閱的consumeQueue中讀取持久化消息的commitLogOffset、size等內容,隨後再根據offset、size從commitLog中讀取消息的真正實體內容。其中,commitLog是混合部署的,所有topic下的消息隊列共用一個commitLog日誌數據文件,consumeQueue類似於索引,同時區分開不同topic下不同MessageQueue的消息。
rocketMQ利用MappedByteBuffer及PageCache加速對持久化文件的讀寫操作。rocketMQ通過MappedByteBuffer將日誌數據文件映射到OS的虛擬內存中(PageCache),寫消息時首先寫入PageCache,通過刷盤方式(非同步或同步)將消息批量持久化到磁碟;consumer消費消息時,讀取consumeQueue是順序讀取的,雖然有多個消費者操作不同的consumeQueue,對混合部署的commitLog的訪問時隨機的,但整體上是從舊到新的有序讀,加上PageCache的預讀機制,大部分情況下消息還是從PageCache中讀取,不會產生太多的缺頁中斷(要讀取的消息不在pageCache中)而從磁碟中讀取。
rocketMQ利用mmap()使程序與內核空間共享內核緩沖區,直接對PageCache中的文件進行讀寫操作,加速對消息的讀寫請求,這是其高吞吐量的重要手段。
使用mmap能減少CPU數據拷貝的次數,但也存在一些問題。
從Linux2.1開始,Linux引入sendfile()簡化操作。取消read()/write(),mmap()/write()。
調用sendfile的流程如下:
通過sendfile()的I/O進行了2次應用程序空間與內核空間的上下文切換,以及3次數據拷貝,其中2次是DMA拷貝,1次是CPU拷貝。sendfile相比起mmap,數據信息沒有進入到應用程序空間,所以能減少2次上下文切換的開銷,而數據拷貝次數是一樣的。
上述流程也可以看出,sendfile()適合對文件不加修改的I/O操作。
sendfile()只是減少應用程序空間與內核空間的上下文切換,並沒有減少CPU數據拷貝的次數,還存在一次內核空間的兩個緩沖區的數據拷貝。要實現CPU零數據拷貝,需要引入一些硬體上的支持。在上一小節的sendfile流程中,數據需要從內核緩沖區拷貝到內核空間socket緩沖區,數據都是在內核空間,如果socket緩沖區到網卡的這次DMA數據傳輸操作能直接讀取到內核緩沖區中的數據,那麼這一次的CPU數據拷貝也就能避免。要達到這個目的,DMA需要知道存有文件位置和長度信息的緩沖區描述符,即socket緩沖區需要從內核緩沖區接收這部分信息,DMA需要支持數據收集功能。
sendfile()調用後,數據從磁碟文件拷貝到內核緩沖區中,然後將文件位置和長度信息的緩沖區描述符傳遞到socket緩沖區,此時數據並沒有被拷貝。之後網卡子系統根據socket緩沖區中的文件信息利用DMA技術收集拷貝數據。整個過程進行了2次內核空間和應用程序空間的上下文切換,及2次DMA數據拷貝,CPU不需要參與數據拷貝工作,從而實現零拷貝。當然DMA收集拷貝功能需要硬體和驅動程序的支持。
在操作系統中,硬體和軟體之間的數據傳輸可以通過DMA來進行,DMA進行數據傳輸的過程幾乎不需要CPU參與,但是在內核緩沖區(頁緩存)與應用程序緩沖區之間的數據拷貝並沒有類似於DMA之類的工具可以使用,mmap、sendfile都是為了減少數據在內核空間與應用程序空間傳輸時的數據拷貝和上下文切換次數,有效地改善數據在兩者之間傳遞的效率。
linux操作系統的零拷貝技術並不單指某一種方式,現有的零拷貝技術種類非常多,在不同的Linux內核版本上有不同的支持。常見的,如果應用程序需要修改數據,則使用mmap(),如果只進行文件數據傳輸,則可選擇sendfile()。
另外,關於零拷貝技術適用於什麼場景?在上述的描述中,數據在傳遞過程中,除了mmap外,應用程序和操作系統幾乎是沒有改變數據的,mmap的內存映射也是沒有改變數據的,也就是說在靜態資源的讀取場景下,零拷貝更能發揮作用。正如其名,拷貝是在不改變數據的情況下,零是利用手段去減少CPU參與數據拷貝的次數,以釋放CPU去進行其他系統調用與計算。