程序間通信-管道
程序間通信-管道
一、管道簡述
- 管道是Unix中最古老的
的形式。程序間通信
- 我們把從一個程序連接配接到另一個程序的一個資料流稱為一個“
”管道
- 我們通常把是把一個程序的
連接配接或“輸出
”(經過管道來連接配接)到另一個程序的管接
輸入
- 管道實作
:通信原理
- 每個程序各自有不同的使用者位址空間,任何一個程序的全局變量在另一個程序中都看不 到,是以程序之間要交換資料必須通過核心,在核心中開辟一塊緩沖區,程序1把資料從使用者空間拷到核心緩沖區,程序2再從核心緩沖區把資料讀走,核心提供的這種機制稱為程序間通信(IPC,InterProcess Communication)。
二、在shell中使用管道
-
:把一個程序的輸出直接饋入另一個的輸入,指令格式如下連結shell指令
command1 | command2 | command3
操作符是:”
|
”,它隻能處理經由前面一個指令傳出的
正确輸出資訊
,對
錯誤資訊
沒有直接處理能力。然後,
傳遞
給下一個指令,作為
标準的輸入
.
管理指令的輸出說明:
【指令1】 正确輸出,作為 【指令2】的輸入 然後**【指令2】的輸出作為【指令3】的輸入 ,【指令3】輸出就會直接顯示在螢幕**上面了。
通過管道之後【指令1】和【指令2】的正确輸出不顯示在螢幕上面
【提醒注意】:
- 管道指令隻處理前一個指令正确輸出,不處理錯誤輸出;
- 管道指令右邊指令,必須能夠接收标準輸入流指令才行;
舉例應用:
- 讀出logcat.log檔案的内容,通過管道轉發給grep作為輸入内容
cat logcat.log | grep –n ‘ActivitManager’
- who|grep tty|wc –l 顯示從控制台登入的人數
who|grep tty|wc –l
三、pipe管道
3.1- pipe函數說明
管道是一種最基本的IPC機制,可由pipe函數建立:
#include <unistd.h>
int pipe(int filedes[2]);
-
作用于pipe管道
的程序之間,通過有血緣關系
來傳遞fork
- 調用
時在pipe函數
中開辟一塊核心
緩沖區(稱為管道)
用于通信,它有一個讀端一個
寫端,然後通過
傳出給使用者程式兩個filedes參數
檔案描述符
,filedes[0]指向管道的讀
端,filedes[1]指向管道的寫端(很好記,就像0是标準輸入1是标準輸出一樣)。是以管道 在使用者程式看起來就像一個打開的檔案,通過
或者read(filedes[0]);
向這個檔案讀寫資料其實是在讀寫核心緩沖區。write(filedes[1]);
-
調用成功傳回 ,調用失敗傳回pipe函數
。-1
3.2- pipe函數實作通信的原理
- 1.
調用pipe開辟管道,得到兩個父程序
指向檔案描述符
。管道的兩端
- 2.父程序調用
建立fork
,那麼子程序也有兩個檔案描述符指向同一子程序
。管道
- 3.
。父程序可以往管道裡寫,子程序可以從管道裡讀,管道是用父程序關閉管道讀端,子程序關閉管道寫端
實作的,資料從寫端流入從讀端流出,這樣就實作了環形隊列
。程序間通信
3.3-應用示例: 父程序寫,子程序讀
#include <unistd.h>
#include <stdio.h>
int main()
{
int fd[2];
pid_t pid;
char str_write[1024] = "hello, I am content.";
char str_read[1024] = {0};
//建立管道,參數fd作為要傳出去的兩個檔案描述符
if(pipe(fd) < 0)
{
//建立管道失敗
perror("pipe");
exit(1);
}
//建立子程序
pid = fork();
//fd[0] 讀端 , fd[1] 寫端
//父程序
if(pid > 0)
{
close(fd[0]);//父程序 關閉讀端
write(fd[1],str_write,strlen(str_write));
wait(NULL);//立即阻塞父程序,等待子程序退出
}
//子程序
else if (pid == 0)
{
int len;
close(fd[1]);//子程序 關閉寫端
len = read(fd[0],str_read,sizeof(str_read));
write(STDOUT_FILENO,str_read,len);//把str_read輸出到終端
}
return 0;
}
3.4- 管道讀寫規則
- 如果試圖
,或者向管道讀端寫入資料都将導緻錯誤發生從管道寫端讀取資料
- 當
沒有資料可讀時
,read調用就會阻塞,即程序暫停執行,一直等到有資料來到為止。
如果管道的
,也就是沒有程序打開這個管道并向它寫資料時,read調用就會阻塞另一端已經被關閉
- 如果管道的
,則認為已經讀到了資料的末尾,讀函數傳回的讀出位元組數為0;寫端不存在
- 當管道的
時,如果請求的位元組數目大于PIPE_BUF,則傳回管道中現有的資料位元組數,如果請求的位元組數目不大于PIPE_BUF,則傳回管道中現有資料位元組數(此時,管道中資料量小于請求的資料量);或者傳回請求的位元組數(此時,管道中資料量不小于請求的資料量)。寫端存在
- 向管道中
時,linux将不保證寫入的寫入資料
,管道緩沖區一有空閑區域,寫程序就會試圖向管道寫入資料。如果讀程序不讀走管道緩沖區中的資料,那麼寫操作将一直阻塞。原子性
四、fifo有名管道
4.1-fifo有名管道簡述
-
應用的一個限制就是隻能在具有匿名管道
(具有共同祖先
親緣關系
)的程序間通信。
如果我們想在
的程序之間交換資料,可以使用不相關
來做這項工作,它經常被稱為FIFO檔案
。命名管道
- 命名管道可以從指令行上建立,指令行方法是使用下面這個指令:
-
$ mkfifo filename
-
- 命名管道也可以從程式裡建立,相關函數有:
-
int mkfifo(const char *filename,mode_t mode); ```
-
建立的mkfifo
的檔案屬性的第一位是FIFO檔案
p
4.2- FIFO的打開規則:
- 如果目前打開操作是為
而打開FIFO時,若已經有相應程序為讀
而打開該FIFO,則目前打開操作将成功傳回;否則,可能寫
(目前打開操作設定了阻塞标志);或者,成功傳回(目前打開操作沒有設定阻塞标志)。阻塞直到有相應程序為寫而打開該FIFO
- 如果目前打開操作是為
而打開FIFO時,如果已經有相應程序為寫
而打開該FIFO,則目前打開操作将成功傳回;否則,讀
(目前打開操作設定了阻塞标志);或者,傳回ENXIO錯誤(目前打開操作沒有設定阻塞标志)。可能阻塞直到有相應程序為讀而打開該FIFO
4.3-應用示例
用來寫FIFO檔案:fifo_w.c
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<string.h>
// write
void sys_err(char *str,int exitno)
{
perror(str);
exit(exitno);
}
int main(int argc, char*argv[])
{
int fd;
char buf[1024] = "hello world\n";
if(argc < 2)
{
printf("enter fifoname\n");
exit(1);
}
fd = open(argv[1],O_WRONLY);
if(fd<0)
{
sys_err("open",1);
}
write(fd,buf,strlen(buf));
close(fd);
return 0;
}
用來讀FIFO檔案:fifo_r.c
#include<stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<string.h>
#include<stdio.h>
#include<unistd.h>
//read
void sys_err(char *str,int exitno)
{
perror(str);
exit(exitno);
}
int main(int argc, char*argv[])
{
int fd;
char buf[1024];
int len;
if(argc < 2)
{
printf("enter fifoname\n");
exit(1);
}
fd = open(argv[1],O_RDONLY);
if(fd<0)
{
sys_err("open",1);
}
len= read(fd,buf,sizeof(buf));
write(STDOUT_FILENO,buf,len);
close(fd);
return 0;
}
終端上測試:
1.建立FIOF檔案
2.編譯檔案
3.在
終端1
上執行
fifo_r
(用來讀 建立的 fifo1檔案中的資料)
但是現在沒有程序為
寫
而打開該fifo1,是以終端1
被阻塞
4.打開終端2,執行
fifo_w
(用來向 建立的 fifo1檔案中寫入資料)
5.此時切換回
終端1
,可以看到已經讀到資料