通信:一个进程和另一个进程发送数据;
父子进程,无关系进程;
进程是资源分配单元;不同进程的资源是独立的,没有关联;不能在一个进程中直接访问另一个进程的资源;
匿名管道用在有关系的进程之间,有名管道一般用在没有关系的进程之间;
有关系进程,文件描述符表共享;
例如:
父进程文件描述符5写数据;6读数据;
子进程文件描述符5读数据;6写数据
就可以实现进程间通信;
管道的数据结构:线性队列;
用逻辑的方式设置为环形队列;
pipe管道:两个文件描述符pipefd ;pipefd[0]:读;pipefd[1]:写;
数据写到管道缓冲区;
注意:匿名管道只适用于有关系的进程之间;
在父子进程通信时;要在fork之前建立管道;这样两个进程都有读写管道
子进程写数据到pipefd[1];
父进程从读取端读取数据;
管道默认是堵塞的,管道中没有数据堵塞,直到读取完
每隔一秒发送一次数据;
父进程读了之后写;子进程写了之后读
查看管道缓冲的大小:默认4k
获取管道大小的函数:fpathconf();
获取文件配置的值;
如果去掉sleep收到的是自己的数据;
有sleep的时候子进程写完CPU就被父进程抢过去执行了
没有sleep那就是子进程继续执行,写完数据又继续读数据;
在匿名管道中,一般不会两边都写和两边都读;
在实际操作中一个读一个写:
例如父进程用来读,那么将父进程的写端进行关闭;子进程的写读端进行关闭
关闭读端:close(pipefd[0]);
关闭写端:close(pipefd[1]);
将标准输出文件重定向到管道的写端;
再去执行ps aux;
execlp函数族:会将当前进程用户区的数据替换,输出ps命令,参数为aux;
如果失败打印错误信息并退出;
在父进程中从管道中提取数据;输出;
close(pipefd[1])
char buf[1024]={0};
int len=read(pipefd[1],buf,sizeof(buf)-1);
sizeof(buf)-1去掉一个NULL字符;
问题:只读出了1024-1=1023个字节的数据;也就只输出这么多;
应该定义一个循环读;
输出完了清空数据:memset();
最后wait回收子进程的资源;
问题2:管道大小只有4k写满了就结束了;所以也应该设置循环写;
管道的读写特点和管道设置为非阻塞
使用管道时要注意:假设都是阻塞I/O操作;
1.所有指向管道写端的文件描述符都关闭了(管道写端引用计数为0),进程从管道的读端读数据,那么管道中剩余的数据被读取之后,再次read会返回0;就像读到末尾一样;
2.如果有指向管道写端的文件描述符,(管道写端引用计数大于0),而没有往管道中写数据;那么管道中剩余的数据被读取后再次read会阻塞;直到管道中有数据可以读才读数据并返回数据;
3.如果所有指向管道读端都关闭了;这个时候有进程向管道写端写数据时,该进程会收到一个信号:SIGPIPE会导致进程异常终止;
4.如果有指向管道读端的文件描述符没有关闭,而持有管道读端的进程也没有从管道中读数据;有进程往管道中写数据,那么在管道中被写满的时候再次write会被阻塞,直到有数据被读;
读管道设置非阻塞;
注意:buf输出后要清空memset(buf,0,1024);
子进程写数据后睡眠;
父进程读到数据输出;
读不到继续读,
有名管道
有名管道有文件实体,但是fifo中的内容并不存在于文件中,而是内存中;
通过命令创建FIFO文件;
mode和open中一样,会和u_mask按位与;
实现两个管道通信;
在创建之前可以判断一下,文件是否存在;存在就不创建了;
access()
注意:两个管道都要打开才会开始读写;
读写过程中任意管道关闭另一个管道都会关闭;
有名管道实现简单版聊天功能
只能一人发一条--不能把读写放在一个进程中;这样总有一个是阻塞的;
可以把写管道读管道放在不同的进程中(父子进程);
例如父进程中读,子进程中写,或者相反;这样就可以同步运行;
内存映射
内存映射快比磁盘操作;
当进程1对内存中的数据修改,对内存2去读取相应内存中的数据也就已经修改相当于在进行通信;
mmap:映射;可以实现有关系的内存映射也可以没有关系的;
munmap:解除映射;
头文件:#include<sys/mman.h>
mmap:
MAP_SHARED:同步,进程间通信要打开共享;映射区的数据会自动和磁盘文件进行同步
MAP_PRIVATE:不同步,内存映射区数据变了对原来的文件不会改变,会重新创建一个新的文件(copy on write)写时复制,读时共享;
fd:需要映射的哪个文件的文件描述符
-通过open得到,open的是一个磁盘文件;
-文件大小不能为0;并且open时的权限不能和prot参数有冲突;
offset:偏移量(一般不用,一般指定4k的整数倍)0,不偏移;
munmap:解除映射;
返回值:成功返回内存的首地址;失败返回(void*)-1;
参数:addr:释放的内存首地址
length:要释放的内存大小,和mmap的一样;
注意:内存映射区不阻塞;
lseek(fd,0,seek_end):获取文件大小;
创建子进程:
关闭内存映射区
父进程读数据,子进程写数据;父进程在回收完子进程的数据后在读取数据;