一、常用的方式
(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;
}
运行截图: