信号量函数(3个)

1.semget

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

返回值:失败返回-1,成功返回一个信号集的标识(该标识通过第一个参数key决定)

描述:创建或者获取一个信号量集

参数:key是关键字,一般由系统调用ftok返回,系统内核将此值与系统中其它信号量集的key值进行比     较

    nsems表明要创建的信号量集中信号量的个数

    semflg可取3个

        IPC_CREAT,如果信号量不存在就创建一个新的信号量,如果存在则返回该信号量的标识

        IPC_EXCL,

        IPC_CREAT|IPC_EXCL,如果信号量不存在就创建一个新的信号量,如果存在则以错误返回


=====================================================================

2.semctl

我们可以删除或初始化信号量集


原型:int semctl(int semid,int semnum,int cmd,...)

返回值:失败返回-1,否则返回值取决于cmd命令

描述:这个函数是对标识为semid的信号量集中的第semnum个信号量进行cmd操作

    semnum指明了你要对该集合中的第几个信号量进行操作(下标从0开始,和数组一样)

    cmd为执行的命令

    函数可以有三个或四个参数,取决于cmd命令,如果有第四个参数,则为用户自定义的通用联合体

description:

    semctl() performs the control operation specified by cmd on the semaphore set         identified by semid, or on the semnum-th semaphore of that set.

    (The semaphores in a set are numbered starting at 0.)

    This function has three or four arguments, depending on cmd. When there are four, the     fourth has the type union  semun. 

    The calling program must define this union as follows:

           union semun {

               int          val;    /* Value for SETVAL */

               struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */

               unsigned short  *array;  /* Array for GETALL, SETALL */

               struct seminfo  *__buf;  /* Buffer for IPC_INFO

                                           (Linux-specific) */

           };


参数:

 cmd可以取以下值:

  IPC_RMID:立即删除指定标识的信号量集,第二个参数semnum忽视,设为0就可以了

  IPC_SET:

  SETVAL:

  SETALL:

  其实还有一些,用的时候man就可以了

=====================================================================

3.semop

用户改变信号量的值。也就是使用资源还是释放资源使用权。


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

返回值:成功返回0,失败返回-1


semid标识了要操作的信号量集,sops指向了一个元素为struct sembuf结构体数组的首地址,nsops指出要操作的信号量的个数。


 struct sembuf有如下成员

     

           unsigned short sem_num; //信号在信号集中的索引,0代表第一个信号,1代表第二个信号 

           short       sem_op;  //操作类型

           short       sem_flg;  //操作标志

     sem_op参数:当sem_op<0时,其绝对值又大于信号的现值,操作将会阻塞,直到信号值大于或等于sem_op的绝对值,否则信号量将加上该值(负值相当于减),通常用于释放资源的使用权

             当sem_op=0时,如果没有设置 IPC_NOWAIT,则调用进程进入睡眠状态,直到信号量的值为0;否则进程不回睡眠,直接返回 EAGAIN

             当sem_op>0时,信号量将加上该值,通常用于获取资源的使用权

                             如果其值为正数,该值会加到现有的信号内含值中。通常用于释放所控资源的使用权;如果sem_op       的值为负数,而其绝对值又大于信号的现值,操作将会阻塞,直到信号值大于或等于sem_op的绝对值。通常用于获取资源的使用权;如果sem_op的值为0,则操作将暂时阻塞,直到信号的值变为0


     sem_flg参数:该参数可设置为IPC_NOWAIT或SEMUNDO

             IPC_NOWAIT//对信号的操作不能得到满足时,不会阻塞,而是直接返回并设定错误信息

             SEMUNDO//程序结束时,不论是不是正常返回,都会把信号量的值设定成调用semop()前的值,保证资源的释放(否则会造成资源的永远锁定)。


 每个信号量在信号集中都有如下的信息


           unsigned short  semval;   /* semaphore value */

           unsigned short  semzcnt;  /* # waiting for zero */

           unsigned short  semncnt;  /* # waiting for increase */

           pid_t        sempid;   /* process that did last op */

=====================================================================


代码实现:

test_demo.c

#include<stdio.h>
#include<unistd.h>
#include <comm.h>

int main()
{
    pid_t _id=fork();
    if(_id<0){//failed
        perror("fork");
        return -1; 
    }   
    else if(_id==0){//child
        while(1){
            printf("A");
            sleep(2);
            fflush(stdout);
            printf("A");
            sleep(1);
            fflush(stdout);
        }
     }
     else{//father
        while(1){
            printf("B");
            sleep(3);
            fflush(stdout);
            printf("B");
            sleep(1);
            fflush(stdout);
        }
      }
     return 0;
}

