Linux进程间通信(五) - 信号灯(史上最全)及其经典应用案例

信号灯概述

什么是信号灯

信号灯用来实现同步,用于多线程,多进程之间同步共享资源(临界资源)。

PV原语:信号灯使用PV原语

P原语操作的动作是:

u  sem减1。

u  sem减1后仍大于或等于零,则进程继续执行。

u  若sem减1后小于零,则该进程被阻塞后进入与该信号相对应的队列中,然后转进程调度。

V原语操作的动作是:

u  sem加1。

u  若相加结果大于零,则进程继续执行。

u  若相加结果小于或等于零,则从该信号的等待队列中唤醒一等待进程,然后再返回原进程继续执行或转进程调度。

信号灯分类

按信号灯实现原理,信号灯分两种,一种是有名信号灯,一种是基于内存的信号灯。

有名信号灯,是根据外部名字标识,通常指代文件系统中的某个文件。而基于内存的信号灯,它主要是把信号灯放入内存的。

基于内存的信号灯,同步多线程时,可以放到该多线程所属进程空间里;如果是同步多进程,那就需要把信号灯放入到共享内存中(方便多个进程访问)。

按实现方式,信号灯分为POSIX信号灯和System V信号灯,System V信号灯是由内核维护的,Posix信号灯是由文件系统中的路径名对应的名字来标识的。在目前的Linux中,System V使用更为广泛,POSIX一般是在更老的系统中使用。

信号灯操作

进程在信号灯上的几种操作:

1)  创建一个信号灯。还要求调用者指定初始值,对二值来说通常是1。

2)  等待一个信号灯。测试信号灯的值,如果<=0则等待,否则将其减1。注:测试其值并减1必须作为一个原子操作。

3)  挂出一个信号灯。将信号灯的值加1。挂出操作也必须是原子的。

4)获取信号灯状态。

问题:如何将等待某个信号灯的所有进程排队,如何唤醒这些可能很多的进程中的一个,所幸这些都是由实现来处理的。

二值信号灯可用于互斥目的。除了可以象互斥锁那样使用外,信号灯还有一个互斥锁没有提供的特性:互斥锁必须总是由锁住他的线程解锁,信号灯的挂出却不必由执行过它的等待操作的同一线程执行。比如生产者消费者问题是生产者和消费者互相唤醒的。

共享内存信号灯同时属于两个进程的地址空间。

信号灯有一个与之关联的值,挂出一个信号即使当前没有线程在等待该信号也没关系,与之相反的是,pthread_cond_signal如果当时没有任何线程阻塞在pthread_cond_wait中,则信号丢失。

POSIX信号灯

图1:POSIX有名信号灯和基于内存信号灯系统调用关系

POSIX有名信号灯

函数说明

#include <fcntl.h>           /* For O_* constants */

#include <sys/stat.h>        /* For mode constants */

#include <semaphore.h>

// 用来打开已经存在的信号灯

sem_t *sem_open(const char *name, int oflag);

// 用来创建信号灯

sem_t *sem_open(const char *name, int oflag, mode_t mode, unsigned int value);

// 获得信号灯,得不到就阻塞;如果获得信号灯,信号灯数量减1

int sem_wait(sem_t *sem);

// 尝试获得,得不到返回失败,errno设置为EAGAIN

int sem_trywait(sem_t *sem);

// sem_getvalue返回指定信号灯的当前值,如果该信号灯已上锁,那么返回或为0,或为某个负数,其绝对值就是等待该信号灯解锁的线程数。

int sem_getvalue(sem_t *sem, int *sval);

// 释放信号灯,信号数量加1

int sem_post(sem_t *sem);

// 删除以name命名的信号灯,只有当系统中所有使用该信号灯的进程都释放,才会真的

删除

int sem_unlink(const char *name);

创建信号灯
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <errno.h>
#include <string.h>

sem_t* namedSem;
#define FILENAME "/tmp/count_named_sem.txt"
#define MUTEXNAME "my_named_sem"

