进程间通讯:
每个进程都有自己的用户地址空间,一个进程的全局变量在另一个进程中是看不到的,如果两个进程想要交换信息,就需要通过内核,在内核中开辟一块缓存区,把进程1里的数据拷到内核,进程2从内核读走。内核提供的这种机制就是进程间通讯。
本质:让不同的进程访问同一块系统资源。
1、进程间通讯机制——管道
从本质上来说,管道也是文件,但它与一般的文件不相同,它克服了使用文件通讯的缺点:
(1):管道文件只占据inode,只是传输数据,不在磁盘上占据空间。
(2):管道在内存上也是一个固定大小的缓存区,该缓冲区的大小为1页,即4k字节。文件就不会限制大小,除非磁盘已满。但在写管道时,管道写满,当这种情况发生,随后再对管道的write()调用将被阻塞,等待数据被读取,留出空间再write()。
从管道中读文件是一次性操作,数据一旦被读取,它就从管道中消失,释放空间。
2、管道操作:
有名管道:应用于任意两个进程数据的单向传递
创建:命令方式:mkfifo 函数方式:mkfifo();
打开: int open(const char *pathname,int flag);
写数据: int write(int fd,char* buff,int size);
读数据:int read(int fd,char *buff,size);
关闭:int close(int fd);
代码实现:
功能: 我们需要运行两个不同的程序,实现两个进程之间的数据传递。
1、利用命令 创建管道文件
2、在 a.c 里实现用户循环输入字符串,并写进管道;在 b.c 里实现读取用户输入的字符串
(1) a.c 文件向管道里写:
#include<stdio.h> #include<unistd.h> #include<string.h> #include<fcntl.h> int main() { int fd=open("FIFO",O_WRONLY); if(fd==-1) { exit(0); } char buff[128]={0}; printf("please input:\n"); fgets(buff,128,stdin); while(strncmp(buff,"end",3)!=0) { write(fd,buff,strlen(buff)-1); memset(buff,0,128); fgets(buff,128,stdin); } close(fd); }
(2)b.c文件从管道里读:
#include<stdio.h> #include<fcntl.h> #include<string.h> #include<unistd.h> #include<stdlib.h> int main() { int fd=open("FIFO",O_RDONLY); if(fd==-1) { exit(0); } char buff[128]={0}; while(read(fd,buff,127)>0) { printf("%s\n",buff); } close(fd); }
测试运行结果:
3、最重要的点来了:有名管道的特点就是阻塞运行
阻塞运行函数:函数调用以后并不会立即返回,需要等待某些条件的发生才能返回。
(1):如果一个进程以只写方式打开一个管道文件,那么open函数会阻塞运行,直到有一个进行以只读方式打开管道文件,open才会返回,进程才会接着进行。
(2):如果一个进程以只读方式打开一个管道文件,那么open函数会阻塞运行,直到有一个进程以只写方式打开管道文件,open才会返回,进程才会接着进行。
你运行上面的代码就会知道,两个文件必须同时打开才能运行。
(3):read 函数也会阻塞运行,直到写端写入数据,或者所有的写端都关闭。
(4):read读取数据,并将内存上的已读数据清空。
如果你让写文件 sleep(10);先执行读,那么能读出来吗?
等10s 后,写进去了,才能读出来,那么是谁阻塞?就是read函数啦!
(5):write函数也会阻塞运行,因为管道是在内存中的一小块,当这一小块存满以后,再进行写的时候只能等待,等一部分数据被读走以后,再接着写。
4、无名管道:
相对于有名管道,无名管道是使用时产生,不使用时释放,在系统上不会留下一丝痕迹。
那么来无影去无踪的,连名字也不知道,它该如何找到呢?
我们通过父子进程实现。为什么要用父子进程实现?
父进程创建子进程,子进程和父进程指向同一块区域,那么我们就可以利用来通讯。而且父进程结束,子进程也就结束了。那么依赖父子进程的特性就可以同时占用一个管道,进行通讯了。
一个管道由一个进程创建,然后该进程调用fork。父子进程就可以应用该管道。
管道的创建 :int pipe(int fd[2]);
打开: fd[0] 读 fd[1] 写
读: read(fd[0],buff,size);
写: write(fd[1],buff,len);
关闭:close(fd[0]);
close(fd[1]);
那么如何实现通讯呢?
fork产生后:4个读写,但管道属于半双工通讯,只有一个读,一个写。 父读子写或者父写子读。
代码实现:fifo 文件
实现功能为:父进程进行写,从用户端循环输入字符,写进管道里,子进程从管道里进行读,并输出字符串和长度。
#include<unistd.h> #include<stdio.h> #include<fcntl.h> #include<string.h> int main() { int fd[2]; pid_t pid; char buff[128]; char buff1[128]; if(pipe(fd)<0) { printf("pipe error\n"); } pid=fork(); if(pid<0) { printf("fork error\n"); } else if(pid>0) { close(fd[0]); printf("plesae input\n"); fgets(buff,128,stdin); while(strncmp(buff,"end",3)) { write(fd[1],buff,strlen(buff)-1); memset(buff,0,128); fgets(buff,128,stdin); } } else { close(fd[1]); while(read(fd[0],buff1,127)>0) { printf("%s\n",buff1); printf("num=%d\n",strlen(buff1)); printf("please input\n"); } } }
运行结果: