【Linux系统编程】进程同步与互斥:System V 信号量
信號量概述
信號量廣泛用于進程或線程間的同步和互斥,信號量本質上是一個非負的整數計數器,它被用來控制對公共資源的訪問。
編程時可根據操作信號量值的結果判斷是否對公共資源具有訪問的權限,當信號量值大于 0 時,則可以訪問,否則將阻塞。PV 原語是對信號量的操作,一次 P 操作使信號量減1,一次 V 操作使信號量加1。
在實際應用中兩個進程間通信可能會使用多個信號量,因此?System V?的信號量以集合的概念來管理,具體操作和?Posix 信號量大同小異
信號量集合數據結構:struct semid_ds,此數據結構中定義了整個信號量集的基本屬性。
/* Obsolete, used only for backwards compatibility and libc5 compiles */ struct semid_ds {struct ipc_perm sem_perm; /* permissions .. see ipc.h */__kernel_time_t sem_otime; /* last semop time */__kernel_time_t sem_ctime; /* last change time */struct sem *sem_base; /* ptr to first semaphore in array */struct sem_queue *sem_pending; /* pending operations to be processed */struct sem_queue **sem_pending_last; /* last pending operation */struct sem_undo *undo; /* undo requests on this array */unsigned short sem_nsems; /* no. of semaphores in array */ };
信號量數據結構:struct sem,此數據結構中定義了信號量的基本屬性。
/* One semaphore structure for each semaphore in the system. */ struct sem {int semval; /* current value *信號量的值*/int sempid; /* pid of last operation *最后一個操作信號量的進程號*/struct list_head sem_pending; /* pending single-sop operations */ };
System V 信號量基本操作
使用 shell 命令操作信號量:
查看信號量:ipcs -s
刪除信號量:ipcrm -s?semid
以下函數所需頭文件如下:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
1)創建信號量集合
int semget(key_t key, int nsems, int semflg);
功能:
創建或打開一個信號量集合,該集合中可以包含多個信號量。
參數:
key:進程間通信鍵值,通過調用 ftok() 函數得到的鍵值
nsems:創建的信號量的個數。如果只是訪問而不創建則可以指定該參數為 0,一旦創建了該信號量,就不能更改其信號量個數,只要不刪除該信號量,重新調用該函數創建該鍵值的信號量,該函數只是返回以前創建的值,不會重新創建。
semflg:標識函數的行為及信號量的權限,其取值如下:
IPC_CREAT:創建信號量。
IPC_EXCL:檢測信號量是否存在。
位或權限位:信號量位或權限位后可以設置信號量的訪問權限,格式和 open 函數的 mode_ t 一樣(open() 的使用請點此鏈接),但可執行權限未使用。
返回值:
成功:信號量集標識符
失敗:返回 -1
2)控制信號量集合、信號量
int semctl(int semid, int semnum, int cmd, ...);
功能:
對信號量集合以及集合中的信號量進行操作。
參數:
semid:信號量集標識符。
semnum:集合中信號量的序號,指定對哪個信號量操作, 只對幾個特殊的 cmd?操作有意義。
cmd:信號量控制類型。semctl()?函數可能有3個參數,也可能有4個參數,參數的個數由 cmd 決定。當有4個參數時,第4個參數為聯合體:
union semun{int val; /*信號量的值*/struct semid_ds *buf; /*信號量集合信息*/unsigned short *array;/*信號量值的數組*/struct seminfo *__buf;/*信號量限制信息*/ };
cmd 的取值如下:
GETVAL:獲取信號量的值。此時函數有3個參數。semctl() 函數的返回值即為信號量的值。
SETVAL:設置信號量的值。此時函數有4個參數。第4個參數為聯合體中的val,其值為信號量的值。?
IPC_STAT:獲取信號量集合的信息。此時函數有4個參數。第4個參數為聯合體中的__buf。
IPC_SET:設置信號量集合的信息。此時函數有4個參數。第4個參數為聯合體中的__buf。
IPC_RMID:刪除信號量集。此時函數有3個參數,第2個參數semnum不起作用。
GETALL:獲取所有信號量的值。此時函數有4個參數,第2個參數semnum不起作用。第4個參數為聯合體中的array,其值為用來存放所有信號量值的數組的首地址。
SETALL:設置所有信號量的值 。參數說明同上。
IPC_INFO:獲取信號量集合的限制信息。此時函數有4個參數,第2個參數semnum不起作用。第4個參數為聯合體中的__buf。
GETPID:獲取信號的進程號,即最后操作信號量的進程。此時函數有3個參數。semctl() 函數的返回值即為信號的進程號。
GETNCNT:獲取等待信號的值遞增的進程數。此時函數有3個參數。semctl() 函數的返回值即為進程數。
GETZCNT:獲取等待信號的值遞減的進程數。此時函數有3個參數。semctl() 函數的返回值即為進程數。
返回值:
成功:0
失敗:-1
3)操作信號量
int semop(int semid, struct sembuf *sops, unsigned nsops);
功能:
操作信號量,主要進行信號量加減操作。
參數:
semid:信號量集標識符。
sops:操作信號量的結構體(struct sembuf)數組的首地址(?結構體定義在 sys/sem.h?),此結構體中的數據表明了對信號量進行的操作。
struct sembuf{unsigned short sem_num; /*信號量的序號*/short sem_op; /*信號量的操作值*/short sem_flg; /*信號量的操作標識*/ };
結構體成員使用說明如下:
sem_num:信號量集中信號量的序號
sem_op?取值如下:
sem_op > 0:信號量的值在原來的基礎上加上此值。
sem_op < 0:如果信號量的值小于 semop 的絕對值,則掛起操作進程。如果信號量的值大于等于 semop 的絕對值,則信號量的值在原來的基礎上減去 semop 的絕對值。
sem_op = 0:對信號量的值進行是否為 0 測試。若為 0 則函數立即返回,若不為 0 則阻塞調用進程。
sem_flag?取值如下:?
IPC_NOWAIT:在對信號量的操作不能執行的情況下使函數立即返回。
SEM_UNDO:當進程退出后,該進程對信號量進行的操作將被撤銷。
nsops:操作信號量的結構體數組中元素的個數。返回值:
成功:0
失敗:-1
使用示例
示例一:
#include <sys/types.h> #include <sys/sem.h> #include <sys/ipc.h> #include <stdlib.h> #include <stdio.h>int main(int argc, char *argv[]) {key_t key;//創建key值key = ftok(".", 'a');if(key == -1){perror("ftok");}//查看信號量system("ipcs -s");int semid;//1: 創建的信號量的個數semid = semget(key, 1, IPC_CREAT|0666); //創建信號量if(semid == -1){perror("semget");}system("ipcs -s"); //查看信號量//刪去信號量// 0: 代表對第0個信號量進行操作// IPC_RMID:刪除信號量集semctl(semid, 0, IPC_RMID);system("ipcs -s"); //查看信號量return 0; }
示例二:
#include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> #include <stdlib.h> #include <stdio.h>/*解決編譯出錯的問題*/ #define IPC_INFO 3int main(int argc, char *argv[]) {key_t key;//創建key值key = ftok(".", 'a');if(key == -1){perror("ftok");}system("ipcs -s"); //查看信號量int semid;//1: 創建的信號量的個數semid = semget(key, 1, IPC_CREAT|0666);//創建信號量if(semid == -1){perror("semget");}system("ipcs -s"); //查看信號量struct seminfo buf;/*//struct seminfo相關成員struct seminfo {int semmap;int semmni;int semmns;int semmnu;int semmsl;int semopm;int semume;int semusz;int semvmx;int semaem;};*///IPC_INFO:獲取信號量集合的限制信息。//此時函數有4個參數,第2個參數semnum不起作用。semctl(semid, 0, IPC_INFO, &buf);printf("buf.semmni = %d\n", buf.semmni);printf("buf.semmns = %d\n", buf.semmns);printf("buf.semmnu = %d\n", buf.semmnu);printf("buf.semmsl = %d\n", buf.semmsl);printf("buf.semopm = %d\n", buf.semopm);printf("buf.semume = %d\n", buf.semume);printf("buf.semusz = %d\n", buf.semusz);printf("buf.semvmx = %d\n", buf.semvmx);printf("buf.semaem = %d\n", buf.semaem);//刪去信號量// 0: 代表對第0個信號量進行操作// IPC_RMID:刪除信號量集semctl(semid, 0, IPC_RMID);system("ipcs -s"); //查看信號量return 0; }
運行結果如下:
示例三:
#include <sys/types.h> #include <sys/sem.h> #include <sys/ipc.h> #include <stdlib.h> #include <stdio.h>int main(int argc, char *argv[]) {key_t key;//創建key值key = ftok(".", 'a');if(key == -1){perror("ftok");}//查看信號量system("ipcs -s");int semid;//1: 創建的信號量的個數semid = semget(key, 1, IPC_CREAT|0666); //創建信號量if(semid == -1){perror("semget");}system("ipcs -s"); //查看信號量int ret;/*//SETVAL: 設置信號量的值。此時函數有4個參數。第4個參數為聯合體中的val,其值為信號量的值。 union semun{int val; //信號量的值struct semid_ds *buf; //信號量集合信息unsigned short *array; //信號量值的數組struct seminfo *__buf; //信號量限制信息};*/ret = semctl(semid, 0, SETVAL, 20);if(ret == -1){perror("semctl");}//GETVAL:獲取信號量的值。函數返回值即為信號量的值。ret = semctl(semid, 0, GETVAL);if(ret == -1){perror("semctl");}printf("ret = %d\n", ret);// 0: 代表對第0個信號量進行操作// IPC_RMID:刪除信號量集semctl(semid, 0, IPC_RMID);system("ipcs -s");return 0; }
運行結果如下:
示例四:
#include <sys/types.h> #include <sys/sem.h> #include <sys/ipc.h> #include <stdlib.h> #include <stdio.h> #include <string.h>int main(int argc, char *argv[]) {key_t key;//創建key值key = ftok(".", 'a');if(key == -1){perror("ftok");}//查看信號量system("ipcs -s");int semid;//2: 創建的信號量的個數semid = semget(key, 2, IPC_CREAT|0666); //創建信號量if(semid == -1){perror("semget");}system("ipcs -s"); //查看信號量int ret;unsigned short sem_arry[2] = {30,20};/*//SETALL: 設置所有信號量的值。此時函數有4個參數,第2個參數semnum不起作用。第4個參數為聯合體中的array,其值為用來存放所有信號量值的數組的首地址。union semun{int val; //信號量的值struct semid_ds *buf; //信號量集合信息unsigned short *array; //信號量值的數組struct seminfo *__buf; //信號量限制信息};*/ret = semctl(semid, 0, SETALL, sem_arry);if(ret == -1){perror("semctl");}bzero(sem_arry, sizeof(sem_arry));//GETALL:獲取所有信號量的值。此時函數有4個參數,第2個參數semnum不起作用。第4個參數為聯合體中的array,其值為用來存放所有信號量值的數組的首地址。ret = semctl(semid, 0, GETALL, sem_arry);if(ret == -1){perror("semctl");}printf("sem_arry[0] = %d\n", sem_arry[0]);printf("sem_arry[1] = %d\n", sem_arry[1]);// IPC_RMID:刪除信號量集semctl(semid, 0, IPC_RMID);system("ipcs -s");return 0; }
運行結果如下:
示例五:
//此范例使用信號量來同步共享內存的操作 #include <stdio.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> #include <sys/shm.h> #include <unistd.h> #include <sys/wait.h>#define SHM_KEY 0x33 #define SEM_KEY 0x44 union semun { int val; struct semid_ds *buf; unsigned short *array; }; int P(int semid) { struct sembuf sb;/*//操作信號量的結構體struct sembuf{unsigned short sem_num;//信號量的序號short sem_op; //信號量的操作值short sem_flg; //信號量的操作標識}; */sb.sem_num = 0; sb.sem_op = -1;//SEM_UNDO:當進程退出后,該進程對信號量進行的操作將被撤銷。sb.sem_flg = SEM_UNDO; //操作1個信號量if(semop(semid, &sb, 1) == -1){ perror("semop"); return -1; } return 0; } int V(int semid) { struct sembuf sb;/*//操作信號量的結構體struct sembuf{unsigned short sem_num;//信號量的序號short sem_op; //信號量的操作值short sem_flg; //信號量的操作標識}; */sb.sem_num = 0; sb.sem_op = 1;//SEM_UNDO:當進程退出后,該進程對信號量進行的操作將被撤銷。sb.sem_flg = SEM_UNDO; //操作1個信號量if(semop(semid, &sb, 1) == -1){ perror("semop"); return -1; } return 0; } int main(int argc, char **argv) {pid_t pid; int i, shmid, semid; int *ptr = NULL; union semun semopts; /*union semun{int val; //信號量的值struct semid_ds *buf; //信號量集合信息unsigned short *array; //信號量值的數組struct seminfo *__buf; //信號量限制信息};*///創建一塊共享內存, 存一個int變量if ((shmid = shmget(SHM_KEY, sizeof(int), IPC_CREAT | 0600)) == -1) { perror("msgget"); return -1;} //將共享內存映射到進程, fork后子進程可以繼承映射ptr = (int *)shmat(shmid, NULL, 0);if (ptr == (int *)-1) { perror("shmat");return -1;}*ptr = 0; //賦值為0// 創建一個信號量用來同步共享內存的操作 if ((semid = semget(SEM_KEY, 1, IPC_CREAT | 0600)) == -1) { perror("semget"); return -1;} //初始化信號量 semopts.val = 1; if (semctl(semid, 0, SETVAL, semopts) < 0) { perror("semctl"); return -1;} if ((pid = fork()) < 0) { //創建進程perror("fork");_exit(0);}else if (pid == 0){ // Child// 子進程對共享內存加1 for (i = 0; i < 100000; i++) { P(semid); (*ptr)++; V(semid); printf("child: %d\n", *ptr); } } else { //Parent// 父進程對共享內存減1 for (i = 0; i < 100000; i++) { P(semid); (*ptr)--; V(semid); printf("parent: %d\n", *ptr); } //如果子進程結束,回收其資源wait(NULL);//如果同步成功, 共享內存的值為0 printf("finally: %d\n", *ptr); } return 0; }
運行結果如下:
總結
以上是生活随笔為你收集整理的【Linux系统编程】进程同步与互斥:System V 信号量的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【Linux系统编程】进程同步与互斥:P
- 下一篇: 【Linux系统编程】Linux 进程调