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;
相关推荐
阳光九叶草LXGZXJ2 小时前
达梦数据库-学习-48-DmDrs控制台命令(同步之Manager、CPT模块)
linux·运维·数据库·sql·学习
诸神缄默不语2 小时前
Linux命令行教程
linux
乌恩大侠2 小时前
【笔记】USRP 5G 和 6G 参考架构
笔记·5g
biuyyyxxx2 小时前
Python自动化办公学习笔记(一) 工具安装&教程
笔记·python·学习·自动化
舟舟亢亢3 小时前
Java集合笔记总结
java·笔记
丝斯20113 小时前
AI学习笔记整理(66)——多模态大模型MOE-LLAVA
人工智能·笔记·学习
i建模4 小时前
如何在Arch Linux中重设忘记的root密码
linux·运维·服务器
kida_yuan5 小时前
【Linux】运维实战笔记 — 我常用的方法与命令
linux·运维·笔记
@syh.5 小时前
【linux】进程控制
linux