发布时间:2023-11-21 12:00
(1)PV模块
sem_pv.h
1 #ifndef INCLUDE_SEM_PV_H_ 2 #define INCLUDE_SEM_PV_H_ 3 4 #include5 #include 6 #include 7 #include 8 #include 9 10 union semun { 11 int val; 12 struct semid_ds *buf; 13 unsigned short *array; 14 }; 15 16 /** 初始化 semnums 个信号灯/信号量值(value) */ 17 extern int sem_I(int semnums, int value); 18 19 /** 对信号量集(semid)中的信号灯(semnum)作 P() */ 20 extern void sem_P(int semid, int semnum, int value); 21 22 /** 对信号集(semid) 中的信号灯(semnum)作V(value)操作 */ 23 extern void sem_V(int semid, int semnum, int value); 24 25 /** 销毁信号量集(semid) */ 26 extern void sem_D(int semid); 27 28 #endif /* INCLUDE_SEM_PV_H_ */
sem_pv.c
1 #include \"sem_pv.h\" 2 3 /** 初始化 semnums 个信号灯/信号量值(value) */ 4 int sem_I(int semnums, int value) 5 { 6 /** 创建信号量集 */ 7 int semid; 8 /** 创建信号量集 */ 9 semid = semget(IPC_PRIVATE, semnums, IPC_CREAT | IPC_EXCL | 0777); 10 if(semid < 0){ 11 return -1; 12 } 13 14 union semun un; 15 unsigned short *array = (unsigned short *)calloc(semnums, sizeof(unsigned short)); 16 int i; 17 for(i = 0; i < semnums; i++){ 18 array[i] = value; 19 } 20 un.array = array; 21 22 /** 23 * 初始化信号量集中所有信号灯的初值 24 * 0: 表示要初始化所有的信号灯 25 */ 26 if(semctl(semid, 0, SETALL, un) < 0){ 27 perror(\"semctl error\"); 28 return -1; 29 } 30 free(array); 31 return semid; 32 } 33 34 /** 对信号量集(semid)中的信号灯(semnum)作 P() */ 35 void sem_P(int semid, int semnum, int value) 36 { 37 assert(value >= 0); 38 39 /** 定义 sembuf 类型的结构体数组,放置若干个结构体变量,对应要操作的信号量、P或V操作 */ 40 struct sembuf ops[] = {{semnum, -value, SEM_UNDO}}; 41 if(semop(semid, ops, sizeof(ops)/sizeof(struct sembuf)) < 0){ 42 perror(\"semop error\"); 43 } 44 } 45 46 /** 对信号集(semid) 中的信号灯(semnum)作V(value)操作 */ 47 void sem_V(int semid, int semnum, int value) 48 { 49 assert(value >= 0); 50 51 /** 定义 sembuf 类型的结构体数组,放置若干个结构体变量,对应要操作的信号量、P或V操作 */ 52 struct sembuf ops[] = {{semnum, value, SEM_UNDO}}; 53 if(semop(semid, ops, sizeof(ops)/sizeof(struct sembuf)) < 0){ 54 perror(\"semop error\"); 55 } 56 } 57 58 /** 销毁信号量集(semid) */ 59 void sem_D(int semid) 60 { 61 if(semctl(semid, 0, IPC_RMID, NULL) < 0){ 62 perror(\"semctl error\"); 63 } 64 }
编译:
gcc -o obj/sem_pv.o -Iinclude -c src/sem_pv.c
(2)互斥操作
atm_account.h
1 #ifndef INCLUDE_ATM_ACCOUNT_H_ 2 #define INCLUDE_ATM_ACCOUNT_H_ 3 4 #include5 #include 6 #include 7 8 9 typedef struct { 10 int code; 11 double balance; 12 int semid; ///< 在共享资源上绑定一个信号量集 13 }atm_account; 14 15 /** 取款 */ 16 extern double atm_account_withdraw(atm_account *a, double amt); 17 18 /** 存款 */ 19 extern double atm_account_deposit(atm_account *a, double amt); 20 21 /** 查看账户余额度 */ 22 extern double amt_account_balanceGet(atm_account *a); 23 24 #endif /* INCLUDE_ATM_ACCOUNT_H_ */
atm_account.c
1 #include \"sem_pv.h\" 2 #include \"atm_account.h\" 3 4 /** 取款 */ 5 double atm_account_withdraw(atm_account *a, double amt) 6 { 7 assert(a != NULL); 8 9 /** 对信号量集 semid 中的0号信号量/信号灯作 P(1) 操作 */ 10 sem_P(a->semid, 0, 1); 11 if(amt < 0 || amt > a->balance){ 12 /** 对信号量集 semid 中的0号信号量/信号灯作 V(1) 操作 */ 13 sem_V(a->semid, 0, 1); 14 return 0.0; 15 } 16 17 double balance = a->balance; 18 sleep(1); 19 balance -= amt; 20 a->balance = balance; 21 /** 对信号量集 semid 中的0号信号量/信号灯作 V(1) 操作 */ 22 sem_V(a->semid, 0, 1); 23 return amt; 24 } 25 26 /** 存款 */ 27 double atm_account_deposit(atm_account *a, double amt) 28 { 29 assert(a != NULL); 30 31 /** 对信号量集 semid 中的0号信号量/信号灯作 P(1) 操作 */ 32 sem_P(a->semid, 0, 1); 33 if(amt < 0){ 34 /** 对信号量集 semid 中的0号信号量/信号灯作 V(1) 操作 */ 35 sem_V(a->semid, 0, 1); 36 return 0.0; 37 } 38 double balance = a->balance; 39 sleep(1); 40 balance += amt; 41 a->balance = balance; 42 /** 对信号量集 semid 中的0号信号量/信号灯作 V(1) 操作 */ 43 sem_V(a->semid, 0, 1); 44 45 return amt; 46 } 47 48 /** 查看账户余额度 */ 49 double amt_account_balanceGet(atm_account *a) 50 { 51 assert(a != NULL); 52 /** 对信号量集 semid 中的0号信号量/信号灯作 P(1) 操作 */ 53 sem_P(a->semid, 0, 1); 54 double balance = a->balance; 55 /** 对信号量集 semid 中的0号信号量/信号灯作 V(1) 操作 */ 56 sem_V(a->semid, 0, 1); 57 return balance; 58 }
测试代码:atm_account_test.c
1 #include \"atm_account.h\" 2 #include \"sem_pv.h\" 3 #include4 #include 5 #include 6 #include 7 #include 8 #include 9 10 int main(void) 11 { 12 /** 在共享内存中创建银行账户 */ 13 int shmid; 14 if((shmid = shmget(IPC_PRIVATE, sizeof(atm_account), IPC_CREAT | IPC_EXCL | 0777)) < 0){ 15 perror(\"shmget error\"); 16 return 1; 17 } 18 19 /** 进行共享内存映射(a 为映射的地址) */ 20 atm_account *a = (atm_account *)shmat(shmid, 0, 0); 21 if(a == (atm_account *)-1){ 22 perror(\"shmat error\"); 23 return 1; 24 } 25 a->code = 123456789; 26 a->balance = 10000; 27 28 /** 创建信号量集并初始化(1 个信号量/信号灯,初值为 1) */ 29 a->semid = sem_I(1, 1); 30 if(a->semid < 0){ 31 perror(\"sem_I(1, 1) error\"); 32 return 1; 33 } 34 printf(\"balance: %f\\n\", a->balance); 35 36 pid_t pid; 37 if((pid = fork()) < 0){ 38 perror(\"fork error\"); 39 return 1; 40 } 41 else if(pid > 0){ 42 /** 父进程执行取款操作 */ 43 double amt = atm_account_withdraw(a, 10000); 44 printf(\"pid %d withdraw %f form code %d\\n\", getpid(), amt, a->code); 45 wait(0); 46 47 /** 对共享内存的操作要在解除映射之前 */ 48 printf(\"balance: %f\\n\", a->balance); 49 50 sem_D(a->semid); ///< 销毁信号量集 51 shmdt(a); ///< 解除共享内存的映射 52 shmctl(shmid, IPC_RMID, NULL);///< 释放共享内存 53 } 54 else { 55 /** 子进程进行取款操作 */ 56 double amt = atm_account_withdraw(a, 10000); 57 printf(\"pid %d withdraw %f form code %d\\n\", getpid(), amt, a->code); 58 59 shmdt(a); ///< 解除共享内存的映射 60 } 61 62 return 0; 63 }
编译运行如下:
目的:利用进程信号量的 PV操作实现进程间的同步问题
共享内存中读写数据(读者和写者问题)
1 #include2 #include 3 #include 4 #include 5 #include 6 #include 7 #include 8 #include 9 10 /** 读者和写者的共享资源 */ 11 typedef struct { 12 int val; 13 int semid; 14 }Storage; 15 16 void init(Storage *s) 17 { 18 assert(s != NULL); 19 20 /** 创建信号量集(包含 2 个信号量) */ 21 if((s->semid = semget(IPC_PRIVATE, 2, IPC_CREAT | IPC_EXCL | 0777)) < 0){ 22 perror(\"semget error\"); 23 exit(1); 24 } 25 26 /** 对信号量集中的所有信号量初始化 */ 27 union semun{ 28 int val; 29 struct semid_ds *ds; 30 unsigned short *array; 31 }; 32 union semun un; 33 /** 2 个信号量的初值设置为 0 */ 34 unsigned short array[2] = {0, 0}; 35 un.array = array; 36 if(semctl(s->semid, 0, SETALL, un) < 0){ 37 perror(\"semctl error\"); 38 exit(1); 39 } 40 } 41 42 void destroy(Storage *s) 43 { 44 assert(s != NULL); 45 if(semctl(s->semid, 0, IPC_RMID, NULL) < 0){ 46 perror(\"semctl error\"); 47 exit(1); 48 } 49 } 50 51 void writer(Storage *s, int val) 52 { 53 /** 写入数据到 Storage */ 54 s->val = val; 55 printf(\"%d write %d\\n\", getpid(), val); 56 57 /** 设置信号量 0 号作 V(1) 操作 */ 58 struct sembuf ops_v[1] = {{0, 1, SEM_UNDO}}; 59 /** 设置信号量 1 号作 P(1) 操作 */ 60 struct sembuf ops_p[1] = {{1, -1, SEM_UNDO}}; 61 62 /** V(s1) */ 63 if(semop(s->semid, ops_v, 1) < 0){ 64 perror(\"semop error\"); 65 } 66 67 /** P(s2) */ 68 if(semop(s->semid, ops_p, 1) < 0){ 69 perror(\"semop error\"); 70 } 71 } 72 73 void reader(Storage *s) 74 { 75 assert(s != NULL); 76 77 /** 设置信号量 0 号作 P(1) 操作 */ 78 struct sembuf ops_p[1] = {{0, -1, SEM_UNDO}}; 79 /** 设置信号量 1 号作 V(1) 操作 */ 80 struct sembuf ops_v[1] = {{1, 1, SEM_UNDO}}; 81 /** P(s1) */ 82 if(semop(s->semid, ops_p, 1) < 0){ 83 perror(\"semop error\"); 84 } 85 /** 从 Storage 中读取数据 */ 86 printf(\"%d read %d\\n\", getpid(), s->val); 87 /** V(s2) */ 88 if(semop(s->semid, ops_v, 1) < 0){ 89 perror(\"semop error\"); 90 } 91 } 92 93 int main(void) 94 { 95 /** 将共享资源 Storage 创建在共享内存中 */ 96 int shmid; 97 if((shmid = shmget(IPC_PRIVATE, sizeof(Storage), IPC_CREAT | IPC_EXCL | 0777)) < 0){ 98 perror(\"shmget error\"); 99 exit(1); 100 } 101 102 /** 父进程进行共享内存映射 */ 103 Storage *s = (Storage *)shmat(shmid, 0, 0); 104 if(s == (Storage *)-1){ 105 perror(\"shmat error\"); 106 exit(1); 107 } 108 109 /** 创建信号量并初始化 */ 110 init(s); 111 112 pid_t pid; 113 pid = fork(); 114 if(pid < 0){ 115 perror(\"fork error\"); 116 exit(1); 117 } 118 else if(pid > 0){ 119 int i = 1; 120 for(;i <= 20; i++){ 121 writer(s, i); 122 } 123 wait(0); 124 destroy(s); 125 shmdt(s); 126 shmctl(shmid, IPC_RMID, NULL); 127 } 128 else{ 129 int i = 1; 130 for(;i <= 20; i++){ 131 reader(s); 132 } 133 shmdt(s); 134 } 135 }