導航:首頁 > 操作系統 > linux管道原理

linux管道原理

發布時間:2023-05-09 21:56:14

linux的管道後面一個橫線

表示輸出流。
管道是Linux中很重要的一種通信方式,是把一個程序的輸出直接連接到另一個程序的輸入,常說的管道多是指無名管道,無名管道只能用於具有親緣關系的進程之間,這是它與有名管道的最大區別。有名管道叫named pipe或者FIFO(先進先出),可以用函數mkfifo()創建。在Linux中,管道是一種使用非常頻繁的通信機制。從本質上說,管道也是一種文件,但它又和一般的文件有所不同,管道可以克服使用文件進行通信的兩個問題,具體表現為:
1、限制管道的大小。實際上,管道是一個固定大小的緩沖區。在Linux中,該緩沖區的大小為1頁,即4K位元組,使得它的大小不象文件那樣不加檢驗地增長。使用單個固定緩沖區也會帶來問題,比如在寫管道時可能變滿,當這種情況發生時,隨後對管道的write()調用將默認地被阻塞,等待某些數據被讀取,以便騰出足夠的空間供write()調用寫。
2、讀取進程也可能工作得比寫進程快。當所有當前進程數據已被讀取時,管道變空。當這種情況發生時,一個隨後的read()調用將默認地被阻塞,等待某些數據被寫入,這解決了read()調用返迴文件結束的問題。
注意:從管道讀數據是一次性操作,數據一旦被讀,它就從管道中被拋棄,釋放空間以便寫更多的數據。

❷ linux中的管道的本質到底是什麼呢

簡單來說,管道是一種兩個進程間進行單向通信的機制。因為管道傳遞數據的單向性,管道又稱為半雙工管道。管道的這一特點決定了器使用的局限性。管道是Linux支持的最初Unix IPC形式之一。

❸ linux 管道原理

Linux原理的學習,我打算由淺入深,從上之下,也就是先了解個大概再逐個深入。先了解一下Linux的進程先。

一、Linux進程上下文

Linux進程上下文,我理解就是進程組成元素的集合。包括進程描述符tast_struct,正文段,數據段,棧,寄存器內容,頁表等。

1)tast_struct

它是一種數據結構,存儲著進程的描述信息,例如pid,uid,狀態,信號項,打開文件表等。是進程管理和調度的重要依據。

2)用戶棧和核心棧

顧名思義,用戶棧是進程運行在用戶態使用的棧,含有用戶態執行時候函數調用的參數,局部變數等;核心棧是該進程運行在核心態下用的棧,保存調用系統函數所用的參數和調用序列。這兩個棧的指針都保存在tast_struct結構中。

3)寄存器

保存程序計數器,狀態字,通用寄存器,棧指針。

4)頁表

線性地址到物理地址的映射

5)正文段,數據段。

二、Linux進程的狀態

Linux中進程共有5個狀態:就緒,可中斷睡眠,不可中斷睡眠,暫停,僵死。也就是說,linux不區分就緒和運行,它們統一叫做就緒態。進程所處的狀態記錄在tast_struct中。

三、進程的控制

1)進程樹的形成

計算機啟動後,BIOS從磁碟引導扇區載入系統引導程序,它將Linux系統裝入內存,並跳到內核處執行,Linux內核就執行初始化工作:初始化硬體、初始化內部數據結構、建立進程0。進程0創建進程1,進程1是以後所有創建的進程的祖先,它負責初始化所有的用戶進程。進程1創建shell進程,shell進程顯示提示符,等待命令的輸入。

2)進程的創建

任何一個用戶進程的創建都是由現有的一個進程完成的,進程的創建要經過fork和exec兩個過程。Fork是為新進程分配相應的數據結構,並將父進程的相應上下文信息復制過來。Exec是將可執行文件的正文和數據轉入內存覆蓋它原來的(從父進程復制過來的),並開始執行正文段。

3)進程的終止

系統調用exit()就可自我終結,exit釋放除了tast_struct以外的所有上下文,父進程收到子進程終結的消息後,釋放子進程的tast_struct。

4)進程的調度

