信号量

信号量

  • 信号量的本质是一种数据操作锁
  • 信号量的本质是具有原子性的计数器
  • 信号量本身只是一种外部资源的标识
  • 信号量本身不具有数据交换的功能,而是通过控制其他的通信资源来实现进程间通信,在此过程中负责数据操作的互斥、同步等功能

原子性

  • 两种状态
  • 如果把一个事务可看作是一个程序,它要么完整的被执行,要么完全不执行。这种特性就叫原子性

临界资源

  • 不同进程共同看到的资源
  • 一次仅允许一个进程使用的资源称为临界资源

临界区

  • 共同访问临界资源的那份代码

同步

  • 以特定顺序访问临界资源

互斥

  • 任何时刻,只允许一个进程进入临界区访问临界资源并且属性是原子的

信号量的工作原理

  • 信号量只能进行两种操作等待和发送信号,即P(sv)和V(sv)
  • P(sv):如果sv的值大于零,就给他减1;如果它的值为零,就挂起该进程的执行
  • V(sv):如果有其他进程因等待sv而挂起,就让他恢复运行,如果没有进程因等待sv而挂起,就给他加1
    举个例子,就是两个进程共享信号量sv,一旦其中一个进程执行行了P(sv)操作,它将得到信号量,并可以进入入临界区,使sv减1。而第二个进程将被阻止进入临界区,因为当它试图执行P(sv)时,sv为0,它会被挂起以等待第一个进程离开临界区域并执行V(sv)释放信号量,这时第二个进程就可以恢复执行。

函数

-原型: int semget(key_t key, int nsems, int semflg)

返回值:如果成功,则返回信号量集的IPC标识符。如果失败,则返回-1
errno=EACCESS(没有权限)
EEXIST(信号量集已经存在,无法创建)
EIDRM(信号量集已经删除)
ENOENT(信号量集不存在,同时没有使用IPC_CREAT)
ENOMEM(没有足够的内存创建新的信号量集)
ENOSPC(超出限制)
参数key系统调用semget()的第一个参数是关键字值(一般是由系统调用ftok()返回的)。系统内核将此值和系统中存在的其他的信号量集的关键字值进行比较。
参数semflg中的相关内容:如果单独使用IPC_CREAT,则semget()要么返回新创建的信号量集的标识符,要么返回系统中已经存在的信号量集的标识符。
如果IPC_EXCL和IPC_CREAT一同使用,则要么返回新创建的信号量集的标识符,要么返回-1。
IPC_EXCL单独使用没有意义。
参数nsems指出了一个新的信号量集中应该创建的信号量的个数。

  • 原型:int semctl(int semid, int semnum, int cmd, …)
    函数说明:得到一个信号量集标识符或创建一个信号量集对象并返回信号量集标识符
    参数:
    semid:信号量集标识符
    semnum:信号量集数组上的下标,表示某一个信号量
    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设置信号量集中的一个单独的信号量的值。
    返回值:成功:大于或等于0,失败:-1
union semun {

   short val;          /*SETVAL用的值*/

   struct semid_ds* buf; /*IPC_STAT、IPC_SET用的semid_ds结构*/

   unsigned short* array; /*SETALL、GETALL用的数组值*/

   struct seminfo *buf;   /*为控制IPC_INFO提供的缓存*/

  } arg;
  • int semop(int semid, struct sembuf *sops, unsigned nsops)
    函数说明:对信号量集标识符为semid中的一个或多个信号量进行P操作或V操作
    参数
    semid:信号量集标识符
    struct sembuf *sops:指向进行操作的信号量集结构体数组的首地址,此结构的具体说明如下:
struct sembuf {

    short semnum; /*信号量集合中的信号量编号,0代表第1个信号量*/

    short val;/*若val>0进行V操作信号量值加val,表示进程释放控制的资源 */

/*若val<0进行P操作信号量值减val,若(semval-val)<0(semval为该信号量值),则调用进程阻塞,直到资源可用;若设置IPC_NOWAIT不会睡眠,进程直接返回EAGAIN错误*/

