系列文章:
Linux IPC進程間通信(一):管道
Linux IPC進程間通信(二):共享內存
Linux IPC進程間通信(三):信號量
Linux IPC進程間通信(四):消息隊列
前言
Linux進程間通信(IPC)的一個重要方法就是管道,在學習管道之前,要理解兩個概念
- 半雙工(讀端與寫端必須固定)
- 全雙工(讀端與寫端不固定)
不論是無名管道還是有名管道,都屬於半雙工管道,理解這一點非常重要。
一.標準管道流
FILE *popen(const char *__command, const char *__modes)
command 代表shell指令
modes 分爲 "r"讀 和 "w"寫
具體功能和Linux命令中的管道基本相同
void test1()
{
//讀
FILE *fp = popen("cat /etc/profile", "r");
char buf[1000] = {0};
while (fread(buf, sizeof(char), sizeof(buf), fp))
cout << buf;
cout << endl;
pclose(fp);
//寫
char buf2[] = {"aaa bbb ccc ddd eee fff ggg hhh"};
FILE *fp2 = popen("wc -w", "w");
fwrite(buf2, sizeof(char), sizeof(buf2), fp2);
pclose(fp2);
}
二.無名管道
- 只能在親緣進程之間使用
- 爲半雙工管道(即只能一端讀,一端寫)
- 無名管道是特殊的文件,只能存在於內存中
注意:
- 無名管道中的
read
函數會進行阻塞。 - 因爲
pipe()
函數只能創建出半雙工管道,所以fds[0]只能讀,fds[1]只能寫;如果是socketpair()
函數創建出的全雙工管道就沒有這個限制。
案例:創建一個無名管道 與 父子進程,進行父寫子讀
void test2()
{
int fds[2] = {0};
pipe(fds);//fds[0] = 3, fds[1] = 4;
if (fork() == 0)
{
close(fds[1]);
char buf[100] = {0};
read(fds[0], buf, sizeof(buf));
cout << "child process reading:" << buf << endl;
close(fds[0]);
exit(0);
}
close(fds[0]);
char buf[100] = "Helloworld";
write(fds[1], buf, strlen(buf));
cout << "father process is writing" << endl;
close(fds[1]);
wait(NULL);
}
三.命名管道
命名管道比起無名管道,即便不是親屬進程也能夠進行進程間通信
另外,命名管道可以作爲文件存在。
需要使用 mkfifo
命令創建命名管道
命名管道:讀寫操作案例
分別創建好 讀端
與 寫端
, 並設置退出機制
注意:如果沒有設置退出機制,那麼read端 將會默認一直在接收 0 個字符,讀端將會一直打印空字符串
寫端 (write.cpp)
:
int main()
{
int fdw = open("pipe", O_WRONLY);
ERROR_CHECK(fdw, -1 ,"open w");
char buf[100] = {0};
while (1)
{
memset(buf, 0, sizeof(buf));
read(0, buf, sizeof(buf));
write(fdw, buf, strlen(buf) - 1);
}
return 0;
}
讀端(read.cpp)
:
int main()
{
int fdr = open("pipe", O_RDONLY);
ERROR_CHECK(fdr, -1, "open r");
char buf[100] = {0};
int ret;
while (1)
{
memset(buf, 0, sizeof(buf));
ret = read(fdr, buf, sizeof(buf));
if (ret == 0)
{
cout << "byebye" << endl;
break;
}
cout << "ret = " << ret << endl;
cout << "buf = " << buf << endl;
}
return 0;
}
通過以上程序,可以實現一端寫,一段讀的案例。