进程间通信-信号量(功能:共享资源)

 

  1. 信号量、消息队列和共享内存都是会涉及到进程间的同步和互斥。
    错误:
  2. 在进行这个实验的时候,我一直不能得到想要的结果。所以在信号量的获取semid成功与否,信号量操作semop成功与否,信号量的设置semctl成功与否,这些都要将结果打印出来判断。看看是在哪里出错的,进行改进。
  3. 我查到是在信号量的操作semop这里失败,返回的结果总是-1。所以我就尝试在root下进行实验,结果就能操作成功(现在还不知道为什么,有人知道的话在下面的评论里面给我解答下,感激不尽)。
  4. ipcs -s 这个是查看自己的信号semid是否获取成功。这个也是只有在root下才能看到想要的结果。

一、什么是信号量

  1. 为了防止出现因多个程序同时访问一个共享资源而引发的一系列问题,我们需要一种方法,它可以通过生成并使用令牌来授权,在任一时刻只能有一个执行线程访问代码的临界区域。临界区域是指执行数据更新的代码需要独占式地执行。而信号量就可以提供这样的一种访问机制,让一个临界区同一时间只有一个线程在访问它,也就是说信号量是用来调协进程对共享资源的访问的

  2. 信号量是一个特殊的变量,程序对其访问都是原子操作,且只允许对它进行等待(即P(信号变量))和发送(即V(信号变量))信息操作。最简单的信号量是只能取0和1的变量,这也是信号量最常见的一种形式,叫做二进制信号量。而可以取多个正整数的信号量被称为通用信号量。这里主要讨论二进制信号量。

二、信号量的工作原理

  1. 由于信号量只能进行两种操作等待和发送信号,即P(sv)和V(sv),他们的行为是这样的:
    P(sv):如果sv的值大于零,就给它减1;如果它的值为零,就挂起该进程的执行
    V(sv):如果有其他进程因等待sv而被挂起,就让它恢复运行,如果没有进程因等待sv而挂起,就给它加1.

  2. 举个例子,就是两个进程共享信号量sv,一旦其中一个进程执行了P(sv)操作,它将得到信号量,并可以进入临界区,使sv减1。而第二个进程将被阻止进入临界区,因为当它试图执行P(sv)时,sv为0,它会被挂起以等待第一个进程离开临界区域并执行V(sv)释放信号量,这时第二个进程就可以恢复执行。

三、Linux的信号量机制

Linux提供了一组精心设计的信号量接口来对信号进行操作,它们不只是针对二进制信号量,下面将会对这些函数进行介绍,但请注意,这些函数都是用来对成组的信号量值进行操作的。它们声明在头文件sys/sem.h中。

  1. semget函数:

    打开/创建信号量semget函数:  
    函数原型: int semget(key_t key, int num_sems, int sem_flags);

    [注]:key_t ftok(const char *pathname, int proj_id);创建key键值

    函数功能: 创建一个新信号量或取得一个已有信号量
    函数头文件: 《sys/types.h》《sys/ipc.h》《sys/sem.h》
    函数返回值: 成功返回一个相应信号标识符(非零)sem_id,失败返回-1.
    函数参数: 第一个参数key(最好自己创建,键值唯一)是整数值(唯一非零),不相关的进程可以通过它访问一个信号量,它代表程序可能要使用的某个资源,程序对所有信号量的访问都是间接的,程序先通过调用semget函数并提供一个键,再由系统生成一个相应的信号标识符(semget函数的返回值),只有semget函数才直接使用信号量键,所有其他的信号量函数使用由semget函数返回的信号量标识符。如果多个程序使用相同的key值,key将负责协调工作。
    第二个参数num_sems指定需要的信号量数目,它的值几乎总是1。
    第三个参数sem_flags是一组标志,当想要当信号量不存在时创建一个新的信号量,可以和值IPC_CREAT做按位或操作。设置了IPC_CREAT标志后,即使给出的键是一个已有信号量的键,也不会产生错误。而IPC_CREAT | IPC_EXCL则可以创建一个新的,唯一的信号量,如果信号量已存在,返回一个错误。
  2. semop函数:

    操作信号量semop函数:  
    函数原型: int semop(int semid, struct sembuf *sops, unsigned nsops);
    函数功能: 对semid这个信号量指定的变量进行操作,是选择发送(+1)还是等待(-1);
    函数头文件: 《sys/types.h》《sys/ipc.h》《sys/sem.h》
    函数返回值: 成功返回一个相应信号标识符(非零)sem_id,失败返回-1.
    函数参数: sem_id:是由semget返回的信号量标识符;
    sops:表示要对信号量进行什么操作;
    nsops:是表示操作的信号量个数。

    sembuf:结构的定义如下:
    struct sembuf{
    short sem_num;//除非使用一组信号量,否则它为0 ,从0开始,表示要操作的信号量是第几个;
    short sem_op;//信号量在一次操作中需要改变的数据,通常是两个数,一个是-1,即P(等待)操作, 对第一个变量选择的信号量进行操作;一个是+1,即V(发送信号)操作。
    short sem_flg;//通常为SEM_UNDO,使操作系统跟踪信号, 并在进程没有释放该信号量而终止时,操作系统释放信号量
    };

  3. semctl函数:

    控制信号量semctl函数:  
    函数原型: int semctl(int semid, int semnum, int cmd, …);
    函数功能: 用来直接控制信号量信息;
    函数头文件: 《sys/types.h》《sys/ipc.h》《sys/sem.h》
    函数返回值: 成功返回0,失败返回-1
    函数参数: semid:信号量的标志码(ID),也就是semget()函数的返回值;
    semnum:操作信号在信号集中的编号,从0开始;
    cmd:命令,表示要进行的操作。

    参数cmd中可以使用的命令如下:
    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设置信号量集中的一个单独的信号量的值。

