Linux进程通信(3):信号量

来源:互联网 发布:big mac index 编辑:程序博客网 时间:2024/06/15 05:39

一、信号量概述

信号量与其他进程间通信方式不大相同,它主要提供对进程间共享资源访问控制机制.相当于内存中的标志,进程可以根据它判定是否能够访问某些共享资源,同时,进程也可以

修改该标志.除了用于访问控制外,还可用于进程同步.
信号量有以下两种类型:
二值信号量:最简单的信号量形式,信号量的值只能取0或1,类似于互斥锁
计数信号量:信号量的值可以取任意非负值(当然受内核本身的约束)
注;二值信号量能够实现互斥锁的功能,但两者的关注内容不同.信号量强调共享资源,只要共享资源可用,其他进程同样可以修改信号量的值;互斥锁更强调进程,占用资源的

进程使用完资源后,必须由进程本身来解锁.

二、操作信号量

2.1 创建或者取得信号量

int semget(key_t key, int nsems, int semflg)
参数:key整数值,不相关的进程可以通过它访问同一个信号量
参数;nsems获取的信号量数量,一般为1即可
参数:semflg创建标志,和open的标志类似一般为IPC_CREAT和IPC_EXCL就可以确保得到一个新的唯一的信号量
返回值:返回一个正数,其他信号量操作函数将通过它使用信号量

2.2 信号量操作函数

int semop(int semid, struct sembuf *sops, unsigned nsops);
参数:semid为semget的返回值,用来操作分配的信号量
参数:sops信号量操作设置
struct sembuf {
 unsigned short   sem_num;//操作信号量在整个分配信号量的位置,如果分配一个那么就是0
 short   sem_op;//信号量操作增量,一般1或者-1
 short   sem_flg;//信号量标志一般为SEM_UNDO
};
参数:nsops为sops指向数组的大小即分配的信号量的大小

2.3 信号量控制函数

int semctl(int semid,int semnum,int cmd,union semun arg)
参数:semid为semget的返回值,用来操作分配的信号量
参数:semnum操作的信号量在分配的信号量中的位置,分配为1的话,该值为0
参数:cmd操作的选项
SETVAL   设置信号量的初始值,设置
IPC_RMID 删除一个信号量
参数:arg 当cmd为SETVAL时可以设置里面的val变量指定信号量的初始值
union semnum
{
  int val;
  struct semid_ds *buf;
  unsigned short *array;
}
返回值:调用失败返回-1,成功返回与cmd相关

三、应用举例

#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <sys/types.h>#include <sys/ipc.h>#include <sys/sem.h>#include "semun.h"static int set_semvalue(void);static void del_semvalue(void);static int semaphore_p(void);static int semaphore_v(void);static int sem_id;int main(int argc, char **argv){    int i;    int pause_time;    char op_char = 'O';    srand((unsigned int)getpid());    sem_id = semget((key_t)1234, 1, 0666 | IPC_CREAT);//获得信号量    if(argc > 1)    {        if(!set_semvalue())//设置信号量的初始值        {            fprintf(stderr, "Failed to initialize semaphore/n");            exit(EXIT_FAILURE);        }        op_char = 'X';        sleep(2);    }    for(i=0;i<10;i++)    {        if(!semaphore_p()) exit(EXIT_FAILURE);//信号量P操作,成功就进入临界区,否则等待其他进程V操作        printf("%c", op_char); fflush(stdout);        pause_time = rand() % 3;        sleep(pause_time);        printf("%c", op_char); fflush(stdout);        if(!semaphore_v()) exit(EXIT_FAILURE);//退出临界区前执行V操作        pause_time = rand() % 2;        sleep(pause_time);    }    printf("/n%d - finished/n", getpid());    if(argc > 1)    {        sleep(10);        del_semvalue();//删除信号量    }    exit(EXIT_SUCCESS);    }static int set_semvalue(void){    union semun sem_union;    sem_union.val = 1;    if(semctl(sem_id, 0, SETVAL, sem_union) == -1) return 0;    return 1;}static void del_semvalue(void){    union semun sem_union;    if(semctl(sem_id, 0, IPC_RMID, sem_union) == -1)        fprintf(stderr, "Failed to delete semaphore/n");}static int semaphore_p(void){    struct sembuf sem_b;    sem_b.sem_num = 0;    sem_b.sem_op = -1;    sem_b.sem_flag = SEM_UNDO;    if(semop(sem_id, &sem_b, 1) == -1)    {        fprintf(stderr, "semaphore_p failed/n");        return 0;    }    return 1;}static int semaphore_v(void){    struct sembuf sem_b;    sem_b.sem_num = 0;    sem_b.sem_op = 1;    sem_b.sem_flag = SEM_UNDO;    if(semop(sem_id, &sem_b, 1) == -1)    {        fprintf(stderr, "semaphore_v failed/n");        return 0;    }    return 1;}

# ./sem1 1 &
[1] 1082
# ./sem1
OOXXOOXXOOXXOOXXOOXXOOOOXXOOXXOOXXOOXXXX
1083 - finished
1082 - finished
#
正如我们所看到了,O与X是成对出现的,表明临界区部分被正确的处理了。如果这个程序在我们的系统上不能正常运行,也许我们需要在调用程序之前使用命令stty -tostop来保证生成tty输出的后台程序不会引起信号生成。

参考文章:

http://www.cnblogs.com/hjslovewcl/archive/2011/03/03/2314341.html

Linux程序设计第四版

原创粉丝点击