IPC----信号量

1.为什么引入信号量
Linux 系统采用多道程序设计技术,允许多个进程同时在内核中运行,但同一个系统中的多个进程之间,可能因为进程合作或资源共享,产生制约关系。制约关系分为直接相互制约关系和间接相互制约关系:
(1)直接相互制约关系。 利用管道机制实现进程间通信, 当管道为空时,读进程由于无法从管道中读取数据而进入阻塞;当管道存满时,写进程由于无法向管道中写入数据而进入阻塞, 类似于这种需要进程间协调合作导致的制约关
系,称为直接相互制约关系。
(2) 间接相互制约关系。 若当前系统中只有 1 台打印机,当进程 A 占用打印机时,进程 B 也申请使用打印机,进程 B 就会进入阻塞,等待打印机释放;同样若 B 先获取到打印机, A 进程申请使用打印机后也会进入阻塞。类似于这种因资源共享导致的制约关系,称为间接相互制约关系。
计算机中的多个进程必须互斥地访问系统中的临界资源,用于访问临界资源的代码称为临界区(Critical Section),临界区也属于临界资源,若能保证进程间互斥地进入自己的临界区,就能实现进程对临界资源的互斥访问。
信号量(Semaphore)是专门用于解决进程同步与互斥问题的一种通信机制,它与信号无关,也不同于管道、 FIFO 以及消息队列,一般不用来传输数据,信号量包括一个被称为信号量的表示资源数量的非负整型变量、修改信号量的原子操作 P 和 V,以及该信号量下等待资源的进程队列。
2.信号量本质
信号量的本质是⼀种数据操作锁,它本⾝不具有数据交换的功能,⽽是通过控制其他的通信资源(⽂件,外部设备)来实现进程间通信, 它本⾝只是⼀种外资源的标识。信号量在此过程中负责数据操作的互斥、同步等功能。
3.信号量通信的一般步骤
(1) 创建信号量/信号量集,或获取系统中已有的信号量/信号量集;
(2) 初始化信号量。早期信号量通常被初始为 1,但有些进程一次需要多个同类的临界资源,或多个不同类且不唯一的临界资源,因此可能需要初始化的不是信号量,而是一个信号量集;
(3)信号量的 P、 V 操作,根据进程请求,修改信号量的数量。执行 P 操作会使信号量-1,执行 V 操作会使信号量+1;
(4)从系统中删除不需要的信号量。
4相关函数

1.int semget(key_t key,int nsems,int semflg);

semget()函数的功能为创建一个新的信号集
若该函数调用成功则返回信号量的标识符,否则返回-1
参数key表示信号量的键值,通常为整数,也可以用ftok(const char* pathname,int proj_id)函数来生成;
参数nsems表示创建的信号量数目;参数senflg为标志位,通常为IPC_CREAT和IPC_EXCL

2.int semctl(int semid,int semnum,int cmd...);

若该函数调用成功则根据参数 cmd 的取值返回相应信息,通常为一个非负整数;否则返回-1参数 semid 表示信号量标识符,通常为 semget()的返回值;参数 semnum 表示信号量在信号量集中的编号, 该参数在使用信号量集时才会使用,通常设置为 0,表示取第一个信号;参数 cmd 表示对信号量进行的操作;最后一个参数是一个可选参数,依赖于参数 cmd,使用该参数时,用户必须在程序中自定义一个如下所示的共用体

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 时生效
};
3.int semop(int semid,struct sembuf *sops,unsigned nsops);

semop()函数功能:改变信号量的值
若该函数调用成功返回 0,否则返回-1,并设置 errno。
semop()函数的参数 semid 同样为 semget()返回的信号量标识符; 参数 nsops表示参数 sops 所指数组中元素的个数。参数 sops 为一个 struct sembuf 类型的数组指针, 该数组中的每个元素设置了要对信号量集中的哪个信号做哪种操作, struct sembuf 结构体定义如

struct sembuf{
short sem_num;  //信号量在信号量集中的编号
short sem_op;   //信号量操作
short sem_flag; //标志位
};

