Linux环境编程第六天笔记--system-V IPC

Linux环境编程第六天笔记

system-V IPC

消息队列、共享内存和信号量统称为system-V IPC,在系统中它们都使用key(键值)作为唯一标识,它们都是持续性资源。即被创建后不会因为进程的结束而消失,除非调用特殊的函数或命令删除。

进程每次打开一个IPC对象,都会获得表征这个对象的ID,IPC的key是唯一的,ID是可变的。

ftok()

获取一个当前未用的IPC的key

返回值:成功:返回键值;失败:返回-1;

复制代码
 #include <sys/types.h>
 #include <sys/ipc.h>
 ​
 key_t ftok(const char *pathname, int proj_id);
  • pathname :一个合法的路径。

  • proj_id :一个整数

  1. 如果两个参数相同,那么获得的key值也相同。

  2. 如果同一个目录中的进程需要超过1个IPC对象,可以通过第2个参数来标识。

  3. 即便是不同类型的IPC也不能使用同一个key。

查看或删除当前系统IPC的命令

  • 查看消息队列:ipcs -q

  • 查看共享内存:ipcs -m

  • 查看信号量:ipcs -s

  • 查看所有IPC对象:ipcs -a

  • 删除指定的消息队列:ipcrm -q MSG_IDipcrm _Q msg_key

  • 删除指定共享内存:ipcrm -m SHM_IDipcrm -M shm_key

  • 删除指定的信号量:ipcrm -s SEM_IDipcrm -S sem_key

删除举例:假如某个消息队列的msqid12345key0xabcdef

复制代码
 ipcrm -s 11223   
 ipcrm -S 0x98765 
消息队列(MSG)

消息队列是一种带有数据标识的特殊管道,使得每一段被写入的数据都变成带标识的消息,读取该段消息的进程只要指定这个标识就可以正确的读取。一个代标识的消息队列,就像是多条并存的管道一样。

msgget()

获取消息队列的ID(chuang)

返回值:成功:该消息队列对的ID;失败:-1

复制代码
 #include <sys/types.h>
 #include <sys/ipc.h>
 #include <sys/msg.h>
 ​
 int msgget(key_t key, int msgflg);
  • key:消息队列的键值。如果指定为IPC-PRIVATE系统会使用一个未使用的key,来对应这个消息队列。

  • msgflg:(是一个位屏蔽字)

    • IPC_CREAT:如果key对应的MSG不存在,则创建该对象。

    • IPC_EXCL:如果key对应的MSG已存在,则报错。

    • mode:MSG的访问权限(八进制:如0666)权限只有读和写,执行权限是无效的。

msgsnd()和msgrcv()

msgsnd() 发送消息;msgrcv()接收消息

返回值:成功:msgsnd()返回0,msgrcv()返回读取的字节数;失败:均返回-1

复制代码
 #include <sys/msg.h>
 ​
 // 发送消息
 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);
  • msqid:发送、接收消息的消息队列对的ID

  • msgp:要发送的数据、要接收的数据的存储区域指针

  • msgsz:要发送的数据、要接收的数据的大小

  • msgtyp:(msgrcv独有)要接收的消息标识

    0 - 接收队列中的第一条消息 • >0 - 接收指定类型的第一条消息 • <0 - 接收类型小于等于 |msgtyp| 的第一条消息

  • msgflg:标志位 • 0 - 阻塞接收 • IPC_NOWAIT - 非阻塞读出、写入消息 • MSG_EXCEPT - 接收类型不等于 msgtyp 的消息(msgrcv独有) • MSG_NOERROR - 如果消息尺寸大于msgsz,截断而不报错(msgrcv独有)

发送消息时,消息必须被组织成以下形式

复制代码
 struct msgbuf 
 {
     long mtype;       // 消息标识,必须 > 0
     char mtext[1];    // 消息数据,可以是任意类型
 };

备注:消息的标识可以是任意长整型数值,但不能是0L。参数msgsz的数值是正文大小,不包含消息标识

