侧边栏壁纸
  • 累计撰写 32 篇文章
  • 累计创建 55 个标签
  • 累计收到 2 条评论

目 录CONTENT

文章目录

Linux IPC:System V 与 POSIX 介绍

Testerfans
2022-05-13 / 0 评论 / 32 点赞 / 1,152 阅读 / 6,086 字 / 正在检测是否收录...
温馨提示:
本文最后更新于 2022-06-29,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

前言

在了解Linux IPC Namespace之前,我们需要了解什么是IPC,他是用来干什么的 ,这样有助于我们理解IPC Namespace。IPC是进程间通信(Inter-Process Communication)的缩写,是指在不同进程之间传播或交换信息。IPC有两种类型:System V 与 POSIX,通信方式通常有管道(包括无名管道Pipe和命名管道Named Pipe即FIFO)、消息队列(Message Queues)、信号量(Semaphores)、共享存储(Shared Memory)、Socket、Streams等。其中 Socket和Streams支持不同主机上的两个进程IPC。

System V简介

System V, 曾经也被称为 AT&T System V,是Unix操作系统众多版本中的一支。它最初由 AT&T 开发,在1983年第一次发布。一共发行了4个 System V 的主要版本:版本1、2、3 和 4。System V Release 4,或者称为SVR4,是最成功的版本,成为一些UNIX共同特性的源头,例如 ”SysV 初始化脚本“ (/etc/init.d),用来控制系统启动和关闭,System V Interface Definition (SVID) 是一个System V 如何工作的标准定义。
AT&T 出售运行System V的专有硬件,但许多(或许是大多数)客户在其上运行一个转售的版本,这个版本基于 AT&T 的实现说明。流行的SysV 衍生版本包括 Dell SVR4 和 Bull SVR4。当今广泛使用的 System V 版本是 SCO OpenServer,基于 System V Release 3,以及SUN Solaris 和 SCO UnixWare,都基于 System V Release 4。
System V 是 AT&T 的第一个商业UNIX版本(UNIX System III)的加强。传统上,System V 被看作是两种UNIX"风味"之一(另一个是 BSD)。然而,随着一些并不基于这两者代码的UNIX实现的出现,例如 Linux 和 QNX, 这一归纳不再准确,但不论如何,像POSIX这样的标准化努力一直在试图减少各种实现之间的不同。

POSIX简介

POSIX(Portable Operating System Interface for Computing Systems)是由IEEE 和ISO/IEC 开发的一簇标准。该标准是基于现有的UNIX 实践和经验,描述了操作系统的调用服务接口,用于保证编制的应用程序可以在源代码一级上在多种操作系统上移植运行。它是在1980 年早期一个UNIX 用户组(usr/group)的早期工作的基础上取得的。该UNIX 用户组原来试图将AT&T 的系统V 和Berkeley CSRG的BSD 系统的调用接口之间的区别重新调和集成,从而于1984 年产生了/usr/group 标准。1985 年,IEEE操作系统技术委员会标准小组委员会(TCOS-SS)开始在ANSI 的支持下责成IEEE 标准委员会制定有关程序源代码可移植性操作系统服务接口正式标准。到了1986 年4 月,IEEE 就制定出了试用标准。第一个正式标准是在1988 年9 月份批准的(IEEE 1003.1-1988),也既以后经常提到的POSIX.1 标准。

System V&Posix IPC

