程序間通訊管道
一、常用的方式
(1) 管道
(2) System V
(3) POSIX
二、目的
(1) 資料傳輸
(2) 資源共享
(3) 通知事件
(4) 程序控制
三、 本質
讓兩個不同的程序看到一份公共的資源
四、匿名管道
1. 簡單用法:
2. 建立管道
Makefile:
main:main.c
gcc main.c -o main
.PHONY:clean
clean:
rm -f main
程式碼:
#include <stdio.h> #include <string.h> #include <unistd.h> int main() { int fd[2]; int ret = pipe(fd); if (ret < 0) { perror("pipe"); return 1; } pid_t id = fork(); if (id == 0) { close(fd[0]); const char *msg = "hello child\n"; while (1) { sleep(1); write(fd[1], msg, strlen(msg)); } } else { close(fd[1]); char buf[64]; while (1) { ssize_t s = read(fd[0], buf, sizeof(buf) - 1); if (s > 0) { buf[s] = '\0'; printf("parent saying:%s\n", buf); } } } return 0; }
3. 深度理解管道
(1) 父程序建立管道
(2)父程序 fork 子程序
(3)父程序關閉 fd[0](讀), 子程序關閉 fd[1](寫)
4. 特性
(1) 單向傳遞
(2) 具有血緣關係
(3) 自帶同步互斥
(4) 生命週期隨程序(程序退出,管道釋放)
(5) 提供面向位元組流的服務
5. 概念
(1) 資料不一致:讀寫雙方因為訪問共同資源二導致的資料不一致的問題
例如: hello word 和 shagua
讀第一個讀到空格時,讀到第二個,就出現了錯誤
(2) 臨界資源:把兩個進現在程看到的看到的公共資源稱為臨界資源
(3) 臨界區:每個程序中訪問臨界資源的那段程式碼稱為臨界區
(4) 互斥:在任何一個時間點,臨界區在訪問臨界資源時,有且僅有一個人在訪問,即你用的時候別人都不能用,別人用的時候,你也不能去用
同步:在現有的互斥基礎上,讓多個人訪問臨界資源具有了順序性,即我們大家利用一些共同的資源區,大家一起合作,完成某些事情,但是我在幹某些小事的時候,可能要等到你做完另一些小事
(5) 原子性:在進行某些資源的操作時時,不會有中間狀態
6. 四種情況
(1) 寫方一直在寫,讀端不讀,但讀端不關閉檔案描述符,寫方因為互斥與同步在把管道寫滿之後就會停下來
程式碼檢驗:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
int main()
{
int fd[2];
int ret = pipe(fd);
if (ret < 0)
{
perror("pipe");
return 1;
}
pid_t id = fork();
if (id == 0)
{
close(fd[0]);
int count = 0;
const char *msg = "hello child\n";
while (1)
{
write(fd[1], msg, strlen(msg));
printf("%d\n", count++);
}
}
else
{
close(fd[1]);
char buf[64];
while (1)
{
sleep(5);
ssize_t s = read(fd[0], buf, sizeof(buf) - 1);
if (s > 0)
{
buf[s] = '\0';
printf("parent saying:%s\n", buf);
}
}
}
return 0;
}
(2) 如果讀寫雙方不掛不關閉檔案描述符,一個不寫,一個不讀,雙方都要等待,這個過程就叫同步
程式碼檢驗:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
int main()
{
int fd[2];
int ret = pipe(fd);
if (ret < 0)
{
perror("pipe");
return 1;
}
pid_t id = fork();
if (id == 0)
{
close(fd[0]);
const char *msg = "hello child\n";
while (1)
{
write(fd[1], msg, strlen(msg));
sleep(10);
}
}
else
{
close(fd[1]);
char buf[64];
int count = 0;
while (1)
{
sleep(5);
ssize_t s = read(fd[0], buf, sizeof(buf) - 1);
if (s > 0)
{
buf[s] = '\0';
printf("parent saying:%s\n", buf);
}
printf("parent read:%d\n", count);
}
}
return 0;
}
(3) 如果寫端一直在寫,有朝一日寫端不寫,並把寫端關閉,讀端會一直讀,直到把管道中檔案描述符讀完,最後讀返回值 0,這個 0 代表著讀到了檔案的結尾
程式碼檢驗:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
int main()
{
int fd[2];
int ret = pipe(fd);
if (ret < 0)
{
perror("pipe");
return 1;
}
pid_t id = fork();
if (id == 0)
{
int count = 0;
close(fd[0]);
const char *msg = "hello child\n";
while (1)
{
write(fd[1], msg, strlen(msg));
printf("%d\n", count++);
if (count > 0)
{
close(fd[1]);
break;
}
sleep(1);
}
}
else
{
close(fd[1]);
char buf[64];
while (1)
{
ssize_t s = read(fd[0], buf, sizeof(buf) - 1);
if (s > 0)
{
buf[s] = '\0';
printf("parent saying:%s\n", buf);
}
else if (s == 0)
{
printf("pipe done, break;\n");
break;
}
}
}
return 0;
}
(4) 寫端一直在寫,讀端不讀且關閉檔案描述符,寫端會因為操作異常被作業系統向目標傳送13號訊號終止
程式碼檢驗:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
int main()
{
int fd[2];
int ret = pipe(fd);
if (ret < 0)
{
perror("pipe");
return 1;
}
pid_t id = fork();
if (id == 0)
{
int count = 0;
close(fd[0]);
const char *msg = "hello child\n";
while (1)
{
write(fd[1], msg, strlen(msg));
printf("%d\n", count++);
sleep(1);
}
}
else
{
close(fd[1]);
char buf[64];
while (1)
{
ssize_t s = read(fd[0], buf, sizeof(buf) - 1);
if (s > 0)
{
buf[s] = '\0';
printf("parent saying:%s\n", buf);
sleep(3);
break;
}
}
close(fd[0]);
int status = 0;
wait(&status);
printf("sig:%d\n", status&0x7F);
}
return 0;
}
7. 管道大小
管道大小一般為4096
五、命名管道
1. 建立命名管道
mkfifo filename
2. 程式碼實現管道
reader.c
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
int main()
{
int fd = open("./myfifo", O_RDONLY);
if (fd < 0)
{
perror("open");
return 1;
}
printf("open ok!\n");
while (1)
{
char buf[1024] = {0};
ssize_t read_size = read(fd, buf, sizeof(buf) - 1);
if (read_size < 0)
{
perror("read");
return 1;
}
if (read_size == 0)
{
perror("read done");
return 0;
}
buf[read_size] = '\0';
printf("%s\n", buf);
}
close(fd);
return 0;
}
writer.c
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
int main()
{
int fd = open("./myfifo", O_WRONLY);
if (fd < 0)
{
perror("open");
return 1;
}
while (1)
{
printf("> ");
fflush(stdout);
char buf[1024] = {0};
ssize_t read_size = read(0, buf, sizeof(buf) - 1);
if (read_size < 0)
{
perror("read");
return 1;
}
if (read_size == 0)
{
printf("read done!\n");
return 0;
}
buf[read_size] = '\0';
write(fd, buf, strlen(buf));
}
close(fd);
return 0;
}
執行截圖: