对信号量P/V操作以及共享资源的使用

共享内存(SHM)
   共享内存是效率最高的 IPC,因为他抛弃了内核这个“代理人”,直截了当地将一块裸
露的内存放在需要数据传输的进程面前,让他们自己搞,这样的代价是:这些进程必须小心
谨慎地操作这块裸露的共享内存,做好诸如同步、互斥等工作,毕竟现在没有人帮他们来管
理了,一切都要自己动手。也因为这个原因,共享内存一般不能单独使用,而要配合信号量、
互斥锁等协调机制,让各个进程在高效交换数据的同时,不会发生数据践踏、破坏等意外。
共享内存的思想很朴素,进程与进程之间虚拟内存空间本来相互独立,不能互相访问的,
但是可以通过某些方式,使得相同的一块物理内存多次映射到不同的进程虚拟空间之中,这
样的效果就相当于多个进程的虚拟内存空间部分重叠在一起,看示意图。

使用共享内存的一般步骤是:
1 ,获取共享内存对象的 ID
2 ,将共享内存映射至本进程虚拟内存空间的某个区域
3 ,当不再使用时,解除映射关系
4 ,当没有进程再需要这块共享内存时,删除它
信号量(SEM)
     信号量 SEM全称 Semaphore,中文也翻译为信号灯。作为system-V IPC 的最后一种,信号量跟前面的 MSG SHM 有极大的不同, SEM 不是用来传输数据的, 而是作为“旗语”,用来协调各进程或者线程工作的。
一些基本概念如下:
        1 ,多个进程或线程有可能同时访问的资源(变量、链表、文件等等)称为共享资源,
也叫临界资源(critical resources)。
        2 ,访问这些资源的代码称为临界代码,这些代码区域称为临界区(critical zone)。
        3 ,程序进入临界区之前必须要对资源进行申请,这个动作被称为 P 操作,这就像你要
把车开进停车场之前,先要向保安申请一张停车卡一样, P 操作就是申请资源,如果申请成
功,资源数将会减少。如果申请失败,要不在门口等,要不走人。
        4 ,程序离开临界区之后必须要释放相应的资源,这个动作被称为 V 操作,这就像你把
车开出停车场之后,要将停车卡归还给保安一样, V 操作就是释放资源,释放资源就是让资
源数增加。
         所有一起访问共同临界资源的进程都必须遵循以上游戏规则,否则大家就都乱套了,但
是值得注意的是:这些规则是自愿的,如果有进程就是胡来——在访问资源之前不申请,那
么将会可能导致逻辑谬误,就像开车压死保安直接撞进停车场一样,虽然于情于理都不可以,
物理上阻止不了这种行为。
        
         system-V 的信号量非常类似于停车场的卡牌,想象一个有 N 个车位的停车场,每个
车位是立体的可升降的,能停 n 辆车,那么我们可以用一个拥有 N 个信号量元素,每个信
号量元素的初始值等于 n 的信号量来代表这个停车场的车位资源——某位车主要把他的 m
辆车开进停车场,如果需要 1 个车位,那么必须对代表这个车位的信号量元素申请资源,
如果 n 大于等于 m ,则申请成功,否则不能把车开进去。
            

  对 于共享内存和信号量的使用 

发送端代码如下jack.c

#include<stdio.h>
#include <sys/types.h> 
#include <sys/shm.h>
 #include <sys/ipc.h> 
 #include <sys/sem.h>
 #include <unistd.h>
 #include <stdlib.h>

union semun
{
    int val;                 /* 当 cmd 为 SETVAL 时使用 */ 
    struct semid_ds *buf;    /* 当 cmd 为 IPC_STAT 或 IPC_SET 时使用 */
    unsigned short *array;   /* 当 cmd 为 GETALL 或 SETALL 时使用 */
    struct seminfo *__buf;   /* 当 cmd 为 IPC_INFO 时使用 */
};

 
char * shm_init()
{
    //获取KEY值
     int key=ftok("./",'K');
    
     int shm_id=shmget(key, 4096,IPC_CREAT|0666);
     if(shm_id==-1)
     {
        perror("shmget id erorr");
        exit(1);//直接结束进程
     }
    //映射共享内存
    char *shm_map=shmat(shm_id, NULL, 0);
    if(shm_map==(void *)-1)
    {
        perror("shm map error");
         exit(1);//直接结束进程
    }

    return shm_map;
    
}

int sem_init()
{
    //获取新的KEY值
       int key=ftok("./",'X');
    //获取信号量的ID
     int sem_id=semget(key, 2, IPC_CREAT|0644);
     if(sem_id==-1)
     {
        perror("sem get id error ");
        exit(1);
     }
     //初始化信号量的内容 主要是初始化他们的初始化的资源数
      
     //初始化没有数据,有一个空间
     union semun set;
     set.val=0;   
     semctl(sem_id, 0, SETVAL, set);   //0 初始化数据资源
     set.val=1; 
     semctl(sem_id, 1, SETVAL,set );  //1 初始化空间资源

     return sem_id;
}