進程的調度是由schele()完成的,一種情況是,當處理機從核心態向用戶態轉換之前,它會檢查調度標志是否為1,如果是1,則運行schele(),執行進程的調度。另一種情況是進程自動放棄處理機,時候進行進程調度。

進程的調度過程分為兩步,首先利用相關策略選擇要執行的進程,然後進行上下文的切換。

四、進程的通信

進程的通信策略主要有,消息,管道,消息隊列,共享存儲區和信號量。

1)信息

消息機制主要是用來傳遞進程間的軟中斷信號,通知對方發生了非同步事件。發送進程將信號(約定好的符號)發送到目標進程的tast_struct中的信號項,接收進程看到有消息後就調用相應的處理程序,注意,處理程序必須到進程執行時候才能執行,不能立即響應。

2)管道

我理解就是兩個進程使用告訴緩沖區中的一個隊列(每兩個進程一個),發送進程將數據發送到管道入口,接收進程從管道出口讀數據。

3) 消息隊列

消息隊列是操作系統維護的一個個消息鏈表,發送進程根據消息標識符將消息添加到制定隊列中,接收進程從中讀取消息。

4)共享存儲區

在內存中開辟一個區域,是個進程共享的,也就是說進程可以把它附加到自己的地址空間中,對此區域中的數據進行操作。

5)信號量

控制進程的同步。

❹ Linux系統編程—管道

Linux 實現 IPC 其中的一種方式——管道
管道又分:
1、無名管道:無名管道只能用於有親緣關系的進程。
2、有名管道:有名管道用於任意兩進程間通信。

你就可以把管道理解成位於進程內核空間的「文件」。

給文件加引號,是因為它和文件確實很像,因為它也有描述符。但是它確實又不是普通的本地文件,而是一種抽象的存在。

當進程使用 pipe 函數,就可以打開位於內核中的這個特殊「文件」。同時 pipe 函數會返回兩個描述符,一個用於讀,一個用於寫。如果你使用 fstat函數來測試該描述符,可以發現此文件類型為 FIFO。

而無名管道的無名,指的就是這個虛幻的「文件」,它沒有名字。本質上,pipe 函數會在進程內核空間申請一塊內存(比如一個內存頁,一般是 4KB),然後把這塊內存當成一個先進先出(FIFO)的循環隊列來存取數據,這一切都由操作系統幫助我們實現了。

pipe 函數打開的文件描述符是通過參數(數組)傳遞出來的,而返回值表示打開成功(0)或失敗(-1)。
它的參數是一個大小為 2 的數組。此數組的第 0 個元素用來接收以讀的方式打開的描述符,而第 1 個元素用來接收以寫的方式打開的描述符。也就是說,pipefd[0] 是用於讀的,而 pipefd[1] 是用於寫的。
打開了文件描述符後,就可以使用 read(pipefd[0]) 和 write(pipefd[1]) 來讀寫數據了。

注意事項
1、這兩個分別用於讀寫的描述符必須同時打開才行,否則會出問題。
2、如果關閉讀 (close(pipefd[0])) 端保留寫端,繼續向寫端 (pipefd[1]) 端寫數據(write 函數)的進程會收到 SIGPIPE 信號。
3、如果關閉寫 (close(pipefd[1])) 端保留讀端,繼續向讀端 (pipefd[0]) 端讀數據(read 函數),read 函數會返回 0。

當在進程用 pipe 函數打開兩個描述符後,我們可以 fork 出一個子進程。這樣,子進程也會繼承這兩個描述符,而且這兩個文件描述符的引用計數會變成 2。

如果你需要父進程向子進程發送數據,那麼得把父進程的 pipefd[0] (讀端)關閉,而在子進程中把 pipefd[1] 寫端關閉,反之亦然。為什麼要這樣做?實際上是避免出錯。傳統上 pipe 管道只能用於半雙工通信(即一端只能發,不能收;而另一端只能收不能發),為了安全起見,各個進程需要把不用的那一端關閉(本質上是引用計數減 1)。
步驟一:fork 子進程

步驟二:關閉父進程讀端,關閉子進程寫端

父進程 fork 出一個子進程,通過無名管道向子進程發送字元,子進程收到數據後將字元串中的小寫字元轉換成大寫並輸出。