当结构体成员 sem_op 设置为-1 时,表示 P 操作;设置为+1 时,表示 V 操作。结构体成员 sem_flg 通常设置为 SEM_UNDO,若进程退出前没有删除信号量,信号量将会由系统自动释放。
5.代码展示
//com.h文件

#include<stdio.h>
#include<stdlib.h>
#include<sys/sem.h>
//自定义共用体
union semun{

    int val;                     //cmd为SETVAL时,用于指定信号量值
    struct semid_ds *buf;
    unsigned short *array;
    struct seminfo* _buf;
};
static int sem_id;               //设置全局的信号量id

static int set_semvalue();

static int sem_p();

static int sem_v();

static void del_sem();
//设置信号量值
static int set_semvalue(){
    union semun sem_union;
    sem_union.val=1;
    if(semctl(sem_id,0,SETVAL,sem_union)==-1){
        exit(1);
    }
    return 1;
}
//p操作,获取信号量
static int sem_p(){
    struct sembuf semoper;
    semoper.sem_num=0;
    semoper.sem_op=-1;
    semoper.sem_flg=SEM_UNDO;
    if(semop(sem_id,&semoper,1)==-1){
        perror("sem_p");
        exit(1);
    }
    return 1;
}
//v操作,释放信号量
static int sem_v(){
    struct sembuf sem_b;
    sem_b.sem_num=0;
    sem_b.sem_op=1;
    sem_b.sem_flg=SEM_UNDO;
    if(semop(sem_id,&sem_b,1)==-1){
        perror("sem_v");
        exit(1);
    }
    return 1;
}
//删除信号量
static void del_sem()
{
    union semun sem_union;
    if(semctl(sem_id,0,IPC_RMID,sem_union)==-1){

        perror("del_sem");
        exit(1);
    }
}

#include<stdio.h>
#include"com.h"

int main()
{
    int i;
    pid_t pid;
    char ch='A';
    sem_id=semget((key_t)4050,1,0666|IPC_CREAT|IPC_EXCL);  //创建信号量
        if(sem_id==-1)
        {
            perror("semget");
            exit(1);
        }
    //设置信号量值
    if(set_semvalue()==-1){
        perror("set_semvalue");
        exit(1);
    }
    pid=fork();  //创建子进程
    if(pid==-1){
        perror("fork");
        del_sem();
        exit(1);
    }
    else if(pid==0){
        ch='A';
    }
    else{
        ch='B';
    }
    while(1){
        sem_p();  //p操作
        printf("%c",ch);
        fflush(stdout);
        printf("%c",ch);
        fflush(stdout);
        sleep(1);
        sem_v();  //V操作
    }
    if(pid>0){
        wait(NULL);
        del_sem();
    }

    return 0;
}

这里写图片描述
我们可以通过指令:ipcs -s来查看创建的信号量
这里写图片描述
因为信号量的生命周期是随内核的,程序结束后不能删除已创建的信号量,当再次运行可执行文件时,会出现下面的错误
这里写图片描述
我们可以通过这条指令来删除已创建的信号量ipcrm -s  semid
这里写图片描述
6.struct sembuf结构中,sem_flg设置为SEM_UNDO的作用
(1)sem_flg公认的标志是 IPC_NOWAIT 和 SEM_UNDO。如果操作指定SEM_UNDO,它将会自动撤消该进程终止时。在标准操作程序中的操作是在数组的顺序执行、原子的,那就是,该操作要么作为一个完整的单元,要么不。如果不是所有操作都可以立即执行的系统调用的行为取决于在个人sem_flg领域的IPC_NOWAIT标志的存在。
(2)sembuf结构的sem_flg成员为SEM_UNDO时,它将使操作系统跟踪当前进程对这个信号量的修改情况,如果这个进程在没有释放该信号量的情况下终止,操作系统将自动释放该进程持有的信号量.
(3)只有将 sem_flg 指定为 SEM_UNDO 标志后,semadj (所指定信号量针对调用进程的调整值)才会新.此外,如果此操作指定SEM_UNDO,系统更新过程中会撤消此信号灯的计数(semadj)。此操作可以随时进行,它永远不会强制等待的过程。调用进程必须有改变信号量集的权限。

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值