进程的信号量通信
【预备知识】
在Linux中,一个或多个信号量构成一个信号量集合。使用信号量机制用来实现进程之间的同步和互斥,允许并发进程一次对一组信号量进行相同或不同的操作。每个P、V操作不限于减1或加1,而是可以加减任何整数。在进程终止时,系统可根据需要自动消除所有被进程操作过的信号量的影响。
1.信号量机制中使用的数据结构
系统中的每个信号量都有一个数据结构定义在sys/sem.h中。结构定义如下:
struct sem
{
ushort semval; /*信号量的当前值*/
short sempid; /* 最近一次对该信号操作的进程标识*/
ushort semncnt; /*等待信号量值增加的进程数*/
ushort semzcnt; /*等待信号量值等于0的进程数*/
}
2.信号量集合的数据结构
系统为每个信号集合保持一个头文件定义在sys/sem.h中。结构定义如下:
struct semid_ds
{
struct ipc_perm sem_perm; /*对信号量的访问方式,见消息缓冲*/
time_t sem_otime; /*最后操作信号量集合的时间*/
time_t sem_ctime; /*最后修改信号量集合的时间*/
struct sem *sem_base; /*指向集合中第一个信号量的指针*/
ushort sem_nsems; /*集合中的信号量个数*/
}
3.信号量集合的建立
任何进程在使用信号量之前,通过使用如下函数申请建立一个信号量集合:
# include <sys/types.h>
# include <sys/ipc.h>
# include <sys/sem.h>
int semget(key_t key,int nsems,int semflg)
其中,key为用户进程指定的信号量集合的关键字,nsems为信号量集合中的信号量数,semflg为访问标志。
返回值:成功时,返回信号量集合的标识号,失败时,返回-1。
semflg 由以下成分组成:
·IPC_CREAT,创建一个新的信号量集合。如果该标志没有设置,将查找于key相关的信号量集合。
·IPC_EXCL,与IPC_CREAT一起使用,确保创建一个新的信号量集合。若该段已经存在,出错。
低9位为三类用户的访问方式的定义。
4.对信号量的操作
通过调用semop()函数,进程对一信号量集合中的一个或多个信号执行P/V操作,其操作命令由用户提供的信号操作模版(sembuf)定义,该模版的结构如下:
Structsembuf
{
ushort sem_num; /*信号量的序号*/
short sem_op /*具体执行的操作(即P或V操作)* /
short sem_flg; /*访问标志*/
}
调用语法为:
# include <sys/sem.h>
int semop(int semid,struct sembuf *sops,unsigned nsops);
其中,semid是进程调用semget后返回的信号量集合的标识,sops是用户提供的信号量操作模版数组(sembuf)的指针,nsops为数组sembuf中的元素数(即一次需进行的操作数)。正常返回值为0。
5.对信号量执行控制操作
当要读取和修改信号量集合的有关状态信息,或撤销信号量集合时,调用命令semctl()对信号量执行控制操作。其调用语法为:
int semctl(int semid,int semnum,int cmd,union semun arg);
其中,semid是信号量集合的标识,semnum是要处理信号量集合中的信号量序号,cmd为要执行的操作命令,arg为控制操作需要的参数,是一个指向联合semun的指针。函数semctl成功返回时为0。semun定义为:
union semun
{
int val;/ * value forSETVAL * /
struct semid_ds * buf ; / * buffer for IPC_SET & IPC_STAT */
ushort array [ ];/ * buffor for IPC_INFO * /
} arg;
cmd的可能取值有:
GETPID 返回最近一个队信号量操作的进程标识
GETVAL 返回索引为semnum的信号量的值
GETALL 返回一个集合中所有信号量的值到arg.array中
GETNCNT 返回睡眠等待信号量值为正的进程的数
GETZCNT 返回睡眠等待信号量值为0的进程数
SETVAL 设置索引为semnum的信号量的值为arg.val
SETALL 按照数组arg.array,设置一个集合中所有信号量的值
IPC_STAT 若为读,可将指定semid的信号量头结构读入arg.buf中获得信号量的值
IPC_SET 按照arg.buf设置用户标识、组标识和访问权限。若不是给定的有效用户标识,立即返回
IPC_RMID 删除指定标识的信号量集合
编程实现例<一>
【任务】
编程实现多个进程对同一文件的互斥写入。
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <time.h> #include <sys/types.h> #include <sys/wait.h> #include <sys/ipc.h> #include <sys/sem.h> #define NUM_PROCS 5 #define SEM_ID 250 #define FILE_NAME "/tmp/sem_MUTEX" #define DELAY 400000 union semun { int val; struct semid_ds *buf; ushort *array; }; void update_file(int sem_set_id,char *file_name_path,int number) { struct sembuf sem_op; FILE *file; sem_op.sem_num=0; sem_op.sem_op=-1; sem_op.sem_flg=0; semop(sem_set_id,&sem_op,1); file=fopen(file_name_path, "w"); if(file) { fprintf(file, "%d\n", number); printf("%d\n", number); fclose(file); } sem_op.sem_num=0; sem_op.sem_op=1; sem_op.sem_flg=0; semop(sem_set_id,&sem_op,1); } void do_child_loop(int sem_set_id,char *file_name) { pid_t pid=getpid(); int i,j; for(i=0;i<3;i++) { update_file(sem_set_id,file_name,pid); for(j=0;j<200000;j++); } } int main(int argc,char**argv) { int sem_set_id,child_pid,rc,i; union semun sem_val; sem_set_id=semget(SEM_ID,1,IPC_CREAT|0600); if(sem_set_id==-1) { perror("main's semget error "); exit(1); } sem_val.val=1; rc=semctl(sem_set_id,0,SETVAL,sem_val); if (rc==-1) { perror("main:setcl"); exit(1); } for (i=0;i<NUM_PROCS;i++) { child_pid=fork(); switch(child_pid) { case -1: perror("fork()"); exit(0); case 0: do_child_loop(sem_set_id,FILE_NAME); exit(0); default: break; } } for (i=0;i<NUM_PROCS;i++) { int child_status; wait(&child_status); } printf("main is done.\n"); fflush(stdout); return 0; }
进程的信号量通信
最新推荐文章于 2024-05-23 21:10:33 发布