设为首页 加入收藏

TOP

用信号量为共享内存添加同步机制(一)
2018-07-13 06:07:03 】 浏览:251
Tags:信号 共享 内存 添加 同步 机制

进程间通信的方式中,我们将多个进程共享同一块存储区来进行数据交换的方式称为共享内存通信。源于它直接将“内存”共享的特殊机制,它成为最快的一种IPC通信方式;然而它也不完美,它虽快,但是没有同步机制;通常在一个服务进程对共享存储区还未完成写操作之前,客户进程是不应当去取这些数据的,可没了同步,那可就乱了套了。


这种情况不是我们所愿意看到的,所以基于此 我们常常需要为用到的共享内存段添加上同步的机制,使之“完美”起来。通常呢,实现同步我们很自然地会想到信号量,是的,这里我就是这么干的。用基于信号量的PV操作实现完成一个带同步机制的共享内存段,这样它就可以像一个“fifo”队列一样了,并且它传输效率还会表现得非常不错,虽然它还比较简陋~。


信号量及相关函数


--------------------------------------------------------------------------------


先来说说用到的信号量及处理函数吧。信号量和其它的IPC结构(前面总结过管道、消息队列)不同。它本质就是一个计数器,用于为多个进程提供对共享数据对象的访问。通常进程为了获得共享的资源,需要执行以下操作:


①测试控制该资源的信号量


②若此信号量>0,则进程可以使用该资源。此种情况下,进程会将信号量值减1,表明它使用了一个资源单位。


③否则,此信号量的值为0,则使该进程进入休眠状态,直至信号量值>1。如果有进程正在休眠状态等待此信号量,则唤醒它们


还有就是为了正确的实现信号量,信号量值的测试及减1操作还应当是原子的。为此,信号量通常是在内核中实现的。一般而言,信号量初值可以是任意一个正值,该值表明有多少个共享资源单位可供共享应用。然而遗憾的是,这里我用到的XSI信号量也是有缺陷的。


这源于①信号量并非是单个非负值,它被定义为一个可能含有多个信号量值的集合。通常在创建信号量的时候,对该集合中信号量数量进行指定 ②信号量创建独立于它的初始化。这是最致命的,因为这将导致的是不能原子的创建一个信号量集合,并对该集合中的各个信号量值赋初值。③有的程序1在终止时可能并没有释放掉已经分配给它的信号量。


而对于信号处理函数通常有3个,首先是


1.semget函数


作用:创建一个新的信号量或取得一个已有的信号量


原型:int semget(key_t key, int nsems, int semflg)


参数:


int nsems  //它代表信号量集合中有多少个信号量。如果是创建新集合,则必须指定它;如果是引用现有的信号集(通常客户进程中),则将其指定为0.


int semflg  //和前面IPC机制类似,用来初始化信号集维护的semid_ds结构中的ipc_perm结构成员sem_perm。通常用IPC_CREAT|0644创建,要直接打开已存在的话 也直接填0就好


2.semctl函数


用途:该函数用来直接控制信号量信息.也就是直接删除信号量或者初始化信号量.


原型:int semctl(int semid, int semnum, int cmd, ...)


参数:


  int semid    //semget函数返回的信号量标识符.  


  int semnum,  //它代表要给该信号量集中的第几个信号量设置初值


  int cmd   //通常用SETVAL/GETVAL设置和获取信号量集中的一个单独的信号量。具体还有


IPC_STAT //读取一个信号量集的数据结构semid_ds,并将其存储在semun中的buf参数中。


IPC_SET  //设置信号量集的数据结构semid_ds中的元素ipc_perm,其值取自semun中的buf参数。
IPC_RMID  //将信号量集从内存中删除。
 
GETALL  //用于读取信号量集中的所有信号量的值。


GETNCNT  //返回正在等待资源的进程数目。


GETPID  //返回最后一个执行semop操作的进程的PID。


GETVAL  //返回信号量集中的一个单个的信号量的值。


GETZCNT  //返回这在等待完全空闲的资源的进程数目。


SETALL  //设置信号量集中的所有的信号量的值。


SETVAL  //设置信号量集中的一个单独的信号量的值


cmd的种类


如果有第四个参数,取决于所请求的命令,如果使用该参数,它通常是一个union semum结构,定义如下:


union semun
{   
    int val;    /* Value for SETVAL */通常就要它就够了
    struct semid_ds *buf;   
    unsigned short *arry;   
}; 
赋值形式:semun.val = 2  //初值放在val中


执行PV操作


3.semop函数


用途:用来改变信号量的值,该函数是具有原子性的。


原型: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结构来管理数据的读、写位置和大小的信息。同时,shimfif

首页 上一页 1 2 3 4 下一页 尾页 1/4/4
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇浅析fork()和底层实现 下一篇Java值类型的当前状态

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目