进程间通信
进程间的通信方式有如下7种
- 传统的通信方式
- 无名管道
- 有名管道
- 信号
- system V IPC对象
- 共享内存
- 消息队列
- 信号灯集
- BSD
- 套接字(socket)
无名管道
特点
a. 只能用于具有==亲缘关系==的进程之间的通信
b. 半双工的通信模式
c. 管道可以看成是一种特殊的文件,对于它的读写可以使用文件IO如read、write函数.
d. 管道是基于文件描述符的通信方式。当一个管道建立时,它会创建两个文件描述fd[0]和fd[1],其中fd[0]固定用于读管道,而fd[1]固定用于写管道。
函数模型
#include <unistd.h>
int pipe(int pipefd[2]);
功能:创建无名管道
参数:pipefd:创建的文件描述符,下标为0和1的两个文件描述符
返回值:成功:0
失败:-1
特性
-
当管道中无数据时,读操作会阻塞;
-
管道中装满(管道大小64K)数据时,写阻塞,一旦有4k空间,写继续
-
只有在管道的读端存在时,向管道中写入数据才有意义。否则,会导致管道破裂,向管道中写入数据的进程将收到内核传来的SIGPIPE信号 (通常Broken pipe错误)。
int main(int argc, char const *argv[])
{
int fd[2] = {0};
if(pipe(fd) < 0)
{
perror("pipe err");
return -1;
}
printf("%d %d\n", fd[0], fd[1]); //3 4
// char buf[32] = "";
//1.当管道中没有数据时,读阻塞
// read(fd[0], buf, 32);
// printf("buf:%s\n", buf);
//2.当管道写满(64k)时,写阻塞
// char buf[65536] = "";
// write(fd[1], buf, 65536);
// 至少读出4k空间,才能继续写操作
// read(fd[0], buf, 4096);
// printf("write befor\n");
// write(fd[1], "a", 1);
// printf("write after\n");
//3.关闭读端,写操作时会导致管道破裂(收到SIGPIPE信号进程结束)
close(fd[0]);
write(fd[1], "a", 1);
//下面语句不会输出
printf("aaa\n");
return 0;
}
例子 : 创建子进程,父进程循环从终端输入字符串,子进程循环打印数据,当输入quit时程序结束。
int main(int argc, char const *argv[])
{
int fd[2] = {0};
char buf[32] = "";
if (pipe(fd) < 0)
{
perror("pipe err");
return -1;
}
pid_t t = fork();
if (t < 0)
{
perror("fork err");
return -1;
}
else if (t == 0)
{
while (1)
{
read(fd[0], buf, 32);
if (!strcmp(buf, "quit"))
break;
printf("buf:%s\n", buf);
}
}
else
{
while (1)
{
scanf("%s", buf);
write(fd[1], buf, strlen(buf) + 1);
if (!strcmp(buf, "quit"))
break;
}
wait(NULL);
}
return 0;
}
有名管道
特点
1.有名管道可以使互不相关的两个进程互相通信
2.有名管道可以通过路径名来指出,并且在文件系统中可见,但内容存放在内存中
3.通过文件IO来操作有名管道
4.遵循先进先出规则
5.不支持如lseek() 操作
函数
int mkfifo(const char *pathname, mode_t mode);
功能:创建有名管道
参数:pathname:管道文件名
mode:权限 用八进制数表示0666 0777
返回值:成功:0
失败:-1,并设置errno号
补充:
1.当管道文件存在(报错提示file exists)时的处理方式:
判断errno的值为EEXIST时,只是打印提示语句,if(errno == EEXIST)
2.注意代码中出现errno,需要添加头文件#include
3.注意写入管道的数据并不是写入文件的,数据是在内核空间
#include <errno.h>
int main(int argc, char const *argv[])
{
if (mkfifo("./fifo", 0666) < 0)
{
//errno :错误码变量
if (errno == EEXIST)
printf("file exist\n");
else
{
perror("mkfifo err");
return -1;
}
}
printf("mkfifo ok\n");
int fd = open("./fifo", O_RDWR);
if(fd < 0)
{
perror("open err");
return -1;
}
char buf[32] = "";
write(fd, "hello", 5);
read(fd, buf, 32);
printf("buf:%s\n", buf);
return 0;
}
读写特性
a. 只写方式,写阻塞(阻塞在打开文件的位置),一直到另一个进程把读打开
b. 只读方式,读阻塞(阻塞在打开文件的位置),一直到另一个进程把写打开
c. 可读可写,如果管道中没有数据,读阻塞,
d.当写端关闭,读操作,如果管道中没有数据,read立刻返回
共享内存
信号灯集
信号灯(semaphore),也叫信号量。它是不同进程间或一个给定进程内部不同线程间同步的机制;
System V信号灯集是一个或者多个信号灯的一个集合。其中的每一个都是单独的计数信号灯。而Posix信号灯指的是单个计数信号灯。
步骤
- 创建key值-ftok
- 创建或打开信号灯集-semget
- 初始化信号灯集-semctl
- PV操作-semop
- 删除信号灯集-semget