Linux进程通信
1.为什么要进程通信?2.匿名管道
1.管道符号:ps aux | gre xxx2、管道本质3.管道的接口
1.创建匿名管道2.要让不同的进程通过匿名管道进行交换数据(进程间通信),进程应该具备什么样的条件呢? 4.管道的特性
1.半双工:2.生命周期与大小3.管道提供字节流服务4.原子性5.阻塞属性 5.设置非阻塞属性6.命名管道
1.创建命名管道2.特性3.代码验证
1.为什么要进程通信?2.匿名管道 1.管道符号:ps aux | gre xxx每一个进程的数据都是存储在物理内存中,进程通过各自的进程虚拟地址空间进行访问,访问的时候通过各自的页表映射关系访问到物理内存。至于物理内存如何存储,页表如何映射,进程是不清楚的,这也就造成了进程的独立性,所以本质上进程通信是进程和进程之间交换数据的手段.
常见的进程通信方式有:管道、共享内存、消息队列、信号量、信号、网络
目的是实现:数据传输、资源共享、通知时间、进程控制
图解:
2、管道本质3.管道的接口 1.创建匿名管道管道在内核当中是一块缓冲区,供应进程进行读写,交换数据
int pipe(int pipefd[2]);//创建匿名管道本质上就是在内核上创建一个缓冲区
pipefd是数组,有两个元素:
返回值: 0创建成功,-1创建失败
代码验证:
#include#include int main(){ int fd[2]; int ret=pipe(fd); if(ret<0){ perror("pipe"); return 0; } //正常通信 printf("fd[0]:%dn",fd[0]); printf("fd[1]:%dn",fd[1]); while(1){ sleep(1); } return 0; }
可见管道的读端与写端对应的是一个文件描述符(对应在struct task_struct中的file数组)
由图可见不同的进程要用同一个匿名管道进行通信,要求两个进程拥有管道读写两端的文件描述符,因此采用父子进程进行通信(子进程拷贝父进程的PCB中的文件描述符);
#include4.管道的特性 1.半双工:#include #include int main(){ int fd[2]; int ret=pipe(fd); if(ret<0){ perror("pipe"); return 0; } //正常通信 printf("fd[0]:%dn",fd[0]); printf("fd[1]:%dn",fd[1]); pid_t pid=fork(); if(pid<0){ perror("fork"); return 0; }else if(pid==0){ //child printf("child,fd[0]:%dn",fd[0]); printf("child,fd[1]:%dn",fd[1]); char buf1[1024]={0}; read(fd[0],buf1,sizeof(buf1)-1); printf("buf1:%sn",buf1); } else{ //father printf("father,fd[0]:%dn",fd[0]); printf("father,fd[1]:%dn",fd[1]); char buf[]="i am pipe_test"; write(fd[1],buf,strlen(buf)); } while(1){ sleep(1); } return 0; }
2.生命周期与大小没有标识符的情况下,只有具有亲缘性关系的进程可以实现通信,并且为了保证子进程的匿名管道中有父进程匿名管道读写两端的文件描述符,必须父进程先创建匿名管道,父进程再创建子进程
3.管道提供字节流服务管道的生命周期跟随进程,进程退出之后,管道也就随之销毁了,其大小为64K.
4.原子性从读端进行读的时候,是将数据从管道中读走了(并不是拷贝)
读端可以自定义选择读多少内容
5.阻塞属性当pipe_size小于4096字节时,读/写操作在同一时刻只有一个进程在操作,保证进程在操作的时候不会被其他进程打扰阻塞属性(
5.设置非阻塞属性读写两端的文件描述符的初始属性为阻塞属性
当write一直调用写,而读端不去读,则写满之后就会阻塞
当read一直进行读,当管道内部被读完之后,read就会阻塞
int fcntl(int fd,int cmd,…/arg/)
参数:fd:要操作的文件描述符
cmd:告知fcntl函数做什么操作
F CETFL:获取文件描述符的属性信息
F SETFL:设置文件描述符的属性信息
返回值:
0,设置成功,-1,设置失败
设置非阻塞属性的时候,宏的名字为0_NONBLOCK
第一步:先获取文件描述符原来的属性:
int flag=fcntl(fd[0],F_GETFL);
第二部:设置新属性的时候不要忘记带上原来的属性:新的属性=老的属性|增加的属性
fcntl(fd[0],F_SETFL,flag | 0_NONBLOCK);
代码:
#include#include #include #include int main(){ int fd[2]; int ret=pipe(fd); if(ret<0){ perror("pipe"); return 0; } //正常通信 int read_ret=fcntl(fd[0],F_GETFL); printf("read_ret:%dn",read_ret); int write_ret=fcntl(fd[1],F_GETFL); printf("write_ret:%dn",write_ret); fcntl(fd[1],F_SETFL,write_ret | O_NONBLOCK); write_ret=fcntl(fd[1],F_GETFL); printf("write_ret:%dn",write_ret); return 0; }
读设置成为非阻塞属性
写不关闭,一直读,读端调用read函数之后,返回值为-1,errno设置为EAGAIN
写关闭,一直读,读端read函数返0,表示什么都没有读到
写设置成为非阻塞
读不关闭,一直写,当把管道写满之后,则在调用write.就会返回-1
读关团·一直写,写端调用write进行写的时候,就会发生崩溃。(本质原因是因为写端的进程,收到SIGPIPE信号,导致写端进程崩溃)
代码验证让父子进程做不一样的事,这里让子进程写close(fd[0]),父进程读close(fd[1]);
#include6.命名管道 1.创建命名管道#include #include int main(){ int fd[2]; int ret = pipe(fd); if(ret < 0){ perror("pipe"); return 0; } pid_t pid = fork(); if(pid < 0){ perror("fork"); return 0; }else if(pid == 0){ //child //下面调用sleep(3), 就是想让父进程先将fd[0]关闭掉, 子进程在向下运行 sleep(3); close(fd[0]); int flag = fcntl(fd[1], F_GETFL); fcntl(fd[1], F_SETFL, flag | O_NONBLOCK); ssize_t write_size; while(1){ write_size = write(fd[1], "s", 1); //为了验证上面调用write是否正常, 如果正常调用则下面的输出一定会打印 printf("i am child, i run haren"); if(write_size < 0){ break; } } printf("write_size: %ldn", write_size); perror("write"); }else{ //father close(fd[1]); //关闭读端 close(fd[0]); //不关闭读端 while(1){ sleep(1); } } return 0; }
1.mkfifo命令,fifo命名管道文件,相当于内核缓冲区的标识符,不同的进程用过这个文件可以找到内核的缓冲区
2.函数创建:
int mkfifio(const char*pathname,mode_t mode);
参数:
pathname:要创建的命名管道的路径以及文件名
mode_t:命名管道文件的权限,八进制数组(0664)
返回值:0,成功,-1,失败
#include2.特性#include int main(){ mkfifo("./mkfifo_func_fifo",0664); return 0; }
3.代码验证支持不同间进程进行通信
往管道中写:
#include#include #include #include int main(){ //1.打开命名管道文件 int fd = open("../fifo", O_WRONLY); if(fd < 0){ perror("open"); return 0; } //2.调用write往管道当中写内容 while(1){ const char* content = "i am write process..., hhhh"; write(fd, content, strlen(content)); sleep(1); } close(fd); return 0; }
往管道中读;
#include#include #include int main(){ //1.open int fd = open("../fifo", O_RDONLY); if(fd < 0){ perror("open"); return 0; } //2.read while(1){ char buf[1024] = { 0 }; read(fd, buf, sizeof(buf) - 1); printf("buf is %sn", buf); } close(fd); return 0; }



