zoukankan      html  css  js  c++  java
  • Linux-信号量与P,V操作

    Linux-信号量与P,V操作

    内容

    • 使用信号量实现进程互斥
    • 使用信号量及PV实现子进程读写同步

    机理

    Linux信号量集

    Linux信号量作为IPC机制的一种,与其他通信方式类似,Linux也是通过kern_ipc_perm结构中的key来唯一标志一个信号量集,并通过该结构设置并检查访问权限。针对信号量集,系统维护一个由信号量集组成的数组,数组中的每个单元指向一个信号量集。

    PV原语

    PV操作是典型的同步机制之一。用一个信号量与一个消息联系起来,当信号量的值为0时,表示期望的消息尚未产生;当信号量的值非0时,表示期望的消息已经存在。用PV操作实现进程同步时,调用P操作测试消息是否到达,调用V操作发送消息。

    调用函数说明

    创建一个新的信号量集或获取一个已存在的信号量集

    int semget(key_t key ,int nsems ,int semflg)

    参数说明:

    key

    • 使用IPC_PRIVATE,由系统产生key值并返回标识符,或者返回key值已存在的信号集的标识符。
    • 用户指定一个非0整数型值,对信号量集的打开或存取依赖于semflg参数的取值。

    nsems指定打开或者新创建的信号量集将包含的信号量的数目。

    semflg当key不为IPC_PRIVATE时使用

    • 若只设置semflg的IPC_CREAT位,则创建一个信号量集,如果该信号量集已经存在,则返回其标识符。
    • semflg的IPC_CREAT|IPC_EXCL位,则创建一个新的信号量集,如果该key值的信号量已经存在,则返回错误信息。
    • 只设置IPC_EXCL而不设置IPC_CREAT位没有任何意义

    返回值:正确返回信号量集的标识符,错误时返回-1。

    如,创建一个只包含一个信号量的信号量集:

    • semid = semget(IPC_PRIVATE,1,IPC_CREAT|0666);

    对信号量的PV操作

    int semop(int semid ,struct sembuf * sops ,unsigned nsops);

    参数说明:

    • semid是信号量集的标识符,由semget()得到
    • sops指向一个sembuf结构数组,该数组的每一个元素对以一次信号量操作。
    struct sembuf
    {
        unsigned short sem_num;		/*semaphore index in array*/
        short sem_op;				/*semaphore operation*/
        short sem_flg;				/*operation flags*/
    };
    /*
    sem_num标明它是信号量集的第几个元素,从0开始
    sem_op指定信号量采取的操作
    	<0相当于P操作,占有资源
    	>0相当于V操作,释放资源
    	=0进程睡眠直到信号量的值为0
    sem_flg指明操作的执行模式,两个标志位。一个是IPC_NOWAIT,指明以非阻塞方式操作信号量。一个是SEM_UNDO,指明内核为信号量操作保留恢复值。
    */
    

    返回值:正确返回0,错误时返回-1

    信号量集的控制函数

    int semctl(int semid ,int semnum ,int cmd ,union semun arg);

    参数说明:

    • semid是信号量集的标识符,由semget()得到
    • semnum指定semid信号量集的第几个信号量,在撤销信号量集时,此参数可缺省。
    • cmd指定操作类型。
    取值 含义
    GETVAL 返回semnum指定的信号量的semval域值
    SETVAL 指定semval域值为arg.val
    GETPID 返回semnum指定信号量sempid
    GETNCNT 返回semncnt
    GETZCNT 返回semzcnt
    GETALL 返回所有信号量的值,结果保存到arg.array中
    SETALL 通过arg.array更新所有信号量的值
    IPC_STAT 获取信号量集的arg.array,存入arg.buf
    IPC_SET 将arg.buf数据结构的sem_perm.uid,sem_perm.gid,sem_perm.mode赋给sem_array,此操作仅限root、sem_perm.cuid或sem_perm.uid
    IPC_RMID 删除指定信号量集。此操作仅限root、sem_perm.cuid或sem_perm.uid
    IPC_INFO 获取信号量集的相关信息存放于arg.buf中
    • arg为5中数据类型的共同体类型semnu,该类型在include/linux/sem.h中定义
    union senum
    {
    	int val;					/*value for setval*/
    	struct semi_ds *buf;		/*buffer for IPC_STAT & IPC_SET*/
    	unsigned short *array;		/*array for GETALL & SETALL*/
    	struct seminfo *_buf;		/*buffer for IPC_INFO*/
    	void *_pad
    };
    

    返回值:正确时根据cmd的的不同返回值或0,错误时返回-1。

    撤销信号量集

    semctl(semid ,IPC_RMID ,0)

    思路

    • 定义信号量标志符:int semid;
    • 定义信号量数据结构
      • 定义PV操作所用的数据结构:struct sembuf P,V;
      • 定义给信号量赋初值的参数数据结构:union semun arg;
    • 申请一个信号量的信号量集
    • 对每一个信号量semid赋初值
    • 定义信号量的P操作
    • 定义信号量的V操作
    • 执行PV操作
    • 撤销信号量

    实例

    • 利用信号量实现进程互斥
    #include <unistd.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <sys/sem.h>
    /*
    父子进程共享一个临界资源(这里就是stdout),
    每个进程循环进入临界区3次
    父进程进入后显示parent in
    父进程出去后显示parent out
    子进程进入后显示chld in
    子进程出去后显示chld out
    */
    union semun{
    	int val;
    	struct semid_ds *buf;
    	unsigned short *array;
    };
    int mutexid;									//互斥信号量
    
    int main()
    {
    	int chld,i,j;
    	
    	struct sembuf P,V;
    	union semun arg;
    	/*****申请只有一个信号量的信号量集*****/
    	mutexid=semget(IPC_PRIVATE,1,0666|IPC_CREAT);
    	/*****分别对每个信号量赋初值*****/
    	arg.val=1;
    	if(semctl(mutexid,0,SETVAL,arg)==-1)perror("semctl setval error");
    	/*****定义PV操作*****/
    	P.sem_num=0;
    	P.sem_op=-1;
    	P.sem_flg=SEM_UNDO;
    	V.sem_num=0;
    	V.sem_op=1;
    	V.sem_flg=SEM_UNDO;
    	
    	while((chld=fork())==-1);
    	if(chld>0)
    	{/*****父进程块*****/
    		i=1;
    		while(i<=3)
    		{
    			sleep(1);
    			semop(mutexid,&P,1);			/*占有临界资源*/
    			printf("parent in
    ");
    			sleep(1);
    			printf("parent out
    ");
    			semop(mutexid,&V,1);			/*释放临界资源*/
    			i++;
    		}
    		wait(0);
    		/*****撤销信号量集*****/
    		semctl(mutexid,IPC_RMID,0);
    		exit(0);
    	}
    	else
    	{/*****子进程块*****/
    		j=1;
    		while(j<=3)
    		{
    			sleep(1);
    			semop(mutexid,&P,1);
    			printf("chld in
    ");
    			sleep(1);
    			printf("chld out
    ");
    			semop(mutexid,&V,1);
    			j++;
    		}
    		exit(0);
    	}
    }
    
    

    运行结果

    • 利用信号量实现进程同步
    #include<unistd.h>
    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    #include<sys/types.h>
    #include<sys/sem.h>
    #include<sys/shm.h>
    /*
    父子进程共用一个存储区,
    子进程写入信息,父进程
    读取信息。
    */
    union semun{
    	int val;
    	struct semid_ds *buf;
    	unsigned short *array;
    };
    
    int emptyid;
    int fullid;									//同步信号量
    
    main()
    {
    	int chld;
    	
    	struct sembuf P,V;
    	union semun arg;
    	
    	int shmid;
    	char *viraddr;
    	char buffer[BUFSIZ];
    	/*****申请只有一个信号量的信号量集*****/
    	emptyid=semget(IPC_PRIVATE,1,IPC_CREAT|0666);
    	fullid=semget(IPC_PRIVATE,1,IPC_CREAT|0666);
    	/*****分别对每个信号量赋初值*****/
    	arg.val=1;
    	if(semctl(emptyid,0,SETVAL,arg)==-1)perror("semctl setval error");
    	arg.val=0;
    	if(semctl(fullid,0,SETVAL,arg)==-1)perror("semctl setval error");
    	/*****定义PV操作*****/
    	P.sem_num=0;
    	P.sem_op=-1;
    	P.sem_flg=SEM_UNDO;
    	V.sem_num=0;
    	V.sem_op=1;
    	V.sem_flg=SEM_UNDO;
    	/*****申请共享内存空间*****/
    	shmid=shmget(IPC_PRIVATE,BUFSIZ,0666|IPC_CREAT);
    	viraddr=(char*)shmat(shmid,0,0);
    	
    	while((chld=fork())==-1);
    	if(chld>0)
    	{
    		while(1)
    		{
    			semop(fullid,&P,1);
    			printf("Your message is:
    %s",viraddr);
    			semop(emptyid,&V,1);
    			if(strncmp(viraddr,"end",3)==0)break;
    		}
    		wait(0);
    		shmdt(viraddr);
    		/*****撤销信号量集、释放共享内存*****/
    		shmctl(shmid,IPC_RMID,0);
    		semctl(emptyid,IPC_RMID,0);
    		semctl(fullid,IPC_RMID,0);
    		printf("Parent exit!
    ");
    		exit(0);
    	}
    	else
    	{
    		while(1)
    		{
    			semop(emptyid,&P,1);
    			puts("Enter your text:");
    			fgets(buffer,BUFSIZ,stdin);
    			strcpy(viraddr,buffer);
    			semop(fullid,&V,1);
    			if(strncmp(viraddr,"end",3)==0)
    			{
    				sleep(1);
    				break;
    			}
    		}
    		printf("Child exit!
    ");
    		exit(0);
    	}
    }
    
    

    运行结果

  • 相关阅读:
    引用的难点:函数返回值是引用(引用当左值)
    引用的意义与本质
    引用做函数参数
    Uva
    Uva
    Uva
    暑假集训-8.06总结
    暑假集训-8.05总结
    CH1801( 括号画家)
    最大异或对
  • 原文地址:https://www.cnblogs.com/goodswarm/p/10496715.html
Copyright © 2011-2022 走看看