int main(int argc, char** argv)
{
    int fd, inum = 0;
    namedSem = sem_open(MUTEXNAME, O_CREAT|O_EXCL, 0644, 1);
    if (SEM_FAILED == namedSem)
    {
        if (errno != EEXIST)
        {
            printf("sem_open error : %s\n", strerror(errno));
            return -1;
        }
        printf("sem_open %s exist! so open\n", MUTEXNAME);
        namedSem = sem_open(MUTEXNAME, O_RDWR);
    }
    printf("sem_open succ\n", MUTEXNAME);

    fd = open(FILENAME, O_RDWR|O_CREAT|O_TRUNC, 0777);
    write(fd, &inum, sizeof(int));
    return 0;
}

结果说明

[[email protected] ipc]# g++ -g -o ipc_posix_named_sem_create ipc_posix_named_sem_create.cpp –lrt

[[email protected] ipc]# ./ipc_posix_named_sem_create

sem_open succ

[[email protected] ipc]# ./ipc_posix_named_sem_create

sem_open my_named_sem exist! so open

sem_open succ

删除信号灯
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <errno.h>
#include <string.h>

sem_t* namedSem;
#define FILENAME "/tmp/count_named_sem.txt"
#define MUTEXNAME "my_named_sem"

int main(int argc, char** argv)
{
    namedSem = sem_open(MUTEXNAME, O_CREAT|O_EXCL, 0644, 1);
    if (SEM_FAILED == namedSem)
    {
        if (errno != EEXIST)
        {
            printf("sem_open error : %s\n", strerror(errno));
            return -1;
        }
        printf("sem_open %s exist! so open\n", MUTEXNAME);
        namedSem = sem_open(MUTEXNAME, O_RDWR);
    }

    int ret = sem_unlink(MUTEXNAME);
    if (-1 == ret)
    {
        printf("sem_unlink error: %s\n", strerror(errno));
        return -1;
    }
    printf("sem_unlink %s succ!\n", MUTEXNAME);
    return 0;
}

结果说明

[[email protected] ipc]# g++ -g -o ipc_posix_named_sem_unlink ipc_posix_named_sem_unlink.cpp -lrt

[[email protected] ipc]# ./ipc_posix_named_sem_unlink

sem_open my_named_sem exist! so open

sem_unlink my_named_sem succ!

[[email protected] ipc]# ./ipc_posix_named_sem_unlink

sem_unlink my_named_sem succ!

案例设计:使用信号灯加锁更新文件

u  生成一个文件,我们在里面写一个int,值为0,并初始化一个信号灯,信号数量为1

u  用一个使用信号灯加锁的进程,启动多份更新

u  用一个未使用信号灯加锁的进程,启动多份更新

u  检查加锁和不加锁更新的结果是否符合预期

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <errno.h>
#include <string.h>

sem_t* namedSem;
#define FILENAME "/tmp/count_named_sem.txt"
#define MUTEXNAME "my_named_sem"

int main(int argc, char** argv)
{
    int fd, inum, iloop;
    if (argc != 2)
    {
        printf("usage: ./ipc_posix_sem_countlock <loopnum>\n");
        return 0;
    }
    namedSem = sem_open(MUTEXNAME, O_CREAT|O_EXCL, 0644, 1);
    if (SEM_FAILED == namedSem)
    {
        if (errno != EEXIST)
        {
            printf("sem_open error : %s\n", strerror(errno));
            return -1;
        }
        printf("sem_open %s exist! so open\n", MUTEXNAME);
        namedSem = sem_open(MUTEXNAME, O_RDWR);
    }
    printf("sem_open succ\n", MUTEXNAME);

    iloop = atoi(argv[1]);
    fd = open(FILENAME, O_RDWR);
    for (int i = 0; i < iloop; ++i)
    {
        sem_wait(namedSem);
        lseek(fd, 0, SEEK_SET);
        read(fd, &inum, sizeof(int));
        usleep(1000);
        inum++;
        lseek(fd, 0, SEEK_SET);
        write(fd, &inum, sizeof(int));
        sem_post(namedSem);
    }
    printf("pid %d countlock down\n", getpid());
    return 0;
}

结果说明

[[email protected] ipc]# od -i /tmp/count_named_sem.txt

0000000           0

0000004

[[email protected] ipc]# ./ipc_posix_named_sem_countlock 1000 &

[[email protected] ipc]# ./ipc_posix_named_sem_countlock 1000 &

[[email protected] ipc]# ./ipc_posix_named_sem_countlock 1000 &

[[email protected] ipc]# ./ipc_posix_named_sem_countlock 1000 &

pid 115794 countlock down

pid 115795 countlock down

pid 115796 countlock down

pid 115797 countlock down

[[email protected] ipc]# od -i /tmp/count_named_sem.txt

0000000        4000

0000004

运行不加锁版本(去掉上面代码中的sem_wait和sem_post)

[[email protected] ipc]# od -i /tmp/count_named_sem.txt

0000000           0

0000004

[[email protected] ipc]# ./ipc_countlock_without_sem 1000 &

[[email protected]et ipc]# ./ipc_countlock_without_sem 1000 &

[[email protected] ipc]# ./ipc_countlock_without_sem 1000 &

[[email protected] ipc]# ./ipc_countlock_without_sem 1000 &

pid 116351 countlock down

pid 116352 countlock down

pid 116353 countlock down

pid 116354 countlock down

[[email protected] ipc]# od -i /tmp/count_named_sem.txt

0000000        2245  没加锁,这里的结果是不正确的

0000004

POSIX基于内存的信号灯

POSIX基于内存的信号灯的sem_wait和sem_post和POSIX有名信号灯是同一个实现,唯一不同在于构造和析构是在内存中进行的,而不是基于文件系统的某个路径名。

在sem_init函数中,如果shared为0,那么待初始化的信号灯是在同一进程的各个线程间共享的,否则该信号灯是在进程间共享的。当shared非0时,该信号灯必须存放在即将使用他的所有进程都能访问的某种类型的共享内存区中。

基于内存的信号灯的持续性由它所在的内存持续性决定。

函数说明

// 初始化一个信号量

int sem_init(sem_t *sem, int pshared, unsigned int value);

// 释放信号量

int sem_destory(sem_t *sem)

基于内存的信号灯的使用(线程之间进行同步)
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h> 

sem_t binSem;

void* helloWorld(void* arg)
{
    while(1)
    {
        // Wait semaphore
        sem_wait(&binSem);
        printf("Hello World\n");
     }
}

int main(int argc, char** argv)
{
    // Result for System call
    int res = 0;

    // Initialize semaphore
    sem_init(&binSem, 0, 0);

    // Create thread
    pthread_t thdHelloWorld;
    pthread_create(&thdHelloWorld, NULL, helloWorld, NULL);

    while(1)
    {
        // Post semaphore
        sem_post(&binSem);
        printf("In main, sleep several seconds.\n");
        sleep(1);
     }

    // Wait for thread synchronization
    void *threadResult;
    pthread_join(thdHelloWorld, &threadResult);

    return 0;
}

结果说明

[[email protected] ipc]# g++ -g -o ipc_posix_sem_thread ipc_posix_sem_thread.cpp -lrt

[[email protected] ipc]# ./ipc_posix_sem_thread

In main, sleep several seconds.

Hello World

In main, sleep several seconds.

Hello World

In main, sleep several seconds.

Hello World

In main, sleep several seconds.

基于内存的信号灯的使用(进程之间进行同步,使用共享内存存放信号灯)

# 创建

#include <sys/mman.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <pthread.h>
#include <semaphore.h> 

int main(int argc, char** argv)
{
    sem_t* shm_sem;

    const char* name = "/dev/shm/my_systemv_shm1";
    key_t key = ftok(name,0);
    if (key == -1)
    {
        perror("ftok error");
        return -1;
    }
    int shm_id=shmget(key, sizeof(sem_t), IPC_CREAT);
    if(shm_id == -1)
    {
        perror("shmget error");
        return -1;
    }
    shm_sem=(sem_t*)shmat(shm_id,NULL,0);

    // Initialize semaphore
    sem_init(shm_sem, 1, 0); // pshared = 1
    return 0;
}

结果说明

[[email protected] ipc]# ipcs

------ Shared Memory Segments --------

key        shmid      owner      perms      bytes      nattch     status

0x00000000 0          gdm        600        393216     2          dest

0x00000000 32769      gdm        600        393216     2          dest

0x00000000 65538      gdm        600        393216     2          dest

0x00000000 98307      gdm        600        393216     2          dest

[[email protected] ipc]# g++ -g -o ipc_posix_sem_mmap_create ipc_posix_sem_mmap_create.cpp –lrt

[[email protected] ipc]# ./ipc_posix_sem_mmap_create

[[email protected] ipc]# ipcs

------ Shared Memory Segments --------

key        shmid      owner      perms      bytes      nattch     status

0x00000000 0          gdm        600        393216     2          dest

0x00000000 32769      gdm        600        393216     2          dest

0x00000000 65538      gdm        600        393216     2          dest

0x00000000 98307      gdm        600        393216     2          dest

0x00108d43 229380     root       0          32         0

这里看到已经创建成功共享内存并初始化了信号灯。

# 信号灯V操作

#include <sys/mman.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <pthread.h>
#include <semaphore.h> 

int main(int argc, char** argv)
{
    sem_t* shm_sem;

    const char* name = "/dev/shm/my_systemv_shm1";
    key_t key = ftok(name,0);
    if (key == -1)
    {
        perror("ftok error");
        return -1;
    }
    int shm_id=shmget(key, sizeof(sem_t), IPC_CREAT);
    if(shm_id == -1)
    {
        perror("shmget error");
        return -1;
    }
    shm_sem=(sem_t*)shmat(shm_id,NULL,0);

    while(1)
    {
        // Post semaphore
        sem_post(shm_sem);
        printf("In main, sleep several seconds.\n");
        sleep(1);
    }

    return 0;
}

# 信号灯P操作

#include <sys/mman.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <pthread.h>
#include <semaphore.h> 

int main(int argc, char** argv)
{
    sem_t* shm_sem;

    const char* name = "/dev/shm/my_systemv_shm1";
    key_t key = ftok(name,0);
    if (key == -1)
    {
        perror("ftok error");
        return -1;
    }
    int shm_id=shmget(key, sizeof(sem_t), IPC_CREAT);
    if(shm_id == -1)
    {
        perror("shmget error");
        return -1;
    }
    shm_sem=(sem_t*)shmat(shm_id,NULL,0);

    int semvalue;
    sem_getvalue(shm_sem, &semvalue);
    printf("current sem value = %d\n", semvalue);

    while(1)
    {
        // Wait semaphore
        sem_wait(shm_sem);
        printf("Hello World\n");
    }

    return 0;
}

结果说明

writer先跑起来

[[email protected] ipc]# ./ipc_posix_sem_mmap_writer

In main, sleep several seconds.

In main, sleep several seconds.

In main, sleep several seconds.

In main, sleep several seconds.

In main, sleep several seconds.

In main, sleep several seconds.

In main, sleep several seconds.

In main, sleep several seconds.

In main, sleep several seconds.

在另一个终端启动reader

[[email protected] ipc]# ./ipc_posix_sem_mmap_reader

current sem value = 7

Hello World

Hello World

Hello World

Hello World

Hello World

Hello World

Hello World

System V信号灯

函数说明

#include <sys/types.h>

#include <sys/ipc.h>

#include <sys/sem.h>

// 创建一个新的信号量或是获得一个已存在的信号量键值

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

key:所创建或打开信号量集的键值。需要是唯一的非零整数。

nsems:创建的信号量集中的信号量的个数,该参数只在创建信号量集时有效。

flag:调用函数的操作类型,也可用于设置信号量集的访问权限。

// 用来改变信号量的值(包含P操作和V操作)

struct  sembuf{
    unsigned short sem_num;  /* semaphore number *//*信号灯在信号灯集中的编号*/
    short          sem_op;   /* semaphore operation *//*P操作或者V操作*/
    short          sem_flg;  /* operation flags */
};

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

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

sem_num:是相对应的信号量集中的某一个资源,所以其值是一个从0到相应的信号量集的资源总数(ipc_perm.sem_nsems)之间的整数。除非使用一组信号灯了,否则它的取值一般为0。

sem_op:是信号量在一次操作中需要改变的数值。通常只会用到两个值:-1---P操作,1---V操作。

sem_flg:说明函数semop的行为。通常被设置为SEM_UNDO。它将使得操作系统跟着当前进程对这个信号量的修改情况,如果这个进程在没有释放该信号量的情况下终止,操作系统将自动释放该进程持有的信号量。用一个通俗的说法:IPC_UNDO标志保证进程终止后,它对信号量的修改都撤销,好像它从来没有操作过信号量一样。这个标志要特别注意,使用不当容易造成一些诡异的问题。

这里需要强调的是semop同时操作多个信号灯,在实际应用中,对应多种资源的申请或释放。semop保证操作的原子性,这一点尤为重要。尤其对于多种资源的申请来说,要么一次性获得所有资源,要么放弃申请,要么在不占有任何资源情况下继续等待,这样,一方面避免了资源的浪费;另一方面,避免了进程之间由于申请共享资源造成死锁。关于这一点,可以参考http://www.cnblogs.com/linuxbug/p/4840148.html里面的银行家算法,semop就是银行家算法的一个实现。

也许从实际含义上更好理解这些操作:信号灯的当前值记录相应资源目前可用数目;sem_op > 0对应相应进程要释放sem_op数目的共享资源;sem_op=0可以用于对共享资源是否已用完的测试;sem_op<0相当于进程要申请-sem_op个共享资源。再联想操作的原子性,更不难理解该系统调用何时正常返回,何时睡眠等待。

// 允许信号量信息的直接控制(包含初始化信号灯和删除信号灯)

// 这个联合体需要在程序声明,用于semctl函数的SETVAL选项的传值,作为第四个参数
union semun {
    int val;
    struct semid_ds *buf;
    unsigned short *array;
}

int semctl(int semid, int semnum, int cmd, …/*union semun arg*/);


IPC_STAT


获取信号灯信息,信息由arg.buf返回;


IPC_SET


设置信号灯信息,待设置信息保存在arg.buf中(在manpage中给出了可以设置哪些信息);


GETALL


返回所有信号灯的值,结果保存在arg.array中,参数sennum被忽略;


GETNCNT


返回等待semnum所代表信号灯的值增加的进程数,相当于目前有多少进程在等待semnum代表的信号灯所代表的共享资源;


GETPID


返回最后一个对semnum所代表信号灯执行semop操作的进程ID;


GETVAL


返回semnum所代表信号灯的值;


GETZCNT


返回等待semnum所代表信号灯的值变成0的进程数;


SETALL


通过arg.array更新所有信号灯的值;同时,更新与本信号集相关的semid_ds结构的sem_ctime成员;


SETVAL


设置semnum所代表信号灯的值为arg.val;

信号灯创建并获取状态

#include <sys/mman.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <pthread.h>
#include <sys/sem.h>

union semun
{
    int              val;    /* Value for SETVAL */
    struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
    unsigned short  *array;  /* Array for GETALL, SETALL */
    struct seminfo  *__buf;
};  

int main(int argc, char** argv)
{
    const char* name = "/dev/shm/my_systemv_shm2";
    union semun un;

    key_t key = ftok(name,0);
    if (key == -1)
    {
        perror("ftok error");
        return -1;
    }
    int semid = semget(key, 1, 0666|IPC_CREAT|IPC_EXCL);
    if (-1 == semid)
    {
        if (errno != EEXIST)
        {
            printf("semget error: %s\n", strerror(errno));
            return -1;
        }
        semid = semget(key, 0, 0666);
        printf("semget get succ\n");
    }
    else
    {
        printf("semget create succ\n");
        un.val = 1;
        if (semctl(semid, 0, SETVAL, un) == -1)
        {
            printf("semctl error: %s\n", strerror(errno));
            return -1;
        }
    }
    struct semid_ds buf;
    un.buf = &buf;
    int ret = semctl(semid, 0, IPC_STAT, un);
    if (-1 == ret)
    {
        printf("semctl error: %s\n", strerror(errno));
        return -1;
    }
    printf("semid = %d, semvalue = %d\n", semid, un.buf->sem_nsems);

    return 0;
}

结果说明

[[email protected] ipc]# g++ -g -o ipc_systemv_sem_mmap_create ipc_systemv_sem_mmap_create.cpp

[[email protected] ipc]# ./ipc_systemv_sem_mmap_create

