管道是一種最基本的IPC機制,由pipe函數創建:
#include <unistd.h>
int pipe(int filedes[2]);
調用pipe函數就是在內核區開辟一塊緩沖區(稱為管道)。filedes[0]指向管道的讀端,filedes[1]指向管道的寫端。管道實際上就是一個打開的文件。pipe函數成功返回0,失敗返回-1.
如何用管道實現兩個進程間的通信?
1.父進程調用pipe函數開辟管道,得到兩個文件描述符指向管道的兩端。
2.父進程調用fork()創建子進程,那麼子進程也有兩個文件描述符指向該管道。
3.父進程關閉管道讀端,子進程關閉管道寫端。父進程可以往管道寫,子進程可以從管道
讀,管道是環形隊列實現的,數據從寫端流,從讀端流出,這樣就實現了進程間通信。
代碼實現:
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
int main()
{
int _pipe[2];
int ret = pipe(_pipe);
if (ret == -1){
printf("create pipe error! errno code is : %d\n", errno);
return 1;
}
pid_t id = fork();
if (id < 0){
printf("fork error!");
return 2;
}
else if (id == 0){ //child
close(_pipe[0]);
int i = 0;
char *_mesg_c = NULL;
while (i<100){
_mesg_c = "i am child!";
write(_pipe[1], _mesg_c, strlen(_mesg_c) + 1);
sleep(1);
i++;
}
}
else{ //father
close(_pipe[1]);
char _mesg[100];
int j = 0;
while (j<100){
memset(_mesg, '\0', sizeof(_mesg));
read(_pipe[0], _mesg, sizeof(_mesg));
printf("%s\n", _mesg);
j++;
}
}
return 0;
}
使用管道需要注意的4中情況:
1. 如果所有指向管道寫端的文件描述符都關閉了(管道寫端的引用計數等於0),仍然有
進程從管道的讀端讀數據,那麼管道中剩余的數據都被讀取後,再次read會返回0,就像
讀到件末尾樣。
測試代碼:
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/wait.h>
int main()
{
int _pipe[2];
int ret = pipe(_pipe);
if (ret == -1){
printf("create pipe error! errno code is : %d\n", errno);
return 1;
}
pid_t id = fork();
if (id < 0){
printf("fork error!");
return 2;
}
else if (id == 0){ //child
close(_pipe[0]);
int i = 0;
char *_mesg_c = NULL;
while (i<10){
_mesg_c = "i am child!";
write(_pipe[1], _mesg_c, strlen(_mesg_c) + 1);
sleep(1);
i++;
}
close(_pipe[1]);
}
else{ //father
close(_pipe[1]);
char _mesg[100];
int j = 0;
while (j<100){
memset(_mesg, '\0', sizeof(_mesg));
int ret = read(_pipe[0], _mesg, sizeof(_mesg));
printf("%s : code is : %d\n", _mesg, ret);
j++;
}
if (waitpid(id, NULL, 0)< 0)
{
return 3;
}
}
return 0;
}
2. 如果有指向管道寫端的文件描述符沒關閉(管道寫端的引用計數大於0),持有管道寫
端的進程也沒有向管道中寫數據,這時有進程從管道讀端讀數據,那麼管道中剩余的數
據都被讀取後,再次read會阻塞,直到管道中有數據可讀了才讀取數據並返回。
測試代碼:
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/wait.h>
int main()
{
int _pipe[2];
int ret = pipe(_pipe);
if (ret == -1){
printf("create pipe error! errno code is : %d\n", errno);
return 1;
}
pid_t id = fork();
if (id < 0){
printf("fork error!");
return 2;
}
else if (id == 0){ //child
close(_pipe[0]);
int i = 0;
char *_mesg_c = NULL;
while (i<20){
if (i < 10){
_mesg_c = "i am child!";
write(_pipe[1], _mesg_c, strlen(_mesg_c) + 1);
}
sleep(1);
i++;
}
close(_pipe[1]);
}
else{ //father
close(_pipe[1]);
char _mesg[100];
int j = 0;
while (j<20){
memset(_mesg, '\0', sizeof(_mesg));
int ret = read(_pipe[0], _mesg, sizeof(_mesg));
printf("%s : code is : %d\n", _mesg, ret);
j++;
}
if (waitpid(id, NULL, 0)< 0)
{
return 3;
}
}
return 0;
}
3. 如果所有指向管道讀端的文件描述符都關閉了(管道讀端的引用計數等於0),這時有進
程向管道的寫端write,那麼該進程會收到信號SIGPIPE,通常會導致進程異常終。
測試代碼:
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/wait.h>
int main()
{
int _pipe[2];
int ret = pipe(_pipe);
if (ret == -1){
printf("create pipe error! errno code is : %d\n", errno);
return 1;
}
pid_t id = fork();
if (id < 0){
printf("fork error!");
return 2;
}
else if (id == 0){ //child
close(_pipe[0]);
int i = 0;
char *_mesg_c = NULL;
while (i<20){
if (i < 10){
_mesg_c = "i am child!";
write(_pipe[1], _mesg_c, strlen(_mesg_c) + 1);
}
sleep(1);
i++;
}
}
else{ //father
close(_pipe[1]);
char _mesg[100];
int j = 0;
while (j<3){
memset(_mesg, '\0', sizeof(_mesg));
int ret = read(_pipe[0], _mesg, sizeof(_mesg));
printf("%s : code is : %d\n", _mesg, ret);
j++;
}
close(_pipe[0]);
sleep(10);
if (waitpid(id, NULL, 0)< 0)
{
return 3;
}
}
return 0;
}
4. 如果有指向管道讀端的文件描述符沒關閉(管道讀端的引用計數大於0),持有管道讀
端的進程也沒有從管道中讀數據,這時有進程向管道寫端寫數據,那麼在管道被寫滿時
再次write會阻塞,直到管道中有空位置了才寫入數據並返回。