linux 信号量编程

来源:互联网 发布:卢卡奇物化理论知乎 编辑:程序博客网 时间:2024/05/05 19:59

                                       linux 信号量编程

    信号量可以管理资源,实现资料的互斥。如多进程下实现一块共享内存区域的互斥等。

    信号量也是一种ipc机制,所以管理的方式也是用key来管理。

    linux里的信号量是一个集合,每个集合里可以管理一个资源。如:

一个集合里可以有n个元素,每个元素可以管理一个资源。

比如编号为0的信号量0,表示资源数据为1. 这个值可以在创建信号量集合的时候设置。编号如数组,从0开始。

semget(m_key,SEMNUM,IPC_EXCL);

值的说明:

当只有IPC_CREAT选项打开时,不管是否已存在该块共享内存,则都返回该共享内存的ID,若不存在则创建共享内存

当只有IPC_EXCL选项打开时,不管有没有该快共享内存,shmget()都返回-1

所以当IPC_CREAT | IPC_EXCL时, 如果没有该块共享内存,则创建,并返回共享内存ID。
                            若已有该块共享内存,则返回-1;

 

创建一个信号量集合。

//SEMNUM为信号量集合里有元素的个数。如上同n个元素。如果管理一个资源,一个集合一个元素就可以,即SEMNUM=1.

紧接着设置元素的资源值。如果下面设置编号为0的元素的资源为1.

 arg.val = 1;
 semctl(m_semid,0,SETVAL,arg)

 

信号量通过管理这个值来实现资源的互斥。

原理:

如果使用进程A在使用这个资源,可以把这个值减1. 这时,资源值为0. (可称为加锁。) 

再进程B要申请时,由于这个值已经为0,表示没有资源可以使用,B进程就进入休眠了。

直到进程A释放这个资源,即对这个值进行加1的操作,这时,资源值为1(可称为解锁),说明这个资源可用。

进程 B被唤醒。资源又进行加锁状态,进程B使用完了后再对资源进行解锁。

这样就实现的资源的互斥。

 

看看编程怎么加解锁:

首先了解加解锁的函数:

--------------------------------------------------------------

操作一个或一组信号。
  用法:
  int semop(int semid, struct sembuf *sops, unsigned nsops);
  int semtimedop(int semid, struct sembuf *sops, unsigned nsops, struct timespec *timeout);

  参数:
  semid:信号集的识别码,可通过semget获取。
  sops:指向存储信号操作结构的数组指针,信号操作结构的原型如下
  struct sembuf
  {
  unsigned short sem_num; /* semaphore number */
  short sem_op; /* semaphore operation */
  short sem_flg; /* operation flags */
  };
  这三个字段的意义分别为:


  sem_num:操作信号在信号集中的编号,第一个信号的编号是0。

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

  sem_flg:信号操作标志,可能的选择有两种
  IPC_NOWAIT //对信号的操作不能满足时,semop()不会阻塞,并立即返回,同时设定错误信息。
  IPC_UNDO //程序结束时(不论正常或不正常),保证信号值会被重设为semop()调用前的值。这样做的目的在于避免程序在异常情况下结束时未将锁定的资源解锁,造成该资源永远锁定。

 

  timeout:当semtimedop()调用致使进程进入睡眠时,睡眠时间不能超过本参数指定的值。如果睡眠超时,semtimedop()将失败返回,并设定错误值为EAGAIN。如果本参数的值为NULLsemtimedop()将永远睡眠等待。

----------------------------------------------------------------------

使用semop,可以先定义一些操作:

struct sembuf OP_A_WAIT={SEM_0,0,SEM_UNDO};
struct sembuf OP_A_LOCK={SEM_0,-1,SEM_UNDO};
struct sembuf OP_A_UNLOCK={SEM_0,+1,SEM_UNDO};

SEM_0是元素的编号。

 

OP_A_LOCK 是加锁操作,即把资源的值减去1。

OP_A_UNLOCK 是解锁操作,即把资源加1.

 

使用调用semop就可以实现加解锁。

 semop(m_semid,&OP_A_LOCK,1)

semop(m_semid,&OP_A_UNLOCK,1)

 

