简介
第一章
1、IPC对象的持续性(声明周期)
随进程持续的IPC:一直存在直到最后一个使用IPC对象的进程关闭为止,如管道和FIFO;
随内核持续的IPC:一直存在到内核重新自举或者显示删除,如System V 消息队列、信号量…;
随文件系统持续的IPC:一直存在到显示删除IPC对象;
使用IPC时要注意IPC对象的持续性。
2、errno值
Linux中系统调用的错误都存储于 errno中,errno由操作系统维护,每个进程都有,存储就近发生的错误,即下一次的错误码会覆盖掉上一次的错误。
第二章:Posix IPC
posix ipc 包括: posix消息队列(mqueue.h)、posix信号量(semaphore.h)、posix共享内存(sys/mman.h),并以路径名作为标识,创建或打开xxopen
1、IPC权限
第三章:System V IPC
System V IPC 包括:System V消息队列(sys/msg.h)、System V信号量(sys/sem.h)、System V共享内存(sys/shm.h),创建或打开xxxget
1、key_t键 和 ftok 函数
#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id);
ftok函数把一个已经存在的路径名(参数1)和 一个整数标识(参数2 id)转换成一个key_t值,称为IPC键(至少为32位)。
使用IPC的进程将给定的pathname和id转化成一个IPC键。
IPC键的组成:pathname指向文件的stat->st_dev + stat->st_ino + id[7:0].
id的[7:0]位不能全为0,否则ftok函数行为无效
若pathname或进程无访问权限ftok返回-1.
2、System V IPC 对象结构体 ipc_perm
/* Obsolete, used only for backwards compatibility and libc5 compiles */
<sys/ipc.h>
struct ipc_perm
{
__kernel_key_t key;//键
__kernel_uid_t uid;//用户id
__kernel_gid_t gid;//用户组id
__kernel_uid_t cuid;//创建者用户id
__kernel_gid_t cgid;//创建者组id
__kernel_mode_t mode;//读写权限
unsigned short seq;//槽位使用情况序列
};
3、IPC权限
4、标识符重用
5、ipcs 和 ipcrm
共享内存
IPC中最快的方式
mmap munmap msync
mmap将一个文件或者posix共享内存区对象映射到调用进程的地址空间。使用该函数的目的有:
1)对普通文件提供内存映射IO
2)对特殊文件提供匿名内存映射
3)使用shm_open以提供无亲缘关系的进程之间的posix共享内存
#include <sys/mman.h>
void *mmap(void *addr, size_t length, int prot, int flags,
int fd, off_t offset);
int munmap(void *addr, size_t length);
#include <sys/mman.h>
int msync(void *addr, size_t length, int flags);
参数:
mmap
addr:映射到进程中的内存首地址,NULL,由内核指定
length:映射空间大小
prot:映射内存的保护参数,读、写、执行、不可访问
flags:共享、私有、FIXED
fd:文件描述符
offset:文件偏移位置
return:成功返回映射空间首地址,失败MAP_FAILED设置errno
mmap成功后可以关闭文件描述符
munmap
return:成功返回0,失败返回-1
msync
flags:同步方式,同步写、异步写、使高速缓存失效
return:成功返回0,失败返回-1
匿名空间映射
不使用文件进行空间映射,匿名空间映射可以在父子进程之间实现共享内存,且不需要打开文件。
mmap flags参数添加MAP_ANON,fd 为-1.
Posix共享内存
无亲源关系的进程间共享内存区
1)内存映射文件
2)共享内存区对象
这两种方法都需要使用mmap函数,不同之处在于文件描述符的不同一个由open得到,一个由shm_open得到,随内核持续特性
shm_open、 shm_unlink
#include <sys/mman.h>
#include <sys/stat.h> /* For mode constants */
#include <fcntl.h> /* For O_* constants */
int shm_open(const char *name, int oflag, mode_t mode);
int shm_unlink(const char *name);
参数:
shm_open
name:共享内存对象路径
oflag和mode参数参考open函数,oflag至少未可读。
return:成功返回文件描述符,失败-1
共享内存对象路径可由任意进程调用
shm_unlink:
删除一个共享内存区对象
删除名字不会影响已经打开共享内存的进程,知道共享内存使用完毕(引用为0);
不过删除后open会失败;
return:成功返回0,失败-1
fturncate
调整不同文件或者共享内存的大小
int truncate(const char *path, off_t length);
int ftruncate(int fd, off_t length);
对于普通文件:
大小大于length,则多余的丢弃,小于则是否增加文件大小未说明
对于共享内存:调整未length大小
fstat
通过文件描述符获取共享内存的信息
创建共享内存的步骤
1)shm_open打开或创建一个共享内存,返回文件描述符
2)ftruncate调整共享内存大小
3)mmap映射共享内存到进程
共享内存的使用
1)shm_open打开一个共享内存,返回文件描述符
2)fstat获取大小
3)根据大小mmap映射共享内存到进程
4)根据大小写入或读取(非系统调用)
对共享内存的读写也需要使用某种方式同步
创建时先使用shm_unlink 防止对象已经存在
System V 共享内存
System V 共享内存信息结构
struct shmid_ds {
struct ipc_perm shm_perm; /* operation perms */
int shm_segsz; /* size of segment (bytes) */
__kernel_time_t shm_atime; /* last attach time */
__kernel_time_t shm_dtime; /* last detach time */
__kernel_time_t shm_ctime; /* last change time */
__kernel_ipc_pid_t shm_cpid; /* pid of creator */
__kernel_ipc_pid_t shm_lpid; /* pid of last operator */
unsigned short shm_nattch; /* no. of current attaches */
unsigned short shm_unused; /* compatibility */
void *shm_unused2; /* ditto - used by DIPC */
void *shm_unused3; /* unused */
};
shmget函数
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);
参数
打开或创建一个共享内存对象
key:ftok的返回值或者IPC_PRIVATE
size:创建时为一个大于0的值,操作已存在对象时应指定为0
shmflg:权限值
return:成功返回共享内存对象(标识符),失败-1
shmat
#include <sys/types.h>
#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr, int shmflg);
参数
通过shmget打开或创建一个共享内存对象后使用shmat将对象附加到进程的地址空间
shmid:shmget的返回值(对象标识符)
shmaddr:为非空的指针时返回地址取决于shmflg是否指定了SHM_RND,未指定地址由该参数决定,指定的话返回地址由该参数向下舍入一个SHMLBA值。
若为空指针,返回地址由系统决定,系统推荐方法
shmflg:读写权限
返回值:成功返回共享内存对象映射的首地址,失败-1
shmdt
int shmdt(const void *shmaddr);
参数
断开进程对共享内存的使用,在进程结束时自动断开
shmaddr:shmat的返回值
return:成功返回0,失败-1
shmctl
#include <sys/ipc.h>
#include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
参数
提供多种对共享内存的操作
shmid:shmget的返回值
cmd:命令值
IPC_RMID 删除对象并回收
IPC_SET 通过buf设置对象信息
IPC_STAT 通过buf返回对象信息
buf:共享内存对象信息结构体
return:成功返回0,失败-1
posix和system v共享内存的区别
1)system v共享内存大小在创建时固定,posix可以通过ftruecate修改
消息传递
管道(无名管道)
为啥叫无名管道?
没有系统标识符:文件名,所以在不考虑描述符传递的情况下,无名管道一般用于父子进程之间。
pipe
struct fd_pair {
long fd[2];
};
struct fd_pair pipe();
/* On all other architectures */
int pipe(int pipefd[2]);
pipe函数创建一个管道,提供一个单向的数据流,通过文件文件描述符访问,f[0]用于读,f[1]用于写。
IS_FIFO
该宏定义通过st_mode 判断文件是否为管道,搭配fstat使用(获取文件属性)
无名管道的使用
无名管道通过pipe创建了两个文件文件描述符,没有文件路径标识。一个用于向管道写,一个用于向文件读。
所以在进程间使用无名管道时需要使用相同的文件描述符,要实现不同进程使用同一个无名管道要么将文件描述符在进程之间传递,要么使用父子进程,子进程继承父进程的管道的文件描述符。
之后在一个进程关闭管道的读或写端,另一个进程关闭另一个端口。如此管道在一个进程读,另一个进程写,实现了单向半双工的管道,要实现双向半双工的管道可以使用两个管道实现。
shell命令中的管道
who|sort
在shell中管道使用 **|**标识,以上的命令开启了两个进程,通过管道符,将who进程的标准输出复制到管道的写入端,将管道的读出端复制到sort的标准输入。
全双工管道
全双工管道意味着,不论从哪一个端口写入管道,写入内容都会附加到管道缓存的尾部,不论从那个端口写,都会将内容附加到缓存的头部。实现全双工的管道可以使用两个单向数据流的半双工管道实现,否则读和写在同意缓存,会出现读写混乱。
popen 和 pclose
#include <stdio.h>
FILE *popen(const char *command, const char *type);
int pclose(FILE *stream);
popen函数创建一个管道并启动另一个进程,该进程为command字符串指定的shell行命令,popen在主进程和调用命令开启的进程之间创建一个管道,主进程具体要从管道读出还是写入由type参数决定。如果是r,则从管道读取新启动进程的标准输出,如果是w,则通过管道向新进程的标准输入写入。
pclose关闭popen打开的文件描述符,并等待新进程终止,返回shell命令的终止状态。
FIFO(有名管道)
为啥叫有名管道
FIFO也是半双工的单向数据流,但是又文件路径标记,所以可以在不同进程之间使用文件路径共同使用。
mkfifo 创建有名管道
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
参数:
pathname:fifo的文件路径名
mode:类似于open的mode参数。可以指定 O_CREAT | O_EXCL |0666,存在返回EEXIT错误,不存在则创建文件权限为0666.
返回值:成功创建返回0,否则返回-1
注意:不论创建失败或者成功都需要使用open或者fopen打开FIFO
当fifo存在时可以使用open或者fopen打开(一切皆文件,fifo也不例外)。
fifo只能读或者写,不能以可读可写的模式打开。
FIFO的写总是在头,读总是在尾。
FIFO不能使用lseek。
FIFO在所用调用的进程关闭后消失,但是系统中的文件名要使用unlink从文件系统系统中删除
打开FIFO读时,若没有任何进程打开该FIFO写,则读FIFO的进程将会阻塞。
管道和FIFO阻塞读写
系统对FIFO和管道的限制
- OPEN_MAX 一个进程在任意时刻可以打开的最大的文件描述符的个数,sysconf 函数 或者 ulimit 命令可查询
- PIPE_BUF 可原子的向FIFO或管道写入的数据的最大程度,即FIFO和管道的缓存大小。通常在<limits.h>中定义。
可以在程序中调用 pathconf 或 fpathconf 获取。 或者getconf命令。
问题记录
1、posix IPC 和 System V IPC 有和区别和联系 ?
Posix Portable Operation System Interfice 可移植操作系统接口
POSIX接口和System V接口是用于多线程和进程间通信的两种主要编程接口。它们各自有不同的特点、功能和适用场景。以下是对这两种接口的详细介绍及其异同点。
2、指定权限受到当前进程的文件模式创建掩码修正??
umask函数或命令
3、errno的定义
4、进程的文件创建模式掩码??
5、管道和FIFO的计数器
内核为管道和FIFO维护一个计数器,用于统计使用管道和FIFO的进程的个数,计数为0则关闭管道和FIFO。在使用ulink删除文件系统中的FIFO名称时,删除文件系统中FIFO的名称之后,已经打开的文件描述符不受影响(计数器不为0),但是删除后不可再通过文件路径访问FIFO。