【Linux之进程间通信】
项目代码获取:https://gitee.com/chenshao777/linux-processes.git
(麻烦点个免费的Star哦,您的Star就是我的写作动力!)
03.有名管道
上一个博客介绍了无名管道
无名管道的特点是:
1.只能在父子进程中使用
2.无名管道实际上是内核空间的一个队列
3.读取无名管道数据后,被读取的数据在管道中会被删除
4.无名管道写满是65536字节,写溢出的话会阻塞
5.无名管道为空时,读取会阻塞
那么如果如何在两个无亲缘关系的进程中进行通信呢?
可以使用 “有名管道”
有名管道是Linux系统中七大文件类型之一
七大文件类型有
文件类型 | 标识符 |
---|---|
普通文件 | - |
目录 | d |
管道 | p |
链接文件 | l |
套接字 | s |
字符文件 | c |
块设备文件 | b |
其中管道、套接字、字符文件、块设备文件都是不占用磁盘空间的
回来继续介绍有名管道
有名管道的创建
int mkfifo(const char *pathname, mode_t mode);
需要包含头文件
#include <sys/types.h>
#include <sys/stat.h>
创建一个有名管道
#include <sys/types.h>
#include <sys/stat.h>
int main()
{
省略......
mkfifo("./myfifo", 0777);
省略......
}
创建有名管道和使用fopen函数创建文件一样,也是要赋权限的,实际权限也是需要减去umask的
另外一种创建有名管道的方法,直接在命令行使用 mkfifo xxxx 命令
hc@hc-vm:~/Linux_ARM/git/linux-processes/03.有名管道$ mkfifo myfifo
hc@hc-vm:~/Linux_ARM/git/linux-processes/03.有名管道$ ls
03.mkfifo_read.c 03.mkfifo_write.c myfifo read write
hc@hc-vm:~/Linux_ARM/git/linux-processes/03.有名管道$ ll
总用量 40
drwxrwxr-x 2 hc hc 4096 5月 22 22:36 ./
drwxrwxr-x 6 hc hc 4096 5月 22 21:22 ../
-rw-rw-r-- 1 hc hc 685 5月 22 21:09 03.mkfifo_read.c
-rw-rw-r-- 1 hc hc 1372 5月 22 21:11 03.mkfifo_write.c
prw-rw-r-- 1 hc hc 0 5月 22 22:36 myfifo|
-rwxrwxr-x 1 hc hc 8536 5月 22 21:07 read*
-rwxrwxr-x 1 hc hc 8576 5月 22 21:08 write*
hc@hc-vm:~/Linux_ARM/git/linux-processes/03.有名管道$
可以发现有名管道文件是不占磁盘空间的
代码实例:使用有名管道实现两个无亲缘关系的进程通信,使得进程1先运行,进程2后运行
事先使用 mkfifo 命令创建一个 myfifo 有名管道
写管道进程1代码:
打开有名管道myfifo,然后写入数据1
然后输出三行数据
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
/*
事先用mkfifo命令创建一个有名管道
创建有名管道,并写入数据
通过有名管道,实现一个进程先打印,另一个后打印
运行命令: ./write
结果:写进程先运行,由于有名管道读端还没有运行,所以该进程会阻塞
直到读进程运行,写进程输出 “创建好有名管道写端”,和三行数据
接着写入一个数据到管道
读进程读到数据后结束阻塞,接着输出三行数据
*/
int main(int argc, char *argv[])
{
// /*创建有名管道文件*/
// if(0 == mkfifo("./myfifo", 0777))
// {
// printf("有名管道创建成功\n");
// }else{
// printf("有名管道创建失败\n");
// return -1;
// }
/*打开有名管道文件,此时才会在内核中创建队列*/
int fd;
fd = open("./myfifo", O_WRONLY);
if(fd > 0){
printf("创建好有名管道写端, fd = %d\n", fd);
}
/*输出三行数据, 这里会阻塞,因为要等另一个进程创建好有名管道读端*/
for(int i = 0; i < 3; i++)
{
printf("first process\n");
}
sleep(2);
char data = 1;
/*写入数据*/
write(fd, &data, 1);
close(fd);
return 0;
}
读管道进程2代码:
打开myfifo管道,读取一个字节数据
判断是否为1,若为1,则输出三行数据
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
/*
打开有名管道,读出数据
运行命令: ./read
*/
int main(int argc, char *argv[])
{
/*打开有名管道文件,此时才会在内核中创建队列*/
int fd;
fd = open("./myfifo", O_RDONLY);
if(fd > 0){
printf("创建好有名管道读端, fd = %d\n", fd);
}
char data = 0;
/*读数据*/
read(fd, &data, 1);
printf("读取到管道数据为 %d\n", data);
while(data == 0);
/*输出三行数据*/
for(int i = 0; i < 3; i++)
{
printf("second process\n");
}
close(fd);
return 0;
}
实验结果:
- 先运行写管道进程1,发现没有任何输出,因为此时有名管道只初始化了写端,进程1等待进程2初始化读端;
- 接着运行读管道进程2,发现进程1打印了 “创建好有名管道写端, fd = 3”和三行数据
- 接着延时2秒后进程2打印 “创建好有名管道读端, fd = 3”和三行数据
- 最后两个进程都结束运行
可以看出有名管道的特点如下:
1.如果只初始化了管道的读写其中一端,则会阻塞,直到另一端初始化成功;
2.有名管道可以在两个无亲缘关系的进程间进行通信;
3.有名管道可普通文件一样,创建的时候可以进行权限的设置;
4.有名管道不占用磁盘空间
另外,其他特性均和无名管道相同
5.内核空间的一个队列
6.读取管道数据后,被读取的数据在管道中会被删除
7.管道写满是65536字节,写溢出的话会阻塞
8.管道为空时,读取会阻塞