msgctl()
复制代码
 #include <sys/types.h>
 #include <sys/ipc.h>
 #include <sys/msg.h>
 ​
 int msgctl(int msqid, int cmd, struct msqid_ds *buf);
  • msqid:消息队列标识符,由 msgget() 返回

  • cmd

    命令 功能
    IPC_STAT 获取消息队列的状态信息,存储在msqid_ds()中
    IPC_SET 设置消息队列的参数,存储在msqid_ds()中//使用前需要IPC_STAT获取一下原状态
    IPC_RMID 立即删除消息队列,并且唤醒所有阻塞在消息队列上的进程,同时忽略第三个参数
    IPC_INFO 获取系统消息队列限制信息
    MSG_INFO 获取系统消息队列资源使用信息
    MSG_STAT 通过索引获取消息队列状态

    返回值:成功:

    IPC_STATIPC_SETIPC_RMID返回0

    IPC_INFOMSG_INFO返回系统中已分配的消息队列总数

    MSG_STAT返回消息队列的ID

  • buf:相关消息结构体的缓冲区
复制代码
 //msqid_ds 结构体
 struct msqid_ds {
     struct ipc_perm msg_perm;     /* 所有权和权限 */
     time_t          msg_stime;    /* 最后发送时间 */
     time_t          msg_rtime;    /* 最后接收时间 */
     time_t          msg_ctime;    /* 最后修改时间 */
     unsigned long   __msg_cbytes; /* 队列中当前字节数 */
     msgqnum_t       msg_qnum;     /* 队列中消息数量 */
     msglen_t        msg_qbytes;   /* 队列最大字节数 */
     pid_t           msg_lspid;    /* 最后发送进程的PID */
     pid_t           msg_lrpid;    /* 最后接收进程的PID */
 };
 //ipc_perm 结构体
 struct ipc_perm {
     key_t          __key;    /* 消息队列的key值 */
     uid_t          uid;      /* 所有者的有效用户ID */
     gid_t          gid;      /* 所有者的有效组ID */
     uid_t          cuid;     /* 创建者的有效用户ID */
     gid_t          cgid;     /* 创建者的有效组ID */
     unsigned short mode;     /* 权限模式 */
     unsigned short __seq;    /* 序列号 */
 };
共享内存(SHM)

共享内存是效率最高的IPC,因为它抛弃了内核这个"代理人",共享内存一般不能单独使用,需要配合信号量、互斥锁等协调机制。共享的内存是同一块内存映射到不同内存的虚拟空间。

使用共享内存的一般步骤

  1. 获取共享内存对象的ID

  2. 将共享内存映射到本进程虚拟内存空间的某个区域

  3. 当不在映射时,解除映射关系

  4. 当没有进程再需要这块内存时,删除它

shmget()

获取共享内存的ID

返回值:成功:返回该共享内存的ID;失败:返回-1;

复制代码
 #include <sys/ipc.h>
 #include <sys/shm.h>
 ​
 int shmget(key_t key, size_t size, int shmflg);
  • key:共享内存的键值

  • size:共享内存的尺寸

  • shmflg:

作用
IPC_CREAT 如果键值对应的共享内存不存在,则创建
IPC_EXCL 如果键值对应的共享内存存在,则报错
SHM_HUGETLB 使用大页面来分配内存
SHM_NORESERVE 不在交换分区中为这块共享内存保留空间
mode 共享内存的访问权限(八进制:如0644)

所谓大页面是指采用比默认尺寸4KB更大的页面,Linux内核支持以2MB作为物理分页的基本单位,以减少缺页中断。

shmat()和hshmdt()

对共享内存进行映射,或解除映射

复制代码
#include <sys/types.h>
#include <sys/shm.h>

void *shmat(int shmid, const void *shmaddr, int shmflg);
int shmdt(const void *shmaddr);
  • shmid:共享内存的ID

  • shmaddr:

    shmat:

    • 值为NULL:系统自动选择一个合适的虚拟内存地址去映射共享内存

    • 不为NULL:系统会根据shmaddr来选择一个合适的内存区域

    shmdt

    • 共享内存的首地址
  • shmflg

    • SHM_RDONLY:以只读的方式映射共享内存

    • SHM_REMAP:重新映射,此时shmaddr不能为NULL

    • SHM_RND:选择比shmaddr小的最大地址来对齐地址

    • 0:默认:可读写

备注:共享内存只能以只读或可读写的方式映射,无法以只写的方式映射。

解除映射后系统不再允许访问SHM

shmctl()

获取或设置共享内存的相关属性

返回值:成功:SHM_STAT:返回shmid的ID;失败:-1

