信号量编程应用:对共享内存的同步访问
system V 信号量编程应用
- 对共享内存的同步访问
- 通过读写信号量实现对共享内存的同步互斥访问
- 实现一个二元信号量协议
- 读信号量:当为1时,读进程才能进行P操作、读取数据,否则会阻塞
- 写信号量:当为1时,写进程才能进行p操作、写入数据,否则会阻塞
- int semctl(int semid, int semnum, int cmd, ...);
semctl函数对一个信号量执行各种控制操作。
semctl() 在?semid?标识的信号量集上,或者该集合的第semnum?个信号量上执行?cmd?指定的控制命令。(信号量集合索引起始于零。)
根据?cmd?不同,这个函数有三个或四个参数。
当有四个参数时,第四个参数的类型是?union semun?。调用程序?必须按照下面方式定义这个联合体:
union semun {?
? ? ? ?int val; ? ? // SETVAL使用的值 ??
? ? ? ? ?struct semid_ds *buf; ? // IPC_STAT、IPC_SET 使用缓存区
? ? ? ? ?unsigned short *array; // GETALL,、SETALL 使用的数组?
? ? ? ? ?struct seminfo *__buf; // IPC_INFO(Linux特有) 使用缓存区?
};
union semun {?
? ? ? **? ?int val; ? ? ? ? ? ? ? // SETVAL使用的值 ??**
? ? ? ? ?struct semid_ds *buf; ? // IPC_STAT、IPC_SET 使用缓存区
? ? ? ? ?unsigned short *array; // GETALL,、SETALL 使用的数组?
? ? ? ? ?struct seminfo *__buf; // IPC_INFO(Linux特有) 使用缓存区?
};
注意:该联合体没有定义在任何系统头文件中,因此得用户自己声明。
semid_ds 数据结构在头文件 <sys/sem.h> 有如下定义:
struct semid_ds {?
? ? ? ?struct ipc_perm sem_perm; ? // 所有者和权限
? ? ? ?time_t sem_otime; ? ?? // 上次执行 semop 的时间 ?
? ? ? ?time_t sem_ctime; ? ? // 上次更新时间?
? ? ? ?unsigned short sem_nsems; ? // 在信号量集合里的索引
?};
结构体 ipc_perm 在头文件 <sys/ipc.h> 中的定义如下(高亮的字段可以使用 IPC_SET 设置):
struct ipc_perm {?
? ? ? ?key_t __key; ? ?// 提供给 semget()的键?
? ? ? ?uid_t uid; ? ? ?// 所有者有效 UID ?
? ? ? ?gid_t gid; ? ? ?// 所有者有效 GID?
? ? ? ?uid_t cuid; ? ? // 创建者有效 UID?
? ? ? ?gid_t cgid; ? ? // 创建者有效 GID
? ? ? ?unsigned short mode; ? ? // 权限?
? ? ? ?unsigned short __seq; ? ?// 序列号
};
cmd 的有效值是:
IPC_STAT从关联于?semid?的内核数据结构复制数据到?arg.buf?指向的semid_ds* 数据结构。参数?semnum?被忽略。调用进程必须在保量集合里有读权限。
IPC_SET把?arg.buf?指向的?semid_ds?结构的一个成员值写入相关于该信号量集合内核结构,同时更新?sem_ctime?成员。结构中下列成员被更新:sem_perm.uid?、sem_perm.gid?以及sem_perm.mode* (低端 9位)。调用进程的有效用户ID必须匹配信号量集合的所有者(sem_perm.uid?)或创建者(sem_perm.cuid?),或者调用者必须有特权。参数semnum?被忽略。
IPC_RMID立即删除信号量集合,唤醒所有因调用semop() 阻塞在该信号量集合里的所有进程(相应调用会返回错误且?errno?被设置为 EIDRM)。调用进程的有效用户ID必须匹配信号量集合的创建者或所有者,或者调用者必须有特权。参数semnum 被忽略。
IPC_INFO?(Linux 定义的)通过?arg.buf?指向的结构返回系统范围内的信号量限制和参数。这个结构的类型是seminfo*,如果宏 _GNU_SOURCE 特性宏被定义,则该结构定义在头文件<sys/sem.h> 。
struct seminfo {?
? ? ?int semmap; // 信号量映射里的条数,内核未使用?
? ? ?int semmni; // 信号量集合的最大个数?
? ? ?int semmns; // 在所有信号量集合里信号量个数上限 ?
? ? ?int semmnu; // 系统范围内的 undo 结构最大个数,内核未使用?
? ? ?int semmsl; // 一个信号量集合里信号量个数上限?
? ? ?int semopm; // 执行的最大操作个数 ?
? ? ?int semume; // 每个进程内 undo 结构最大个数,内核未使用
? ? ?int semusz; // 结构 sem_undo 的尺寸?
? ? ?int semvmx; // 信号量值的上限
? ? ?int semaem; // Max. value that can be recorded for semaphore adjustment (SEM_UNDO)?
};
P:-1 V:+1
write.c
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ipc.h>
#include <sys/shm.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; /* Buffer for IPC_INFO */
};
int sem_id;
void sem_init(int semid, int nsignum, int sem_value)
{
union semun sem_union;
sem_union.val = sem_value;
if (semctl(semid, nsignum, SETVAL, sem_union) == -1)
{
perror("semctl");
exit(EXIT_FAILURE);
}
}
/* P:-1 */
void sem_p(int semid, int nsignum)
{
struct sembuf sops;
sops.sem_num = nsignum;
sops.sem_op = -1;
sops.sem_flg = SEM_UNDO;
if (semop(semid, &sops, 1) == -1)
{
perror("semop");
exit(EXIT_FAILURE);
}
}
/* V:+1 */
void sem_v(int semid, int nsignum)
{
struct sembuf sops;
sops.sem_num = nsignum;
sops.sem_op = 1;
sops.sem_flg = SEM_UNDO;
if (semop(semid, &sops, 1) == -1)
{
perror("semop");
exit(EXIT_FAILURE);
}
}
void sem_print(int semid, int nsignum)
{
int sem_value;
sem_value = semctl(semid, nsignum, GETVAL);
printf("sem[%d] = %d\n", nsignum, sem_value);
}
int main(int argc, char *argv[])
{
int shm_id;
key_t shm_key = ftok("./", 5151);
key_t sem_key = ftok("./", 5152);
shm_id = shmget(shm_key, 1028, IPC_CREAT|0644);
char *shm_addr = shmat(shm_id, NULL, 0);
sem_id = semget(sem_key, 2, IPC_CREAT|0664);
if (sem_id == -1)
{
sem_id = semget(sem_key, 2, 0664);
}
else
{
sem_init(sem_id, 0, 0); //sem[0]:for read
sem_init(sem_id, 1, 1); //sem[1]:for write
}
while(1) //write
{
sem_p(sem_id, 1);
fgets(shm_addr, 1024, stdin);
sem_v(sem_id, 0);
}
return 0;
}
read.c
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>
union semun {
int val;
struct semid_ds *buf;
unsigned short int *array;
struct seminfo *__buf;
};
int sem_id;
void sem_init(int semid, int nsignum, int sem_value)
{
union semun sem_union;
sem_union.val = sem_value;
if (semctl(semid, nsignum, SETVAL, sem_union) == -1)
{
perror("semctl");
exit(EXIT_FAILURE);
}
}
/* P : -1 */
void sem_p(int semid, int nsignum)
{
struct sembuf sops;
sops.sem_num = nsignum;
sops.sem_op = -1;
sops.sem_flg= SEM_UNDO;
if (semop(semid, &sops, 1) == -1)
{
perror("semop");
exit(EXIT_FAILURE);
}
}
/* V : +1 */
void sem_v(int semid, int nsignum)
{
struct sembuf sops;
sops.sem_num = nsignum;
sops.sem_op = 1;
sops.sem_flg = SEM_UNDO;
if (semop(semid, &sops, 1) == -1)
{
perror("semop");
exit(EXIT_FAILURE);
}
}
void sem_print(int semid, int nsignum)
{
int sem_value;
sem_value = semctl(semid, nsignum, GETVAL);
printf("sem[%d] = %d\n", nsignum, sem_value);
}
int main(int argc, char *argv[])
{
int shm_id;
key_t shm_key = ftok("./", 5151);
key_t sem_key = ftok("./", 5152);
shm_id = shmget(shm_key, 1024, IPC_CREAT|0664);
char *shm_addr = shmat(shm_id, NULL, 0);
sem_id = semget(sem_key, 2, IPC_CREAT|0644);
if (sem_id == -1)
{
perror("semget");
exit(EXIT_FAILURE);
}
else
{
sem_init(sem_id, 0, 0); //sem[0]: For read
sem_init(sem_id, 1, 1); //sem[1]: For write
}
while(1)
{
sem_p(sem_id, 0);
printf("%s\n", shm_addr);
sem_v(sem_id, 1);
}
return 0;
}
|