OS55.【Linux】System V消息队列的简单了解

目录

[1.知识回顾: 进程间通信的前提](#1.知识回顾: 进程间通信的前提)

2.消息队列的原理

3.消息队列的系统调用

msgget

msgctl

msgsnd

查看消息队列的命令

删除消息队列的命令

4.shmid_ds、msqid_ds、semqid_ds共同点

有统一的结构

[如何管理struct ipc_perm?](#如何管理struct ipc_perm?)


1.知识回顾: 进程间通信的前提

之前在前面的几篇文章都反复强调了进程间通信的前提: 必须先让不同进程间看到同一份资源

,同一份资源可以是文件缓冲区也可以是内存块也可以是:对于System V消息队列,即必须先让不同进程间看到同一个队列,如下图:

2.消息队列的原理

进程间以数据块的形式发送数据,这些数据块需要排队形成队列

这个数据块是带类型的数据块

假设进程A和进程B使用消息队列进行通信,那么进程A和要区分是自己发送的数据块还是进程B发给它的

其次,该队列肯定由操作系统提供,不可能是进程提供的,因为进程间具有独立性

3.消息队列的系统调用

类比OS52.【Linux】System V 共享内存(1)文章讲的:shmget、shmat、shmdt、shmctl系统调用

msgget

作用: 获取或创建消息队列

和之前一样, key仍然用ftok算

若要创建全新的消息队列,则msgflg为IPC_CREAT | IPC_EXCL

返回值也和shmget类似:

注意: 消息队列的msgget和共享内存的shmget有区别, 消息队列不用提供大小

msgctl

若要关闭消息队列,buf传空指针即可

msgsnd

msgsnd的snd是send的缩写,作用: 发送消息,换句话说,是发送数据块

msqid、msgflg分别类似shmid、shmflg

msgp指向要发送的数据块,msgsz为要发送数据块的大小

注意到: msgp的类型是const void*,用户可以自己定义类型进行传递

手册指出"caller-defined structure": 大致定义为struct msgbuf

查看消息队列的命令

bash 复制代码
ipcs -q

删除消息队列的命令

bash 复制代码
ipcrm -M key
#或
ipcrm -m shmid

4.shmid_ds、msqid_ds、semqid_ds共同点

shmid_ds负责描述共享内存:

cpp 复制代码
struct shmid_ds {
    struct ipc_perm shm_perm;    /* Ownership and permissions */
    size_t          shm_segsz;   /* Size of segment (bytes) */
    time_t          shm_atime;   /* Last attach time */
    time_t          shm_dtime;   /* Last detach time */
    time_t          shm_ctime;   /* Creation time/time of last
                                    modification via shmctl() */
    pid_t           shm_cpid;    /* PID of creator */
    pid_t           shm_lpid;    /* PID of last shmat(2)/shmdt(2) */
    shmatt_t        shm_nattch;  /* No. of current attaches */
    ...
};
 
struct ipc_perm {
        key_t          __key;    /* Key supplied to shmget(2) */
        uid_t          uid;      /* Effective UID of owner */
        gid_t          gid;      /* Effective GID of owner */
        uid_t          cuid;     /* Effective UID of creator */
        gid_t          cgid;     /* Effective GID of creator */
        unsigned short mode;     /* Permissions + SHM_DEST and
                                    SHM_LOCKED flags */
        unsigned short __seq;    /* Sequence number */
    };

msqid_ds负责描述消息队列:

cpp 复制代码
struct msqid_ds {
    struct ipc_perm msg_perm;   /* Ownership and permissions */
    time_t          msg_stime;  /* Time of last msgsnd(2) */
    time_t          msg_rtime;  /* Time of last msgrcv(2) */
    time_t          msg_ctime;  /* Time of creation or last
                                    modification by msgctl() */
    unsigned long   msg_cbytes; /* # of bytes in queue */
    msgqnum_t       msg_qnum;   /* # number of messages in queue */
    msglen_t        msg_qbytes; /* Maximum # of bytes in queue */
    pid_t           msg_lspid;  /* PID of last msgsnd(2) */
    pid_t           msg_lrpid;  /* PID of last msgrcv(2) */
};

struct ipc_perm {
    key_t          __key;       /* Key supplied to msgget(2) */
    uid_t          uid;         /* Effective UID of owner */
    gid_t          gid;         /* Effective GID of owner */
    uid_t          cuid;        /* Effective UID of creator */
    gid_t          cgid;        /* Effective GID of creator */
    unsigned short mode;        /* Permissions */
    unsigned short __seq;       /* Sequence number */
};

进程间通信其实还可以用信号量,semqid_ds负责描述信号量:

cpp 复制代码
struct semid_ds {
    struct ipc_perm sem_perm;  /* Ownership and permissions */
    time_t          sem_otime; /* Last semop time */
    time_t          sem_ctime; /* Creation time/time of last
                                    modification via semctl() */
    unsigned long   sem_nsems; /* No. of semaphores in set */
};

struct ipc_perm {
    key_t          __key; /* Key supplied to semget(2) */
    uid_t          uid;   /* Effective UID of owner */
    gid_t          gid;   /* Effective GID of owner */
    uid_t          cuid;  /* Effective UID of creator */
    gid_t          cgid;  /* Effective GID of creator */
    unsigned short mode;  /* Permissions */
    unsigned short __seq; /* Sequence number */
};

有统一的结构

System V的进程间通信被精心设计过,三个结构体的第一个字段类型都是struct ipc_perm,尽管shm_perm msg_perm sem_perm的名字不一样

cpp 复制代码
struct xxxid_ds {
    struct ipc_perm xxx_perm;  /* Ownership and permissions */
    //......
};

struct ipc_perm {
    key_t          __key;  
    uid_t          uid;   
    gid_t          gid;   
    uid_t          cuid;  
    gid_t          cgid; 
    unsigned short mode;  
    unsigned short __seq;
};

这里面有面向对象的思想,struct xxxid_ds含有struct ipc_perm xxx_perm,显然后者是基类,前者是子类

如何管理struct ipc_perm?

答:先描述再组织

先描述: ipc权限使用struct ipc_perm

再组织: 使用数据结构将struct ipc_perm串起来

++描述ipc权限有两种,struct ipc_perm是用户层的描述,struct kern_ipc_perm是内核层的描述++

例如linux 2.6.18内核的/include/linux/ipc.h定义了struct kern_ipc_perm:

cpp 复制代码
/* used by in-kernel data structures */
struct kern_ipc_perm
{
	spinlock_t	lock;
	int		deleted;
	key_t		key;
	uid_t		uid;
	gid_t		gid;
	uid_t		cuid;
	gid_t		cgid;
	mode_t		mode; 
	unsigned long	seq;
	void		*security;
};

在/ipc/util.h中定义了struct ipc_id_ary ,其使用变长数组织ipc权限:

cpp 复制代码
struct ipc_id_ary {
	int size;
	struct kern_ipc_perm *p[0]; //写在结构体末尾,是变长数组
};

将shm_perm、msg_perm、sem_perm的地址写入struct kern_ipc_perm *p[0],那么就能很方便地使用箭头**->和强制类型转换**访问struct shmid_ds msqid_ds semid_ds的每一个字段:

因为三个结构体的第一个字段类型都是struct ipc_perm,即struct ipc_perm字段的地址等同于struct xxxid_ds的地址

cpp 复制代码
//addr1、addr2、addr3的类型都为struct ipc_perm*
((struct shmid_ds*)addr1)->???
((struct msqid_ds*)addr2)->???
((struct semqid_ds*)addr3)->???

管理ipc权限就相当于对struct kern_ipc_perm *p[0]进行增删改查

++描述xxid有两种,struct xxxid_ds是用户层的描述,struct xxxid_kernel是内核层的描述++

例如Linux 6.18.6中的/ipc/shm.c:

cpp 复制代码
struct shmid_kernel /* private to the kernel */
{
	struct kern_ipc_perm	shm_perm;
	struct file		*shm_file;
	unsigned long		shm_nattch;
	unsigned long		shm_segsz;
	time64_t		shm_atim;
	time64_t		shm_dtim;
	time64_t		shm_ctim;
	struct pid		*shm_cprid;
	struct pid		*shm_lprid;
	struct ucounts		*mlock_ucounts;

	/*
	 * The task created the shm object, for
	 * task_lock(shp->shm_creator)
	 */
	struct task_struct	*shm_creator;

	/*
	 * List by creator. task_lock(->shm_creator) required for read/write.
	 * If list_empty(), then the creator is dead already.
	 */
	struct list_head	shm_clist;
	struct ipc_namespace	*ns;
} __randomize_layout;

补: shmid msqid semid是数组的下标,是线性递增的

相关推荐
小草cys6 小时前
在 openEuler 上安装 DDE 图形桌面环境(适用于华为鲲鹏服务器/PC)
运维·服务器
天才奇男子14 小时前
HAProxy高级功能全解析
linux·运维·服务器·微服务·云原生
小李独爱秋14 小时前
“bootmgr is compressed”错误:根源、笔记本与台式机差异化解决方案深度指南
运维·stm32·单片机·嵌入式硬件·文件系统·电脑故障
学嵌入式的小杨同学14 小时前
【Linux 封神之路】信号编程全解析:从信号基础到 MP3 播放器实战(含核心 API 与避坑指南)
java·linux·c语言·开发语言·vscode·vim·ux
酥暮沐14 小时前
iscsi部署网络存储
linux·网络·存储·iscsi
❀͜͡傀儡师15 小时前
centos 7部署dns服务器
linux·服务器·centos·dns
Dying.Light15 小时前
Linux部署问题
linux·运维·服务器
S190115 小时前
Linux的常用指令
linux·运维·服务器
萤丰信息15 小时前
AI 筑基・生态共荣:智慧园区的价值重构与未来新途
大数据·运维·人工智能·科技·智慧城市·智慧园区
小义_15 小时前
【RH134知识点问答题】第7章 管理基本存储
linux·运维·服务器