Linux进程间通信
1、进程间通信的类型
- 管道(pipe)和命名管道(FIFO)
- 信号(signal)
- 共享内存
- 消息队列
- 信号量
- 套接字
2、管道
管道是Linux中最常用的进程间通信 IPC 机制。使用管道时,一个进程的输出可以成为另外一个进程的输入。
当输入/输出的数据量特别大时,管道的这种 IPC 机制就非常有用。
在Linux中,通过将两个file结构指向同一个临时的VFS节点,这个VFS节点又指向同一个物理页而实现管道。
3、pipe管道
若要创建一个简单的管道,可以使用系统调用pipe(),它接受一个参数,也就是一个包括两个整数的数组。如果系统调用成功,此数组将包括管道使用的两个文件描述符,一个为读端,一个为写端。pipe管道是半双工的,数据只能向一个方向流动,需要双方通信时,就建立两个管道。pipe管道只能用于父子进程或者兄弟进程之间。
头文件: #include <unistd.h>
函数原型: int pipe( int filedes[2])
filedes: 管道文件描述符,当为0时,pipe端代码工程为读端;当为1时,pipe端代码工程为写端;
filedes[0]: 管道的读端
filedes[1]: 管道的写端
函数返回值:成功为 0 ;错误为 1 。
#include <stdio.h>
#include <unistd.h>
#include <string.h>
/*父子进程通过无名管道进行通信*/
int main(int argc, char *argv[])
{
//1, 创建无名管道
int fd[2];
char buf[64];
int ret = pipe(fd);
if(-1 == ret)
{
perror("pipe failed");
return -1;
}
//2, 创建子进程
pid_t pid = fork();
if(-1 == pid)
{
perror("fork faied");
return -1;
}
//3, 父进程循环从键盘获取数据写入无名管道 --> 写入端 fd[1]
if(pid > 0)
{
while(1)
{
memset(buf, 0, sizeof(buf));
fgets(buf, sizeof(buf), stdin);
write(fd[1], buf, sizeof(buf));
}
}
//4, 子进程循环从无名管道中读取数据
if(0 == pid)
{
while(1)
{
memset(buf, 0, sizeof(buf));
read(fd[0], buf, sizeof(buf)); //默认是阻塞的
printf("readbuf:%s", buf);
}
}
return 0;
}
4、命名管道
命名管道与一般管道要一些不同点
1) 命名管道是在文件系统中作为一个特殊的设备文件而存在的。
2) 不同管道的进程之间可以通过命名管道共享数据。
3) 当共享命名管道的进程执行完所有的 I/O 操作后,命名管道将继续保存在文件系统中,以便以后使用。
4) 普通管道只能由父子兄弟相关进程使用,它共同的祖先进程创建了管道。但是,通过命名管道,不相关的进程也能交换数据。
5) 一旦用mkfifo函数创建了一个命名管道,就可以 open 打开它。一般的文件 I/O 函数( close , read , write , unlink )都可用于命名管道。
函数头文件:
#include <sys/types.h>
#include <sys/stat.h>
函数原型:
int mkfifo(const char * pathname,mode mode)
pathname: 管道文件名
mode: 管道创建方式
函数返回值:
成功: 0
失败: -1
代码例程:
读端代码
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#define FIFO_PATH "/home/gec/myfifo"
/*有名管道读端程序*/
int main(int argc, char *argv[])
{
//1, 判断有名管道是否存在 --> 不存在则创建
if( access(FIFO_PATH, F_OK) != 0) //access返回值不等于0,表示文件不存在
{
int ret = mkfifo(FIFO_PATH, 0777);
if(-1 == ret)
{
perror("mkfifo failed");
return -1;
}
}
//2, 直接打开有名管道 --> open()
int fd = open(FIFO_PATH, O_RDWR);
if(-1 == fd)
{
perror("open failed");
return -1;
}
//3, 循环获取读取管道中数据 --> "EXIT\n"
char buf[128];
while(1)
{
memset(buf, 0, sizeof(buf));
read(fd, buf, sizeof(buf));
if( strcmp("EXIT\n", buf) == 0 )
break;
printf("readbuf:%s", buf);
}
//4, 关闭管道文件
close(fd);
return 0;
}
写端代码
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#define FIFO_PATH "/home/gec/myfifo"
/*有名管道写端程序*/
int main(int argc, char *argv[])
{
//1, 判断有名管道是否存在 --> 不存在则创建
if( access(FIFO_PATH, F_OK) != 0) //access返回值不等于0,表示文件不存在
{
int ret = mkfifo(FIFO_PATH, 0777);
if(-1 == ret)
{
perror("mkfifo failed");
return -1;
}
}
//2, 直接打开有名管道 --> open()
int fd = open(FIFO_PATH, O_RDWR);
if(-1 == fd)
{
perror("open failed");
return -1;
}
//3, 循环获取数据写入管道
char buf[128];
while(1)
{
memset(buf, 0, sizeof(buf));
fgets(buf, sizeof(buf), stdin);
write(fd, buf, sizeof(buf));
if( strcmp("EXIT\n", buf) == 0 )
break;
}
//4, 关闭管道文件
close(fd);
return 0;
}