有名管道打破了無名管道的限制,進化出了一個實實在在的 FIFO 類型的文件。這意味著即使沒有親緣關系的進程也可以互相通信了。所以,只要不同的進程打開 FIFO 文件,往此文件讀寫數據,就可以達到通信的目的。

1、文件屬性前面標注的文件類型是 p
2、代表管道文件大小是 0
3、fifo 文件需要有讀寫兩端,否則在打開 fifo 文件時會阻塞

通過命令 mkfifo 創建

通過函數 mkfifo創建

函數返回 0 表示成功,-1 失敗。
例如:

cat 命令列印 test文件內容

接下來你的 cat 命令被阻塞住。
開啟另一個終端,執行:

然後你會看到被阻塞的 cat 又繼續執行完畢,在屏幕列印 「hello world」。如果你反過來執行上面兩個命令,會發現先執行的那個總是被阻塞。

有兩個程序,分別是發送端 send 和接收端面 recv。程序 send 從標准輸入接收字元,並發送到程序 recv,同時 recv 將接收到的字元列印到屏幕。
發送端

接收端

編譯

運行

因為 recv 端還沒打開test文件,這時候 send 是阻塞狀態的。
再開啟另一個終端:

這時候 send 端和 recv 端都在終端顯示has opend fifo
此時在 send 端輸入數據,recv 列印。

❺ linux關於管道說法錯誤的是什麼

Linux關於管道 原創
2018-09-14 12:22:41

Gaodes
碼齡5年
關注
管道的概念

管道是Unix中棗鎮蔽最古老的進程間通信的形式。 我們把從一個進程連接到另一個進程的一個數據流稱為一個「管道」 我們通常把是把一個進程的輸出連接或「管接」(經過管道來連接)到另一個進程的輸入。

管道特點

管道是半雙工的,數據只能向一個方向流動;需要雙方通信時,需要建立起兩個管道 只能用於父子進程或者兄弟進程之間(具有親凳州緣關系的進程)進行通信;通常,一個管道由一個進程創建,然後該進程調用fork,此後父、子進程之間就可應用該管道。

pipe函數

包含頭文件<unistd.h> 功能:創建一無名管道 原型

int pipe(int file_descriptor[2]);

參數 file_descriptor:文件描述符數組,其中file_descriptor[0]表示讀端,file_descriptor[1]表示寫端 返回值:成功返回0,失敗返回錯誤代碼

示例代碼:

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<signal.h>
#include<string.h>

int main(int argc,char *argv[])
{
int fd[2];
printf("f[0]=%d,f[1]=%d\n",fd[0],fd[1]);
pipe(fd);
printf("f[0]=%d,f[1]=%d\n",fd[0],fd[1]);

char buf[1024]={0};
int fid = fork();
if(fid > 0)
{
read(fd[0],buf,1024);
printf("read data %s\n",buf);
}
else if(fid == 0)
{

write(fd[1],"helloworld",strlen("helloworld"));

}
else
{
perror("fork error");
}
return 0;
}
列印結果

管道讀寫規則:如果試圖從管道寫端旅塌讀取數據,或者向管道讀端寫入數據都將導致錯誤發生 當沒有數據可讀時,read調用就會阻塞,即進程暫停執行,一直等到有數據來到為止。 如果管道的另一端已經被關閉,也就是沒有進程打開這個管道並向它寫數據時,read調用就會阻塞

復制文件描述符p

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<signal.h>

int main()
{
int fd = p(1);

printf("file fd= %d\n",fd);
write(fd,"helloworld",strlen("helloworld"));
return 0;
}
列印結果:

1為輸入到終端

shell管道的實現

原理通過把發的fd[1]寫復制到shell的1(標准輸入),fd[0]復制到shell的2(標准輸出)

以下是代碼:

#include<stdio.h>
#include<stdlib.h>
#include<fcntl.h>
#include<unistd.h>
#include<string.h>
#include<signal.h>

