zoukankan      html  css  js  c++  java
  • Linux下用信号量实现对共享内存的访问保护

    转自:http://www.cppblog.com/zjl-1026-2001/archive/2010/03/03/108768.html

    最近一直在研究多进程间通过共享内存来实现通信的事情,以便高效率地实现对同一数据的访问。本文中对共享内存的实现采用了系统V的机制,我们的重点在于通过信号量来完成对不同进程间共享内存资源的一致性访问,共享内存的具体方法请参见相关资料,这里不再赘述。

    首先我们先实现最简单的共享内存,一个进程对其更新,另一个进程从中读出数据。同时,通过信号量的PV操作来达到对共享内存资源的保护。思路如下:
    1.server端首先建立一块共享内存的映射,然后创建一个信号量。通过信号量获得对共享资源的使用权限,更新内存中的内容,等待客户端读进程读取共享资源中的数据后,释放共享内存和信号量,然后退出。

    2.client端获得对server端创建的共享内存的映射,以及信号量的映射,通过信号量获得对共享资源的访问权限,然后读取其内容,接着解除与共享内存的映射后退出。客户端每次读取共享内存数据时首先调用wait_v()检测信号量的值,当信号量值为0时才能读取共享内存数据然后进行打印。

    先来看下程序,然后再对其中的一些API做相关的使用说明。

    server端源码

     1/*编译命令:gcc -o shm shm.c -g */
      2
      3#include<sys/sem.h>
      4#include<sys/ipc.h>
      5
      6#define SEGSIZE   1024
      7#define READTIME  1
      8
      9union semum
     10{
     11    int                 val;
     12    struct semid_ds     *buf;
     13    unsigned short      *array;
     14}arg;
     15
     16/* 创建信号量 */
     17int sem_creat(key_t  key)
     18{
     19    union semun sem;
     20    int         semid;
     21    sem.val = 0;
     22    semid = semget(key, 1, IPC_CREAT | 0666);
     23
     24    if (semid == -1)
     25    {
     26        printf("Create semaphore error
    ");
     27        exit(-1);
     28    }
     29
     30    semctl(semid, 0, SETVAL, sem);
     31
     32    return semid;
     33}
     34
     35/* 删除信号量*/
     36int del_sem(int semid)
     37{
     38    union semun  sem;
     39    sem.val = 0;
     40    semctl(semid, 0, IPC_RMID, sem);
     41}
     42
     43/* 信号量的P操作,使得信号量的值加1 */
     44int p(int semid)
     45{
     46    struct sembuf sops = {0,
     47                          +1,
     48                          IPC_NOWAIT
     49                         };
     50
     51    return (semop(semid, &sops, 1));
     52}
     53
     54/* 信号量的v操作,使得信号量的值减1 */
     55int v(int semid)
     56{
     57    struct sembuf sops = {0,
     58                          -1,
     59                          IPC_NOWAIT
     60                         };
     61
     62    return (semop(semid, &sops, 1));
     63}
     64
     65/* server主程序 */
     66int main(int argc, char **argv)
     67{
     68    key_t            key;
     69    int              shmid, semid;
     70    char             *shm;
     71    char             msg[7] = "-data-";
     72    char             i;
     73    struct semid_ds  buf;
     74
     75    key = ftok("/", 0);
     76    shmid = shmget(key, SEGSIZE, IPC_CREAT|0604);
     77    
     78    if shmid == -1)
     79    {
     80        printf(" create shared memory error
    ");
     81        return -1;
     82    }
     83
     84    shm = (char *)shmat(shmid, 0, 0);
     85    if (-1 == (int)shm)
     86    {
     87        printf(" attach shared memory error
    ");
     88        return -1;
     89    }
     90
     91    semid = sem_creat(key);
     92
     93    for (i = 0; i <= 3; i++)
     94    {
     95        sleep(1);
     96        p(semid);
     97        sleep(READTIME);
     98        msg[5] = '0' + i;
     99        memcpy(shm,msg,sizeof(msg));
    100        sleep(58);
    101        v(semid);
    102    }
    103
    104    shmdt(shm);
    105
    106    shmctl(shmid,IPC_RMID,&buf);
    107
    108    del_sem(semid);
    109
    110    return 0;
    111
    112}

    client端源码:

     1/* 编译命令:gcc -o client client.c -g*/
     2#include<sys/sem.h>
     3#include<time.h>
     4#include<sys/ipc.h>
     5
     6#define SEGSIZE  1024
     7#define READTIME 1
     8
     9union semun
    10{
    11    int              val;
    12    struct semid_ds  *buf;
    13    unsigned short   *array;
    14}arg;
    15
    16/* 打印程序的执行时间函数 */
    17void out_time(void)
    18{
    19    static long start = 0;
    20    time_t      tm;
    21
    22    if (start == 0)
    23    {
    24        tm = time(NULL);
    25        start = (long)tm;
    26        printf("now start 
    ");
    27    }
    28
    29    printf("second: %d
    ", (long)(time(NULL)) - start);
    30}
    31
    32/* 创建信号量 */
    33int new_sem(key_t key)
    34{
    35    union semun sem;
    36    int semid;
    37    sem.val = 0;
    38    semid = semget(key, 0, 0);
    39
    40    if (-1 ==  semid)
    41    {
    42        printf("create semaphore error
    ");
    43        exit(-1);
    44    }
    45
    46    return semid;
    47}
    48
    49/* 信号量等待函数,等待信号量的值变为0 */
    50void wait_v(int semid)
    51{
    52    struct sembuf sops = {0,
    53                          0,
    54                          0
    55                         };
    56
    57    semop(semid, &sops, 1);
    58}
    59
    60int main(int argc, char **argv)
    61{
    62    key_t  key;
    63    int    shmid, semid;
    64    char   *shm;
    65    char   msg[100];
    66    char   i;
    67
    68    key = ftok("/", 0);
    69    shmid = shmget(key, SEGSIZE, 0);
    70
    71    if (shmid == -1)
    72    {
    73        printf("create shared memory error
    ");
    74        return -1;
    75    }
    76
    77    semid = new_sem(key);
    78
    79    for (i = 0;i < 3;i ++)
    80    {
    81        sleep(2);
    82        wait_v(semid);
    83        printf("Message geted is: %s 
    ",shm + 1);
    84        out_time();
    85    }
    86
    87    shmdt(shm);
    88
    89    return 0;
    90
    91}

    下面我们来解释一下程序中的细节问题。

    一、信号量:

            一个信号量实际上是一个整数,其值大于或等于0代表可供并发进程使用的资源实体;其值小于0时代表正在等待使用的临界区的进程数。用于互斥的信号量初始值应该大于0,且其值只能通过P、V原语操作而改变。

           信号量元素组成:
                1、表示信号量元素的值;
                2、最后操作信号量元素的进程ID
                3、等待信号量元素值+1的进程数;
                4、等待信号量元素值为0的进程数;

    二、主要函数

    1.1 创建信号量
          int semget( key_t key,  /* 标识信号量的关键字,有三种方法:
                                                      1、使用IPC——PRIVATE让系统产生,
                                                      2、挑选一个随机数,
                                                      3、使用ftok从文件路径名中产生
                                             */
                           int nSemes,  /* 信号量集中元素个数 */
                           int flag          /*IPC_CREAT;IPC_EXCL 只有在信号量集不存在时创建*/
     )

     成功:返回信号量句柄
     失败:返回-1
     
     1.2 使用ftok函数根据文件路径名产生一个关键字
            key_t ftok(const char *pathname,int proj_id);
            路径名称必须有相应权限 
     
     1.3 控制信号量
           int semctl( int semid,     /* 信号量集的句柄 */
                           int semnum,  /* 信号量集的元素数 */
                           int cmd,        /* 命令 */
                          /*union senum arg */... //  
                          )
           成功:返回相应的值
           失败:返回-1
     
          命令详细说明:
              IPC_RMID 删除一个信号量
              IPC_EXCL 只有在信号量集不存在时创建
              IPC_SET 设置信号量的许可权
              SETVAL 设置指定信号量的元素的值为 agc.val
              GETVAL 获得一个指定信号量的值
              GETPID 获得最后操纵此元素的最后进程ID
              GETNCNT 获得等待元素变为1的进程数
              GETZCNT 获得等待元素变为0的进程数
      
             union senum 定义如下:
                  union senum{
                                     int val;
                                     struct semid_ds *buf;
                                     unsigned short * array;
                                    }agc;


     其中 semid_ds 定义如下:
                 struct semid_ds{
                                          struct ipc_pem sem_pem;  //operation pemission struct
                                          time_t sem_otime;  //last semop()time
                                          time_t sem_ctime;  //last time changed by semctl()
                                          struct sem *sembase;  //ptr to first semaphore in array
                                          struct sem_queue *sem_pending; //pending operations
                                          struct sem_queue *sem_pending_last; //last pending operations
                                          struct sem_undo *undo;  //undo requests on this arrary
                                          unsigned short int sem_nsems; //number of semaphores in set
                                         };
      
     1.4 对信号量 +1 或 -1 或测试是否为0

             int semop(
                             int semid, 
                             struct sembuf *sops, //指向元素操作数组
                             unsigned short nsops //数组中元素操作的个数
                            )
     
     结构 sembuf 定义
            sembuf{
                       short int sem_num; //semaphore number
                       short int sem_op; //semaphore operaion
                       short int sem_flg //operation flag
                       };

  • 相关阅读:
    NPOIHelper
    NPOI.dll 用法:单元格、样式、字体、颜色、行高、宽度 读写excel
    SQL中的循环、for循环、游标
    .net mvc datatables中orderby动态排序
    MVC中给TextBoxFor设置默认值和属性
    定义实体系列-@JsonIgnoreProperties注解
    微信公众号登录与微信开放平台登录区别
    http-Post请求,Post Body中的数据量过大时出现的问题
    .net core Linux 安装部署
    二、微信公众号开发-获取微信用户信息(.net版)
  • 原文地址:https://www.cnblogs.com/zl1991/p/4758855.html
Copyright © 2011-2022 走看看