信號燈概述
什么是信號燈
信號燈用來實現同步,用於多線程,多進程之間同步共享資源(臨界資源)。
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; }
結果說明
[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_wait和sem_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_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; }
結果說明
[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
可以看到,這里已經成功刪除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; }
結果說明
[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
semvalue為0的時候,semop就會阻塞,直到另一個進程調用sem_v函數。


