linux进程间通信之信号量

1 信号量

信号量与管道和信号不同,它是一个计数器,用于多进程对共享数据对象的访问,通常与共享内存相结合使用。一般在多任务环境系统下,多个进程会同时运行,特别是在多核cpu的情况下,并且有些进程相互之间可能存在一定关联。多个进程可能为了完成同一个任务相互协作,这样形成进程之间的同步关系。而且在不同进程之间,为了争夺有限的系统资源(硬件或软件资源)会进入竞争状态,这就是进程之间的互斥关系。
进程间的互斥和同步关系存在根源在与临界资源。临界资源是在同一个时刻只允许有限个(通常只有一个)进程可以访问或修改资源。通常包括硬件资源(处理器,内存,存储器以及其他处于外围设备)和软件资源(共享代码,共享结构和变量等)。访问临界资源的代码叫做临界区,临界区本身也会成为临界资源
信号量可以用来解决进程之间的同步与互斥问题。通常为了获得共享资源,进行需要执行下列操作:
1:建立初始化控制该资源的信号量(semget(), semctl()函数)
2:若信号量的值为正,则进程可以使用该资源,进程将信号量值减为1,通常称为P操作
3:若信号量的值为0,则进程进入休眠状态,直到值大于零
4:若使用完该资源后,则释放该资源,将信号量值加1,通常称为V操作。

使用信号量临界区的伪代码可以表示为:

INIT_VAL(s) /*初始化信号量*、
非临界区
P(S) /*P操作 -1*/
临界区
V(S) /*V操作 +1*/
非临界区

通常使用信号量只取0和1, 被称为二维信号量。

2: 信号量主要函数

新建或获取信号量函数:
int semget(key_t key, int nsems, int flag)
参数:
Key:信号量的键值,多个进程可以通过它访问同一个信号量,其中有个特殊值IPC_PRIVATE。用于创建当前进程的 私有信号量
nsems: 创建信号量数目,通常为1.
semflag:同open()函数的权限位,也可以用八进制表示法,其中使用IPC_CREATE标志创建新的信号量,即使该信号量已经存在,也不会出错。如果同时使用IPC_EXECL标志可以创建唯一一个新 的信号量,此时如果该信号量已经存在,该函数会返回出错。

信号量操作函数:
int semctl(int semid, int senum, int cmd, union semun arg)
参数:
semid: semget()函数返回的信号量标识符
semnum, 信号量编号,当使用信号量集时才会被用到。通常取值为0,就是使用单个信号量
cmd: 对信号量的各种操作,通常使用一下几种:
IPC_STAT:获得该信号量的semid_ds结构,并存放在由第4个参数arg的buf指向的semid_ds结构中。semid_ds是在系统中描述信号量的数据结构
IPC_SETVL:将信号量值设置为arg的val值
IPC_GETVAL:返回信号量的当前值
IPC_RMID:从系统中,删除信号量(或者信号量集)
arg:是unio semnn结构
union semun
{
int val;
struct semid_ds *buf;
unsigned short *array;
}

实现PV操作重要函数semop:
int semop(int semid, struct sembuf *sops, size_t nsops)
参数:
semid: semget()函数返回的信号量标识符。
sops:指向信号量操作数组,
struct sembuf
{
short sem_num; /* 信号量编号,使用单个信号量时,通常取值为 0 */
short sem_op;
/* 信号量操作:取值为-1 则表示 P 操作,取值为+1 则表示 V 操作*/
short sem_flg;
/* 通常设置为 SEM_UNDO。这样在进程没释放信号量而退出时,系统自动
释放该进程中未释放的信号量 */
}

3:实验用例

下面用一个例子来现实信号量对进程间同步的操作,参考《linux应用程序开发标准教程》中:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/shm.h>

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


int init_sem(int sem_id, int init_value)
{
    union semun sem_union;

    sem_union.val = init_value;
    if(semctl(sem_id, 0, SETVAL, sem_union) == -1)
    {
        perror("Initialize semaphore");
        return -1;
    }

    return 0;
}

int del_sem(int sem_id)
{
    union semun sem_union;

    if(semctl(sem_id, 0, IPC_RMID, sem_union) == -1)
    {
        perror("Delete semaphore");
        return -1;
    }

}


int sem_p(int sem_id)
{
    struct sembuf sem_b;

    sem_b.sem_num = 0;
    sem_b.sem_op = -1;
    sem_b.sem_flg = SEM_UNDO;

    if (semop(sem_id, &sem_b, 1) == -1)
    {
        perror("P operation fail");
        return -1;
    }

    return 0;
    }

int sem_v(int sem_id)
{
    struct sembuf sem_b;

    sem_b.sem_num = 0;
    sem_b.sem_op = 1;
    sem_b.sem_flg = SEM_UNDO;

    if (semop(sem_id, &sem_b, 1) == -1)
    {
        perror("V operation");
        return -1;
    }

    return 0;
}

int main(void)
{
    pid_t result;
    int sem_id;
   /* create sem*/
    sem_id = semget(ftok(".", 'a'), 1, 0666|IPC_CREAT);

    /* init sem */
    init_sem(sem_id, 0);

    result = fork();
    if (result == -1)
    {
        perror("Fork\n");
    }
    else if(result == 0)
    {
        printf("Child process will wait for some seconds... \n");
        sleep(3);
        printf("The returned value is %d in the child process(PID = %d)\n", result, getpid());
        sem_v(sem_id);
    } else
    {
        sem_p(sem_id);
        printf("The return value is %d in the father process(PID = %d)\n", result, getpid());
        sem_v(sem_id);
    }
    exit(0);
}

运行结果:
root@baohua-VirtualBox:/repo/training/fork# ./a.out
Child process will wait for some seconds…
The returned value is 0 in the child process(PID = 4203)
The return value is 4203 in the father process(PID = 4202)

以上实验代码github地址:https://github.com/zhikunhuo/training.git

阅读更多
个人分类: linux应用开发
上一篇ssh: connect to host github.com port 22: Connection timed out
下一篇linux进程间通信之共享内存
想对作者说点什么? 我来说一句

Linux进程间通信.pdf

2010年10月29日 510KB 下载

Linux进程间通信实例

2013年10月16日 18KB 下载

Linux进程间通信的例子

2013年10月16日 11KB 下载

Linux进程间通信效率对比

2015年12月17日 298KB 下载

linux进程间通讯

2013年05月02日 759KB 下载

linux信号量实例代码

2010年10月11日 430KB 下载

进程间通信-2

2015年08月27日 488KB 下载

Linux进程间通信--Linux进程间通信

2011年07月29日 250KB 下载

没有更多推荐了,返回首页

关闭
关闭