semget create succ

semid = 131073, semvalue = 1

[[email protected] ipc]# ipcs

------ Shared Memory Segments --------

key        shmid      owner      perms      bytes      nattch     status

0x00000000 0          gdm        600        393216     2          dest

0x00000000 32769      gdm        600        393216     2          dest

0x00000000 65538      gdm        600        393216     2          dest

0x00000000 98307      gdm        600        393216     2          dest

------ Semaphore Arrays --------

key        semid      owner      perms      nsems

0x00000000 0          root       600        1

0x00108f11 131073     root       666        1

信号灯删除

#include <sys/mman.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <pthread.h>
#include <sys/sem.h>

union semun
{
    int              val;    /* Value for SETVAL */
    struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
    unsigned short  *array;  /* Array for GETALL, SETALL */
    struct seminfo  *__buf;
};  

int main(int argc, char** argv)
{
    const char* name = "/dev/shm/my_systemv_shm2";
    key_t key = ftok(name,0);
    if (key == -1)
    {
        perror("ftok error");
        return -1;
    }
    int semid = semget(key, 1, 0666|IPC_CREAT|IPC_EXCL);
    if (-1 == semid)
    {
        if (errno != EEXIST)
        {
            printf("semget error: %s\n", strerror(errno));
            return -1;
        }
        semid = semget(key, 0, 0666);
        printf("semget get succ\n");
    }
    else
    {
        printf("semget create succ\n");
    }

    int ret = semctl(semid, 0, IPC_RMID);
    if (-1 == ret)
    {
        printf("semctl error: %s\n", strerror(errno));
        return -1;
    }
    printf("semid %d delete succ\n", semid);

    return 0;
}

结果说明

[[email protected] ipc]# g++ -g -o ipc_systemv_sem_mmap_delete ipc_systemv_sem_mmap_delete.cpp

[[email protected] ipc]# ipcs

------ Semaphore Arrays --------

key        semid      owner      perms      nsems

0x00000000 0          root       600        1

0x00108f11 131073     root       666        1

[[email protected] ipc]# ./ipc_systemv_sem_mmap_delete

semget get succ

semid 131073 delete succ

[[email protected] ipc]# ipcs

------ Semaphore Arrays --------

key        semid      owner      perms      nsems

0x00000000 0          root       600        1

可以看到,这里已经成功删除semid为131073的信号灯。

也可以使用ipcrm -s命令删除,ipcsrm -s semid。

信号灯V操作

#include <sys/mman.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <pthread.h>
#include <sys/sem.h>

union semun
{
    int              val;    /* Value for SETVAL */
    struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
    unsigned short  *array;  /* Array for GETALL, SETALL */
    struct seminfo  *__buf;
};  

int sem_p(int sem_id)
{
    struct sembuf sem_buf;
    sem_buf.sem_num = 0; // 信号量编号
    sem_buf.sem_op = -1; // P操作
    sem_buf.sem_flg = 0;
    //sem_buf.sem_flg = SEM_UNDO; // 系统退出前未释放信号量,系统自动释放
    if (semop(sem_id, &sem_buf, 1) == -1)
    {
        perror("Sem P operation");
        exit(1);
    }
    return 0;
}

int sem_v(int sem_id)
{
    struct sembuf sem_buf;
    sem_buf.sem_num = 0;
    sem_buf.sem_op = 1; // V操作
    sem_buf.sem_flg = 0;
    // sem_buf.sem_flg = SEM_UNDO;
    if (semop(sem_id, &sem_buf, 1) == -1)
    {
        perror("Sem V operation");
        exit(1);
    }
    return 0;
}

int main(int argc, char** argv)
{
    const char* name = "/dev/shm/my_systemv_shm2";
    key_t key = ftok(name,0);
    if (key == -1)
    {
        perror("ftok error");
        return -1;
    }
    int semid = semget(key, 1, 0666);
    if (-1 == semid)
    {
        printf("semget get error: %s\n", strerror(errno));
    }
    else
    {
        printf("semget create succ\n");
    }

    union semun un;
    printf("semid = %d, semvalue = %d\n", semid, semctl(semid, 0, GETVAL, 0));

    while(1)
    {
        // Post semaphore
        sem_v(semid);
        printf("In main, sleep several seconds.\n");
        sleep(1);
    }

    return 0;
}