int main(int argc, char const *argv[])
{

    //配置共享内存并初始化
     char *shm_map=shm_init();
     //初始化信号量
     int  sem_id=sem_init();
    //再写入共享内存之前需要现申请一个空间资源
     /* struct sembuf 
        { 
            unsigned short sem_num;     /* 信号量元素序号(数组下标) 
            short sem_op;               /* 操作参数 *
            short sem_flg;              /* 操作选项 
        };*/
    struct sembuf space={     //设置空间资源 
        .sem_num=1,          //需要设置的空间资源元素下表为1
        .sem_flg=0,          //设置标记为0 啥也不选
        .sem_op=-1           //-1表示资源量即将建议  申请资源
    };
      struct sembuf data={     //设置空间资源 
        .sem_num=0,          //需要设置的空间资源元素下表为0
        .sem_flg=0,          //设置标记为0 啥也不选
        .sem_op=1           //1表示资源量即将加1  释放资源
    };
    while (1)
    {
        
       //等待空间资源  会阻塞 如果资源暂时不能得到则会阻塞等待
        semop(sem_id,  &space, 1);
        printf("请输入需要发送的数据\n"); 
        fgets(shm_map,4096,stdin);
        //设置数据资源为1 
        semop(sem_id,  &data, 1);/* code */
    }

    return 0;
}
接收端代码 rocs.c
#include<stdio.h>
#include <sys/types.h> 
#include <sys/shm.h>
 #include <sys/ipc.h> 
 #include <sys/sem.h>
 #include <unistd.h>
 #include <stdlib.h>

union semun
{
    int val;                 /* 当 cmd 为 SETVAL 时使用 */ 
    struct semid_ds *buf;    /* 当 cmd 为 IPC_STAT 或 IPC_SET 时使用 */
    unsigned short *array;   /* 当 cmd 为 GETALL 或 SETALL 时使用 */
    struct seminfo *__buf;   /* 当 cmd 为 IPC_INFO 时使用 */
};

 
char * shm_init()
{
    //获取KEY值
     int key=ftok("./",'K');
    
     int shm_id=shmget(key, 4096,IPC_CREAT|0666);
     if(shm_id==-1)
     {
        perror("shmget id erorr");
        exit(1);//直接结束进程
     }
    //映射共享内存
    char *shm_map=shmat(shm_id, NULL, 0);
    if(shm_map==(void *)-1)
    {
        perror("shm map error");
         exit(1);//直接结束进程
    }

    return shm_map;
    
}

int sem_init()
{
    //获取新的KEY值
       int key=ftok("./",'X');
    //获取信号量的ID
     int sem_id=semget(key, 2, IPC_CREAT|0644);
     if(sem_id==-1)
     {
        perror("sem get id error ");
        exit(1);
     }
     //初始化信号量的内容 主要是初始化他们的初始化的资源数
      
     //初始化没有数据,有一个空间
     union semun set;
     set.val=0;   
     semctl(sem_id, 0, SETVAL, set);   //0 初始化数据资源
     set.val=1; 
     semctl(sem_id, 1, SETVAL,set );  //1 初始化空间资源

     return sem_id;
}

int main(int argc, char const *argv[])
{

    //配置共享内存并初始化
     char *shm_map=shm_init();
     //初始化信号量
     int  sem_id=sem_init();
    //再写入共享内存之前需要现申请一个空间资源
     /* struct sembuf 
        { 
            unsigned short sem_num;     /* 信号量元素序号(数组下标) 
            short sem_op;               /* 操作参数 *
            short sem_flg;              /* 操作选项 
        };*/
    struct sembuf space={     //设置空间资源 
        .sem_num=1,          //需要设置的空间资源元素下表为1
        .sem_flg=0,          //设置标记为0 啥也不选
        .sem_op=1           //1表示资源量即将加以  释放资源
    };
      struct sembuf data={     //设置空间资源 
        .sem_num=0,          //需要设置的空间资源元素下表为0
        .sem_flg=0,          //设置标记为0 啥也不选
        .sem_op=-1          //1表示资源量即将加1  释放资源
    };
    while (1)
    {
        printf("wait 数据\n");
       //等待数据资源  会阻塞 如果资源暂时不能得到则会阻塞等待
        semop(sem_id,  &data, 1);
        printf("Jacl 说:%s\n",shm_map);
        //设置空间资源为1 
        semop(sem_id,  &space, 1);/* code */
    }

    return 0;
}

猜你喜欢

转载自blog.csdn.net/m0_52467164/article/details/127538937