四、示例:

信号量同步和异步,只是sem_op的初始值不同。同步情况下,信号量的初始值是0;异步情况下,信号量的初始值是1 。
1、信号量实现进程间互斥示例:

/*
* FileName:sem1.c
* BuildTime:2017-04-17
* Author:nan
* Description:这个程序是测试信号量的互斥(进程间互斥),命令ipcs和ipcrm可以帮助我们查看-s(信号量)、-q(消息队列)、-m(共享内存)是否创建成功与删除。使用方法:man ipcs 和 man ipcrm
* */
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/sem.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <fcntl.h>
int main( int argc , char **argv ) {

        /*创建信号量sem_id,IPC_CREAT:不存在就创建,存在就获取。*/
        key_t key = ftok( "/home/nan/test",1 ) ;
        if( -1==key ) {
                printf("create key fail.--%d\n" , key) ;
                return -1 ;
        }
        printf( "create key succeed :--%d\n",key ) ;
        int sem_id = semget( key , 1 , IPC_CREAT ) ;//1:表示sem_id信号集中,创建信号量的个数,1个;
        if( -1==sem_id ) {
                printf("create sem_id fail.--%d\n",sem_id) ;
                return -1 ;
        }
        printf( "create sem_id succeed.--%d\n",sem_id ) ;
        /*设置信号量集中的信号量,第二个参数表示操作第1个信号量*/
        semctl( sem_id , 0 , SETVAL , 1 ) ;
        printf( "sem_num is : %d\n",semctl( sem_id , 0 , GETVAL ) ) ;
        /*设置信号集sem_id中的信号量值,-1(等待),+1(发送)*/
        struct sembuf sops = {
                .sem_num = 0 ,//信号集中的第几个信号量,0表示第一个
                .sem_op = -1 ,//执行等待操作,其他进程等着。
        } ;
        int ret = semop( sem_id , &sops , 1 ) ;
        if( -1==ret ) {
                printf( "operator sem fail!--%d\n",ret ) ;
                return -1 ;
        }
        printf( "operator semop succeed.--%d\n",ret ) ;
        /*获取到了锁,则可以实现自己想要的操作,其他进程等着*/
        int fd = open( "/home/nan/test/text",O_RDWR|O_APPEND|O_CREAT,0666 ) ;
        if( -1==fd ){
                printf("open text fail . --%d\n",fd) ;
                return -1 ;
        }
        write( fd , "1" , 2 ) ;
        sleep(10) ;
        write( fd , "2" , 2  ) ;
        close( fd ) ;
        /*操作完成后,+1(发送出去),将锁交给其他进程用*/
        sops.sem_num = 0;
        sops.sem_op = 1;
        semop( sem_id , &sops,1) ;
        return 0 ;
/*
* FileName:sem2.c
* BuildTime:2017-04-17
* Author:nan
* Description:在执行第一个程序时候,执行下面这个程序,可以看到下面的程序处于等待状态。
* */
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/sem.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <fcntl.h>
int main( int argc , char **argv ) {

        key_t key = ftok( "/home/nan/test",1 ) ;
        if( -1==key ) {
                printf("create key fail.--%d\n" , key) ;
                return -1 ;
        }
        printf( "create key succeed :--%d\n",key ) ;
        int sem_id = semget( key , 1 , IPC_CREAT ) ;
        if( -1==sem_id ) {
                printf("create sem_id fail.--%d\n",sem_id) ;
                return -1 ;
        }
        printf( "create sem_id succeed.--%d\n",sem_id ) ;
//      semctl( sem_id , 0 , SETVAL , 1 ) ;
        printf( "sem_num is : %d\n",semctl( sem_id , 0 , GETVAL ) ) ;
        struct sembuf sops = {
                .sem_num = 0 ,
                .sem_op = -1 ,
        } ;
        int ret = semop( sem_id , &sops , 1 ) ;
        if( -1==ret ) {
                printf( "operator sem fail!--%d\n",ret ) ;
                return -1 ;
        }
        printf( "operator semop succeed.--%d\n",ret ) ;
        int fd = open( "/home/nan/test/text",O_RDWR|O_APPEND|O_CREAT,0666 ) ;
        if( -1==fd ){
                printf("open text fail . --%d\n",fd) ;
                return -1 ;
        }
        write( fd , "3" , 2 ) ;
        close( fd ) ;
        return 0 ;
}

2、信号量实现进程间同步示例;
信号量的同步,引入了生产者和消费者的经典问题。必须先生产后消费,所以在生产者执行完相应的代码后,才能释放信号量。
这里写代码片

这里写图片描述

转载:https://blog.csdn.net/a2604539133/article/details/70194862

猜你喜欢

转载自blog.csdn.net/maxiaoyin111111/article/details/81172465