/***********************************************
管道(pipe)是Linux上進程間通信的一種方式,其是半雙工(數據流只能在一個方向上流動(還需要經過內核),及要麼是接收,要麼是發送),並且只能在父子進程 或 具有公共祖先的兩個子進程間通信。
管道(pipe)一般是在調用fork函數之前調用pipe函數創建,這樣的話,fork後子進程將得到父進程的兩個管道描述符副本(相當於調用了兩次dup)。
/***********************************************
相關函數:
#include <unistd.h>
int pipe(int fd[2])
返回值:成功返回0,失敗返回-1
***********************************************/
注意: 函數執行成功後爲參數返回兩個描述符,fd[0]爲讀而打 開,fd[1]爲寫而打開
很多系統在stat結構體st_size成員中儲存管道中可用於讀寫字節數。但這是不可移植的。(對於struct stat不瞭解的請百度)
使用管道有幾點注意事項:
1.當讀(read)一個寫(write)端已經關閉的管道,程序(read)將讀完管道中所有數據後返回0。
2.當寫(write)一個讀(write)端已經關閉的管道,將產SIGPIP信號,程序(write)返回-1,並將錯誤碼置爲EPIPE
3.如果管道中數據爲空,則(read)將永久堵塞直到有數據或寫(write)端關閉。
4.如果管道已被數據填滿,則(write)將會堵塞直到管道中有空與空間 或 寫端(read)關閉,(管道容量可用fpathconf函數得到,使用_PC_PIPE_BUF參數)。
***********************************************/
實例1:
使用管道實現 ls -l | wc -c 操作
分析:
執行ls -l後其結果將會被打印到終端(即使用到STDOUT_FILENO)標準輸出,wc -c(統計單詞數量)是從標準輸入中得到內容(即使用到STDIN_FILENO)然後進行統計。
思路:
子進程中調用exec函數族執行ls -l命令,並調用dup2函數將子進程中的fd[1]描述符複製爲標準輸出(STDOUT_FILENO),這用的話,但向標準輸出中寫數據是就等同於向管道中寫數據。
對於父進程則調用exec函數族執行wc -c命令,然後調用dup2將fd[0]複製爲標準輸入,
實例代碼:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
int main(int argc, char **argv)
{
pid_t pid;
int fd[2];
/**創建管道**/
if (pipe(fd) < 0) {
perror("pipe error");
return EXIT_FAILURE;
}
/**調用fork函數創建子進程**/
if ((pid = fork()) < 0) {
perror("fork error");
return EXIT_FAILURE;
} else if (0 == pid) {
close(fd[0]); /**關閉不必要的描述符**/
if (fd[1] != STDOUT_FILENO) {
if (dup2(fd[1], STDOUT_FILENO) != STDOUT_FILENO) {
perror("dup2 error to stdout");
return EXIT_FAILURE;
}
close(fd[1]);
}
execlp("ls", "ls", "-l", NULL);
exit(EXIT_SUCCESS);
}
close(fd[1]);
if (fd[0] != STDIN_FILENO) {
if (dup2(fd[0], STDIN_FILENO) != STDIN_FILENO) {
perror("dup2 error to stdin");
return EXIT_FAILURE;
}
close(fd[0]);
}
execlp("wc", "wc", "-c", NULL);
/**回收子進程的退出狀態,避免產生僵死進程**/
if (waitpid(pid, NULL, 0) < 0) {
perror("waitpid error");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
實例2:
實現分頁功能 即more命令
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#define PAGE "/bin/more"
#define MAXLINE 1024
int main(int argc, char **argv)
{
pid_t pid;
int fd[2];
char line[MAXLINE];
char *page = NULL, *temp = NULL, *arg = NULL;
FILE *fp = NULL;
if (argc != 2) {
printf("Usage: %s <filename>\n", argv[0]);
return EXIT_FAILURE;
}
if ((fp = fopen(argv[1], "r")) == NULL) {
perror("fopen error");
return EXIT_FAILURE;
}
if (pipe(fd) < 0) {
perror("pipe error");
return EXIT_FAILURE;
}
if ((pid = fork()) < 0) {
perror("fork error");
return EXIT_FAILURE;
} else if (0 == pid) {
close(fd[0]);
int len;
while (fgets(line, MAXLINE, fp) != NULL) {
len = strlen(line);
int offset = 0, n = 0;
while (len) {
n = write(fd[1], line+offset, len);
offset += n;
len -= n;
}
}
if (ferror(fp)) {
perror("fgets errro");
return EXIT_FAILURE;
}
fclose(fp);
close(fd[1]);
}
close(fd[1]);
if (fd[0] != STDIN_FILENO) {
if (dup2(fd[0], STDIN_FILENO) != STDIN_FILENO) {
perror("dup2 error to stdin");
return EXIT_FAILURE;
}
close(fd[0]);
}
/***得到PAGE對應的環境參數***/
if ((page = getenv("PAGE")) != NULL) {
arg = page;
} else {
arg = PAGE;
}
/**
*strrchr函數判斷'\'字符,
*在arg字符串中最後一次出現的位置
**/
if ((temp = strrchr(arg, '/')) != NULL) {
temp++;
} else {
temp = arg;
}
execl(arg, temp, NULL);
if (waitpid(pid, NULL, 0) < 0) {
perror("waitpid error");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}