对比 System V POSIX
标准 AT&T 发布了(1983 年)三种新形式的 IPC 特性,即消息队列、共享内存和信号量。 由 IEEE 指定的便携式操作系统接口标准(Portable Operating System Interface),用于定义应用程序编程接口 (API)。POSIX 涵盖了所有三种形式的 IPC。
IPC方式 SYSTEM V IPC 涵盖了所有的 IPC 机制,即管道、命名管道、消息队列、信号、信号量和共享内存。它还涵盖了套接字和 Unix 域套接字。 几乎所有的基本概念都与 System V 相同,只是接口不同。
共享内存 共享内存接口调用 shmget()、shmat()、shmdt()、shmctl()。 共享内存接口调用 shm_open()、mmap()、shm_unlink()。
共享内存操作 需要 shmctl() 等系统调用、命令(ipcs、ipcrm)来执行状态/控制操作。 可以使用 fstat()、fchmod() 等系统调用检查和操作共享内存对象。
共享内存大小调整 System V 共享内存段的大小在创建时是固定的(通过 shmget())。 我们可以使用 ftruncate() 来调整底层对象的大小,然后使用 munmap() 和 mmap()(或 Linux 特定的 mremap())重新创建映射。
消息队列 消息队列接口调用 msgget()、msgsnd()、msgrcv()、msgctl()。 消息队列接口调用 mq_open()、mq_send()、mq_receive()、mq_unlink() 。
消息队列监控 不适用。 可以使用 select()、poll() 和 epoll API 监控 POSIX 消息队列。
消息队列属性访问 提供 msgctl() 调用。 提供函数(mq_getattr() 和 mq_setattr())来访问或设置属性 11. IPC - System V & POSIX 。
信号量 信号量接口调用 semget()、semop()、semctl()。 信号量接口调用 命名信号量 sem_open()、sem_close()、sem_unlink()、sem_post()、sem_wait()、sem_trywait()、sem_timedwait()、sem_getvalue() 未命名或基于内存的信号量 sem_init()、sem_post()、sem_wait (), sem_getvalue(),sem_destroy()。
IPC对象识别方式 使用密钥和标识符来识别 IPC 对象。 使用名称和文件描述符来识别 IPC 对象。
线程安全 不适用。 多线程安全。涵盖了互斥锁、条件变量、读写锁等线程同步功能。
通知 不适用。 为消息队列提供少量通知功能(例如 mq_notify())。

System V系统调用

API类别 API调用 API 作用
普通 int open(const char *pathname, int flags, mode_t mode) 打开一个已经存在的文件或者创建一个新的文件。
普通 int open(const char *pathname, int flags, mode_t mode) 创建并打开一个新文件。
普通 ssize_t read(int fd, void *buf, size_t count) 将文件的内容读入所需的缓冲区。
普通 ssize_t write(int fd, const void *buf, size_t count) 将缓冲区的内容写入到文件。
普通 int close(int fd) 关闭文件描述符。
普通 int stat(const char *path, struct stat *buf) 提供文件相关信息。
管道 int pipe2(int pipefd[2], int flags) 创建用于通信的管道,该管道返回两个用于读取和写入的文件描述符。
命名管道 int mknod(const char *pathname, mode_t mode, dev_t dev) 创建内存存储设备文件或特殊文件用来创建 FIFO。
命名管道 int mkfifo(const char *pathname, mode_t mode) 创建新的FIFO。
共享内存 int shmget(key_t key, size_t size, int shmflg) 创建一个新的共享内存段或获取现有段的标识符。
共享内存 void *shmat(int shmid, const void *shmaddr, int shmflg) 附加共享内存段并使该段成为调用进程的虚拟内存的一部分。
共享内存 int shmdt(const void *shmaddr) 分离共享内存段。
共享内存 int shmctl(int shmid, int cmd, struct shmid_ds *buf) 对共享内存执行控制操作。共享内存的通用控制操作很少,其中包括:
删除共享内存段(IPC_RMID)
接收共享内存的信息(IPC_STAT)
更新现有共享内存的新值(IPC_SET)
消息队列 int msgget(key_t key, int msgflg) 创建一个新的消息队列或访问一个已经存在的消息队列并获取句柄或标识符以执行与消息队列有关的操作,例如向队列发送消息和从队列接收消息。
消息队列 int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg) 使用所需的标识号将消息发送到所需的消息队列。
消息队列 ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg) 从消息队列接收消息。默认情况下,这是无限等待操作,意味着调用将被阻塞,直到它收到消息。
消息队列 int msgctl(int msqid, int cmd, struct msqid_ds *buf) 对消息队列执行控制操作。消息队列的通用控制操作很少,其中包括:
删除消息队列(IPC_RMID)
接收消息队列的信息(IPC_STAT)
更新现有消息队列的新值(IPC_SET)
信号量 int semget(key_t key, int nsems, int semflg) 创建一个新的信号量或获取现有信号量的标识符。信号量用于在处理同一对象的各种 IPC 之间执行同步。
信号量 int semop(int semid, struct sembuf *sops, unsigned nsops) 对信号量值执行信号量操作。基本的信号量操作是获取或释放信号量上的锁。
信号量 int semctl(int semid, int semnum, int cmd, …) 对信号量执行控制操作。信号量的通用控制操作很少,其中包括:
删除信号量(IPC_RMID)
接收信号量信息(IPC_STAT)
更新现有信号量的新值(IPC_SET)
信号 sighandler_t signal(int signum, sighandler_t handler) 设置信号(信号编号)和信号处理程序的处置。换句话说,注册例程,该例程在发出该信号时执行。
信号 int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact) 与 signal() 相同,设置信号的处置,即在收到注册信号后根据注册的信号处理程序执行某些操作。该系统调用支持对 signal() 进行更精细的控制,例如阻塞某些信号、调用信号处理程序后将信号动作恢复到默认状态、提供用户和系统消耗的时间、发送进程的进程 ID 等信息。
内存映射 void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset) 将文件映射到内存中。一旦映射到内存中,访问文件就像使用地址访问数据一样容易,而且通过这种方式,调用不像系统调用那样昂贵。
内存映射 int munmap(void *addr, size_t length) 从内存中取消被映射的文件。

