进程间通信
两个进程之间,可以进行"数据"的直接传递吗?
不能.
为什么?
- 数据传输:一个进程需要将它的数据发送给另一个进程
- 资源共享:多个进程之间共享同样的资源。
- 通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止 时要通知父进程)。
- 进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另 一个进程的所有陷入和异常,并能够及时知道它的状态改变。
- 需要多个进程协同,共同完成一些事情
是什么?
一个进程把自己的数据,能够交给另一个进程
怎么办?
a. 一般规律
- 交换数据的空间
- 不能由通信双方任何一个实现
- 进程间通信的本质, 先让不同的进程, 看到同一份资源(一般都是OS提供)
b.具体做法
OS提供的"空间"有不同的样式,就决定了有不同的通信方式
- 管道(匿名和命名)
- 共享内存
- 消息队列
- 信号量
管道
一个以w打开文件,一个以r打开,会有两个文件描述符对象,用同一块内存.(先把文件加载到内存,才能读写文件)
创建一个子进程时, 父进程的进程描述符表也需要创建.父进程打开的文件,子进程不需要创建.
基于文件的,让不同进程看到同一份资源的方式,叫做管道.(管道只能被设计成单向通信)
struct file是允许多个进程通过指针指向!
为什么父进程最开始,就要按照rw,打开同一个文件呢?
答:一个人读,一个人写,为了支持我们进行管道通信,系统调用pipe()
int pipe(int fildes[2]);
fildes:输出型参数,得到两个fd, r, w
不需要向磁盘中刷新&&磁盘中并不存在的文件
内存级的文件,匿名文件(管道)
匿名管道:如何做到让不同的进程看到同一份资源呢?
创建子进程,子进程会进程父进程的属性信息.
匿名管道:可以(只能)进行具有血缘关系的进程, 进行进程间通信.
代码:
1.验证代码
查看进程:
while :; do ps ajx | head -1 && ps ajx | grep testpipe | grep -v grep; sleep 1; done
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
void writer(int wfd) {
const char *str = "hello father, i am child";
char buffer[128];
int cnt = 0;
pid_t pid = getpid();
while (1) {
snprintf(buffer, sizeof(buffer), "message = %s, pid = %d, cnt = %d\n", str, pid, cnt);
write(wfd, buffer, strlen(buffer));
cnt++;
sleep(1);
}
}
void reader(int rfd) {
char buffer[1024];
while (1) {
ssize_t n = read(rfd, buffer, sizeof(buffer) - 1);
(void)n;
printf("father get a message: %s", buffer);
}
}
int main() {
int pipefd[2];
int n = pipe(pipefd);
if (n < 0)
return 1;
printf("pipefd[0]: %d, pipefd[1]: %d\n", pipefd[0], pipefd[1]);
pid_t id = fork();
if (id == 0) {
// child:w
close(pipefd[0]);
writer(pipefd[1]);
exit(0);
}
// father
close(pipefd[1]);
reader(pipefd[0]);
wait(NULL);
return 0;
}
a.4种情况
- 管道内部没有数据 && 子进程不关闭自己的写端文件fd, 读端(父)就要阻塞等待,直到pipe有数据
- 管道内部被写满 && 父进程(读端)不关闭自己的fd,写端(子)写满之后,就要阻塞等待
- 对于写端而言: 不写了 && 关闭了pipe,读端会将pipe中的数据读完,最后就会读到返回值为0,表示读结束,类似读到文件的结尾
- 读端不读 && 关闭,写端再写,OS会直接终止写入的进程(子进程), 通过信号 13) SIGPIPE 信号杀掉
如何验证退出信号是13?
//wait(NULL);
int status = 0;
pid_t rid = waitpid(id, &status, 0);
if (rid == id)
printf("exit code: %d, exit signal: %d\n", WEXITSTATUS(status), status&0x7F);
b.5种特性
- 自带同步机制
- 血缘关系进程进行通信,常见与父子
- pipe是面向字节流的
- 父子退出,管道自动释放,文件的生命周期是随进程的
- 管道只能单向通信,半双工的一种特殊情况
PIPE_BUF(4096byte):
当要写入的数据量不大于PIPE_BUF时,linux将保证写入的原子性。 当要写入的数据量大于PIPE_BUF时,linux将不再保证写入的原子性。
2.功能代码