匿名管道通訊
管道是Linux支持的最初Unix IPC形式之一,具有以下特點:
1.管道是半雙工的,數據只能向一個方向流動;需要雙方通信時,需要建立起兩個管道;
2.只能用於父子進程或者兄弟進程之間(具有親緣關係的進程);
什麼是管道
管道對於管道兩端的進程而言,就是一個文件,但它不是普通的文件,它不屬於某種文件系統,而是自立門戶,單獨構成一種文件系統,並且只存在與內存中。
數據的讀出和寫入
一個進程向管道中寫的內容被管道另一端的進程讀出。寫入的內容每次都添加在管道緩衝區的末尾,並且每次都是從緩衝區的頭部讀出數據。
管道的創建
#include int pipe(int fd[2]) |
管道兩端可分別用描述字fd[0]以及fd[1]來描述,需要注意的是,管道的兩端是固定了任務的。即一端只能用於讀,由描述字fd[0]表示,稱其爲管道讀端;另一端則只能用於寫,由描述字fd[1]來表示,
管道的規則
1. 當管道內容長度爲0時,讀端將處於阻塞狀態,等待寫端向管道寫入內容
2. 當寫端數據長度小於緩衝區長度時,數據將以原子性寫入緩衝區。
對讀進程來說:
3. 當寫端被關閉時,所有數據被讀出後,read返回0。
4. 當寫端未被關閉時,所有數據被讀出後,讀端阻塞。
對寫進程來說:
5. 當讀端關閉時,如寫端數據長度大於管道最大長度時,寫完管道長度時,產生信號SIGPIPE後退出程序。(以存入管道的數據讀進程可以讀取到)
6. 當讀端未被關閉時,如寫端數據長度大於管道最大長度時,寫完管道長度時,寫端將處於阻塞狀態
規則分析1
#include<unistd.h>
#include<stdio.h>
#include<string.h>
#include<sys/types.h>
#include<stdlib.h>
#include<sys/wait.h>
int main() {
int fd[2];
pid_t cid;
if (pipe(fd) == -1) {
perror("管道創建失敗!");
exit(1);
}
cid = fork();
switch (cid) {
case -1:
perror("子進程創建失敗");
exit(2);
break;
case 0:
close(fd[1]);
char message[1000];
int num = read(fd[0], message, 1000);
printf("子進程讀入的數據是:%s,長度是=%d", message, num);
close(fd[0]);
break;
default:
close(fd[0]);
char *writeMsg = "父進程寫入的數據!";
sleep(10);//1
write(fd[1], writeMsg, strlen(writeMsg));
close(fd[1]);
break;
}
return 0;
}
[root@ Release 18$] ps -C processcomm -opid,ppid,stat,cmd
PID PPID STAT CMD
5973 2488 S /root/workspace/processcomm/Release/processcomm
5976 5973 S /root/workspace/processcomm/Release/processcomm
=>讀端由於阻塞中,其所在進程(子進程)處於sleep狀態
控制檯輸出
父進程工作PID=5973,PPID=2488
子進程工作PID=5976,PPID=5973
子進程讀入的數據是:父進程寫入的數據!,長度是=27
規則分析2
switch (cid) {
case -1:
perror("子進程創建失敗");
exit(2);
break;
case 0:
printf("子進程工作PID=%d,PPID=%d\n", getpid(), getppid());
break;
default:
printf("父進程工作PID=%d,PPID=%d\n", getpid(), getppid());
close(fd[0]);
const long int writesize=4000;
char writeMsg[writesize];
int i;
for(i=0;i<writesize;i++)
{
writeMsg[i]='a';
}
int writenum=write(fd[1], writeMsg, strlen(writeMsg));
printf("父進程寫入的數據長度是=%d\n", writenum);
close(fd[1]);
wait(NULL);
break;
}
控制檯輸出
父進程工作PID=7072,PPID=2488
父進程寫入的數據長度是=4001
子進程工作PID=7077,PPID=7072
規則分析3
switch (cid) {
case -1:
perror("子進程創建失敗");
exit(2);
break;
case 0:
printf("子進程工作PID=%d,PPID=%d\n", getpid(), getppid());
close(fd[1]);
char message[40001];
int num = read(fd[0], message, 4001);
printf("子進程讀入的數據長度是=%d\n", num);
num = read(fd[0], message, 4000);
printf("子進程再次讀入的數據長度是=%d", num);
close(fd[0]);
break;
default:
printf("父進程工作PID=%d,PPID=%d\n", getpid(), getppid());
close(fd[0]);
const long int writesize = 4000;
char writeMsg[writesize];
int i;
for (i = 0; i < writesize; i++) {
writeMsg[i] = 'a';
}
int writenum = write(fd[1], writeMsg, strlen(writeMsg));
printf("父進程寫入的數據長度是=%d\n", writenum);
close(fd[1]);
// wait(NULL);
break;
}
[root@ Release30$] ps -C processcomm -o pid,ppid,stat,cmd
PID PPID STAT CMD
=>讀寫進程都已退出
控制檯輸出
父進程工作PID=8004,PPID=2488
父進程寫入的數據長度是=4001
子進程工作PID=8009,PPID=1
子進程讀入的數據長度是=4001
子進程再次讀入的數據長度是=0
規則分析4
switch (cid) {
case -1:
perror("子進程創建失敗");
exit(2);
break;
case 0:
printf("子進程工作PID=%d,PPID=%d\n", getpid(), getppid());
char message[40001];
int num = read(fd[0], message, 4001);
printf("子進程讀入的數據長度是=%d", num);
num = read(fd[0], message, 4000);
printf("子進程再次讀入的數據長度是=%d", num);
break;
default:
printf("父進程工作PID=%d,PPID=%d\n", getpid(), getppid());
close(fd[0]);
const long int writesize = 4000;
char writeMsg[writesize];
int i;
for (i = 0; i < writesize; i++) {
writeMsg[i] = 'a';
}
int writenum = write(fd[1], writeMsg, strlen(writeMsg));
printf("父進程寫入的數據長度是=%d\n", writenum);
close(fd[1]);
break;
}
[root@ Release29$] ps -C processcomm -o pid,ppid,stat,cmd
PID PPID STAT CMD
7916 1 S /root/workspace/processcomm/Release/processcomm
=>讀進程阻塞
控制檯輸出:
父進程工作PID=7914,PPID=2488
父進程寫入的數據長度是=4001
子進程工作PID=7916,PPID=1
規則分析5
switch (cid) {
case -1:
perror("子進程創建失敗");
exit(2);
break;
case 0:
printf("子進程工作PID=%d,PPID=%d\n", getpid(), getppid());
close(fd[1]);
char message[65535];
int num = read(fd[0], message, 65535);
printf("子進程讀入的數據長度是=%d", num);
close(fd[0]);
break;
default:
printf("父進程工作PID=%d,PPID=%d\n", getpid(), getppid());
close(fd[0]);
const long int writesize = 80000;
char writeMsg[writesize];
int i;
for (i = 0; i < writesize; i++) {
writeMsg[i] = 'a';
}
int writenum = write(fd[1], writeMsg, strlen(writeMsg));
printf("父進程寫入的數據長度是=%d\n", writenum);
close(fd[1]);
wait(NULL);
break;
}
[root@ Release25$] ps -C processcomm -o pid,ppid,stat,cmd
PID PPID STAT CMD
=>所有進程都以退出
控制檯輸出
父進程工作PID=7776,PPID=2488
子進程工作PID=7778,PPID=7776
子進程讀入的數據長度是=65535
規則分析6
switch (cid) {
case -1:
perror("子進程創建失敗");
exit(2);
break;
case 0:
printf("子進程工作PID=%d,PPID=%d\n", getpid(), getppid());
break;
default:
printf("父進程工作PID=%d,PPID=%d\n", getpid(), getppid());
const long int writesize=80000;
char writeMsg[writesize];
int i;
for(i=0;i<writesize;i++)
{
writeMsg[i]='a';
}
int writenum=write(fd[1], writeMsg, strlen(writeMsg));
printf("父進程寫入的數據長度是=%d\n", writenum);
wait(NULL);
break;
}
父進程工作PID=7309,PPID=2488
子進程工作PID=7314,PPID=7309
[root@ Release24$] ps -C processcomm -o pid,ppid,stat,cmd
PID PPID STAT CMD
7309 2488 S /root/workspace/processcomm/Release/processcomm
7314 7309 Z [processcomm]<defunct>
管道代碼舉例
1. 當發送信息小於管道最大長度
#include<unistd.h>
#include<stdio.h>
#include<string.h>
#include<sys/types.h>
#include<stdlib.h>
#include<sys/wait.h>
int main() {
int fd[2];
pid_t cid;
if (pipe(fd) == -1) {
perror("管道創建失敗!");
exit(1);
}
cid = fork();
switch (cid) {
case -1:
perror("子進程創建失敗");
exit(2);
break;
case 0:
printf("子進程工作PID=%d,PPID=%d\n", getpid(), getppid());
close(fd[1]);
char message[1000];
int num;
do {
num = read(fd[0], message, 1000);
printf("子進程讀入的數據長度是=%d\n", num);
} while (num != 0);
close(fd[0]);
break;
default:
printf("父進程工作PID=%d,PPID=%d\n", getpid(), getppid());
close(fd[0]);
const long int writesize = 37;
char writeMsg[writesize];
int i;
for (i = 0; i < writesize-1; i++) {
writeMsg[i] = 'a';
}
writeMsg[writesize-1]='\0';
int writenum = write(fd[1], writeMsg, strlen(writeMsg)+1);
printf("父進程寫入的數據長度是=%d\n", writenum);
close(fd[1]);
break;
}
return 0;
}
2. 當發送信息大於管道最大長度
此例子主要應該規則6,當發送信息大於管道長度時且寫進程在未全部將新數據寫入管道中,寫進程處於阻塞狀態,直到所有數據寫入管道
int main() {
int fd[2];
pid_t cid;
if (pipe(fd) == -1) {
perror("管道創建失敗!");
exit(1);
}
cid = fork();
switch (cid) {
case -1:
perror("子進程創建失敗");
exit(2);
break;
case 0:
printf("子進程工作PID=%d,PPID=%d\n", getpid(), getppid());
close(fd[1]);
char message[1000];
int num;
do {
num = read(fd[0], message, 1000);
printf("子進程讀入的數據長度是=%d\n", num);
}while(num!=0);
close(fd[0]);
break;
default:
printf("父進程工作PID=%d,PPID=%d\n", getpid(), getppid());
const long int writesize = 80000;
char writeMsg[writesize];
int i;
for (i = 0; i < writesize-1; i++) {
writeMsg[i] = 'a';
}
writeMsg[writesize-1]='\0';
int writenum = write(fd[1], writeMsg, strlen(writeMsg)+1);
printf("父進程寫入的數據長度是=%d\n", writenum);
close(fd[0]);
close(fd[1]);
break;
}
return 0;
}
3. 寫進程多次寫入
此例應用規則6,防止多次寫入,寫入數據長度管道最大長度int main() {
int fd[2];
pid_t cid;
if (pipe(fd) == -1) {
perror("管道創建失敗!");
exit(1);
}
cid = fork();
switch (cid) {
case -1:
perror("子進程創建失敗");
exit(2);
break;
case 0:
printf("子進程工作PID=%d,PPID=%d\n", getpid(), getppid());
close(fd[1]);
char message[1000];
int num;
do {
num = read(fd[0], message, 1000);
if (num > 0) {
printf("子進程讀入的數據長度是=%d %s\n", num, message);
}
} while (num != 0);
close(fd[0]);
break;
default:
printf("父進程工作PID=%d,PPID=%d\n", getpid(), getppid());
const long int writesize = 10;
char writeMsg[writesize];
int i;
for (i = 0; i < writesize - 1; i++) {
writeMsg[i] = 'a';
}
writeMsg[writesize - 1] = '\0';
int writenum = write(fd[1], writeMsg, strlen(writeMsg));
printf("父進程寫入的數據長度是=%d\n", writenum);
char *newmsg = "helloworld";
writenum = write(fd[1], newmsg, strlen(newmsg) + 1);
printf("父進程再次寫入的數據長度是=%d\n", writenum);
close(fd[0]);
close(fd[1]);
break;
}
return 0;
}
4. 兄弟間的管道通訊
int main() {
int fd[2];
pid_t cid, did;
if (pipe(fd) == -1) {
perror("管道創建失敗!");
exit(1);
}
cid = fork();
switch (cid) {
case -1:
perror("兄進程創建失敗");
exit(2);
break;
case 0:
printf("兄進程工作PID=%d,PPID=%d\n", getpid(), getppid());
close(fd[1]);
char message[1000];
int num;
do {
num = read(fd[0], message, 1000);
if (num > 0) {
printf("兄進程讀入的數據長度是=%d,%s\n", num, message);
}
} while (num != 0);
close(fd[0]);
break;
default:
did = fork();
if (did == 0) {
printf("弟進程工作PID=%d,PPID=%d\n", getpid(), getppid());
const long int writesize = 10;
char writeMsgs[writesize];
int i;
for (i = 0; i < writesize - 1; i++) {
writeMsgs[i] = 'a';
}
writeMsgs[writesize - 1] = '\0';
int writenum = write(fd[1], writeMsgs, strlen(writeMsgs) + 1);
printf("弟進程寫入的數據長度是=%d\n", writenum);
close(fd[0]);
close(fd[1]);
} else if (did == -1) {
perror("弟進程創建失敗!");
exit(3);
}
break;
}
return 0;
}
5. 父子雙通道管道通訊
int main() {
int fd[2], backfd[2];
pid_t cid;
if (pipe(fd) == -1) {
perror("管道創建失敗!");
exit(1);
}
if (pipe(backfd) == -1) {
perror("管道創建失敗!");
exit(2);
}
cid = fork();
switch (cid) {
case -1:
perror("子進程創建失敗");
exit(2);
break;
case 0:
printf("子進程工作PID=%d,PPID=%d\n", getpid(), getppid());
close(fd[1]);
char message[10000];
int num;
do {
num = read(fd[0], message, 10000);
printf("子進程讀入的數據長度是=%d\n", num);
} while (num != 0);
close(fd[0]);
close(backfd[0]);
char *msg1 = "消息返回成功啊!";
write(backfd[1], msg1, strlen(msg1) + 1);
close(backfd[1]);
break;
default:
printf("父進程工作PID=%d,PPID=%d\n", getpid(), getppid());
const long int writesize = 80000;
char writeMsg[writesize];
int i;
for (i = 0; i < writesize - 1; i++) {
writeMsg[i] = 'a';
}
writeMsg[writesize - 1] = '\0';
int writenum = write(fd[1], writeMsg, strlen(writeMsg) + 1);
printf("父進程寫入的數據長度是=%d\n", writenum);
close(fd[0]);
close(fd[1]);
close(backfd[1]);
char msg2[1000];
int num1 = read(backfd[0], msg2, 1000);
printf("返回消息是:%s", msg2);
close(backfd[0]);
break;
}
return 0;
}