四、进程之间的通信。
1、 为什么要学习进程之间的通信?
例如:
./1 -> 开启了一个名字为1的进程。
./2 -> 开启了一个名字为2的进程。
通过学习进程之间的通信,使得不同的进程之间都是可以实现数据的交换。例如进程1发送数据给进程2,进程2收到数据之后,根据数据来做不同的事情。(间接地实现进程1控制进程2)
2、在linux下,进程之间的通信方式有哪些,都有什么特点?
1)管道通信。
管道通信分为有名管道与无名管道,管道是一种特殊的文件,进程通过将数据写入到管道中,另外一个进程从管道中读取数据出来。
2)信号。
在linux下,有非常多信号,例如:暂停,继续,停止..,某一个进程通过发送信号给另外一个进程,从而控制另外一个进程。
3)消息队列。
某一个进程把消息发送到队列上,另外一个进程就可以读取队列上的数据,消息队列好处:进程可以读取队列上某一个特定的数据。
4)共享内存。(信号量)
多个进程访问同一片内存区域。
五、学习进程之间的通信。 --- 无名管道
1、 什么是无名管道?作用机制如何?
无名管道只能作用于亲缘关系之间的进程,例如父子进程。
无名管道其实就是一个数组来的,这个数组有两个端,分别是读端与写端,进程想写入数据到管道中,就往写端中写,如果进程想读取管道中的数据,就读取读端上的数据。
2、使用无名管道步骤:
1)申请数组。
int fd[2]; -> 里面并不是读端与写端。
2)使用函数初始化数组。 -> pipe() -> man 2 pipe
初始化数组 -> 初始化数组中读端与写端的值。
功能: pipe, - create pipe
//创建一条管道
头文件:#include <unistd.h>
原型:
int pipe(int pipefd[2]);
参数:
pipefd: 一个具有2个int类型数据的数组。
返回值:
成功:0
失败:-1
3)初始化成功
pipefd[0] -> 读端
pipefd[1] -> 写端
例子1: 写一个代码测试无名管道中读端与写端的值是多少。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main(int argc,char *argv[])
{
//1、 申请数组
int fd[2] = {0};
printf("fd[0] = %d\n",fd[0]);
printf("fd[1] = %d\n",fd[1]);
//2、 使用pipe()去初始化这个数组
int ret;
ret = pipe(fd);
printf("ret = %d\n",ret);
if(ret == 0)
{
//3、 初始化成功后,第一个成员就是读端
// 第二个成员就是写端
printf("init pipe success!\n");
}
//4、 打印读端与写端的值。
printf("fd[0] = %d\n",fd[0]);
printf("fd[1] = %d\n",fd[1]);
return 0;
}
结果:
fd[0] = 0
fd[1] = 0
ret = 0
init pipe success!
fd[0] = 3 -> 读端
fd[1] = 4 -> 写端
例子2: 尝试使用无名管道,让父子进程之间进行通信。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <string.h>
int main(int argc,char *argv[])
{
//1、 申请数组并初始化数组
int fd[2] = {0};
pipe(fd);
//2、 带着这条无名管道去创建一个新的进程。
pid_t x;
x = fork();
//3、 通过fork()函数的返回值去判断。
if(x > 0)
{
//4、 父进程:
//1) 准备缓冲区
char buf[20] = {0};
//2) 把需要写入的数据放在缓冲区中。
fgets(buf,sizeof(buf),stdin);
//3) 将缓冲区中的数据写入到无名管道中的写端即可。
write(fd[1],buf,strlen(buf));
//4) 主动回收子进程的资源。
wait(NULL);
//5) 父进程正常退出
exit(0);
}
if(x == 0)
{
//5、 子进程
//1) 准备缓冲区
char buf[20] = {0};
//2) 直接读取无名管道中的读端,将数据读取到缓冲区中。
read(fd[0],buf,sizeof(buf));
//3) 打印一下读取出来的数据。
printf("from pipe:%s",buf);
//4) 子进程正常退出
exit(0);
}
return 0;
}
结果:
gec@ubuntu:/mnt/hgfs/GZ2057/09 系统编程/02/code$ ./pipe_test2
nihao
from pipe:nihao
六、学习进程之间通信。 -- 有名管道
1、什么是有名管道?机制如何?
无名管道是一个数组来的,只能作用于同一个文件中。 -> 只能作用于亲缘关系之间的进程。
有名管道是一个文件来的,文件存在于文件系统中,所以所有的进程都可以看到这个文件。
机制:有名管道的机制.jpg
2、如何创建有名管道? -> mkfifo() -> man 3 mkfifo
功能: make a FIFO special file (a named pipe)
//创建一个特殊的管道文件(命名管道)
头文件:#include <sys/types.h>
#include <sys/stat.h>
原型:
int mkfifo(const char *pathname, mode_t mode);
参数:
pathname: 需要创建的管道文件的路径 绝对路径/相对路径 "/home/gec/fifo_test"
mode:八进制权限 调用mkfifo之前,要先调用umask(0000)设置掩码
例如: 0777
返回值:
成功:0
失败:-1
例子1: 尝试在家目录创建一个有名管道,名字为fifo_test。
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
int main(int argc,char *argv[])
{
//1、 设置掩码为0000
umask(0000);
//2、 直接创建有名管道,名字为fifo_test。
int ret;
ret = mkfifo("/home/gec/fifo_test",0777);
//3、 如果成功,那么就去家目录下看看有没有fifo_test这个文件。
if(ret == 0)
{
printf("mkfifo success!\n");
}
return 0;
}
结果:
创建成功,并且在家目录下看到fifo_test这个文件。
gec@ubuntu:~$ ls -l fifo_test
prwxrwxrwx 1 gec gec 0 Sep 21 02:21 fifo_test
例子2: 尝试使用有名管道,让两个陌生的进程进行通信。
发送端:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
int main(int argc,char *argv[])
{
//1. 设置掩码。
umask(0000);
//2. 创建有名管道。
int ret;
ret = mkfifo("/home/gec/fifo_test",0777);
//3. 打开有名管道文件。
int fd;
fd = open("/home/gec/fifo_test",O_RDWR);
//4. 准备缓冲区
char buf[100] = {0};
while(1)
{
//5. 从键盘中获取数据,存放在缓冲区中。
fgets(buf,sizeof(buf),stdin);
//6. 将缓冲区的数据写入到文件。
write(fd,buf,strlen(buf));
//7. 如果发送了一个"quit"到管道文件中,那么发送端程序就结束。
// 如果不是,则继续发送。
if(strncmp(buf,"quit",4) == 0)
{
break;
}
}
//8. 关闭文件。
close(fd);
return 0;
}
作业1:完成有名管道接收端。
参考: 有名管道的机制.jpg
发送端代码 -> fifo/
作业2:使用无名管道,实现子进程发送一个"showbmp"这个字符串给父进程,父进程就显示一张图片。
作业3:完成练习.doc第一题。