原型:int semop(int semid, struct sembuf *sops, size_t nsops)参数:
sem_id //是由semget函数返回的信号量标识符.
struct sembuf *sops //sops是一个指针,指向一个有sembuf结构表示的信号量操作数组,本质上就代表了一个数组地址。
size_t nsops //相当于数组元素个数
该第二个参数维护数据结构struct sembuf定义如下:
struct sembuf
{
unsigned short sem_num; /* semaphore number */除非使用一组信号量,否则它为0
short sem_op; /* semaphore operation */ p -1, v 1
short sem_flg; /* operation flags */ 填 0就好 SEM_NOWAIT SEM_UNDO
}
注意这当中的sem_op参数,信号量集合中的各个成员的操作都应由相应的sem_op值规定。此值可正可负可为0,相应的值就代表对于进程中占用的资源数量,
同时这值会加到信号量值上,若指定undo标志,还从信号量调整值上减去sem_op.
共享内存增添同步机制
--------------------------------------------------------------------------------
下面就可以开始操作一段共享内存,使其带有同步的机制,然后模拟重现我们操作系统书上的那个经典的生产消费者问题,并且解决它。这里我画个图帮助整理思路:
首先,定义出一个管理内存段的“shmfifo”结构,该结构中具体可用一个shm_head结构来管理数据的读、写位置和大小的信息。同时,shimfifo结构中还维护了3个用于解决互斥和同步的信号量sem_mutex,sem_empty, sem_full。
维护的shm_head结构存放在共享内存的头部,写入数据从payload处开始写入一个数据块大小,每次写入之后,便更新头部的wr_idx位,payload可由_head+1得到;同样,最开始读出数据时也是从payload处开始读,每次读完便更新wr_idx。好,到这里就有了大致的思路。于是可以实现出来它的头文件
#ifndef __SHMFIFO_H__
#define __SHMFIFO_H__
#include <sys/ipc.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>
typedef struct shmhead {
int rd_idx; // 读入数据索引
int wr_idx; // 写数据索引
int blocks; // 存数据块数量
int blksz; // 每个数据块大小
}shmhead_t;
typedef struct shmfifo {
shmhead_t *p_head; // 共享内存的起始地址
char * p_payload; // 有效数据的起始地址
int shmid; // 打开的共享内存id
int sem_mutex; // 互斥量
int sem_empty; // 还剩多少个可以消费<br> int sem_full; // 剩余多少个地方可以生产
}shmfifo_t;
// 初始化函数
shmfifo_t *shmfifo_init(key_t key, int blocks, int blksz);
// 放入数据
void shmfifo_put(shmfifo_t *fifo, const void *buf);
// 取得数据
void shmfifo_get(shmfifo_t *fifo, void *buf);
// 结构销毁
void shmfifo_destroy(shmfifo_t *fifo);
#endif //__SHMFIFO_H__
紧接着要考虑就该结构的初始化。首先,肯定是先要为结构开出空间来,其大小不难分析应该为shm_head大小加上blocks*blksz,其次就是一步步对这些变量和信号进行初始化了。
紧接着,对于放数据和取数据就依葫芦画瓢就好了,值得注意就是对信号量的处理,进行PV操作时,针对写数据时,我们需要先P(sem_empty)保证先有地方可以放数据,其次才进行P(sem_mutex)保证互斥性。(否则,会因为在放入数据时进行了P(sem_mutex)操作,在还没来得及读数据时,就将内存段放满,进而使取数据操作阻塞在信号量sem_mutex<0条件上,最终导致死锁。但这里若能保证在内存段还未放满时,读数据进程能得到调度,那么就不会有这样的问题了;比如,这里可以让写数据sleep一会,然后执行读数据操作或者 先进行读数据,然后再写数据;你可以去试试看~ )
然后放数据完成,便可进行V(sem_full)接着V(sem_mutex)。在进行取数据操作时同理。基于此,便可有以下代码: