在创建子进程的时候,会将父进程中的资源复制一份给子进程,然后他们各自使用自己的资源,那如果父进程想与子进程通信,如何达到呢,如果说采用套接字的方法,那未免太慢了,是否可以创建一个共同使用的内存,双方都可以进行访问呢,这样子进程要给父进程的数据,放到这块内存,父进程直接拿就可以了。操作系统就实现了这种机制,来支持进程之间进行通信。
管道实现进程间通信
操作系统为进程间通信提供了管道这一内存工具,管道属于操作系统,就像套接字一样,他是不属于进程的(进程只是拥有对应于套接字的文件描述符,而非套接字)。这样,在使用fork
函数创建子进程时,就无法复制管道了。通过下列函数可以创建管道:
#include <unistd.h>
int pipe(int fuledes[2]); // 创建成功返回0,失败返回-1
父进程中调用该函数,然后通过传入一个长度为2的数组,来创建管道,分别保存用来读的文件描述符和用来写的文件描述符。一般来说,0用来读,1用来写。接下来看看具体如何使用管道:
#include <stdio.h>
#include <unistd.h>
#define BUF_SIZE 30
int main(int argc, char *argv[])
{
int fds[2]; // 用来保存文件描述符
char str[] = "hello world!";
pid_t pid;
pipe(fds); // 创建管道
pid = fork();
if(pid == 0)
{
write(fds[1], str, sizeof(str));
}
else
{
char buf[BUF_SIZE];
read(fds[0], buf, BUF_SIZE);
puts(buf);
}
return 0;
}
运行结果如下:
现在只是实现了单向的一个通信,那单条管道是否可以实现双向的通信呢,此处我们尝试一下:
#include <stdio.h>
#include <unistd.h>
#define BUF_SIZE 30
int main(int argc, char *argv[])
{
int fds[2]; // 用来保存文件描述符
char str1[] = "hello world!";
char str2[] = "who are you?";
char buf[BUF_SIZE];
pid_t pid;
pipe(fds); // 创建管道
pid = fork();
if(pid == 0)
{
write(fds[1], str1, sizeof(str1));
read(fds[0], buf, BUF_SIZE);
printf("child process output: %s \n", buf);
}
else
{
read(fds[0], buf, BUF_SIZE);
printf("parent process output: %s \n", buf);
write(fds[1], str2, sizeof(str2));
}
return 0;
}
运行结果如下:
子进程写入消息后,子进程先将消息读取了出来,然后父进程由于其中已经没有消息可读,阻塞在read
函数处。也就是说写入管道的消息,可能由于非指定对象读取,而导致被我们不想让读取的文件描述符获取到这个信息。
当然我们可以在子进程写入消息后,为其设置一个睡眠时间,从而让父进程先读取这个消息,但是实际编程中,如果进程过多,这种设置方法未免太过耗费精力,且影响系统性能。所以我们可以通过设置两条管道,一条用来子进程读父进程写,一条用来父进程读子进程写。
#include <stdio.h>
#include <unistd.h>
#define BUF_SIZE 30
int main(int argc, char *argv[])
{
int fds1[2], fds2[2]; // 用来保存文件描述符
char str1[] = "hello world!";
char str2[] = "who are you?";
char buf[BUF_SIZE];
pid_t pid;
pipe(fds1), pipe(fds2); // 创建管道
pid = fork();
if(pid == 0)
{
write(fds1[1], str1, sizeof(str1));
read(fds2[0], buf, BUF_SIZE);
printf("child process output: %s \n", buf);
}
else
{
read(fds1[0], buf, BUF_SIZE);
printf("parent process output: %s \n", buf);
write(fds2[1], str2, sizeof(str2));
}
return 0;
}
运行如下:
这样就实现了进程间的双向通信。