进程间通信:
ipc:进程间通信(InterProcess Communication),每块虚拟内存0-3g相互独立,而不同的进程想要通信,由4-3g虚拟空间内核所提供的缓冲区进行信息交换,也叫进程间通信(InterProcess Communication)
ipc通信的几种方式:
⦁ pipe匿名管道—最简单
⦁ fifo 有名管道
⦁ mmap 文件映射(共享)io -----速度最快 (在内存开辟缓冲区,把文件映射到内存上,直接操作内存就可以)
⦁ 本地socket(套接字)最稳定
⦁ 信号:携带信息量最小
⦁ 共享内存:在内存中申请特定api
⦁ 消息队列
管道函数pipe通信
int pipe(int pipefd[2]);
pipefd两个文件描述符,0代表读,1代表写
返回值:失败返回-1,成功返回0
两个运行的进程怎样通信呢?
创建一个伪文件,也就是一个缓冲区(管道),这个管道有两端,一端是读端一端是写端,这个管道必须在fork()子进程之前创建,这样父子进程才都能获得fd文件描述符
pipe通信必须有血缘关系才能够进行通信
常见的通信方式:单工(广播),半双工(对讲机)一方讲话一方不能讲,全双工(电话)
pie管道:半双工通信
父子进程实现ps与grep功能
但是现在代码运行起来存在问题就是代码不会退出而是死等你继续输入
这个是因为,子进程和父进程都控制了管道的读写两端,所以觉得可能还会继续输入,所以就死等,所以解决办法就是在重定向之前关闭父子进程不需要的文件描述符
代码如下
运行结果:退出了
这里在说明一下我对文件描述符新的理解,文件描述符就是一个索引,创建一个进程,系统会为这个进程创建一个文件描述符表来记录打开的文件信息,相同的文件描述符可以指向不同的文件,而不同进程打开的同一个文件可以用相同的文件描述符记录
一个文件文件描述符表有1024个单位,每个单位记录着对一个文件的控制标志和文件指针,一个文件打开无非就是读或写,也就是说一个进程连续打开很多文件的时候,控制标志无非是状态标志,比如只读模式、读写模式、追加模式、覆盖模式等,但是文件指针的指向不相同
管道的读写行为:
读管道:
-
写端全部关闭-----read读到0,相当于读到文件末尾 写端没有全部关闭
-
有数据-------read读到数据
-
没有数据-------read阻塞,fcntl可以更改非阻塞
写管道:
-
读端全部关闭-------程序异常终止
-
读端未全部关闭
-
管道已满-----write阻塞
-
管道未满------write正常写入
-
ulimit -a查看系统资源上限
pipe size = 512 * 8 个字节(pipe创建的管道大小)
-
管道的优劣:
-
优点:简单
-
缺点:只能有血缘关系的进程进行通信,父子进程只能单向通信,要想双向通信就要两根管道
FIFO通信:
- fifo有名管道通信:可以实现无血缘进程进程通信
mkfifo创建管道伪文件命令
int mkfifo(const char *pathname, mode_t mode);
函数创建伪文件:
- 内核会针对fifo文件开辟一个缓冲区,操作fifo文件,可以操作缓冲区,实现进程间通信----实际上就是对文件的读写,与pipe不同
首先创建fifo文件,然后两个非血缘关系的进程,上面这个进程负责写入内容,下面这个负责读取内容
现在讨论一下细节操作,如果先打开负责写入的进程打开文件fifo文件之后并不会立即进行写入,而是阻塞在open函数那里等待另一个读取进程的打开,打开读进程之后才会继续执行open函数之下的命令。其次先打开读取命令也是一样。
先打开写端进程FIFO文件的话的话,等到读端进程打开FIFO文件之后才会写入内容,完了退出进程
倘若有一个进程负责写入,两个进程负责读取,那么写入的内容只会被读取一次,也就是说读端a读取内容后,内容将不在,读端b无法读取和a相同的内容
举例:
读端代码:
写端代码:
若先写端先openFIFO文件,只会打印write open begin,然后就阻塞在open函数,等到读端也openFIFO文件之后,写端才会打印write open end;并继续。
注释:fifo文件不但可以实现无血缘关系的进程通信,也可以实现有血缘关系进程通信,只是有血缘关系的不必用fifo
void *mmap(void *addr,size_t length,int prot,int flags,int fd,off_t offset);
- 注意返回值,泛型
- addr:早期设计的时候传地址但是比较鸡肋,最后只需要传NULL就可以了
- length:映射区长度
prot:
可执行,可读,可写,不可访问
常用可读可写这两个参数
flags:常用的就两个参数,共享的和私有的(指的是映射区),如果是共享的对内存的修改会影响到源文件,私有的不会
fd:文件描述符,通过文件建立映射区,所以因该在建立映射区之前先打开一个文件
offset:偏移量
返回值:成功返回可用内存首地址,失败返回MAP_FAILED
int munmap(void *addr, size_t length);释放内容
-
addr:传mmap的返回值
-
length:创建的长度
-
返回值:成功返回0,失败返回-1
-
.txt文件初始内容(也可以不先创建可以在mmap函数内部创建)
mmap注意事项:
-
1.不能
-
2.文件大小对映射区操作有影响尽量不要越界
-
3.offset偏移量只能是4k(4096)的整数倍还有0
-
4.没有影响,因为映射完毕fd已经没有用了管道
-
5.新创建一个空文件不可以,因为open创建的文件大小都为0,怎么去内存映射多大空间,会报错,所以要用ftruncate对文件进行拓展后,才可以操作
-
6.不可以,没有权限,因为在映射文件到内存的时候,会存在一次隐藏的读操作,因为要映射到内存,然后在内存进行控制文件,必须包含一次隐藏的读操作,把文件内容读取到内存
-
7.不可以,报错没有权限,总结即使mmap参数中给出的内存权限必须<=open打开文件的时候给定的权限,因为假如内存要对文件进行改写,那你起码文件打开的时候具备写操作吧,就是这个道理
-
整体来说,映射要改变文件内容可以,但是无论你映射了多大的空间,改写了多大内容,但是文件不够大也无法完全写入,而是阶段,所以无论你内存操作多猛,最后还要取决于文件大小
mmap实现父子进程件通信
加上特定的宏就可以实现匿名映射
上面fd也就是第五个参数位置为-1,是因为不需要打开文件了,也就不需要文件描述符了,注意一下这一点
第五个参数MAP_ANON 或者ANONYMOUS在unix系统里面没有那么就可以使用系统无底洞文件
/dev/zero聚宝盆和/dev/null无底洞来省下磁盘空间
插入一个小技巧:取消高亮,在代码中匹配有的字符之后会一直发亮,低行模式输入
:nohl回车就可以取消
mmap实现非血缘关系的进程通信:
写入文件的进程代码:运行时要带文件名
读取文件的进程代码:运行时带文件名
循环写入循环读取,就算两个进程同时读取也不会存在FIFO通信的情况,因为mmap操作的是内存,只有在覆盖写入的时候才会清空