复制代码
#include <sys/ipc.h>
#include <sys/shm.h>

int shmctl(int shmid, int cmd, struct shmid_ds *buf);

struct shmid_ds {
    struct ipc_perm shm_perm;    /* 所有权和权限 */
    size_t          shm_segsz;   /* 段大小(字节) */
    time_t          shm_atime;   /* 最后附加时间 */
    time_t          shm_dtime;   /* 最后分离时间 */
    time_t          shm_ctime;   /* 最后修改时间 */
    pid_t           shm_cpid;    /* 创建者PID */
    pid_t           shm_lpid;    /* 最后操作者PID */
    shmatt_t        shm_nattch;  /* 当前附加数量 */
    // 平台特定字段可能还有更多
};

struct ipc_perm {
    key_t          __key;    /* 共享内存的key值 */
    uid_t          uid;      /* 所有者的有效用户ID */
    gid_t          gid;      /* 所有者的有效组ID */
    uid_t          cuid;     /* 创建者的有效用户ID */
    gid_t          cgid;     /* 创建者的有效组ID */
    unsigned short mode;     /* 权限模式 */
    unsigned short __seq;    /* 序列号 */
};
  • shmid:共享内存标识符

  • cmd:

    命令 功能
    IPC_STAT 获取共享内存段的状态信息,放到buf中
    IPC_SET 设置共享内存段的参数为buf指向的内容,需要先使用IPC_STAT获取原信息
    IPC_RMID 标记共享内存段为即将被删除
    IPC_INFO 获取系统共享内存限制信息
    SHM_INFO 获取系统共享内存资源使用情况
    SHM_STAT 通过索引获取共享内存信息
    SHM_LOCK 锁定共享内存到物理内存(防止交换)
    SHM_UNLOCK 解锁共享内存(允许交换)
  • buf:指向 shmid_ds 结构体的指针,用于传入或传出信息

信号量(SEM)

信号量是一种用于进程间同步和互斥的IPC机制,可以看作是一个计数器,用于控制多个进程对共享资源的访问。

  1. 多个进程或线程有可能同时访问的资源(变量、链表、文件等)称为共享资源。也称为临界资源。

  2. 访问这些资源的代码称为临界代码,这些代码区域称为临界区。

  3. P操作是申请资源,信号量-1;V操作时释放资源,信号量+1。

semget()

获取信号量ID

返回值:成功:返回该信号量ID;失败:返回-1;

复制代码
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

int semget(key_t key, int nsems, int semflg);
  • key:信号量的键值。

  • nsems:信号量的元素个数。

  • semflg:

    • IPC_CREAT:如果key对应的信号量不存在,则创建。

    • IPC_EXCL:如果key对应的信号量已存在,则报错

    • mode:信号量的访问权限(八进制:如0644)。

    创建信号量时,还受到以下系统信息的影响:

    1,SEMMNI:系统中信号量的总数最大值。

    2,SEMMSL:每个信号量中信号量元素的个数最大值。

    3,SEMMNS:系统中所有信号量中的信号量元素的总数最大值。

    Linux中,以上信息在/proc/sys/kernel/sem中查看

semop()

对信号量进行P/V操作,或0操作

返回值:成功:0;失败:-1;

复制代码
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

int semop(int semid, struct sembuf *sops, size_t nsops);

//结构体sembuf
struct sembuf {
    unsigned short sem_num;  /* 信号量在信号量集中的索引(从0开始) */
    short          sem_op;   /* 操作值(正数、负数、0) */
    short          sem_flg;  /* 操作标志 */
};
  • semid:信号量ID;

  • sops:信号量操作结构体数组;

    sem_op 操作类型
    正数 释放资源(V操作)
    负数 申请资源(P操作)
    0 等待归零
    sem_flg 说明
    0 默认,阻塞操作
    IPC_NOWAIT 非阻塞操作,无法立即完成则返回 EAGAIN
    SEM_UNDO 进程结束时自动撤销操作,防止死锁
  • nsops:结构体数组元素个数;

备注:

