linux 信号量、共享内存、和消息队列

Linux的信号量机制

#include<sys/sem.h>

int semctl(int sem_id , int sem_num , int command , ...);

int semget(key_t key , int num_set , int sem_flags);

int semop(int sem_id struct sembuf *sem_ops , size_t num_sem_ops);

头文件sys/sem.h通常依赖于另外两个有文件sys/types.h和sys/ipc.h。一般情况下,他们会被sys/sem.h自动包含,因此不需要为它们明确添加相应的#include语句。

参数key的作用很像一个文件名,它代表程序可能要使用的某个资源,如果多个程序使用相同的key值,它将负责协调工作。

1.semget函数

semget函数的作用是创建一个新信号量或取得一个已有信号量的键:

int semget(key_t key , int num_set , int sem_flags);

第一个参数是整数值,不相关的进程可以通过它访问同一个信号量。只有semget函数才直接使用信号量键,所有其他的信号量函数都是使用有semget函数返回的信号量标识符。

num_sems指定需要的信号量的数目,它几乎总是取值为1。

sem_flags参数是一组标志,它与open函数的标志非常相识。

2.semop函数

semop函数用于改变信号量的值,它的定义如下所示:

int semop(int sem_id , struct sembuf *sem_ops , size_t num_sem_ops);

第一个参数sem_id是由semget返回的信号量标识符。第二个参数sem_ops是指向一个结构数组的指针,每个数组元素至少包含一下几个成员:

struct sembuf

{

short sem_num;

short sem_op;

short sem_flg;

}

第一个成员sem_num是信号量编写,除非你需要使用一组信号量,否则它的取值一般是0。

sem_op成员的值是信号量在一次操作中需要改变的数值。通常只会用到两个值,一个是-1,也就是P操作,它等待信号量变为可用。另外一个是V操作,它是+1,它发送信号表示信号量现在可用。

最后一个成员sem_flg通常被设置为SEM_UNDO。它将是的操作系统跟踪当前进程对这个信号量的修改情况。如果这个进程在没有释放该信号量的情况下终止,操作系统将自动释放该进程持有的信号量。

3.semctl函数

int semctl(int sem_id , int sem_num ,  int command , ...);

第一个参数sem_id是由semget返回的信号量标识符。sem_num参数是信号量编写,当需要用到成组的信号量时,就要用到这个参数,它一般取值为0,表示这是第一个唯一的一个信号量。command参数是将要采取的动作。如果还有第四个参数,它将会是一个union semun结构,它至少包含一下几个成员:

union semun

{

int val;

struct semid_ds *buf;

unsigned short *array;

}

semctl函数中的command参数可以设置许多不同的值,但只有下面介绍的两个值最常用。

SETVAL:用来把信号量初始化为一个已知的值。这个用过union semun中的val成员设置。

IPC_RMID:用于删除一个已经无需继续使用的信号量标识符。

/* After the #includes, the function prototypes and the global variable, we come to the
 main function. There the semaphore is created with a call to semget, which returns the
 semaphore ID. If the program is the first to be called (i.e. it's called with a parameter
 and argc > 1), a call is made to set_semvalue to initialize the semaphore and op_char is
 set to X. */

#include <unistd.h>
#include <stdlib.h>
#include <stdio.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);
    }

/* Then we have a loop which enters and leaves the critical section ten times.
 There, we first make a call to semaphore_p which sets the semaphore to wait, as
 this program is about to enter the critical section. */

    for(i = 0; i < 10; i++) {        

        if (!semaphore_p()) exit(EXIT_FAILURE);
        printf("%c", op_char);fflush(stdout);
        pause_time = rand() % 3;
        sleep(pause_time);
        printf("%c", op_char);fflush(stdout);

/* After the critical section, we call semaphore_v, setting the semaphore available,
 before going through the for loop again after a random wait. After the loop, the call
 to del_semvalue is made to clean up the code. */

        if (!semaphore_v()) exit(EXIT_FAILURE);
        
        pause_time = rand() % 2;
        sleep(pause_time);
    }    

    printf("\n%d - finished\n", getpid());

    if (argc > 1) {    
        sleep(10);
        del_semvalue();
    }
        
    exit(EXIT_SUCCESS);
}

/* The function set_semvalue initializes the semaphore using the SETVAL command in a
 semctl call. We need to do this before we can use the semaphore. */

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);
}

/* The del_semvalue function has almost the same form, except the call to semctl uses
 the command IPC_RMID to remove the semaphore's ID. */

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");
}

/* semaphore_p changes the semaphore by -1 (waiting). */

static int semaphore_p(void)
{
    struct sembuf sem_b;
    
    sem_b.sem_num = 0;
    sem_b.sem_op = -1; /* P() */
    sem_b.sem_flg = SEM_UNDO;
    if (semop(sem_id, &sem_b, 1) == -1) {
        fprintf(stderr, "semaphore_p failed\n");
        return(0);
    }
    return(1);
}

/* semaphore_v is similar except for setting the sem_op part of the sembuf structure to 1,
 so that the semaphore becomes available. */

static int semaphore_v(void)
{
    struct sembuf sem_b;
    
    sem_b.sem_num = 0;
    sem_b.sem_op = 1; /* V() */
    sem_b.sem_flg = SEM_UNDO;
    if (semop(sem_id, &sem_b, 1) == -1) {
        fprintf(stderr, "semaphore_v failed\n");
        return(0);
    }
    return(1);
}


你可能感兴趣的:(linux 信号量、共享内存、和消息队列)