用信号量为共享内存添加同步机制(2)

原型: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)。在进行取数据操作时同理。基于此,便可有以下代码:

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/90042d57dd40cc272c9826cdc5d6cf2f.html