System V 消息队列

1、必须让不同进程看到同一个队列
2、允许不同的进程,向内核中发送带类型的数据块,数据块的类型,用来区分这个数据块是属于哪个进程

操作系统将A进程的数据块链接到消息队列(由操作系统提供的)中,操作系统将B进程的数据块链接到消息队列中,此时A进程可以通过消息队列获取B进程的数据块,B进程也可以通过消息队列获取A进程的数据块
从而实现让双方进程以数据块的形式发送数据
消息队列与信号量【Linux】-LMLPHP
总结:
消息队列和共享内存一样,消息队列的资源也必须自行删除,否则不会自动清除,因为system V IPC资源的生命周期是随内核的

msgget

单独使用ipcs命令时,会默认列出消息队列、共享内存以及信号量相关的信息,若只想查看它们之间某一个的相关信息,可以选择携带以下选项:

-q:列出消息队列相关信息。
-m:列出共享内存相关信息。
-s:列出信号量相关信息。

创建消息队列
消息队列与信号量【Linux】-LMLPHP
创建消息队列也需要使用ftok函数生成一个key值,这个key值作为msgget函数的第一个参数。
msgget函数的第二个参数,与创建共享内存时使用的shmget函数的第三个参数相同。
消息队列创建成功时,msgget函数返回的一个有效的消息队列标识符(用户层标识符)。

msgflg

1、IPC_CREAT(单独使用):
如果内核中不存在键值与key相等的消息队列,则新建一个消息队列,如果存在这样的消息队列,则直接返回该消息队列的句柄

2、IPC_CREAT | IPC_EXCL:
如果内核中不存在键值与key相等的消息队列,则新建一个消息队列,如果存在这样的消息队列,则出错返回

释放消息队列
msgctl
消息队列与信号量【Linux】-LMLPHP
第一个参数msqid,表示所控制消息队列的用户级标识符。
第二个参数cmd,表示具体的控制动作。

shmctl函数的第二个参数传入的常用的选项有以下三个
1、IPC_STAT,获取消息队列的当前关联值,此时参数buf作为输出型参数
2、IPC_SET ,在进程有足够权限的前提下,将消息队列的当前关联值设置为buf所指的数据结构中的值
3、IPC_RMID ,删除消息队列段

第三个参数buf,用于获取或设置所控制消息队列的数据结构

msgsnd && msgrcv

消息队列与信号量【Linux】-LMLPHP
第一个参数msqid,表示消息队列的用户级标识符。
第二个参数msgp,表示待发送的数据块。
msgsnd函数的第二个参数必须为以下结构:

struct msgbuf{
	long mtype;       /* message type, must be > 0 */
	char mtext[1];   //mtext即为待发送的信息,当我们定义该结构时,mtext的大小可以自己指定
};

第三个参数msgsz,表示所发送数据块的大小
第四个参数msgflg,表示发送数据块的方式,一般默认为0即可。

msgrcv
消息队列与信号量【Linux】-LMLPHP
第一个参数msqid,表示消息队列的用户级标识符。
第二个参数msgp,表示获取到的数据块,是一个输出型参数。
第三个参数msgsz,表示要获取数据块的大小
第四个参数msgtyp,表示要接收数据块的类型

return val :
msgsnd调用成功,返回实际获取到mtext数组中的字节数。
msgsnd调用失败,返回-1。

System V信号量

当进程A正在写入,写入了一部分,就被进程B拿走了,导致进程双方发送和接受的数据不完整 (数据不一致问题)

进程A和进程B看到的同一份资源,这份共享资源,如果不加保护,可能会导致数据不一致问题
解决方案:
任何时刻,只允许一个执行流访问共享资源 (互斥

关于临界资源
1、共享的,任何时刻只允许一个执行流访问(就是执行访问代码)的资源,这种资源叫做临界资源,临界资源一般是内存空间,
2、访问临界资源的代码,这种代码叫做临界区

信号量/信号灯的本质是一把计数器
关于计数器:
1、申请计数器成功,就表示我具有访问资源的权限了
2、申请了计数器资源,当前可以访问我要的资源了吗?
不能。申请了计数器资源只是对资源的预订机制
3、计数器可以有效保证进入共享资源的执行流的数量
4、所以每一个执行流,想访问共享资源中的一部分的时候,不是直接访问,而是先申请计数器资源。

关于信号量
1、执行流申请资源,必须先申请信号量资源,得到信号量之后,才能访问临界资源
2、申请信号量的本质:是对临界资源的预订机制
3、我们把值只能为1,0两态的计数器叫做二元信号量,二元信号量就是互斥功能,二元信号量本质就是一个锁,锁的作用就是保护
4、申请信号量,本质是对计数器–(P操作)
释放资源,释放信号量,本质是对计数器进行++操作(V操作)

信号量的申请和释放,PV操作,这种操作是原子的,如何理解原子
原子:一件事情要么不做,要做就做完,没有正在做的概念

多个信号量 :
和信号量是几

在系统当中也为信号量维护了相关的内核数据结构。
信号量的数据结构如下:

struct semid_ds {
	struct ipc_perm sem_perm;       /* permissions .. see ipc.h */
	__kernel_time_t sem_otime;      /* last semop time */
	__kernel_time_t sem_ctime;      /* last change time */
	struct sem  *sem_base;      /* ptr to first semaphore in array */
	struct sem_queue *sem_pending;      /* pending operations to be processed */
	struct sem_queue **sem_pending_last;    /* last pending operation */
	struct sem_undo *undo;          /* undo requests on this array */
	unsigned short  sem_nsems;      /* no. of semaphores in array */
};

信号量数据结构的第一个成员也是ipc_perm类型的结构体变量,ipc_perm结构体的定义如下:

struct ipc_perm{
	__kernel_key_t  key;
	__kernel_uid_t  uid;
	__kernel_gid_t  gid;
	__kernel_uid_t  cuid;
	__kernel_gid_t  cgid;
	__kernel_mode_t mode;
	unsigned short  seq;
};

创建信号量集 semget

int semget(key_t key, int nsems, int semflg);

1、使用ftok函数生成一个key值
2、nsems,表示创建信号量的个数。
3、semflg与创建共享内存时使用的shmget函数的第三个参数相同。
4、信号量集创建成功时,semget函数返回的一个有效的信号量集标识符(用户层标识符)

删除信号量集

int semctl(int semid, int semnum, int cmd, ...);

如果如果只有一个信号量semnum设为0,
如果cmd设为SETVAL,在可变参数部分将联合体semun 的val设置出来

 union semun {
               int              val;    /* Value for SETVAL */
               struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
               unsigned short  *array;  /* Array for GETALL, SETALL */
               struct seminfo  *__buf;  /* Buffer for IPC_INFO
                                           (Linux-specific) */
           };

如果想要删除信号量,cmd参数不用填
如果想要获取信号量对应的属性,cmd传IPC_STAT

struct semid_ds {
               struct ipc_perm sem_perm;  /* Ownership and permissions */
               time_t          sem_otime; /* Last semop time */
               time_t          sem_ctime; /* Last change time */
               unsigned long   sem_nsems; /* No. of semaphores in set */
           };

信号量集的操作
对信号量集进行操作我们需要用semop函数

int semop(int semid, struct sembuf *sops, unsigned nsops);
struct sembuf 
{
 unsigned short sem_num;  //如果只有一个信号量 ,sem_num就传0,
           short          sem_op;  //正1 表示实现V操作,-1表示实现P操作
           short          sem_flg;  /* operation flags */
}
07-15 15:28