主題:Linux管道知識
概要:進程間通信,管道相關的知識
編輯:新建 20151024
參考資料:
Linux程序設計,第四版,人民郵電出版社
1 什麼是管道 ?
當從一個進程連接數據流到另一個進程時,使用術語管道,表述把一個進程的輸出通過管道連接到另一個進程的輸入。
2 Popen調用
2.1 函數定義
popen和pclose函數是最簡單的在兩個程序之間傳遞數據的函數,原型如下:
#include <stdio.h>
FILE *popen(const char *command, const char *type);
int pclose(FILE *stream);
popen函數允許一個程序將另一個程序作爲新進程來啓動,並可以傳遞數據給它或者給它傳遞接收數據。
command:要調用程序的程序名和相應的參數
open_mode:”r”或”w”,”r”是指調用程序讀取被調用程序的輸出爲輸入;”w”是指調用程序能向被調用程序寫入數據。
pclose:關閉調用程序與被調用程序之間的數據流。
例1:
void Popen1()
{
FILE * read_fp;
char buffer[BUFSIZ+1];
int chars_read;
memset(buffer,'\0',sizeof(buffer));
read_fp=popen("uname -a","r");
if (read_fp!=NULL)
{
chars_read=fread(buffer,sizeof(char),BUFSIZ,read_fp);
if (chars_read>0)
{
printf("Out put is:%s \n",buffer);
printf("Total len is:%d\n",chars_read);
}
pclose(read_fp);
}
}
popen函數調用一個程序時,會先啓動一個shell,然後將command字符串作爲參數傳給它。這樣做一方面,好處是,通過popen能夠啓動非常複雜的shell命令,不必自己去完成shell擴展。壞處是,對每個popen調用,不僅要啓動一個被請求的程序,還要啓動一個shell,調用成本略高。
3 PIPE調用
3.1 函數定義
pipe函數提供了通過兩個文件描述符在兩個程序之間傳遞數據的功能,函數原型如下:
#include<unistd.h>
int pipe(int filedes[2])
參數爲兩個整數類型文件描述符組成的數組,該函數在數組中填上兩個新的文件描述符後返回值0 ,如果失敗返回1並設置errno來表明失敗的原因。兩個文件描述符以一種特殊的方式連接起來,寫到filedes[1]的數據都可以從filedes[0]讀出來。數據根據先進先出的順序進行處理。
例2:
一個簡單的寫入-讀出程序:
void Pipe1()
{
int date_processed;
int fife_pipes[2];
const char * some_data="who are you";
char buffer[BUFSIZ+1];
memset(buffer,'\0',sizeof(buffer));
if (pipe(fife_pipes)==0)
{
date_processed=write(fife_pipes[1],some_data,strlen(some_data));
printf("Wrote %d bytes\n",date_processed);
date_processed=read(fife_pipes[0],buffer,BUFSIZ);
printf("Read %d bytes,is :%s \n",date_processed,buffer);
}
}
例3:
結合fork調用,通過pipe函數能輕易的進行兩個進程間的數據傳遞。
//--通過pipe進行父子程序間通信
void Pipe2()
{
int date_processed;
int fife_pipes[2];
const char * some_data="who are you";
char buffer[BUFSIZ+1];
memset(buffer,'\0',sizeof(buffer));
pid_t fork_result;
if (pipe(fife_pipes)==0)
{
fork_result=fork();
if (fork_result==-1)
{
fprintf(stderr,"fork error");
}
else if (fork_result==0)
{
date_processed=read(fife_pipes[0],buffer,BUFSIZ);
printf("Read from [%d],total %d bytes,is :%s \n ",getpid(),date_processed,buffer);
}
else
{
date_processed=write(fife_pipes[1],some_data,strlen(some_data));
printf("Wrote to [%d],total %d bytes\n",getpid(),date_processed);
}
}
}
父進程向管道中寫數據,而子進程從管道中讀取數據,如圖所示:
例4:
fork函數只能在父子進行間通信,而通過調用exec可進行兩個完全不同的程序之間的通信。
寫-生產者程序:
void Pipe3()
{
int date_processed;
int fife_pipes[2];
const char * some_data="who are you";
char buffer[BUFSIZ+1];
memset(buffer,'\0',sizeof(buffer));
pid_t fork_result;
if (pipe(fife_pipes)==0)
{
fork_result=fork();
if (fork_result==-1)
{
fprintf(stderr,"fork error");
}
else if (fork_result==0)
{
sprintf(buffer,"%d",fife_pipes[0]);
execl("PipeClient","PipeClient",buffer,(char *) 0);
}
else
{
date_processed=write(fife_pipes[1],some_data,strlen(some_data));
printf("Wrote to [%d],total %d bytes\n",getpid(),date_processed);
}
}
}
讀-消費者程序 (PipeClient.c)
#include <unistd.h>
#include<stdlib.h>
#include<stdio.h>
#include <string.h>
int main(int argc,char * argv[])
{
int data_processed;
char buffer[BUFSIZ+1];
int file_descriptor;
memset(buffer,0,sizeof(buffer));
sscanf(argv[1],"%d",&file_descriptor);
data_processed=read(file_descriptor,buffer,BUFSIZ);
printf("%d -read %d bytes :%s/n",getpid(),data_processed,buffer);
exit(EXIT_SUCCESS);
}
生產者程序子進程中通過execl函數調用了PipeClient進程,把讀文件描述符作爲參數傳遞給被調用進程,消費者程序通過傳給它的讀描述符讀取數據。
4 FIFO命名管道
前面的進程通信都由一個共同的進程祖先啓動,而想在兩個完全不相關的進程間通信還是不很方便,可以用FIFO文件來完成這一功作,通常也被稱爲命名管道。命名管道是一種特殊類型的文件,在文件系統中以文件名的形式存在,行爲與上面沒有名字的管道類似。
函數原型:
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo( const char *pathname, mode_t mode );
第一個參數(pathname)是將要在文件系統中創建的一個專用文件。第二個參數(mode)用來規定FIFO的讀寫權限。Mkfifo函數如果調用成功的話,返回值爲0;如果調用失敗返回值爲-1。
例4:
創建一個命名管道
void Fifo1()
{
int res=mkfifo("/home/book/chapter13/my_fifo",0777);
if (0==res)
{
printf("FIFO created \n");
}else
{
printf("FIFO created error \n");
}
exit(EXIT_SUCCESS);
}
mkfifo函數創建一個特殊的文件,文件類型爲p,文件模式由參數mode和用戶掩碼(umask)設置共同確定。
4.1 使用open打開FIFO文件
open函數的原型爲:
#include <fcntl.h>
int open(const char *pathname, int oflag, ... );
oflag 用於指定文件的打開/創建模式。
使用O_RDONLY模式:
open(const char *pathname, O_RDONLY );
open調用將阻塞,除非一個進程以寫方式打開同一個FIFO,否則它不會返回。
使用O_RDONLY|O_NONBLOCK 模式:
open(const char *pathname, O_RDONLY |O_NONBLOCK );
即使沒有其他進程以寫方式打開FIFO,這個調用也能成功返回。
使用O_WDONLY模式:
open(const char *pathname, O_WDONLY );
open調用將阻塞,除非一個進程以讀方式打開同一個FIFO,否則它不會返回。
使用O_WDONLY|O_NONBLOCK 模式:
open(const char *pathname, O_WDONLY |O_NONBLOCK );
這個函數調用總是立刻返回,但如果沒有進程以讀方式打開FIFO,open調用將返回一個錯誤並且FIFO也不會被打開。如果確實有一個進程以讀方式打開FIFO,那麼可以通過它返回的文件描述符對這個FIFO文件進行寫操作。
例5:
創建一個生產者程序,它在需要時創建管道,並儘可能快的向管道中寫入數據。
void Fifo3()
{
int pipe_fd;
int res;
int open_mode=O_WRONLY;
int bytes_sent=0;
char buffer[BUFSIZ+1];
int end_tag=65535;
if(access(FIFO_NAME,F_OK)==-1)
{
res=mkfifo(FIFO_NAME,0777);
if (res!=0)
{
fprintf(stderr,"Created fifo error");
exit(EXIT_FAILURE);
}
}
printf("Prosess [%d] open fifo\n",getpid());
pipe_fd=open(FIFO_NAME,open_mode);
printf("Prosess [%d] open fifo result [%d]\n",getpid(),pipe_fd);
if (pipe_fd!=-1)
{
while(bytes_sent<end_tag)
{
res=write(pipe_fd,buffer,BUFSIZ);
if (res==-1)
{
fprintf(stderr,"Write error on pipe\n");
exit(EXIT_FAILURE);
}
bytes_sent+=res;
}
close(pipe_fd);
}
else
{
exit(EXIT_FAILURE);
}
printf("Prosess [%d] finished\n",getpid());
exit(EXIT_SUCCESS);
}
創建消費者模式,它從FIFO中讀取數據並拋棄它們。
#include <unistd.h>
#include<stdlib.h>
#include<stdio.h>
#include <string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#define FIFO_NAME "/home/book/chapter13/my_fifo"
int main(int argc,char * argv[])
{
int pipe_fd;
int res;
int open_mode=O_RDONLY;
int bytes_read=0;
char buffer[BUFSIZ+1];
memset(buffer,0,sizeof(buffer));
printf("Prosess [%d] open fifo\n",getpid());
pipe_fd=open(FIFO_NAME,open_mode);
printf("Prosess [%d] open fifo result [%d]\n",getpid(),pipe_fd);
if (pipe_fd!=-1)
{
do
{
res=read(pipe_fd,buffer,BUFSIZ);
bytes_read+=res;
} while (res>0);
close(pipe_fd);
}
else
{
exit(EXIT_FAILURE);
}
printf("Prosess [%d] finished,read [%d] bytes\n",getpid(),bytes_read);
exit(EXIT_SUCCESS);
}
作爲演示,上面的各函數均可以在main函數裏直接調用,如:
#include <unistd.h>
#include<stdlib.h>
#include<stdio.h>
#include <string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#define FIFO_NAME "/home/book/chapter13/my_fifo"
int main()
{
Fifo1();
return 0;
}