int main()
{
int fd[2];
char buf[1024] ={0};
pipe(fd);
int pid = fork();

if(pid > 0)
{
read(fd[0],buf,1024);
printf(buf);
}
else if(pid == 0)
{
p2(fd[1],1);
close(fd[0]);
close(fd[1]);
execlp("ls","ls","-al",NULL);
}
else
{

}
return 0;
}
實現結果:

popen函數

作用:允許一個程序把另外一個程序當作一個新的進程來啟 動,並能對它發送數據或接收數據

FILE* popen(const char *command, const char *open_mode);

command:待運行程序的名字和相應的參數 open_mode:必須是「r」或「w」 如果操作失敗,popen會返回一個空指針

以下代碼:

#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<string.h>

int main()
{
FILE *file = popen("ls -al","r");
char buf[1024] = {0};
fread(buf,1,1024,file);
fclose(file);

FILE *wcfile = popen("wc","w");
fwrite(buf,1,strlen(buf),wcfile);
fclose(wcfile);
return 0;
}
代碼結果:

命名管道破裂測試

我們首先要知道命名管道,要讀段和寫段同時開啟,才能向文件讀寫數據。

貼上代碼來理解命名管道的規則

首先是讀端:

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<signal.h>
#include<string.h>
#include<fcntl.h>

int main(int argc,char *argv[])
{
printf("open before\n");
int fd = open("/home/gao/tmp/fifo",O_RDONLY);
printf("open after\n");

//休眠5秒,讀端退出
sleep(5);
return 0;
}
接下來是寫端:

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<signal.h>
#include<string.h>
#include<fcntl.h>

void handle(int signo)
{
printf("cat signale = %d\n",signo);
}

int main(int argc,char *argv[])
{
signal(SIGPIPE,handle);
printf("open before\n");
int fd = open("/home/gao/tmp/fifo",O_WRONLY);
printf("open after\n");
//命名管道規則,如果寫入讀斷被中斷,寫入會返回-1,並且管道會破裂,產生信號(SIGPIPE)
while(1)
{
int wrsize = write(fd,"helloworld",strlen("helloworld"));
printf("size data:%d\n",wrsize);
sleep(1);
}
}
執行寫端:

它在等待另一端的開啟,才能向裡面寫入數據

此時我們開啟讀端:

馬上可以看到寫段可以寫數據

而執行5秒後,我們可以看到寫的時候返回-1,並且獲取到管道破裂的信息(SIGPIPE)

所以這里就是我們所注意的點,當我們寫客戶端和伺服器進行管道傳輸的時候,如果客戶端一旦退出來,就會使管道破裂,所以我們必須通過捕捉信號,來避免這種事情發生。

❻ 操作系統裡面的管道是什麼最好有詳細的例子說明,也能夠通俗地解釋就最好。謝~

以Linux下的為例吧,管道是一種進程間通信的方式,在linux中磨帶分為有名管道和無名管道。有名管道就是把一個進程的輸出寫到一個文件中,再把此判巧文件作為另一個進程的輸入。

無名管道也是如此,只不過這個管道文件不直接可見而已,通常無名管道都作為一個進程組的形式完成,如ls | grep 'a',這就是一種進程間單向的通信方式。

更詳細的建議看一下《Understanding Linux Kernel》第三版中進掘游鍵程通信的那章。

❼ linux中管道是指什麼,重定向是指什麼

管道是把一個命令的輸出作為下一個命令的輸入,
如 ls /etc | more
cat /etc/passwd | grep root
重定向是把一個命令的輸出重定向到另一個文件
如 echo 'hello' > abc.txt 一個大於號是抹除源文件內容並寫入,如果沒有這個文件就創建這個文件並寫入
echo 'hello world' >> /var/log/messages 兩個大於號是追加內容到這個文件,沒有這個文件就創建並寫入
cat /etc/sysconfig/network-scripts/ifc-eth0 >>abcd.txt

break 2>/dev/null

❽ Linux的管道命令如何使用