结果说明

[[email protected] ipc]# ./ipc_systemv_sem_mmap_writer

semget create succ

semid = 294913, semvalue = 0

In main, sleep several seconds.

In main, sleep several seconds.

In main, sleep several seconds.

In main, sleep several seconds.

In main, sleep several seconds.

In main, sleep several seconds.

信号灯P操作

#include <sys/mman.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <pthread.h>
#include <sys/sem.h>

union semun
{
    int              val;    /* Value for SETVAL */
    struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
    unsigned short  *array;  /* Array for GETALL, SETALL */
    struct seminfo  *__buf;
};  

int sem_p(int sem_id)
{
    struct sembuf sem_buf;
    sem_buf.sem_num = 0; // 信号量编号
    sem_buf.sem_op = -1; // P操作
    sem_buf.sem_flg = 0;
    //sem_buf.sem_flg = SEM_UNDO; // 系统退出前未释放信号量,系统自动释放
    if (semop(sem_id, &sem_buf, 1) == -1)
    {
        perror("Sem P operation");
        exit(1);
    }
    return 0;
}

int sem_v(int sem_id)
{
    struct sembuf sem_buf;
    sem_buf.sem_num = 0;
    sem_buf.sem_op = 1; // V操作
    sem_buf.sem_flg = 0;
    //sem_buf.sem_flg = SEM_UNDO;
    if (semop(sem_id, &sem_buf, 1) == -1)
    {
        perror("Sem V operation");
        exit(1);
    }
    return 0;
}

int main(int argc, char** argv)
{
    const char* name = "/dev/shm/my_systemv_shm2";
    key_t key = ftok(name,0);
    if (key == -1)
    {
        perror("ftok error");
        return -1;
    }
    int semid = semget(key, 1, 0666);
    if (-1 == semid)
    {
        printf("semget get error: %s\n", strerror(errno));
    }
    else
    {
        printf("semget create succ\n");
    }

    union semun un;
    printf("semid = %d, semvalue = %d\n", semid, semctl(semid, 0, GETVAL, 0));

    while(1)
    {
        // Wait semaphore
        sem_p(semid);
        printf("Hello World\n");
        printf("semid = %d, semvalue = %d\n", semid, semctl(semid, 0, GETVAL, 0));
    }

    return 0;
}

结果说明

[[email protected] ipc]# ./ipc_systemv_sem_mmap_reader

semget create succ

semid = 294913, semvalue = 3

Hello World

semid = 294913, semvalue = 2

Hello World

semid = 294913, semvalue = 1

Hello World

semid = 294913, semvalue = 0

semvalue为0的时候,semop就会阻塞,直到另一个进程调用sem_v函数。

时间: 2024-10-10 23:22:34

Linux进程间通信(五) - 信号灯(史上最全)及其经典应用案例的相关文章

【转帖】史上最全PostgreSQL体系结构

史上最全PostgreSQL体系结构 2019年07月16日 18:00:00 Enmotech 阅读数 35 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/enmotech/article/details/96225598 墨墨导读:本文主要从日志文件.参数文件.控制文件.数据文件.redo日志(WAL).后台进程这六个方面来讨论PostgreSQL的结构. 7月16日晚20:30-2

.Net魔法堂:史上最全的ActiveX开发教程——ActiveX与JS间交互篇

一.前言 经过上几篇的学习,现在我们已经掌握了ActiveX的整个开发过程,但要发挥ActiveX的真正威力,必须依靠JS.下面一起来学习吧! 二.JS调用ActiveX方法 只需在UserControl子类中(即自定义的ActiveX控件中),编写公共方法即可. C# [Guid("0203DABD-51B8-4E8E-A1EB-156950EE1668")] public partial class Uploader : UserControl, IObjectSafety { p

[No00004F]史上最全Vim快捷键键位图(入门到进阶)

