每个进程各自有不同的用户地址空间,任何一个进程的全局变量在另一个进程中都看不到,所以进程之间要交换数据必须通过内核,在内核中开辟一块缓冲区,进程1把数据从用户空间拷到内核缓冲区,进程2再从内核缓冲区把数据读走,内核提供的这种机制称为进程间通信(IPC,InterProcess Communication)。如下图所示。
图 下进程间通信
1. 管道(Pipe)及有名管道(named pipe):
管道可用于具有亲缘关系进程间的通信,有名管道克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允许无亲缘关系进程间的通信;
管道是一种最基本的IPC机制,由pipe
函数创建:
#include <unistd.h> int pipe(int filedes[2]);
调用pipe
函数时在内核中开辟一块缓冲区(称为管道)用于通信,它有一个读端一个写端,然后通过filedes
参数传出给用户程序两个文件描述符,filedes[0]
指向管道的读端,filedes[1]
指向管道的写端(很好记,就像0是标准输入1是标准输出一样)。所以管道在用户程序看起来就像一个打开的文件,通过read(filedes[0]);
或者write(filedes[1]);
向这个文件读写数据其实是在读写内核缓冲区。pipe
函数调用成功返回0,调用失败返回-1。
开辟了管道之后如何实现两个进程间的通信呢?比如可以按下面的步骤通信。
-
父进程调用
pipe
开辟管道,得到两个文件描述符指向管道的两端。 -
父进程调用
fork
创建子进程,那么子进程也有两个文件描述符指向同一管道。 -
父进程关闭管道读端,子进程关闭管道写端。父进程可以往管道里写,子进程可以从管道里读,管道是用环形队列实现的,数据从写端流入从读端流出,这样就实现了进程间通信。
简单程序实例如下:
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#define MAXLINE 80
int main(void)
{
int n;
int fd[2];
pid_t pid;
char line[MAXLINE];
if (pipe(fd) < 0) {
perror("pipe");
exit(1);
}
if ((pid = fork()) < 0) {
perror("fork");
exit(1);
}
if (pid > 0) { /* parent */
close(fd[0]);
write(fd[1], "hello world\n", 12);
wait(NULL);
} else { /* child */
close(fd[1]);
n = read(fd[0], line, MAXLINE);
write(STDOUT_FILENO, line, n);
}
return 0;
}
2.信号是比较复杂的通信方式,信号是在软件层次上对中断机制的一种模拟,是一种异步通信方式。用于通知接受进程有某种事件发生,除了用于进程间通信外,进程还可以发送信号给进程本身;linux除了 支持Unix早期信号语义函数sigal外,还支持语义符合Posix.1标准的信号函数sigaction(实际上,该函数是基于BSD的,BSD为了 实现可靠信号机制,又能够统一对外接口,用sigaction函数重新实现了signal函数);
父进程从终端输入signum,然后发给子进程
- #include <stdio.h>
- #include <sys/types.h>
- #include <signal.h>
- #include <stdlib.h>
- int main()
- {
- int pid;
- if((pid = fork()) < 0)
- {
-
- perror("Fail to fork");
- exit(EXIT_FAILURE);
-
- }else if(pid == 0){
-
- while(1);
-
- }else{
-
- int signum;
-
- while(scanf("%d",&signum) == 1)
- {
- kill(pid,signum);
- system("ps -aux | grep a.out");
- }
- }
- return 0;
-
}
剩下的几种方式放在以后在说