前言:進程之間交換信息的唯一方法是經由f o r k或e x e c傳送打開文件,或通過文件系統。本章將說明進程之間相互通信的其他技術—I P C(InterProcess Communication)。今天將介紹半雙工的管道。 一、匿名管道 1、匿名管道介紹: 管道有兩種限制; (1) 它們是半雙 ...
前言:進程之間交換信息的唯一方法是經由f o r k或e x e c傳送打開文件,或通過文件系統。本章將說明進程之間相互通信的其他技術—I P C(InterProcess Communication)。今天將介紹半雙工的管道。
一、匿名管道
1、匿名管道介紹:
管道有兩種限制;
(1) 它們是半雙工的。數據只能在一個方向上流動。
(2)它們只能在具有公共祖先的進程之間使用。通常,一個管道由一個進程創建,然後該進程調用f o r k,此後父、子進程之間就可應用該管道。
管道是由調用p i p e函數而創建的:
#include <unistd.h>
int pipe(intf i l e d e s [ 2 ]) ;
返回:若成功則為0,若出錯則為 - 1
經由參數f i l e d e s返回兩個文件描述符: f i l e d e s [ 0 ]為讀而打開, f i l e d e s [ 1 ]為寫而打開。 f i l e d e s [ 1 ]
的輸出是f i l e d e s [ 0 ]的輸入。
程式1- 1創建了一個從父進程到子進程的管道,並且父進程經由該管道向子進程傳送數據。
#include "my.h" int main() { int pfd[2],ret; ret = pipe(pfd);//創建管道 if(ret<0) { perror("pipe error!"); exit(-1); } pid_t pid = fork(); if(pid<0) { perror("fork error!"); exit(-1); } else if(pid>0)//父進程 { close(pfd[0]); int num; puts("please input your num:"); scanf("%d",&num); write(pfd[1],&num,sizeof(num)); //wait(NULL); } else //子進程 { close(pfd[1]); int num; read(pfd[0],&num,sizeof(num)); printf("num:%d\n",num); } return 0; }
註:頭文件my.h見這篇博客:http://www.cnblogs.com/liudw-0215/p/8946879.html
運行示例,如下圖:
接下來介紹幾個跟管道有關的函數。
2、dup和d u p 2函數
下麵兩個函數都可用來複制一個現存的文件描述符:
#include <unistd.h>
int dup(intf i l e d es) ;
int dup2(int f i l e d e s, int f i l e d e s 2) ;
兩函數的返回:若成功為新的文件描述符,若出錯為- 1
由d u p返回的新文件描述符一定是當前可用文件描述符中的最小數值。用 d u p 2則可以用f i l e d e s 2
參數指定新描述符的數值。如果 f i l e d e s 2已經打開,則先將其關閉。如若f i l e d e s等於f i l e d e s 2,則
d u p 2返回f i l e d e s 2,而不關閉它。
優化程式1-1,程式1-2如下:
#include "my.h" int main() { int pfd[2],ret; ret = pipe(pfd); if(ret<0) { perror("pipe error!"); exit(-1); } pid_t pid = fork(); if(pid<0) { perror("fork error!"); exit(-1); } else if(pid>0) { close(pfd[0]); int num; puts("please input your num:"); scanf("%d",&num); write(pfd[1],&num,sizeof(num)); wait(NULL); } else { close(pfd[1]); dup2(pfd[0],STDIN_FILENO);//pfd[0]複製到標準輸入 int num; read(STDIN_FILENO,&num,sizeof(num)); printf("num:%d\n",num); } return 0; }
3、popen和p c l o s e函數
因為常見的操作是創建一個連接到另一個進程的管道,然後讀其輸出或向其發送輸入,所
以標準I / O庫為實現這些操作提供了兩個函數 p o p e n和p c l o s e。這兩個函數實現的操作是:創建
一個管道, f o r k一個子進程,關閉管道的不使用端, e x e c一個s h e l l以執行命令,等待命令終止。
#include <stdio.h>
FILE *popen(const char * c m d s t r i n g, const char * t y p e) ;
返回:若成功則為文件指針,若出錯則為 N U L L
int pclose(FILE * f p) ;
返回: c m d s t r i n g的終止狀態,若出錯則為 - 1
函數popen 先執行f o r k,然後調用e x e c以執行c m d s t r i n g,並且返回一個標準 I / O文件指針。
如果t y p e是"r",則文件指針連接到c m d s t r i n g的標準輸出。
如果t y p e 是"w",則文件指針連接到c m d s t r i n g 的標準輸入。
程式1-3將用popen函數實現下圖功能:
#include "my.h" int main() { int c; while((c = getchar()) != EOF) { if(isupper(c)) //是否有大寫 c = tolower(c); //轉為小寫 if(putchar(c) == EOF) puts("put error!"); if(c == '\n') fflush(stdout); //刷新標準輸出 } exit(0); }
---isupper.c---
#include "my.h" #define MAXLINE 4096 int main() { char line[MAXLINE]; FILE *fpin; if((fpin = popen("./upper","r")) == NULL) perror("popen error!"); for(;;){ fputs("prompt > ",stdout); fflush(stdout); if(fgets(line,MAXLINE,fpin) == NULL) break; if(fputs(line,stdout) == EOF) perror("puts error!"); } if(pclose(fpin) == -1) perror("pclose error!"); putchar('\n'); return 0; }
---popen.c---
運行演示如下圖:
二、有名管道(命名管道)
1、簡介
命名管道有時被稱為FIFO。管道只能由相關進程使用,它們共同的祖先進程創建了管道。
但是,通過F I F O,不相關的進程也能交換數據。
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char * p a t h n a m e, mode_tm o d e) ;
返回:若成功則為0,若出錯則為 - 1
m k f i f o函數中m o de參數的規格說明與o p e n函數中的m o d e相同。
一旦已經用 m k f i f o創建了一個 F I F O,就可用 o p e n打開它。確實,一般的文件 I / O函數
(c l o s e、 r e a d、 w r i t e、 u n l i n k等)都可用於F I F O。
程式2-1演示有名管道通信,一個寫端、一個讀端:
#include "my.h" typedef struct{ char name[16]; int age; double height; }Person; int main() { mkfifo("pipe",0644);//創建管道 名字為pipe int fd = open("pipe",O_WRONLY); if(fd < 0) { perror("open error!"); exit(-1); } Person p; puts("please input your name,age,height:"); scanf("%s%d%lf",p.name,&p.age,&p.height); write(fd,&p,sizeof(p)); close(fd); return 0; }
---fwrite.c---
#include "my.h" typedef struct{ char name[16]; int age; double height; }Person; int main() { int fd = open("pipe",O_RDONLY); if(fd < 0) { perror("open error!"); exit(-1); } Person p; read(fd,&p,sizeof(p)); printf("name:%-5sage:%-5dheight:%-5lf\n ",p.name,p.age,p.height); close(fd);
unlink("pipe");//刪除管道文件 return 0; }
--- fread.c ---
運行演示:先編譯fwrite.c生成w可執行用戶,./w執行,再編譯fread.c然後執行,寫端輸入數據,讀端輸出數據:
總結:主要介紹了進程間通信的管道,主要分為匿名管道和有名管道,還介紹了popen等函數