最后说一个semctl函数设计理解。

 

semctl 可以实现

很可以操作:这个可以选一个操作类型参数,而不同的操作可以输入的参数不一样,

这时,semctl是用union联合体来定义参数。

union在这里有点泛型的味道。相当于让编译器解析出各种内置类型。

这样就不用写很多函数来重载了。

 

【semctl系统调用】  
   
功能描述:
在指定的信号集或信号集内的某个信号上执行控制操作。

 
用法: 
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

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


参数:  
semid:信号集的标识符,即是信号表的索引。
semnum:信号集的索引,用来存取信号集内的某个信号。
cmd:需要执行的命令,有效值有

IPC_STAT //将与semid关联的内核数据结构拷贝到由arg.buf指针指向的内存区。
IPC_SET //将由arg.buf指针指向的semid_ds的一些成员写入相关联的内核数据结构,同时更新它的sem_ctime成员。
IPC_RMID //立即删除信号集,唤醒所有被阻塞的进程。
IPC_INFO //Linux特有命令,返回系统范围内关于信号集的制约和其它参数,并存放在arg.__buf指向的内存区。其结构形态如下:

struct  seminfo {
    int semmap;  /* # of entries in semaphore map; unused */
    int semmni;  /* Max. # of semaphore sets */
    int semmns;  /* Max. # of semaphores in all semaphore sets */
    int semmnu;  /* System-wide max. # of undo structures; unused */
    int semmsl;  /* Max. # of semaphores in a set */
    int semopm;  /* Max. # of operations for semop() */
    int semume;  /* Max. # of undo entries per process; unused */
    int semusz;  /* size of struct sem_undo */
    int semvmx;  /* Maximum semaphore value */
    int semaem;  /* Max. value that can be recorded for semaphore adjustment (SEM_UNDO) */
};

SEM_INFO //返回和IPC_INFO相同的信息,不同点有:semusz字段包含有当前系统存在的信号集总量。semaem字段包含有系统内所有信号集的信号总量。
SEM_STAT //返回和IPC_STAT相同的信息。不过参数semid不是一个信号集标识,而是内核内部维持所有信号集信息的数组索引。
GETALL //将所有信号的值存入semun.array中。
GETNCNT //等待信号值增加的进程的总数。
GETPID //前一个对此信号进行操作的进程的识别码。
GETVAL //根据semnun返回信号的值。
GETZCNT //等待信号值变为0的进程的总数。
SETALL //将所有semun.array的值设定到信号集中。
SETVAL //根据semun设定信号的值。

...:对于不同的命令,可能需要用到也可能不需要,是一个联合体,原型如下

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) */
};
 
semid_ds结构体定义在<sys/sem.h>,原型如下

struct semid_ds {
    struct ipc_perm sem_perm;  /* Ownership and permissions
    time_t          sem_otime; /* Last semop time */
    time_t          sem_ctime; /* Last change time */
    unsigned short  sem_nsems; /* No. of semaphores in set */
};

ipc_perm结构体定义在<sys/ipc.h>,原型如下

struct ipc_perm {
    key_t key;            /* Key supplied to semget() */
    uid_t uid;            /* Effective UID of owner */
    gid_t gid;            /* Effective GID of owner */
uid_t cuid;           /* Effective UID of creator */
    gid_t cgid;           /* Effective GID of creator */
    unsigned short mode;  /* Permissions */
    unsigned short seq;   /* Sequence number */
};

          

返回说明:  
成功执行时,根据不同的命令返回不同的非负值

GETNCNT //返回semncnt的值
GETPID  //返回sempid的值
GETVAL  //返回semval的值
GETZCNT //返回semzcnt的值
IPC_INFO //返回内核内部关于信号集信息的最大可用入口索引
SEM_INFO //如同IPC_INFO.
SEM_STAT //返回信号集标识
剩下的命令返回0。


失败返回-1,errno被设为以下的某个值  
EACCES:访问出错,权能不允许
EFAULT:arg.buf 或 arg.array所指的空间不可访问
EIDRM:信号集已被删除
EINVAL;参数无效
EPERM:权能不允许
ERANGE:给出的参数无效

 

 

 

 

 

原创粉丝点击