POSIX系统调用

类别 系统调用 API 作用
共享内存 int shm_open(const char *name, int oflag, mode_t mode) 创建共享内存
共享内存 int shm_unlink(const char *name) 结束到共享内存的连接,并在最后一个进程关闭它时将其删除
共享内存 void mmap(void addr, size_t length, int prot, int flags, int fd, off_t offset) 映射内存
消息队列 mqd_t mq_open(const char name, int oflag, mode_t mode, struct mq_attr attr) 创建命名消息队列
消息队列 mqd_t mq_close(mqd_t mqdes) 结束到开放式消息队列的连接
消息队列 mqd_t mq_unlink(const char *name) 结束到开放式消息队列的连接,并在最后一个进程关闭此队列时将其删除
消息队列 mqd_t mq_send(mqd_t mqdes, const char *msg_ptr, size_t msg_len, unsigned msg_prio) 将消息放入队列
消息队列 ssize_t mq_receive(mqd_t mqdes, char msg_ptr, size_t msg_len, unsigned msg_prio) 在队列中接收消息
消息队列 mqd_t mq_notify(mqd_t mqdes, const struct sigevent *notification) 通知进程或线程消息已存在于队列中
消息队列 mqd_t mq_getattr(mqd_t mqdes, struct mq_attr attr) 、mqd_t mq_setattr(mqd_t mqdes, struct mq_attr newattr, struct mq_attr *oldattr) 设置或获取消息队列属性
信号量 sem_t sem_open(const char name, int oflag, mode_t mode, unsigned int value) 创建命名信号量
信号量 int sem_init(sem_t *sem, int pshared, unsigned int value) 初始化信号量结构
信号量 int sem_close(sem_t *sem) 结束到开放式信号量的连接
信号量 int sem_unlink(const char *name) 结束到开放式信号量的连接,并在最后一个进程关闭此信号量时将其删除
信号量 int sem_getvalue(sem_t sem, int sval) 将信号量的值复制到指定整数中
信号量 int sem_wait(sem_t *sem) 递减信号量计数,当其他进程拥有信号量时进行阻塞,或者当其他进程拥有信号量时返回错误
信号量 int sem_post(sem_t *sem) 递增信号量计数

本文参考:
System V 与 POSIX 初步认识
Inter Process Communication - Overview
man手册

32

评论区