Linux學習記錄--匿名管道通訊

匿名管道通訊


管道是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;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章