目录
1. 匿名管道
管道
- 从一个进程连接到另一个进程的数据流称为一个“管道”。
- 管道的生命周期是随进程结束而结束的
- 管道是自带同步与互斥机制的
比如在shell终端执行命令: ps -a | grep -Ei "test"
这个操作就是把ps -a的输出(stdout)通过管道输入(stdin)到可执行程序grep
- 我们将一次只允许一个进程使用的资源,称为临界资源
匿名管道:是一种特殊的管道,仅限于本地父子进程之间的通信
pipe函数:创建匿名管道
int pipe(int pipefd[2]);
pipe函数调用成功时返回0,调用失败时返回-1。
pipefd[0] : 管道的读fd
pipefd[1]:管道的写fd
匿名管道使用步骤:
1.使用pipe()函数创建管道(可读写)
2.使用fork()函数创建子进程(父子进程均可对管道读写)
3.父进程关闭写fd(fd[1]), 子进程关闭读fd(fd[0]), 保证达到如下效果
注意:
- 管道只能够单向通信,因此当父进程创建完子进程后,需要确认父子进程谁读谁写,然后关闭相应的读写端。
- 从管道写端写入的数据会被内核缓冲,直到从管道的读端被读取。
- 管道是半双工通信的。
- 如果需要实现全双工通信,需要进程建立两根管道
5.管道的容量可通过ultimate -a进行设置,查看
2. 命名管道
匿名管道只能用于相关的进程之间的通信,要让两个无关的进程通信,需要用到命名管道。
命名管道就是一种特殊类型的文件。
- 创建命名管道
[cl@VM-0-15-centos fifo]$ mkfifo fifo
可在mkfifo的目录下查看到当前目录多了个fifo文件
如下例子;一个进程写到fifo文件(echo "hello" > fifo),一个进程从fifo文件去读(cat < fifo)
- 函数创建fifo
int mkfifo(const char *pathname, mode_t mode);
第一个参数是pathname,表示要创建的命名管道文件。
mkfifo函数的第二个参数是mode,表示创建命名管道文件的默认权限。(创建出来文件的权限为:mode&(~umask))
- 项目中用到的主要是用于,代码逻辑自测,用通过echo指令向一个fifo写入一些命令来测试某些函数。
- 将fifo创建,读取的函数写在一个线程里面,让程序一直去读去终端echo输入的命令。
3.命令行中的管道("|")
管道(“|”)连接起来的各个进程是有亲缘关系的,它们之间互为兄弟进程。
使用命令行的位置并不存在fifo文件,所以:命令行对应的是匿名管道!!
4. 进程间通信 IPC
system V IPC是操作系统特地设计的一种通信方式。system V IPC提供的通信方式有以下三种:
system V共享内存:线程或进程之间消息交互
system V消息队列:线程或进程之间消息交互
system V信号量:同步与互斥
5. 共享内存
- 基本概念
让不同的进程看到同一份物理内存,这块物理内存就叫做共享内存。
当申请了一块共享内存后,为了让各个进程看到同一个共享内存,因此每个共享内存被申请的时候都有一个key值,用于标识唯一性。
- 命令:ipcs -q/ ipcs -m /ipcs -s列出消息队列/共享内存/信号量相关信息
- 进程结束了,共享内存依旧存在
- 5.1 共享内存的创建
1.在物理内存中申请共享内存空间
2.将申请到的共享内存挂接到地址空间,建立映射关系
- 5.2 共享内存的释放
1.将共享内存与地址空间去关联,取消映射关系。
2.释放共享内存空间,将物理内存还给系统。
- 可在C++的构造函数ftok(); shmget()的方式赋值到类的私有指针,在析构函数去shmdetach()
5.1 共享内存的创建
- 共享内存的建立
1.在物理内存中申请共享内存空间
int shmget(key_t key, size_t size, int shmflg);
- key: 共享内存在系统中的唯一标识符(传入的key参数是通过:ftok()函数的返回值决定的)
- size:待创建的共享内存大小
- shmflg:创建共享内存的方式
调用成功:返回一个有效的fd, 失败返回-1.
key_t ftok(const char *pathname, int proj_id);
将一个已存在的路径名pathname和一个整数标识符proj_id转换成一个key值,称为IPC键值
- pathname:路径名
- proj_id:有如下两种参数,决定创建共享内存的方式。
(1)使用组合IPC_CREAT,如果内核中不存在键值与key相等的共享内存,则新建一个共享内存并返回该共享内存的句柄;如果存在这样的共享内存,则直接返回该共享内存的句柄
- 适用:每个进程都去调用ftok()和shmget(),这样可以保证不会重复创建。
(2)如果内核中不存在键值与key相等的共享内存,则新建一个共享内存并返回该共享内存的句柄;如果存在这样的共享内存,则出错返回
2.将申请到的共享内存挂接到地址空间,建立映射关系
5.2 共享内存的释放
- 共享内存的释放
(1)可以通过
ipcrm -m shmid
命令释放指定sharememory id的共享内存资源。(2)控制共享内存可用shmctl函数
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
- shmid: 共享内存的id
- cmd:需要对shm执行的动作
cmd传入的常用的选项有以下三个:
选项 作用 IPC_STAT 获取共享内存的当前关联值,此时参数buf作为输出型参数 IPC_SET 在进程有足够权限的前提下,将共享内存的当前关联值设置为buf所指的数据结构中的值 IPC_RMID 删除共享内存段
- buf:设置或获取所控制的共享内存的数据结构
函数返回0代表调用成功,返回-1代表调用失败。
5.2 共享内存的关联和去关联
- 共享内存并没有提供任何的保护机制,包括同步与互斥。
- 共享内存的attach和deattach针对的是进程和共享内存两者之间!!
- shmat()操作之后,能够让进程以操作地址的方式操作共享内存。好处嘛:当时是方便代码的移植。
1. 共享内存的关联(shm attach)
将共享内存连接到进程地址空间。
void *shmat(int shmid, const void *shmaddr, int shmflg);
- shmid: sharedmemory id
- shmaddr: 共享内存映射到进程地址空间的某一地址,通常设为NULL,让内核决定
- shmflg: 一些属性
调用成功:返回共享内存映射到进程地址空间的起始地址。
调用失败:返回(void*) -1
注:需要shmget()的第二个参数需要增加0666的权限。
关联查看
ipcs -m可看到 mattch的参数变为1
2. 共享内存的去关联(shm deatch)
int shmdt(const void *shmaddr);
- 传入shmaddr; 这个是shmat()函数的返回值,
shmdt调用成功,返回0。
shmdt调用失败,返回-1。
- 小结:当共享内存创建好后就不再需要调用系统接口进行通信了
- 两个进程之间都去读写shared memory, 可以通过在共享内存里写标志位,比如变量A:1标识可读,2标识可写。每次进程在读写共享内存之前都看下这个变量A。写个while循环直到这个标志位被改变。
6. 消息队列
- 用于一个进程向另外一个进程发送数据块的方法!!
- 消息队列的资源也必须自行删除,否则不会自动清除
6.1 消息队列的创建
int msgget(key_t key, int msgflg);
- 第一个参数key, 通过ftok()函数生成,这个key作为msgget()的第一个参数。
- msgflag: 一些标志位
- 调用成功返回一个fd
6.2 消息队列的释放
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
6.2 消息队列发送数据
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
6.2 消息队列获取数据
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);