  /*若val==0时阻塞等待信号量为0,调用进程进入睡眠状态,直到信号值为0;若设置IPC_NOWAIT,进程不会睡眠,直接返回EAGAIN错误*/

    short flag;  /*0 设置信号量的默认操作*/

/*IPC_NOWAIT设置信号量操作不等待*/

/*SEM_UNDO 选项会让内核记录一个与调用进程相关的UNDO记录,如果该进程崩溃,则根据这个进程的UNDO记录自动恢复相应信号量的计数值*/

  };

nsops::进行操作信号量的个数,即sops结构变量的个数,需大于或等于1。最常见设置此值等于1,只完成对一个信号量的操作

命令

  • ipcs -s 查看信号量
  • ipcrm -s semid(65535) 删除信号量

代码


#ifndef _SEM_H_
#define _SEM_H_
#include
#include
#include
#include
#include
#include
#include
#define PATHNAME "."
#define PROJ_ID 0x6666
union semun
{
    int val;
    struct semid_ds* buf;
    unsigned short *array;
    struct seminfo* _buf;
};
int create_sem(int nsem_num);
int init_sem(int semid,int which);
int get_sem();
int P(int semid,int which);
int V(int semid,int which);
int destory(int semid);
#endif//_SEM_H_
.c>
#include"sem.h"
static int comm_sem(int nsem_num,int semflag)
{
    //int semget(key_t key ,int nsem, int semflag);//
    //key_t ftok(const char*pathname, int proj_id);//生成key值

    key_t key =  ftok(PATHNAME, PROJ_ID);
   int semid = semget(key , nsem_num, semflag);
   if(semid<0)
   {
       perror("semget");
       return 0;
   }
   return semid;
}
int create_sem(int nsem_num)//创建信号量
{
    return comm_sem(nsem_num,IPC_CREAT|IPC_EXCL|0666);
}

int get_sem()//获取信号量
{
    return comm_sem(0,IPC_CREAT);
}
static int sem_op(int semid, int op,int which)
{
    struct sembuf sem;
    memset(&sem,'\0',sizeof(sem));
    sem.sem_num = which;
    sem.sem_op = op;
    sem.sem_flg = 0;
    return semop(semid,&sem,1);
}
int P(int semid,int which)//P操作 -1
{
    int ret = sem_op(semid,-1,which);
    if(ret < 0)
    {
        perror("semop_p");
        return ret;
    }
    return ret; 
}
int V(int semid,int which)//V操作 +1
{
    int ret = sem_op(semid,1,which);
    if(ret < 0)
    {
       perror("semop_v");
       return ret;
    }
    return ret;
}
// int semctl(int semid,int semnum,int cmd,...);
int init_sem(int semid,int which)//初始化
{
    union semun un;
    un.val = 1;
    int ret = semctl(semid,which,SETVAL,un);
    if(ret < 0)
    {
       perror("init_sem");
       return ret;
    }
    return ret;
}
int destory(int semid)//销毁
{
     int ret = semctl(semid,0,IPC_RMID,NULL);
     if(ret < 0)
     {
    perror("semctl");
        return -1;
     }
     return ret;
}

#include"sem.h"
int main()
{  
    int semid = create_sem(1);
    sleep(10);
    init_sem(semid,0);
    pid_t id = fork();
    if(id == 0)
    {//child
        int semid =  get_sem(); 
        while(1)
        {
            P(semid,0);
            printf("A");
            usleep(10000);
            fflush(stdout);
            printf("A");
            usleep(11000);
            fflush(stdout);
            V(semid,0);
        }
    }else{
           int semid =  get_sem(); 
            while(1)
            {
                P(semid,0);
                printf("B");
                usleep(20000);
                fflush(stdout);
                printf("B");
                usleep(22000);
                fflush(stdout);
                V(semid,0);
            }
    }
    //destory(semid);
    return 0;
}

test_sem:test_sem.c sem.c
    gcc -o $@ $^ 
.PHONY:clean
clean:
    rm -f test_sem

你可能感兴趣的:(信号量,linux,信号量)