Linux进程间通信 -2信号量

最初有TA&T System V.2 UNIX系统引入,被称为System V IPC。

  • 信号量 : 用于管理对资源的访问。
  • 内存共享: 用于程序之间高效地共享数据。
  • 消息队列: 在程序之间传递数据的一种简单方法。

信号量

先来理解一下临界代码的含义,需要确保只有一个进程或者一个线程进入这个这个临界代码并拥有资源的独占访问权。

(现场之间可以通过信号量semaphore或者互斥量控制临界区的访问,但是这里讲的信号量并不同于线程之间所用的信号量,这里的方法更加通用。)

1 semget函数

semget创建一个新的信号量或者取得已有信号的键。

       #include <sys/types.h>
       #include <sys/ipc.h>
       #include <sys/sem.h>

       int semget(key_t key, int nsems, int semflg);

DESCRIPTION
The semget() system call returns the System V semaphore set identifier associated with the argument key. A new set of nsems sema‐phores is created if key has the value IPC_PRIVATE or if no existing semaphore set is associated with key and IPC_CREAT is specified in semflg.

If semflg specifies both IPC_CREAT and IPC_EXCL and a semaphore set already exists for key, then semget() fails with errno set to EEXIST. (This is analogous to the effect of the combination O_CREAT | O_EXCL for open(2).)

Upon creation, the least significant 9 bits of the argument semflg define the permissions (for owner, group and others) for the sema‐phore set. These bits have the same format, and the same meaning, as the mode argument of open(2) (though the execute permissions are not meaningful for semaphores, and write permissions mean permission to alter semaphore values).

The values of the semaphores in a newly created set are indeterminate. (POSIX.1-2001 is explicit on this point.) Although Linux, like many other implementations, initializes the semaphore values to 0, a portable application cannot rely on this: it should explic‐itly initialize the semaphores to the desired values.

When creating a new semaphore set, semget() initializes the set’s associated data structure, semid_ds (see semctl(2)), as follows:

sem_perm.cuid and sem_perm.uid are set to the effective user ID of the calling process.

sem_perm.cgid and sem_perm.gid are set to the effective group ID of the calling process.

The least significant 9 bits of sem_perm.mode are set to the least significant 9 bits of semflg.

sem_nsems is set to the value of nsems.

sem_otime is set to 0.

sem_ctime is set to the current time.

The argument nsems can be 0 (a don’t care) when a semaphore set is not being created. Otherwise nsems must be greater than 0 and less than or equal to the maximum number of semaphores per semaphore set (SEMMSL).

If the semaphore set already exists, the permissions are verified.

RETURN VALUE
If successful, the return value will be the semaphore set identifier (a nonnegative integer), otherwise -1 is returned, with errno indicating the error.

ERRORS
On failure errno will be set to one of the following:EACCES A semaphore set exists for key, but the calling process does not have permission to access the set, and does not have the CAP_IPC_OWNER capability.

EEXIST A semaphore set exists for key and semflg specified both IPC_CREAT and IPC_EXCL.

EINVAL nsems is less than 0 or greater than the limit on the number of semaphores per semaphore set (SEMMSL), or a semaphore set cor‐responding to key already exists, and nsems is larger than the number of semaphores in that set.

ENOENT No semaphore set exists for key and semflg did not specify IPC_CREAT.

ENOMEM A semaphore set has to be created but the system does not have enough memory for the new data structure.

ENOSPC A semaphore set has to be created but the system limit for the maximum number of semaphore sets (SEMMNI), or the system wide maximum number of semaphores (SEMMNS), would be exceeded.

2 semop

       #include <sys/types.h>
       #include <sys/ipc.h>
       #include <sys/sem.h>

int semop(int semid, struct sembuf *sops, unsigned nsops);

int semtimedop(int semid, struct sembuf *sops, unsigned nsops,struct timespec *timeout);

Feature Test Macro Requirements for glibc (see feature_test_macros(7)):
semtimedop(): _GNU_SOURCE

semop是用来改变信号量的值;

3semctl

       #include <sys/types.h>
       #include <sys/ipc.h>
       #include <sys/sem.h>

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

semctl用于控制信号量。

4 使用信号量

多个程序访问临界代买区域,通过二进制信号量进行控制。

/* 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);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值