管道命令就是用來連接多條指令的,前一條指令的輸出流向會作為後一條指含掘鬧令的操作對象。
管道命令的操作符是:|,它只能處理由前面一條指令傳出的正確輸出信息,對錯誤信息是沒有直接處理能力的。然後,傳遞給下一條指令,作為操作對象。
基本格式:
指令1 | 指令2 | …
【指令1】正確輸出,作散廳為【指令2】的輸入,然後【指令2】的輸出作為【指令3】的輸入,如果【指令3】有輸出,那麼輸出談罩就會直接顯示在屏幕上面了。通過管道之後【指令1】和【指令2】的正確輸出是不顯示在屏幕上面的。
【提醒注意】
管道命令只能處理前一條指令的正確輸出,不能處理錯誤輸出;
管道命令的後一條指令,必須能夠接收標准輸入流命令才能執行。
使用示例
1、分頁顯示/etc目錄中內容的詳細信息
$ ls -l /etc | more
2、將一個字元串輸入到一個文件中
$ echo 「hello world」 | cat > hello.txt

❾ 淺談Android之Linux pipe/epoll

管道

管道的概念:

管道是一種最基本的IPC機制,作用於有血緣關系的進程之間臘仔掘,完成數據傳遞。調用pipe系統函數即可創建一個管道。有如下特質:

1. 其本質是一個偽文件(實為內核緩沖區)

2. 由兩個文件描述符引用,一個表示讀端,一個表示寫端。

3. 規定數據從管道的寫端流入管道,從讀端流出。

管道的原理: 管道實為內核使用環形隊列機制,藉助內核緩沖區(4k)實現。

管道的局限性:

① 數據自己讀不能自己寫。

② 數據一旦被讀走,便不在管道中存在,不可反復讀取。

③ 由於管道採用半雙工通信方式。因此,數據只能在一個方向上流動。

④ 只能在有公共祖先的進程間使用管道。

常見的通信方式有,單工通信、半雙工通信、全雙工通信。

簡單來說這個管道是一個文件,但又和普通輪核文件不通:管道緩沖區大小一般為1頁,即4K位元組,管道分讀端和寫端,讀端負責從管道拿數據,當數據為空時則阻塞;寫端向管道寫數據,當管道緩存區滿時則阻塞。

pipe函數

創建管道

    int pipe(int pipefd[2]); 成功:0;失敗:-1,設置errno

函數調用成功返回r/w兩個文件描述符。無需open,但需手動close。規定:fd[0] → r; fd[1] → w,就像0對應標准輸入,1對應標准輸出一樣。向管道文件讀寫數據其實是在讀寫內核緩沖區。

管道創建成功以後,創建該管道的進程(父進程)同時掌握著管道的讀端和寫端。如何實現父子進程間通信呢?通常可以採用如下步驟:

1. 父進程調用pipe函數創建管道,得到兩個文件描述符fd[0]、fd[1]指向管道的讀端和寫端。

2. 父進程調用fork創建子進程,那麼子進程也有兩個文件描述符指向同一管道。

3. 父進程關閉管道讀端,子進程關閉管道寫端。父進程可以向管道中寫入數據,子進程將管道中的數據讀出。由於管道戚芹是利用環形隊列實現的,數據從寫端流入管道,從讀端流出,這樣就實現了進程間通信。

管道的讀寫行為

    使用管道需要注意以下4種特殊情況(假設都是阻塞I/O操作,沒有設置O_NONBLOCK標志):

1. 如果所有指向管道寫端的文件描述符都關閉了(管道寫端引用計數為0),而仍然有進程從管道的讀端讀數據,那麼管道中剩餘的數據都被讀取後,再次read會返回0,就像讀到文件末尾一樣。

2. 如果有指向管道寫端的文件描述符沒關閉(管道寫端引用計數大於0),而持有管道寫端的進程也沒有向管道中寫數據,這時有進程從管道讀端讀數據,那麼管道中剩餘的數據都被讀取後,再次read會阻塞,直到管道中有數據可讀了才讀取數據並返回。

3. 如果所有指向管道讀端的文件描述符都關閉了(管道讀端引用計數為0),這時有進程向管道的寫端write,那麼該進程會收到信號SIGPIPE,通常會導致進程異常終止。當然也可以對SIGPIPE信號實施捕捉,不終止進程。具體方法信號章節詳細介紹。