运行结果:

wKiom1cPoqiCH-wcAAAZ9Rvyj9Y551.png

从上图可看出A和B的输出没有一点规律可言,表明子进程和父进程是并发执行的,但我们想让A和B成对的出现就要用到信号量,下面代码实现了这个功能。


代码实现:

test_demo.c

#include<stdio.h>
#include<unistd.h>
#include<comm.h>

int main()
{
    int _sem_id=creat_sem_set(1);
    init_sem_set(_sem_id,0,1);
    pid_t _id=fork();
    if(_id<0){//failed
        perror("fork");
        return -1; 
    }   
    else if(_id==0){//child
        int _child_sem_id=get_sem_set(1); 
        while(1){
            p_sem_elem(_child_sem_id);
            printf("A");
            sleep(2);
            fflush(stdout);
            printf("A");
            sleep(1);
            fflush(stdout);
            v_sem_elem(_child_sem_id);
        }
    }
    else{//father
        while(1){
            p_sem_elem(_sem_id);
            printf("B");
            sleep(3);
            fflush(stdout);
            printf("B");
            sleep(1);
            fflush(stdout);
            v_sem_elem(_sem_id);
        }
    }
    destroy_sem_set(_sem_id);
    return 0;
}
运行结果:

wKioL1cPpaPBpt1OAAAZ7-KbC6E758.png


下图是我的目录结构

wKioL1cPtufyZHadAAAihxfG1dM466.png

我打包了静态库,只要头文件中包含comm.h就可以用comm.h中包含的方法了

wKioL1cPrxOxACL7AABtRKOE7_w892.png


下面是各个文件中的代码:

test_sem/comm.h

#ifndef _SEM_
#define _SEM_

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

#define _PATH_ "."
#define _PROJ_ID_ 0x67

union semun
{
    int val;
    struct semid_ds *buf;
    unsigned short *array;
    struct seminfo *__buf;
};

int creat_sem_set(int);
int get_sem_set(int);
void init_sem_set(int sem_id,int _semnum,int _val);
void p_sem_elem(int sem_id);
void v_sem_elem(int sem_id);
void destroy_sem_set(int sem_id);

#endif

test_sem/comm.c

#include"comm.h"

static int comm_sem_set(int _semnu, int flags)
{
    key_t _key=-1;
    if((_key=ftok(_PATH_,_PROJ_ID_))<0){
        perror("ftok");
        return -1; 
    }   
    int _sem_id=-1;
    if((_sem_id=semget(_key,_semnu,flags))<0){
        perror("semget");
        return -1; 
    }   
    return _sem_id;
}

int creat_sem_set(int _semnum)
{
    return comm_sem_set(_semnum,IPC_CREAT|IPC_EXCL|0666);
}

int get_sem_set(int _semnum)
{
    return comm_sem_set(_semnum,IPC_CREAT);
}

void init_sem_set(int sem_id,int _semnum,int _val)
{
    union semun _semun;
    _semun.val=_val;
    if(semctl(sem_id,_semnum,SETVAL,_semun)<0){
        perror("semctl");
    }
}

static void pv_sem(int sem_id,int _op)
{
    struct sembuf _sembuf;
    _sembuf.sem_num=0;
    _sembuf.sem_op=_op;
    _sembuf.sem_flg=SEM_UNDO;
    if(semop(sem_id,&_sembuf,1)<0){
        perror("semop");
    }
}
void p_sem_elem(int sem_id)
{
    pv_sem(sem_id,-1);
}

void v_sem_elem(int sem_id)
{
    pv_sem(sem_id,1);
}

void destroy_sem_set(int sem_id)
{
    if(semctl(sem_id,0,IPC_RMID)<0){
        perror("msgctl");
    }
}

test_sem/Makefile

libmysem.a:comm.o
    ar rcs $@ $^
comm.o:comm.c
    gcc -c $<

.PHONY:clean
clean:
    rm -rf libmysem.a comm.o output

.PHONY:output
output:
    mkdir output
    cp libmysem.a output
    cp comm.h output
    rm -rf libmysem.a comm.o


demo/Makefile

test_demo:test_demo.c
    gcc -o $@ $^ -I../output -L../output -lmysem

.PHONY:clean
clean:
    rm -f test_demo