当 sem_op 等于 0 时:进行等零操作,如果此时 semval 恰好为 0, 则 semop ( ) 立即成功返回,否则如果 IPC_NOWAIT 被设置,则立即出错返回并将 errno 设置为 EAGAIN, 否则将使得进程进入睡眠,直到以下情况发生:

  1. semval 变为 0。

  2. 信号量被删除。(将导致 semop ( ) 出错退出,错误码为 EIDRM)

  3. 收到信号。(将导致 semop ( ) 出错退出,错误码为 EINTR)

semctl()

获取或设置信号量的相关属性

返回值:失败:-1

复制代码
 #include <sys/types.h>
 #include <sys/ipc.h>
 #include <sys/sem.h>
 ​
 int semctl(int semid, int semnum, int cmd, ...);
 ​
 struct semid_ds {
     struct ipc_perm sem_perm;  /* 所有权和权限 */
     time_t          sem_otime; /* 最后semop时间 */
     time_t          sem_ctime; /* 最后修改时间 */
     unsigned long   sem_nsems; /* 信号量集中的信号量数量 */
 };
 ​
 struct ipc_perm {
     key_t          __key;    /* 信号量集的key值 */
     uid_t          uid;      /* 所有者的有效用户ID */
     gid_t          gid;      /* 所有者的有效组ID */
     uid_t          cuid;     /* 创建者的有效用户ID */
     gid_t          cgid;     /* 创建者的有效组ID */
     unsigned short mode;     /* 权限模式 */
     unsigned short __seq;    /* 序列号 */
 };
  • semid:信号量ID;

  • semnum:信号量元素序号(数组下标)

  • cmd:

    命令 功能说明 是否需要参数四
    IPC_STAT 获取当前信号量的属性信息(如权限、所有者、创建时间等)
    IPC_SET 设置当前信号量的属性信息(需有对应权限,如修改权限、所有者)
    IPC_RMID 立即删除该信号量,参数 semnum 会被忽略(信号量被标记为删除,后续无法访问)
    IPC_INFO 获取系统级信号量的限制值(如最大信号量数、每个信号量的最大元素数)
    SEM_INFO 获取系统中信号量的资源消耗信息(如已创建的信号量数、占用的内存等)
    SEM_STAT 类似 IPC_STAT,但通过内核中信号量信息数组的下标(而非信号量 ID)获取系统中所有信号量的信息
    GETALL 返回当前信号量集中所有信号量元素 的值,参数 semnum 被忽略
    GETNCNT 返回正阻塞在该信号量元素 P 操作(减 1) 上的进程总数
    GETPID 返回最后一个对该信号量元素执行操作的进程 PID
    GETVAL ``返回该信号量元素的当前值
    GETZCNT 返回正阻塞在该信号量元素 等零操作(sem_op=0 上的进程总数
    SETALL 设置当前信号量集中所有信号量元素 的值,参数 semnum 被忽略
    SETVAL 设置该信号量元素的当前值
  • ...:可变参数,通常是union semun

复制代码
 // 必须自己定义 union semun
 union semun {
     int              val;    /* 用于 SETVAL */
     struct semid_ds *buf;    /* 用于 IPC_STAT, IPC_SET */
     unsigned short  *array;  /* 用于 GETALL, SETALL */
     struct seminfo  *__buf;  /* 用于 IPC_INFO (Linux特有) */
 };
 ​
 // 也可以这样简化定义(更通用)
 typedef union {
     int val;
     struct semid_ds *buf;
     unsigned short *array;
 } semun_t;
相关推荐
AlfredZhao6 小时前
vi 删除指定范围的行,不用再反复按 dd
linux·vi
用户97183563346612 小时前
银河麒麟 KY10 申威(SW64) 安装 nginx-1.16.1-2.p01.ky10.sw_64.rpm 详细步骤
linux
猪脚踏浪13 小时前
linux 拷贝文件或目录到指定的位置
linux
摇滚侠1 天前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql
bush41 天前
嵌入式linux学习记录十四、术语
linux·嵌入式
载数而行5201 天前
Linux 11 动态监控指令top
linux
不会C语言的男孩1 天前
Linux 系统编程 · 第 8 章:进程基础
linux·c语言
闪闪发亮的小星星1 天前
高斯光以及高斯光公式解释
笔记
古城小栈1 天前
Unix 与 Linux 异同小叙
linux·服务器·unix
cqbzcsq1 天前
CellFlow虚拟细胞论文阅读
论文阅读·人工智能·笔记·学习·生物信息