文章目录
.一.进程间通信概述
进程间通信(IPC:Inter Processes Communication)
每个进程都是一个独立的资源分配单元,一个进程中不能访问另一个进程,但是不同的进程需要进行数据的交互,因此需要进程间通信。
进程间通信功能
数据传输:一个进程需要将它的数据发送给另一个进程。
资源共享:多个进程之间共享同样的资源。
通知事件:一个进程需要向另一个或一组进程发送消息,通知它们发生了某种事件。
进程控制:有些进程希望完全控制另一个进程的执行(如 Debug 进程),此时控制进程希望能够拦截另一个进程的 所有操作,并能够及时知道它的状态改变。
三.进程间的通信-- 无名管道和有名管道
3.1管道概述
管道又称为无名管道,具体体现在应用层的两个文件描述符上。
3.2管道的特点
- 半双工,同一时刻只能在一个方向流动
- 数据只能从管道一遍写入,从另一端读出,比如进程A把数据通过管道写入,进程B从管道里面读取出来
- 先入先出
- 因为数据是无格式的,所以要约定好数据的格式
- 管道存在于内存中,并对应着一个缓冲区,每个系统的缓冲区不太一样
- 管道数据被取走后将会立马清空
- 无名管道只能在父子进程中使用
3.3无名管道的创建pipe函数
#include <unistd.h>
int pipe(int fd[2])
返回两个文件描述符
参数:
fd[0]为读并且打开
fd[1]为写并且打开
返回子:
成功:0
失败:-1
代码的编写
在子进程中写,父进程中读
/* ************************************************************************
* Filename: 01_pipe_simple.c
* Description:
* Version: 1.0
* Created: 2021年09月07日 10时08分18秒
* Revision: none
* Compiler: gcc
* Author: 魔动山霸 ,
* Company:
* ************************************************************************/
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
int main(int argc,char *argv[])
{
/*01--创建*/
int fd[2];
char buf[] = "hello world";
pid_t pid;
if(pipe(fd) < 0)
{
perror("pipe");
}
pid = fork();
if(pid == 0)
{
write(fd[1],argv[1],strlen(argv[1]));
_exit(0);
}
else if(pid > 0)
{
wait(NULL);
memset(buf,0,sizeof(buf));
read(fd[0],buf,sizeof(buf));
printf("子进程传过来的数据:%s\n",buf);
}
}
运行效果:
读写的特点
- read从管道读默认带阻塞
- write缓冲区满时也会阻塞
- 通信过程中,读端口全部关闭后,写进程向管道内写数据时,写进程会(收到 SIGPIPE 信号)退出。
第三点意思是说读端口关闭后,即close(fd[0])
这个时候即使你还在往管道写入,内核会发送一个信号退出程序。
如何通过fcntl设置管道的阻塞特性
我们一般都是在open的时候就设置了文件的特性,但是管道不一样,我们可以通过fcntl这个函数进行
函数原型:
int fcntl(int fd, int cmd);
int fcntl(int fd, int cmd, long arg);
int fcntl(int fd, int cmd, struct flock *lock);
描述:
fcntl()针对(文件)描述符提供控制.参数fd是被参数cmd操作(如下面的描述)的描述符.
针对cmd的值,fcntl能够接受第三个参数(arg)
fcntl函数有5种功能:
1.复制一个现有的描述符(cmd=F_DUPFD).
2.获得/设置文件描述符标记(cmd=F_GETFD或F_SETFD).
3.获得/设置文件状态标记(cmd=F_GETFL或F_SETFL).
4.获得/设置异步I/O所有权(cmd=F_GETOWN或F_SETOWN).
5.获得/设置记录锁(cmd=F_GETLK,F_SETLK或F_SETLKW).
设置为阻塞: fcntl(fd, F_SETFL, 0);
设置为非阻塞: fcntl(fd, F_SETFL, O_NONBLOCK);
4.文件描述符的复制
dup 和 dup2 是两个非常有用的系统调用,都是用来复制一个文件的描述符,使新的文件描述符也标识旧的文件描述符所标识的文件
int dup(int oldfd);
int dup2(int oldfd, int newfd);
这两个系统调用常用来重定向进程的标准输入,标准输出和标准错误
4.1dup函数
#include <unistd.h> int dup(int oldfd);
功能:复制 oldfd 文件描述符,并分配一个新的文件描述符,新的文件描述符是调用进程文件描述符表中最小可用 的文件描述符。
参数:要复制的文件描述符 oldfd。 返回值:成功:新文件描述符。
失败:返回-1,错误代码存于 errno 中。
5.有名管道
概念:有名管道和无名管道大体相同
函数原型:
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
- 半双工
- 写入先出
- 约定好数据格式
- FIFO 在文件系统中作为一个特殊的文件而存在,但 FIFO 中的内容却存放在内存中。
- 管道在内存中对应一个缓冲区。不同的系统其大小不一定相同。
- 从 FIFO 读数据是一次性操作,数据一旦被读,它就从 FIFO 中被抛弃,释放空间以便写更多的数据
- 当使用 FIFO 的进程退出后,FIFO 文件将继续保存在文件系统中以便以后使用。
- 特殊类型文件,可通过ls进行查看,用于不相通进程间的通信
5.1创建有名管道
操作 FIFO 文件时的特点
系统调用的 I/O 函数都可以作用于 FIFO,如 open、close、read、write 等。
打开 FIFO 时,非阻塞标志(O_NONBLOCK)产生下列影响: 特点一: 不指定 O_NONBLOCK(即 open 没有位或 O_NONBLOCK)
1、open 以只读方式打开 FIFO 时,要阻塞到某个进程为写而打开此 FIFO
2、open 以只写方式打开 FIFO 时,要阻塞到某个进程为读而打开此 FIFO。
3、open 以只读、只写方式打开 FIFO 时会阻塞,调用 read 函数从 FIFO 里读数据时 read 也会阻塞
4.以阻塞模式,验证 read 函数也会阻塞
5.非阻塞方式打开命名管道,验证 open 和 read 都不阻塞
6.通信过程中若写进程先退出了,则调用 read 函数从 FIFO 里读数据时不阻塞;若写进程又重新运行,则调用 read 函数从 FIFO 里读数据时又恢复阻塞。
7.阻塞方式打开命名管道,验证 写进程退出,会导致 read 不阻塞
8.阻塞方式打开命名管道,验证 读进程结束后,写进程再向管道写数据写进程会收到信号 退出
读操作
9.调用 write 函数向 FIFO 里写数据,当缓冲区已满时 write 也会阻塞。
指定 O_NONBLOCK(即 open 位或 O_NONBLOCK)
1、先以只读方式打开:如果没有进程已经为写而打开一个 FIFO, 只读 open 成功,并且 open 不阻塞。
2、先以只写方式打开:如果没有进程已经为读而打开一个 FIFO,只写 open 将出错返回-1。
3、read、write 读写命名管道中读数据时不阻塞。
4、通信过程中,读进程退出后,写进程向命名管道内写数据时,写进程也会(收到 SIGPIPE 信号)退出
写操作