4. 如果有指向管道讀端的文件描述符沒關閉(管道讀端引用計數大於0),而持有管道讀端的進程也沒有從管道中讀數據,這時有進程向管道寫端寫數據,那麼在管道被寫滿時再次write會阻塞,直到管道中有空位置了才寫入數據並返回。

總結:

① 讀管道: 1. 管道中有數據,read返回實際讀到的位元組數。

2. 管道中無數據:

(1) 管道寫端被全部關閉,read返回0 (好像讀到文件結尾)

  (2) 寫端沒有全部被關閉,read阻塞等待(不久的將來可能有數據遞達,此時會讓出cpu)

    ② 寫管道: 1. 管道讀端全部被關閉, 進程異常終止(也可使用捕捉SIGPIPE信號,使進程不終止)

2. 管道讀端沒有全部關閉:

(1) 管道已滿,write阻塞。

(2) 管道未滿,write將數據寫入,並返回實際寫入的位元組數。

Epoll的概念

Epoll可以使用一次等待監聽多個描述符的可讀/可寫狀態.等待返回時攜帶了可讀的描述符或者自定義的數據.不需要為每個描述符創建獨立的線程進行阻塞讀取,

Linux系統中的epoll機制為處理大批量句柄而作了改進的poll,是Linux下多路復用IO介面select/poll的增強版本,它能顯著減少程序在大量並發連接中只有少量活躍的情況下的系統CPU利用率

(01) pipe(wakeFds),該函數創建了兩個管道句柄。

(02) mWakeReadPipeFd=wakeFds[0],是讀管道的句柄。

(03) mWakeWritePipeFd=wakeFds 1 ,是寫管道的句柄。

(04) epoll_create(EPOLL_SIZE_HINT)是創建epoll句柄。

(05) epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, & eventItem),它的作用是告訴mEpollFd,它要監控mWakeReadPipeFd文件描述符的EPOLLIN事件,即當管道中有內容可讀時,就喚醒當前正在等待管道中的內容的線程。

回到Android中的epoll大致流程如下:​

Looper.loop -> MessageQueue.nativePollOnce

epoll_create()   epoll_ctl() 注冊事件的回調

looper.pollInner() -> epoll_wait() 等待接受事件喚醒的回調

 MessageQueue.enqueueMessage(Message msg, long when)    ->  MessageQueue.nativeWake(long ptr)

參考鏈接如下

鏈接:https://www.jianshu.com/p/8656bebc27cb

鏈接:https://blog.csdn.net/oguro/article/details/53841949

❿ Linux 系統中「|」管道的作用是什麼

「|」是管道命令操作符,簡稱管道符。利用Linux所提供的管道符「|」將兩個命令隔開,管道符左邊命令的輸出就會作為管道符右邊命令的輸入。連續使用管道意味著第一個命令的輸出會作為 第二個命令的輸入,第二個命令的輸出又會作為第三個命令的輸入,依此類推。

它僅能處理經由前面一個指令傳出的正確輸出信息,也就是 standard output 的信息,對於 standard error 信息沒有直接處理能力。

用法示例:

ls -l | more

該命令列出當前目錄中的文檔,並把輸出送給more命令作為輸入,more命令分頁顯示文件列表。

閱讀全文

與linux管道原理相關的資料

熱點內容
打開遠程桌面的命令 瀏覽:836
樹莓派如何搭建mqtt伺服器 瀏覽:587
怎麼加密w8文件 瀏覽:609
linuxprogram 瀏覽:708
php介面編程思想 瀏覽:92
如何下載電話軟體app 瀏覽:906
java命令行解析 瀏覽:572
雲伺服器白嫖 瀏覽:917
程序員小清新 瀏覽:989
編譯器地址8位元組對齊 瀏覽:464
三菱plc編程win1064 瀏覽:258
高中英語單詞pdf 瀏覽:425
編譯原理詞法分析常見問題 瀏覽:197
車小藝app怎麼更新 瀏覽:77
手機app被管控如何移除 瀏覽:753
51單片機溫濕度檢測 瀏覽:575
安卓抖音顯示沒網路是怎麼回事 瀏覽:818
2d我的世界源碼 瀏覽:620
怎樣製作貼天花板的解壓球 瀏覽:337
伺服器如何打開蘋果 瀏覽:97