• Linux15 --- 信号量


    1、IPC机制:

    进程间通信(管道、信号量、共享内存、消息队列、套接字)

    2、信号量

    可以类比于红绿灯,对于路口这个共享的通行权,谁得到红绿灯的通行信号,才可以得到路口的通行权,没得到通信信号,就只能等待。

    1)信号量的定义:

    信号量是一个原子操作,例如+1、-1,不能被打断,只有等其操作完才能去使用。这里的+1 、 -1类比于资源的释放和获取,为二值信号量,只有两个值,其初始值为1,就只有0和1两个值。

    定义:

    信号量是一个特殊的变量,一般取正数值。它的值代表允许访问的资源数目, 获取资源时,需要对信号量的值进行原子减一,该操作被称为 P 操作。当信号量值为 0时,代表没有资源可用, P 操作会阻塞。释放资源时,需要对信号量的值进行原子加一,该操作被称为 V操作。信号量主要用来同步进程。 信号量的值如果只取 0,1,将其称为二值信号量。如果信号量的值大于 1,则称之为计数信号量。注意:正数值; 加一减一是一个原子操作;

    2)临界资源和临界区的概念:

    临界资源:计算机的软硬件资源,即同一时刻,只允许一个进程或者线程访问的资源;
    临界区:访问临界资源的代码段;

    2、信号量的一个例子

    不加控制模拟使用打印机:
    比如:进程 a 和进程 b 模拟访问打印机,进程 a 输出第一个字符‘ a’表示开始使用打印机,输出第二个字符‘ a’表示结束使用, b 进程操作与 a 进程相同。(由于打印机同
    一时刻只能被一个进程使用,所以输出结果不应该出现 abab这样交替的结果)
    在这里插入图片描述
    代码示例:
    a.c

    //a.c
    #include 
    #include 
    #include 
    #include 
    #include 
    int main()
    {
    	int i=0;
    	for(;i<5;i++)
    	{
    		printf("A");
    		fflush(stdout);
    		int n=rand()%3;
    		sleep(n);
    		printf("A");
    		fflush(stdout);
    		n=rand()%3;
    		sleep(n);
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    b.c:

    //b.c
    #include 
    #include 
    #include 
    #include 
    #include 
    int main()
    {
    	int i=0;
    	for(;i<5;i++)
    	{
    		printf("B");
    		fflush(stdout);
    		int n=rand()%3;
    		sleep(n);
    		printf("B");
    		fflush(stdout);
    		n=rand()%3;
    		sleep(n);
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    3、信号量的接口

    (1)semget --创建或者获取一个已经存在的信号量

    参数:

    int semget(key_t key,int nsems,int semflg);
    
    • 1

    (key_t 其实为int类型,只不过)

    key:给相同的key值,能得到相同的信号量。
    nsems:创建几个信号量;
    semflg:标志位,如果为创建:IPC_CREAT;
    –如果为创建:为IPC_CREAT;
    –如果为全新创建,也就是不知道是否有人创建过,则 IPC_CREATE | IPC_EXCL ,就是如果没有则创建,如果有则创建失败;

    (2)semop --对信号量进改变,做p操作或者v操作;

    p操作为获取资源;
    v操作为释放资源;

    参数:

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

    nsops :为结构体长度

    semid:信号量的id号,也就是刚才semget的返回值;4;说明对哪个信号量进行操作;
    sops:结构体指针,指向sembuf的结构体指针,

    sembuf 结构体有三个成员变量:
    sem_num 表示信号量的编号(即指定信号量集中的 信号量下标);
    sem_op 表示是p还是v操作;1为v操作(加1),-1为p操作(减1);
    sem_flg 为标志位;

    (3)semct --对信号量进行控制;

    参数:

    int semctl(int semid,int semnum,int cmd,...);
    
    • 1

    semid:信号量id;
    semnum:信号量编号;
    cmd:命令:SETVAL:初始化信号量; IPC_RMID:删除信号量;

    注意:联合体semun,这个联合体需要自己定义;
    在这里插入图片描述
    思路:
    在这里插入图片描述

    4、控制打印机的代码如下:

    1)基础代码

    a.c :

    #include 
    #include 
    #include 
    #include 
    #include 
    
    int main()
    {
        int i = 0;
        for(;i < 5;i++)
        {
            printf("A");
            fflush(stdout);
            int n = rand()%3;
            sleep(n);
            printf("A");
            fflush(stdout);
            n = rand()%3;
            sleep(n);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    b.c :

    #include 
    #include 
    #include 
    #include 
    #include 
    
    int main()
    {
        int i = 0;
        for(;i < 5;i++)
        {
            printf("B");
            fflush(stdout);
            int n = rand()%3;
            sleep(n);
            printf("B");
            fflush(stdout);
            n = rand()%3;
            sleep(n);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    同时运行(加&,后台运行):./a& ./b&
    结果不一定完全相同,但都是成块出现,都是两个A两个B
    在这里插入图片描述

    2)思路

    在这里插入图片描述
    代码实现:

    sem.h
    //sem.h
    #include 
    #include 
    #include 
    
    union semun
    {
    	int val;
    };
    
    void sem_init();
    void sem_p();
    void sem_v();
    void sem_destroy();
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    sem.c
    //sem.c
    #include "sem.h"
    
    static int semid=-1;
    
    //信号量的初始化实现
    void sem_init()
    {
    	semid=semget((key_t)1234,1,IPC_CREAT|IPC_EXCL|0600);//1234为自己定义的
    	if(semid==-1)//全新创建失败
    	{
    		semid=semget((key_t)1234,1,0600);//获取已经存在的信号量
    		if(semid==-1)//获取再失败,是真的错了
    		{
    			perror("semget error");
    		}
    	}
    	else//全新创建成功
    	{
    		//初始化
    		union semun a;
    		a.val=1;
    		if(semctl(semid,0,SETVAL,a)==-1)//初始化,只有一个信号量,所以为0
    		{
    			perror("semctl init error");
    		}
    	}
    }
    
    //p操作
    void sem_p()
    {
    	struct sembuf buf;
    	buf.sem_num=0;
    	buf.sem_op=-1;//p操作
    	buf.sem_flg=SEM_UNDO;//相当于操作系统记住你进行了p操作,如果异常结束,无法进行v操作,系统会帮你操作
    	if(semop(semid,&buf,1)==-1)//出错处理
    	{
    		perror("p error");
    	}
    }
    
    //v操作
    void sem_v()
    {
    	struct sembuf buf;
    	buf.sem_num=0;
    	buf.sem_op=1;//v操作
    	buf.sem_flg=SEM_UNDO;//相当于操作系统记住你进行了p操作,如果异常结束,无法进行v操作,系统会帮你操作
    	if(semop(semid,&buf,1)==-1)//出错
    	{
    		perror("v error");
    	}
    }
    
    //销毁操作
    void sem_destroy()
    {
    	if(semctl(semid,0,IPC_RMID)==-1)//0表示占位
    	{
    		perror("destroy sem error");
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    a.c
    //a.c
    #include 
    #include 
    #include 
    #include 
    #include 
    #include "sem.h"
    int main()
    {
    	int i=0;
    	sem_init();
    	for(;i<5;i++)
    	{
    		//p v 操作之间为临界区,访问临界资源的代码段
    		sem_p();
    		
    		printf("A");
    		fflush(stdout);
    		int n=rand()%3;
    		sleep(n);
    		printf("A");
    		fflush(stdout);
    		sem_v();
    		
    		n=rand()%3;
    		sleep(n);//sem_v();可以放在此代码之后,但不好,效率过低
    	}
    	sleep(10);
    	sem_destroy();//如果忘记销毁,或者程序出错,信号量可能还在,需要通过ipcs查看
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    b.c
    //b.c
    #include 
    #include 
    #include 
    #include 
    #include 
    #include "sem.h"
    int main()
    {
    	int i=0;
    	sem_init();
    	for(;i<5;i++)
    	{
    		sem_p();
    		printf("B");
    		fflush(stdout);
    		int n=rand()%3;
    		sleep(n);
    		printf("B");
    		fflush(stdout);
    		sem_v();
    		n=rand()%3;
    		sleep(n);
    	}
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    5、三个进程分别输出"A"、“B”、“C”,要求输出结果必须是"AABBCCAABB…"

    思路如下:
    在这里插入图片描述

    三、ipcs 命令 – 查看信号量、消息队列和共享内存

    ipcs :

    在这里插入图片描述
    运行./a& ./b& 之后
    在这里插入图片描述
    其中4d2,为1234
    对应代码semid=semget((key_t)1234,1,IPC_CREAT|IPC_EXCL|0600);

    在这里插入图片描述

    ipcs -s :只查看信号量

    在这里插入图片描述

    ipcs -m :只查看共享内存

    在这里插入图片描述

    ipcs -q :只查看消息队列

    在这里插入图片描述

    ipcrm -s 编号 :删除信号量

  • 相关阅读:
    Flink中的时间和窗口
    Aspose.OCR for Java Crack by Xacker
    探索设计模式:从组合到享元的软件架构之旅 (软件设计师笔记)
    数据结构与算法概述
    Starlink全系卫星详细介绍,波段频谱、激光星间链路技术、数据传输速率等等
    【微服务|Nacos】Nacos实现多环境和多租户的数据隔离
    NLP:生成熟悉NLP开源工具,如NLTK、 HanLP等,并搜寻、下载和熟悉PKU、 CoreNLP, LTP MSR, AS CITYI 等语料库。
    java计算机毕业设计酒店员工管理系统源码+mysql数据库+系统+lw文档+部署
    CSRF+Self XSS
    Linux定时任务
  • 原文地址:https://blog.csdn.net/kyrie_sakura/article/details/127676742