導航:首頁 > 操作系統 > linux系統調用read

linux系統調用read

發布時間:2023-01-06 17:49:36

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 x0dx0a_syscall0(int,mytest) x0dx0aint main(){ x0dx0aint a; x0dx0aa = mytest(); x0dx0aprintf("%d", a); x0dx0areturn 0; x0dx0a} x0dx0a9.然後gcc test.c編譯之後運行a.out,前面所有步驟都通過,但是每次調用都是返回-1,然後我查過errno為1(表示操作不允許),就不知道為什麼了? x0dx0a系統知道的高手們能夠告知一下,不勝感激!這個問題困擾我很久了! x0dx0ax0dx0a二、新Linux內核添加系統調用x0dx0ax0dx0a如何在Linux系統中添加新的系統調用x0dx0a系統調用是應用程序和操作系統內核之間的功能介面。其主要目的是使得用戶可以使用操作系統提供的有關設備管理、輸入/輸入系統、文件系統和進程式控制制、通信以及存儲管理等方面的功能,而不必了解系統程序的內部結構和有關硬體細節,從而起到減輕用戶負擔和保護系統以及提高資源利用率的作用。x0dx0ax0dx0aLinux操作系統作為自由軟體的代表,它優良的性能使得它的應用日益廣泛,不僅得到專業人士的肯定,而且商業化的應用也是如火如荼。在Linux中,大部分的系統調用包含在Linux的libc庫中,通過標準的C函數調用方法可以調用這些系統調用。那麼,對Linux的發燒友來說,如何在Linux中增加新的系統調用呢? x0dx0a1 Linux系統調用機制x0dx0ax0dx0a在Linux系統中,系統調用是作為一種異常類型實現的。它將執行相應的機器代碼指令來產生異常信號。產生中斷或異常的重要效果是系統自動將用戶態切換為核心態來對它進行處理。這就是說,執行系統調用異常指令時,自動地將系統切換為核心態,並安排異常處理程序的執行。Linux用來實現系統調用異常的實際指令是:x0dx0ax0dx0aInt $0x80x0dx0ax0dx0a這一指令使用中斷/異常向量號128(即16進制的80)將控制權轉移給內核。為達到在使用系統調用時不必用機器指令編程,在標準的C語言庫中為每一系統調用提供了一段短的子程序,完成機器代碼的編程工作。事實上,機器代碼段非常簡短。它所要做的工作只是將送給系統調用的參數載入到CPU寄存器中,接著執行int $0x80指令。然後運行系統調用,系統調用的返回值將送入CPU的一個寄存器中,標準的庫子程序取得這一返回值,並將它送回用戶程序。x0dx0ax0dx0a為使系統調用的執行成為一項簡單的任務,Linux提供了一組預處理宏指令。它們可以用在程序中。這些宏指令取一定的參數,然後擴展為調用指定的系統調用的函數。x0dx0ax0dx0a這些宏指令具有類似下面的名稱格式:x0dx0ax0dx0a_syscallN(parameters)x0dx0ax0dx0a其中N是系統調用所需的參數數目,而parameters則用一組參數代替。這些參數使宏指令完成適合於特定的系統調用的擴展。例如,為了建立調用setuid()系統調用的函數,應該使用:x0dx0ax0dx0a_syscall1( int, setuid, uid_t, uid )x0dx0ax0dx0asyscallN( )宏指令的第1個參數int說明產生的函數的返回值的類型是整型,第2個參數setuid說明產生的函數的名稱。後面是系統調用所需要的每個參數。這一宏指令後面還有兩個參數uid_t和uid分別用來指定參數的類型和名稱。x0dx0ax0dx0a另外,用作系統調用的參數的數據類型有一個限制,它們的容量不能超過四個位元組。這是因為執行int $0x80指令進行系統調用時,所有的參數值都存在32位的CPU寄存器中。使用CPU寄存器傳遞參數帶來的另一個限制是可以傳送給系統調用的參數的數目。這個限制是最多可以傳遞5個參數。所以Linux一共定義了6個不同的_syscallN()宏指令,從_syscall0()、_syscall1()直到_syscall5()。x0dx0ax0dx0a一旦_syscallN()宏指令用特定系統調用的相應參數進行了擴展,得到的結果是一個與系統調用同名的函數,它可以在用戶程序中執行這一系統調用。x0dx0a2 添加新的系統調用 x0dx0a如果用戶在Linux中添加新的系統調用,應該遵循幾個步驟才能添加成功,下面幾個步驟詳細說明了添加系統調用的相關內容。x0dx0ax0dx0a(1) 添加源代碼x0dx0ax0dx0a第一個任務是編寫加到內核中的源程序,即將要加到一個內核文件中去的一個函數,該函數的名稱應該是新的系統調用名稱前面加上sys_標志。假設新加的系統調用為mycall(int number),在/usr/src/linux/kernel/sys.c文件中添加源代碼,如下所示:x0dx0aasmlinkage int sys_mycall(int number) x0dx0a{ x0dx0areturn number; x0dx0a}x0dx0a作為一個最簡單的例子,我們新加的系統調用僅僅返回一個整型值。x0dx0ax0dx0a(2) 連接新的系統調用x0dx0ax0dx0a添加新的系統調用後,下一個任務是使Linux內核的其餘部分知道該程序的存在。為了從已有的內核程序中增加到新的函數的連接,需要編輯兩個文件。x0dx0ax0dx0a在我們所用的Linux內核版本(RedHat 6.0,內核為2.2.5-15)中,第一個要修改的文件是:x0dx0ax0dx0a/usr/src/linux/include/asm-i386/unistd.hx0dx0ax0dx0a該文件中包含了系統調用清單,用來給每個系統調用分配一個唯一的號碼。文件中每一行的格式如下:x0dx0ax0dx0a#define __NR_name NNNx0dx0ax0dx0a其中,name用系統調用名稱代替,而NNN則是該系統調用對應的號碼。應該將新的系統調用名稱加到清單的最後,並給它分配號碼序列中下一個可用的系統調用號。我們的系統調用如下:x0dx0ax0dx0a#define __NR_mycall 191x0dx0ax0dx0a系統調用號為191,之所以系統調用號是191,是因為Linux-2.2內核自身的系統調用號碼已經用到190。x0dx0ax0dx0a第二個要修改的文件是:x0dx0ax0dx0a/usr/src/linux/arch/i386/kernel/entry.Sx0dx0ax0dx0a該文件中有類似如下的清單:x0dx0a.long SYMBOL_NAME()x0dx0ax0dx0a該清單用來對sys_call_table[]數組進行初始化。該數組包含指向內核中每個系統調用的指針。這樣就在數組中增加了新的內核函數的指針。我們在清單最後添加一行:x0dx0a.long SYMBOL_NAME(sys_mycall)x0dx0ax0dx0a(3) 重建新的Linux內核x0dx0ax0dx0a為使新的系統調用生效,需要重建Linux的內核。這需要以超級用戶身份登錄。x0dx0a#pwd x0dx0a/usr/src/linux x0dx0a#x0dx0ax0dx0a超級用戶在當前工作目錄(/usr/src/linux)下,才可以重建內核。x0dx0ax0dx0a#make config x0dx0a#make dep x0dx0a#make clearn x0dx0a#make bzImagex0dx0ax0dx0a編譯完畢後,系統生成一可用於安裝的、壓縮的內核映象文件:x0dx0ax0dx0a/usr/src/linux/arch/i386/boot/bzImage x0dx0a(4) 用新的內核啟動系統 x0dx0a要使用新的系統調用,需要用重建的新內核重新引導系統。為此,需要修改/etc/lilo.conf文件,在我們的系統中,該文件內容如下:x0dx0ax0dx0aboot=/dev/hda x0dx0amap=/boot/map x0dx0ainstall=/boot/boot.b x0dx0aprompt x0dx0atimeout=50 x0dx0ax0dx0aimage=/boot/vmlinuz-2.2.5-15 x0dx0alabel=linux x0dx0aroot=/dev/hdb1 x0dx0a read-only x0dx0ax0dx0aother=/dev/hda1 x0dx0alabel=dos x0dx0atable=/dev/hadx0dx0ax0dx0a首先編輯該文件,添加新的引導內核:x0dx0aimage=/boot/bzImage-new x0dx0alabel=linux-new x0dx0aroot=/dev/hdb1 x0dx0aread-onlyx0dx0ax0dx0a添加完畢,該文件內容如下所示:x0dx0aboot=/dev/hda x0dx0amap=/boot/map x0dx0ainstall=/boot/boot.b x0dx0aprompt x0dx0atimeout=50 x0dx0ax0dx0aimage=/boot/bzImage-new x0dx0alabel=linux-new x0dx0aroot=/dev/hdb1 x0dx0aread-only x0dx0ax0dx0aimage=/boot/vmlinuz-2.2.5-15 x0dx0alabel=linux x0dx0aroot=/dev/hdb1 x0dx0aread-only x0dx0ax0dx0aother=/dev/hda1 x0dx0alabel=dos x0dx0atable=/dev/hdax0dx0ax0dx0a這樣,新的內核映象bzImage-new成為預設的引導內核。為了使用新的lilo.conf配置文件,還應執行下面的命令:x0dx0a#cp /usr/src/linux/arch/i386/boot/zImage /boot/bzImage-newx0dx0ax0dx0a其次配置lilo:x0dx0ax0dx0a# /sbin/lilox0dx0ax0dx0a現在,當重新引導系統時,在boot:提示符後面有三種選擇:linux-new 、linux、dos,新內核成為預設的引導內核。x0dx0a至此,新的Linux內核已經建立,新添加的系統調用已成為操作系統的一部分,重新啟動Linux,用戶就可以在應用程序中使用該系統調用了。x0dx0ax0dx0a(5)使用新的系統調用x0dx0ax0dx0a在應用程序中使用新添加的系統調用mycall。同樣為實驗目的,我們寫了一個簡單的例子xtdy.c。x0dx0ax0dx0a/* xtdy.c */ x0dx0a#include x0dx0a_syscall1(int,mycall,int,ret) x0dx0amain() x0dx0a{ x0dx0aprintf("%d \n",mycall(100)); x0dx0a}x0dx0a編譯該程序:x0dx0a# cc -o xtdy xtdy.cx0dx0a執行:x0dx0a# xtdyx0dx0a結果:x0dx0a# 100x0dx0a注意,由於使用了系統調用,編譯和執行程序時,用戶都應該是超級用戶身份。

⑥ 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去進行其他系統調用與計算。

閱讀全文

與linux系統調用read相關的資料

熱點內容
連漲啟動源碼 瀏覽:161
小奔運動app網路異常怎麼回事 瀏覽:447
php開啟壓縮 瀏覽:303
伺服器主機如何設置啟動 瀏覽:282
linux配置網路命令 瀏覽:774
一張照片怎麼製作視頻app 瀏覽:908
pythonweb和php 瀏覽:976
電腦伺服器地址ip地址 瀏覽:823
對矩陣壓縮是為了 瀏覽:910
setfacl命令 瀏覽:172
linux子系統中斷 瀏覽:342
linux查看進程ps 瀏覽:224
知識庫系統php 瀏覽:623
小波變換壓縮圖像python 瀏覽:151
阿里巴巴程序員怎麼月入百萬 瀏覽:173
如何使用國外伺服器 瀏覽:188
燃燈者pdf 瀏覽:468
編譯器用數學嗎 瀏覽:7
圖形化apk反編譯工具 瀏覽:48
考勤表加密怎麼辦 瀏覽:736