史上最全Vim快捷键键位重磅来袭!!学习Linux的朋友看过来啦,你是不是觉得Linux编辑器Vim操作复杂,步骤繁琐呢?Linux工程师是不是想大幅度提升自己的工作效率呢? 经典版        下面这个键位图应该是大家最常看见的经典版了. 对应的简体中文版. 其实经典版是一系列的入门教程键位图的组合结果,下面是不同编辑模式下的键位图. 1 2 3 4 5 6 7 入门版        基本操作的入门版.进阶版        增强版        下图是一个更新时间较新的现代版,含有的信息也更

史上最全PHP正则表达式实例汇总

收集了一份php正则表达式的实例教程,真心不错,记录下. 正则表达式用于字符串处理.表单验证等场合,实用高效. 一些常用的表达式: $str = preg_replace("/(<a.*?>)(.*?)(<\/a>)/", '\1<span class="link">\2</span>\3', $str); 其中用了三个子模式(每个圆括号中内容为一个子模式),第一个是链接开始标签,第二个是链接文本,第三个是</a

史上最全的hadoop

课程主要涉及Hadoop Sqoop.Flume.Avro重要子项目的技术实战 课程针对人群 1.本课程适合于有一定java基础知识,对数据库和sql语句有一定了解,熟练使用linux系统的技术人员,特别适合于想换工作或寻求高薪职业的人士 2.最好有Greenplum Hadoop.Hadoop2.0.YARN.Sqoop.FlumeAvro等大数据基础,学习过北风课程<Greenplum 分布式数据库开发入门到精通>.<全面深入Greenplum Hadoop大数据分析平台>.&

史上最全编程语言列表_你掌握了哪些?

摘要: 计算机编程语言可用于将指令传达给计算机.下面可能是史上最全编程语言列表,我将它们分为以下几类,你掌握了哪些? 计算机编程语言可用于将指令传达给计算机.下面可能是史上最全编程语言列表,我将它们分为以下几类,你掌握了哪些? 解释型编程语言 函数式编程语言 编译型编程语言 过程式编程语言 脚本编程语言 标记编程语言 基于逻辑的编程语言 并发编程语言 面向对象编程语言 解释型编程语言 解释型语言是这样一种编程语言,其大部分实现直接执行指令,而无需先将程序编译成机器语言指令.解释器直接执行程序,它

史上最全网站降权原因解析

在互联网时代飞速发展的前景下加上昂贵的竞价费用让多数企业苦不堪言于是低成本投入的职业-seo就诞生了,从业人数也每年呈递增状态,然而互联网的垃圾信息也越来越多,于是搜索引擎为了更好的迎合用户体验也频繁的展开了各项算法调整.在这期间当然也有很多网站被降权甚至被K掉,至于什么原因,有很多从职朋友还不能彻底的找到问题的根源,下面笔者以个人多年经验为大家展开一下分享,希望能帮助你早日恢复网站排名. 直接进入正题: 一.空间问题 因为空间访问速度不稳定,有时慢有时打不开的情况,因这个被降权的案列也是很多的

开源框架】Android之史上最全最简单最有用的第三方开源库收集整理,有助于快速开发

[原][开源框架]Android之史上最全最简单最有用的第三方开源库收集整理,有助于快速开发,欢迎各位... 时间 2015-01-05 10:08:18 我是程序猿,我为自己代言 原文  http://blog.csdn.net/caoyouxing/article/details/42418591 主题 开源 安卓开发 http://www.tuicool.com/articles/jyA3MrU Android开源库 自己一直很喜欢Android开发,就如博客签名一样, 我是程序猿,我为自

史上最全的Excel数据编辑处理技巧(转)

史上最全的数据编辑处理技巧,让你在日常数据分析处理的疯魔状态中解放出来. 一.隐藏行列 “不得了了,Excel出现灵异事件,部分区域消失不见了!”办公室里的一个MM跑过来大声喊叫着,着实吓了俺一跳.待退隐江湖已久的名侦探(就是小编了)来一探究竟.经过小编掐指一算,发现原来是行列被隐藏了,没见过世面的妹纸真是大惊小怪(当然,这只是内心的声音). 工 作表中可以对暂时不需要显示的数据进行隐藏.选中需隐藏的行或者列,右键——隐藏,就完成隐藏了.也可以用更快捷的方式,快捷键Ctrl+9隐藏行:快捷 键C