关闭

[置顶] linux进程间通信之信号量

标签: linuxIPC进程间通信信号量semaphore
106人阅读 评论(0) 收藏 举报
分类:

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

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:2667次
    • 积分:133
    • 等级:
    • 排名:千里之外
    • 原创:11篇
    • 转载:0篇
    • 译文:0篇
    • 评论:1条
    文章分类
    文章存档
    最新评论