Linux操作系统——信号量、PV操作&利用信号量实现进程的互斥、同步

Linux操作系统——信号量、PV操作操作&利用信号量实现进程的互斥、同步

  • 调用函数说明
    semget
结构:
	int semget(key_t key, int nsems, int semflg)
举例:
	//创建一个只包含一个信号量的信号量集
	int m = semget(IPC_PRIVATE, 1, 0661|IPC_CREAT)
作用:
	创建或者打开一个已经创建的信号量集,执行成功返回信号量的ID,否则返回-1。
参数说明:
	key:
		-使用IPC_PRIVATE,由系统产生key值并返回标识符,或者返回key值已存在的信号量集的标识符。
		-用户指定一个非0整数型值,对信号量的打开或者存取操作依赖于semflg参数的取值。
	nsems:
		-新建信号量集中的信号量的个数,通常为1
	semflg:
		-对信号量的打开或者存取操作依赖于semflg参数的取值,
		-当key不为IPC_PRIVATE时使用
		-若只创建了semflg的IPC_CREAT位,则创建一个信号量集,如果该信号量集已经存在,则返回其标识符。
		-semflg的IPC_CREAT|IPC_EXCL位,则创建一个新的信号量集,如果该key值的信号量已经存在,则返回错误信息。
		-只设置IPC_EXCL而不设置IPC_CREAT位没有任何意义
返回值:
	正确返回信号量集的标识符,错误时返回-1

semop

函数:
	int semop(int semid, struct sembuf *sops, unsigned nsops)
功能:
	借助sembuf结构体对指定的信号量进行指定的操作,增加或减少信号量值,对应于共享资源的释放和占有。执行成功返回0,否则返回-1.

参数说明:
	semid:
		-信号量集的标识符,有senget()得到
	sops:
		-指向一个sembuf结构数组(下面介绍),该数组的每一个元素对应一次信号量操作




sembuf结构体

作用:

struct sembuf{
	ushort sem_num;		//要操作的信号量在信号量集中的索引值			
	short sem_op;		//负数表示P操作,正数表示V操作
	short sem_flg;		//操作标志,SEM_UNDO,进程意外结束时恢复信号量的操作

}

semctl

功能:
	对信号量属性进行操作,调用成功返回结果与cmd(指定的操作类型)相关,调用失败返回-1.
函数:
	int semctl(int semid, int semnum, int cmd, union semun arg);
举例:
	int semid = semget(IPC_PRIVATE, 1, 0666|IPC_CREAT);
	semctl(semid,0,SETVAL,1);
参数说明:
	semid:
		-是信号量集的标识符,有semget()得到
	semnum:
		-指定semid信号量集的第几个信号量,在撤销信号量集时此参数可缺省
		-要操作的信号量集中信号量的索引值,对于集合上的第一个信号量该值为0
	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
	    };

-撤销信号量集
	semctl(semid ,IPC_RMID ,0)

  • 思路
    定义信号量标志符:int semid;
    定义信号量数据结构
    定义PV操作所用的数据结构:struct sembuf P,V;
    定义给信号量赋初值的参数数据结构:union semun arg;
    申请一个信号量的信号量集
    对每一个信号量semid赋初值
    定义信号量的P操作
    定义信号量的V操作
    执行PV操作
    撤销信号量
  • 举例
    1、利用信号量实现进程互斥
    #include 
    #include 
    #include 
    #include 
    #include 
	#include
	#include
	#include
	#include

    /*
    父子进程共享一个临界资源(这里就是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\n");
                sleep(1);
                printf("parent out\n");
                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\n");
                sleep(1);
                printf("chld out\n");
                semop(mutexid,&V,1);
                j++;
            }
            exit(0);
        }
    }

2、利用信号量实现进程同步

    #include
    #include
    #include
    #include
    #include
    #include
    #include
	#include
	#include
	#include
	#include

    /*
    父子进程共用一个存储区,
    子进程写入信息,父进程
    读取信息。
    */
    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:\n%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!\n");
            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!\n");
            exit(0);
        }
    }

你可能感兴趣的:(Linux操作系统)