多进程间通信学习之信号灯集(信号量数组)

本文详细介绍了C语言中使用信号灯集(信号量数组)实现进程同步的方法,包括创建信号灯集(semget)、控制信号灯集(semctl)和操作信号灯(semop),并通过示例展示了如何在共享内存环境中使用信号灯进行同步操作。
摘要由CSDN通过智能技术生成
  • 信号灯集(信号量数组):
  • 1、实现进程同步的机制
  • 2、在一个信号灯集中,可以有很多信号灯;
  • 3、这些信号灯集中的信号灯相互独立,每个灯的值的改变都不会影响到其他的信号灯;
  • 4、信号灯的值一般设置为二值量,即0或者1,其中0代表没有资源,1代表有资源
  • 常用的接口函数:
  • 一、创建一个信号灯集(semget函数):
	#include <sys/sem.h>
	int semget(key_t key, int nsems, int semflg);
	/*
	参数:
		    key:键值
		        IPC_PRIVATE  key
		    nsems:信号灯集合中信号灯的个数
		    semflag:创建的标志位
		        IPC_CREAT|0666 或 IPC_CREAT|IPC_EXCL|0666
	返回值:
		    成功 semid
		    失败 -1  重置错误码
	*/
  • 二、控制信号灯集(semctl函数):
	#include <sys/sem.h>
	int semctl(int semid, int semnum, int cmd, ...);
	/*
	参数:
		    semid信号灯集的ID
		    senum:信号灯的编号 从0开始
		    cmd:命令码
		        SETVAL:设置信号灯的值 --->第四个参数val选项
		        GETVAL:获取信号灯的值 --->不需要第四个参数
		        IPC_STAT:获取信号灯集的属性--->第四个参数buf选项
		        IPC_SET :设置信号灯集的属性--->第四个参数buf选项
		        IPC_RMID:第二参数被忽略,第4个参数不用填写 
		    @...:可变参
    
    
	返回值:
		    成功:
		        GETVAL:成功返回信号灯的值
		        其余的命令码成功返回0
		    失败 -1  重置错误码
	*/
	union semun{
        int val; /* Value for SETVAL */
        struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
    };
  • 三、操作信号灯集中信号灯(semop函数):
	#include <sys/sem.h>
	int semop(int semid, struct sembuf *sops, size_t nsops);
	/*
	参数:
		    semid:信号灯集的编号
		    sops:操作方式
		        struct sembuf{
		            unsigned short sem_num; //信号灯的编号
		            short sem_op; //操作方式(PV)-1:P操作,申请资源 1:V操作,释放资源
		            short sem_flg; //操作的标志位 0:阻塞 IPC_NOWAIT:非阻塞方式操作
		        }
	    	nsops:本次操作信号灯的个数
	返回值:
		    成功 0
		    失败 -1  重置错误码
	*/
  • 自定义封装信号灯集函数:
  • 自定义头文件:
	#ifndef __SEM_H__
	#define __SEM_H__
	
	int sem_init(int nsems);    //信号灯集中信号灯的个数
	int sem_wait_P(int semid,int sem_num);//信号灯集的编号,信号灯的编号
	int sem_post_V(int semid,int sem_num);
	int sem_exit(int semid);
	
	
	#endif
  • 源码文件:
	#include <stdio.h>
	#include <stdlib.h>
	#include <string.h>
	
	#include <sys/types.h>
	#include <sys/ipc.h>
	
	#include <sys/sem.h>
	#include <sys/shm.h>
	#include <unistd.h>
	#include "sem.h"
	#include <errno.h>
	
	
	
	union semun {
	
	    int val;    //设置信号灯的值
	    struct semid_ds *buf;   //信号灯集的属性
	
	};
	
	/*
	struct sembuf{
	    unsigned short sem_num; 
	//信号灯的编号
	    short sem_op; 
	//操作方式(PV)-1:P操作,申请资源 1:V操作,释放资源
	    short sem_flg; 
	//操作的标志位 0:阻塞 IPC_NOWAIT:非阻塞方式操作
	}
	*/
	
	int sem_init(int nsems)   //信号灯集中信号灯的个数
	{
	    int semid = 0;
	    key_t key = ftok("/home/linux/work", 'K');
	    if(-1 == key)
	    {
	        perror("ftok error");
	        exit(-1);
	    }
	    //创建信号灯集
	    /*
	    当使用IPC_EXCL选项时,如果共享资源已经存在,则创建操作将失败并返回-1。
	    这个选项主要用于确保在创建共享资源时不会发生冲突。
	    */
	    if(-1 == (semid = semget(key,nsems,IPC_CREAT|IPC_EXCL|0666))) 
	    {
	        if(EEXIST == errno)
	        {
	            if(-1 == (semid = semget(key,nsems,IPC_CREAT|0666)))
	            {
	                perror("semget error");
	                exit(-1);
	            }
	            return semid;
	        }
	        else
	        {
	            perror("semget error");
	            exit(-1);
	
	        }
	    }
	    //初始化信号灯集
	    union semun myval = {.val = 1};
	    if(-1 == semctl(semid,0,SETVAL,myval))
	    {
	        perror("semctl error");
	        exit(-1);
	    }
	    myval.val = 0;
	    for(int i = 1; i < nsems; i++)
	    {
	        if(-1 == semctl(semid,i,SETVAL,myval))
	        {
	            perror("semctl error");
	            exit(-1);
	        }
	        
	    }
	    return semid;
	
	}

	//获取资源
	int sem_wait_P(int semid,int sem_num)//信号灯集的编号,信号灯的编号
	{
	    struct sembuf mysem;
	    mysem.sem_num = sem_num;
	    mysem.sem_op = -1;
	    mysem.sem_flg = 0;
	    if(-1 == semop(semid,&mysem,1))
	    {
	        perror("semop error");
	        exit(-1);
	    }
	
	}
	
	//释放资源
	int sem_post_V(int semid,int sem_num)
	{
	    struct sembuf mysem;
	    mysem.sem_num = sem_num;
	    mysem.sem_op = 1;
	    mysem.sem_flg = 0;
	    if(-1 == semop(semid,&mysem,1))
	    {
	        perror("semop error");
	        exit(-1);
	        
	    }
	
	}
	
	//销毁信号灯集
	int sem_exit(int semid)
	{
	    if(-1 == semctl(semid,0,IPC_RMID))
	    {
	        perror("semctl error");
	        exit(-1);
	    }
	
	}

  • 使用信号灯集实现共享内存的同步:
  • 写端:
	#include <stdio.h>
	#include <stdlib.h>
	#include <string.h>
	
	#include <sys/types.h>
	#include <sys/ipc.h>
	
	#include <sys/sem.h>
	#include <sys/shm.h>
	#include <unistd.h>
	#include "sem.h"
	#include <errno.h>
	
	#define PIGE_SIZE 4*1024
	
	int main(int argc, char const *argv[])
	{
	    key_t key = ftok("/home/linux/work", 'K');
	    if(-1 == key)
	    {
	        perror("ftok error");
	        exit(-1);
	    }
	    //创建共享内存
	    int shmid = shmget(key,2*PIGE_SIZE,IPC_CREAT|0666);
	    if(-1 == shmid)
	    {
	        perror("shmget error");
	        exit(-1);
	    }
	    //映射共享内存
	    char *k_addr = shmat(shmid,NULL,0);
	    if((void *)-1 == k_addr)
	    {
	        perror("shmat error");
	        exit(-1);
	    }
	    //创建信号灯集
	    int semid = sem_init(2);
	
	    //向共享内存中写入数据
	    while(1)
	    {
	        sem_wait_P(semid,0); //申请0号灯资源
	        fgets(k_addr,128,stdin);
	        k_addr[strlen(k_addr)-1] = '\0';
	        sem_post_V(semid,1);  //释放1号灯资源
	        if(!strncmp(k_addr,"exit",4))
	        {
	            break;
	        }
	    }
	
	    //取消映射
	    if(-1 == shmdt(k_addr))
	    {
	        perror("shmdt error");
	        exit(-1);
	    }
	
	    //删除共享内存
	    if(-1 == shmctl(shmid,IPC_RMID,NULL))
	    {
	        perror("shmctl error");
	        exit(-1);
	    }
	
	    //销毁信号灯集
	    sem_exit(semid);
	
	    return 0;
	}

  • 读端:
	#include <stdio.h>
	#include <stdlib.h>
	#include <string.h>
	
	#include <sys/types.h>
	#include <sys/ipc.h>
	
	#include <sys/sem.h>
	#include <sys/shm.h>
	#include <unistd.h>
	#include "sem.h"
	#include <errno.h>
	
	#define PIGE_SIZE 4*1024
	
	int main(int argc, char const *argv[])
	{
	    key_t key = ftok("/home/linux/work", 'K');
	    if(-1 == key)
	    {
	        perror("ftok error");
	        exit(-1);
	    }
	    //创建共享内存
	    int shmid = shmget(key,2*PIGE_SIZE,IPC_CREAT|0666);
	    if(-1 == shmid)
	    {
	        perror("shmget error");
	        exit(-1);
	    }
	    //映射共享内存
	    char *k_addr = shmat(shmid,NULL,0);
	    if((void *)-1 == k_addr)
	    {
	        perror("shmat error");
	        exit(-1);
	    }
	    //创建信号灯集
	    int semid = sem_init(2);
	
	    while(1)
	    {
	        sem_wait_P(semid,1);
	        printf("[%s]\n",k_addr);
	        if(!strcmp(k_addr,"exit"))
	        {
	            break;
	        }
	        sem_post_V(semid,0);
	    }
	    //取消映射
	    if(-1 == shmdt(k_addr))
	    {
	        perror("shmdt error");
	        exit(-1);
	    }
	
	    //删除共享内存
	    if(-1 == shmctl(shmid,IPC_RMID,NULL))
	    {
	        perror("shmctl error");
	        exit(-1);
	    }
	
	    //销毁信号灯集
	    sem_exit(semid);
	
	    return 0;
	}

  • 运行结果:
	linux@ubuntu:~/work/MSG/SME$ ./w
	hello
	hi
	I Love China
	linux@ubuntu:~/work/MSG/SME$ ./r
	[hello]
	[hi]
	[I Love China]
  • 18
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值