信号量(Semaphore)

一、什么是信号量?

信号量的本质是一种数据操作锁(一个计数器而已)只是外部资源的标识;
当请求一个使用信号量来表示资源时,进程先要看信号量的值来判断是否可用,大于0就表示可以请求,等于0就是没有资源可用,进程会进入睡眠状态可用直至有资源。
信号量其实没有数据交换的功能,他是控制其他通信文件来实现进程间的通信;

二、为什么要用信号量?

为防止出现因多个程序同时访问一个共享资环二引发的问题,因此:信号量的作用就是:在任一时刻只能有一个执行线程访问代码的临界区;

三、什么是临界区,临界资源?

临界资源:不同的进程运行时看到共同的资源;
临界区:访问临界资源时的程序代码

四、信号量的运行

首先:在信号量中是怎么操作的?
P(sv):如果sv的值大于0,就给它减1;如果它的值为0,就挂起该进程的执行;
V(sv):如果有其他进程因等待sv而被挂起,就让它恢复运行,如果没有进程因等待而别挂起,就给它加1;
1、创建一个信号量的集合

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

返回值:成功返回sem_id,失败-1;
key:用key_t ftok(const char* pathname, int PROJ_ID);
nsems:因为在信号量中,信号量只能用集合来创建,所以这个是你创建信号量集合的个数;
semflg:是创建信号量集合的关键字,创建时:一般是IPC_CREAT|IPC_EXCL没有信号量的话,创建一个新的,有的话返回出错,接受时:IPC_CREAT,没有直接创建,有直接获取,在semflg中就只有这两种组合;
2、信号量的P和V操作:

int semop(int semid, struct sembuf *sops, unsigned nsops);

返回值:成功0,失败-1;
semid 在上面semget中已经得到;
第二个参数是一个结构体指针:

struct sembuf
{
    unsigned short sem_num;
    short          sem_op;//semaphore operation
    short          sem_flg;//operation flags
};

sem_num:信号量集合在系统中类似于一个数组的存在,所以它的数组下标就是用来访问它的数;
sem_op:是信号量的操作方式(P/V操作),P操作是-1,V操作是1;
sem_flag:信号操作标志,有两种状态:IPC_NOWAIT和SEM_UNDO
IPC_NOWAIT:对信号量的操作不能满足时,semop()不会阻塞,而是立即返回,同时设定错误信息。
SEM_UNDO:程序结束时(不管正常还是不正常),保证信号值会被设定semop()调用之前的值,这样做的目的在于避免程序在异常情况下结束未解锁,就会导致死锁;
3、信号量的摧毁与初始化

int semctl(int semid, int semum, int cmd,...);

这个函数我们都知道是可变参数,所以:
在摧毁时调用的是:

semctl(semid, 0, IPC_RMID, NULL);

在初始化的时候:

semctl(semid, which, SETVAL, un);
union semun{
    int              val;
    struct semid_ds  *buf;
    unsigned short   *array;
    struct seminfo   *_buf;
}

在系统函数中它的大小就是4个字节,所以,我们自己定义一个占4个字节的联合体就可以了,不让你=系统无法判定大小,编译通不过;

信号量的特性

信号量的生命周期和msgqueue一样都是随内核,所以当你第一次运行成功够,没有手动删除sem第二次就会出错;
所以必须在root 权限下查看和删除:
查看 ipcs -s
删除 ipcrm -s semid

五、实例

子进程打印AA,父进程打印BB,利用sem来控制:

comm.h
#ifndef _COMM_H_
#define _COMM_H_

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/sem.h>
#include<string.h>
#define PATHNAME "."
#define PROJ_ID 0x666
union semun
{
    int val;
};

int creat_sem(int semset_num);
int get_sem();
int init_sem(int sem_id, int which);
int sem_p(int sem_id, int which);
int sem_v(int sem_id, int which);
int Destroy_sem(int sem_id);

#endif 
comm.c
#include"comm.h"



static int comm_sem(int semset_num, int flags)//创建信号量的公共代码
{
    key_t k = ftok(PATHNAME, PROJ_ID);
    if(k < 0){
        perror("ftok");
        return -1;
    }
    int sem_id = semget(k, semset_num, flags);
    if(sem_id < 0){
        perror("semget");
        return -2;
    }
    return sem_id;
}

int creat_sem(int semset_num)//有出错,没有创建新的
{
    return comm_sem(semset_num, IPC_CREAT|IPC_EXCL|666);
}

int getSem()//直接获取已经存在的
{
    return comm_sem(0, IPC_CREAT);
}

int init_sem(int sem_id, int which)//初始化sem
{
    union semun un;
    un.val = 1;
    int ret = semctl(sem_id, which, SETVAL, un);
    if(ret < 0){
        perror("semctl_init");
        return -1;
    }
    return ret;
}
static int semOp(int sem_id, int op, int which)//semop()的基本操作
{
    struct sembuf sem;
    sem.sem_num = which;
    sem.sem_op = op;
    sem.sem_flg = 0;
    return semop(sem_id, &sem, 1);
}
int sem_p(int sem_id, int which)//P操作为 -1
{
    return semOp(sem_id, -1, which);
}

int sem_v(int sem_id, int which)//V操作为1
{
    return semOp(sem_id, 1, which);
}
int Destroy_sem(int sem_id)//销毁sem
{
    int ret = semctl(sem_id, 0, IPC_RMID, NULL);
    if(ret == -1){
        perror("semctl_Destroy");
        return ret;
    }
    return ret;
}
测试代码sem_test.c
#include"comm.h"

int main()
{
    int sem_id = creat_sem(1);
    init_sem(sem_id, 0);
    pid_t id = fork();
    if(id < 0){
        perror("fork");
        return 1;
    }else if(id == 0){//child
        while(1){
            sem_p(sem_id, 0);
            printf("A");
            fflush(stdout);
            usleep(1000);
            printf("A");
            fflush(stdout);
            usleep(10500);
            sem_v(sem_id, 0);
            }
        }else{//father
        while(1){
            sem_p(sem_id, 0);
            printf("B");
            fflush(stdout);
            usleep(10600);
            printf("B");
            fflush(stdout);
            usleep(11000);
            sem_v(sem_id, 0);
            }
        }
    Destroy_sem(sem_id);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值