Linux進程間通信(五) - 信號燈(史上最全)及其經典應用案例


信號燈概述

什么是信號燈

信號燈用來實現同步,用於多線程,多進程之間同步共享資源(臨界資源)。

PV原語:信號燈使用PV原語

P原語操作的動作是:

u  sem1

u  sem1后仍大於或等於零,則進程繼續執行。

u  sem1后小於零,則該進程被阻塞后進入與該信號相對應的隊列中,然后轉進程調度。

V原語操作的動作是:

u  sem1

u  若相加結果大於零,則進程繼續執行。

u  若相加結果小於或等於零,則從該信號的等待隊列中喚醒一等待進程,然后再返回原進程繼續執行或轉進程調度。

信號燈分類

按信號燈實現原理,信號燈分兩種,一種是有名信號燈,一種是基於內存的信號燈。

有名信號燈,是根據外部名字標識,通常指代文件系統中的某個文件。而基於內存的信號燈,它主要是把信號燈放入內存的。

基於內存的信號燈,同步多線程時,可以放到該多線程所屬進程空間里;如果是同步多進程,那就需要把信號燈放入到共享內存中(方便多個進程訪問)。

 

按實現方式,信號燈分為POSIX信號燈和System V信號燈,System V信號燈是由內核維護的,Posix信號燈是由文件系統中的路徑名對應的名字來標識的。在目前的Linux中,System V使用更為廣泛,POSIX一般是在更老的系統中使用。

信號燈操作

進程在信號燈上的幾種操作:

1  創建一個信號燈。還要求調用者指定初始值,對二值來說通常是1

2  等待一個信號燈。測試信號燈的值,如果<=0則等待,否則將其減1。注:測試其值並減1必須作為一個原子操作。

3  掛出一個信號燈。將信號燈的值加1。掛出操作也必須是原子的。

4獲取信號燈狀態。

問題:如何將等待某個信號燈的所有進程排隊,如何喚醒這些可能很多的進程中的一個,所幸這些都是由實現來處理的。

二值信號燈可用於互斥目的。除了可以象互斥鎖那樣使用外,信號燈還有一個互斥鎖沒有提供的特性:互斥鎖必須總是由鎖住他的線程解鎖,信號燈的掛出卻不必由執行過它的等待操作的同一線程執行。比如生產者消費者問題是生產者和消費者互相喚醒的。

 

clip_image001

共享內存信號燈同時屬於兩個進程的地址空間。

信號燈有一個與之關聯的值,掛出一個信號即使當前沒有線程在等待該信號也沒關系,與之相反的是,pthread_cond_signal如果當時沒有任何線程阻塞在pthread_cond_wait中,則信號丟失。

POSIX信號燈

1POSIX有名信號燈和基於內存信號燈系統調用關系

clip_image002

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

結果說明

[root@rocket ipc]# g++ -g -o ipc_posix_named_sem_create ipc_posix_named_sem_create.cpp –lrt

[root@rocket ipc]# ./ipc_posix_named_sem_create

sem_open succ

[root@rocket 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;
}

結果說明

[root@rocket ipc]# g++ -g -o ipc_posix_named_sem_unlink ipc_posix_named_sem_unlink.cpp -lrt

[root@rocket ipc]# ./ipc_posix_named_sem_unlink

sem_open my_named_sem exist! so open

sem_unlink my_named_sem succ!

[root@rocket 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;
}

結果說明

[root@rocket ipc]# od -i /tmp/count_named_sem.txt

0000000           0

0000004

[root@rocket ipc]# ./ipc_posix_named_sem_countlock 1000 &

[root@rocket ipc]# ./ipc_posix_named_sem_countlock 1000 &

[root@rocket ipc]# ./ipc_posix_named_sem_countlock 1000 &

[root@rocket ipc]# ./ipc_posix_named_sem_countlock 1000 &

pid 115794 countlock down

pid 115795 countlock down

pid 115796 countlock down

pid 115797 countlock down

[root@rocket ipc]# od -i /tmp/count_named_sem.txt

0000000        4000

0000004

運行不加鎖版本(去掉上面代碼中的sem_waitsem_post

[root@rocket ipc]# od -i /tmp/count_named_sem.txt

0000000           0

0000004

[root@rocket ipc]# ./ipc_countlock_without_sem 1000 &

[root@rocket ipc]# ./ipc_countlock_without_sem 1000 &

[root@rocket ipc]# ./ipc_countlock_without_sem 1000 &

[root@rocket ipc]# ./ipc_countlock_without_sem 1000 &

pid 116351 countlock down

pid 116352 countlock down

pid 116353 countlock down

pid 116354 countlock down

[root@rocket ipc]# od -i /tmp/count_named_sem.txt

0000000        2245  沒加鎖,這里的結果是不正確的

0000004

POSIX基於內存的信號燈

POSIX基於內存的信號燈的sem_waitsem_postPOSIX有名信號燈是同一個實現,唯一不同在於構造和析構是在內存中進行的,而不是基於文件系統的某個路徑名。

sem_init函數中,如果shared0,那么待初始化的信號燈是在同一進程的各個線程間共享的,否則該信號燈是在進程間共享的。當shared0時,該信號燈必須存放在即將使用他的所有進程都能訪問的某種類型的共享內存區中。

基於內存的信號燈的持續性由它所在的內存持續性決定。

函數說明

// 初始化一個信號量

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

結果說明

[root@rocket ipc]# g++ -g -o ipc_posix_sem_thread ipc_posix_sem_thread.cpp -lrt

[root@rocket 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;
}

結果說明

[root@rocket 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

[root@rocket ipc]# g++ -g -o ipc_posix_sem_mmap_create ipc_posix_sem_mmap_create.cpp –lrt

[root@rocket ipc]# ./ipc_posix_sem_mmap_create

[root@rocket 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先跑起來

[root@rocket 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

[root@rocket 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;
}

結果說明

[root@rocket ipc]# g++ -g -o ipc_systemv_sem_mmap_create ipc_systemv_sem_mmap_create.cpp

[root@rocket ipc]# ./ipc_systemv_sem_mmap_create

semget create succ

semid = 131073, semvalue = 1

[root@rocket 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;
}

結果說明

[root@rocket ipc]# g++ -g -o ipc_systemv_sem_mmap_delete ipc_systemv_sem_mmap_delete.cpp

[root@rocket ipc]# ipcs

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

key        semid      owner      perms      nsems    

0x00000000 0          root       600        1        

0x00108f11 131073     root       666        1 

[root@rocket ipc]# ./ipc_systemv_sem_mmap_delete

semget get succ

semid 131073 delete succ

[root@rocket ipc]# ipcs

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

key        semid      owner      perms      nsems    

0x00000000 0          root       600        1 

可以看到,這里已經成功刪除semid131073的信號燈。

也可以使用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;
}

結果說明

[root@rocket 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;
}

結果說明

[root@rocket 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

semvalue0的時候,semop就會阻塞,直到另一個進程調用sem_v函數。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM