❶ 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命令分頁顯示文件列表。