Linux系统编程:进程part_2(信号相关)

news2024/11/17 23:58:11

前言

这一节内容我没怎么认真写,就是纯当草草过了一遍,这部分不是很重要当然能掌握肯定更好。
更多的是有个印象然后知道遇到这样的问题能回想起来知道怎么解决即可(虽然不太可能遇到)。

信号量

在这里插入图片描述

实现PV操作

P:测试并加锁,sem <= 0 会阻塞,但如果sem > 0的话 就 --sem

V:解锁,即++sem

实现PV操作可以分为几步,首先定义PV操作然后是调用PV操作。

定义PV操作需要用到的系统调用为:
在这里插入图片描述
对该系统调用的一些解释:
在这里插入图片描述
信号量的简单使用:
在这里插入图片描述
SEM_UNDO的作用是在进程终止的时候,把减去的资源给加回来,这个在下面的生产者消费者问题中有所体现。

生产者消费者问题

在考研课程还有什么哲学家进餐问题乱七八糟的,这些只对考研有用,在实际工作生产中,只要懂消费者生产者问题即可,所以这个很重要,利用之前学过的知识很容易写出这样的代码:

#include <43func.h>
int main()
{
    int shmid = shmget(IPC_PRIVATE, 4096, IPC_CREAT | 0600);
    ERROR_CHECK(shmid, -1, "shmget");
    int *p = (int *)shmat(shmid, NULL, 0);
    ERROR_CHECK(p, (void *)-1, "shmat");
    p[0] = 10; // p[0] 表示仓库的个数
    p[1] = 0;  // p[1] 表示商品的个数
    int semid = semget(1000, 1, IPC_CREAT | 0600);
    ERROR_CHECK(semid, -1, "semget");
    int ret = semctl(semid, 0, SETVAL, 1);
    ERROR_CHECK(ret, -1, "semctl SETVAL");
    ret = semctl(semid, 0, GETVAL);
    ERROR_CHECK(ret, -1, "semctl GETVAL");
    printf("semval = %d\n", ret);
    struct sembuf P, V;
    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;
    if (fork() == 0)
    {
        while (1)
        {
            semop(semid, &P, 1);
            if (p[0] > 0)
            {
                printf("before produce, space = %2d, good = %2d, total = %d\n", p[0], p[1], p[0] + p[1]);
                --p[0];
                ++p[1];
                printf("after produce, space = %2d, good = %2d, total = %d\n", p[0], p[1], p[0] + p[1]);
            }
            semop(semid, &V, 1);
            // sleep(1);
        }
    }
    else if (fork() == 0)
    {
        while (1)
        {
            semop(semid, &P, 1);
            if (p[0] > 0)
            {
                printf("before produce, space = %2d, good = %2d, total = %d\n", p[0], p[1], p[0] + p[1]);
                --p[0];
                ++p[1];
                printf("after produce, space = %2d, good = %2d, total = %d\n", p[0], p[1], p[0] + p[1]);
            }
            semop(semid, &V, 1);
            // sleep(1);
        }
    }
    else
    {
        while (1)
        {
            semop(semid, &P, 1);
            if (p[1] > 0)
            {
                printf("before consume , space = %2d, good = %2d, total = %d\n", p[0], p[1], p[0] + p[1]);
                --p[1];
                ++p[0];
                printf("after consume, space = %2d, good = %2d, total = %d\n", p[0], p[1], p[0] + p[1]);
            }
            semop(semid, &V, 1);
            // usleep(100000);
        }
        wait(NULL);
    }
    shmdt(p);
    shmctl(shmid, IPC_RMID, NULL);
}

写这种PV操作时一定要分析清楚临界资源有哪些,一定要把所有对共享资源的访问保护到。

但是还是老话,实际工作当中我们不会用这样的方式,会用一些更加高效的手段,这后面再说。

消息队列

所谓消息队列其实有两种,我们这里说的是在进程间通信的消息队列,是一种IPC机制,而另一种消息队列是广义消息队列,它是一种网络通信的中间件,比如RacketMq之类的。

这里说的消息队列和之前说的管道其实非常相似,区别在于消息队列可以保留消息的边界。

什么意思?

先来说管道没有保留消息边界的含义:
比如我们用write系统调用写了两句消息,“你好”,“坏人”,通过管道传输给另一端,因为管道是流式消息,它根据先进先出原则在管道另一头的消息是:“人”“坏”“好”“你”,在管道另一边我们可以选择直接全部取出来也可以选择只读几个字,假如读三个字的话:“你好坏”,最后只剩个“人”了,消息含义就变了,
这就是由于管道没有保留消息边界所造成的:
在这里插入图片描述
后面学习网络编程时TCP也是流式传送数据的。

消息队列的做法则不同,还是以上面说的为例,发送方发送你好坏人,每一条消息都会以数据包的形式发送,会一个包一个包的发送给另一端,另一端自然只能一个包一个包的接收,这样就可以保留消息边界:
在这里插入图片描述
后面学习网络编程时UDP也是按数据包的形式传送的。

我们通过msgget系统调用来创建消息队列,注意介绍的依然是System V标准的系统调用,因为Posix标准的太难用:
在这里插入图片描述
对该系统调用的一些解析(注意消息队列是先进先出的):
在这里插入图片描述
代码简单测试:
发送数据的程序:

#include <43func.h>
typedef struct msgbuf{
    long mtype;
    char mtext[256];
} myMsg_t;
int main(){
    int msqid = msgget(1000,IPC_CREAT|0600);
    ERROR_CHECK(msqid,-1,"msgget");
    myMsg_t msg1;//Huangxiaoming
    myMsg_t msg2;//Wuyifan
    myMsg_t msg3;//Caixukun
    msg1.mtype = 1;
    strcpy(msg1.mtext,"Ganenguoqusuoyou,weilairenshijiaren");
    msg2.mtype = 2;
    strcpy(msg2.mtext,"skr skr~");
    msg3.mtype = 3;
    strcpy(msg3.mtext,"jinitaimei");
    msgsnd(msqid,&msg1,strlen(msg1.mtext), 0);
    msgsnd(msqid,&msg2,strlen(msg2.mtext), 0);
    msgsnd(msqid,&msg3,strlen(msg3.mtext), 0);
    puts("send over");

}

接收数据的程序:

#include <43func.h>
typedef struct msgbuf{
    long mtype;
    char mtext[256];
} myMsg_t;
int main(){
    int msqid = msgget(1000,IPC_CREAT|0600);
    ERROR_CHECK(msqid,-1,"msgget");
    long type;
    printf("who are you? 1 huangxiaoming 2 wuyifan 3 caixukun\n");
    scanf("%ld",&type);
    myMsg_t msg;
    memset(&msg,0,sizeof(msg));
    //msgrcv(msqid,&msg,sizeof(msg.mtext),type,0);
    //msgrcv(msqid,&msg,sizeof(msg.mtext),0,0);
    int ret = msgrcv(msqid,&msg,sizeof(msg.mtext),0,IPC_NOWAIT);
    ERROR_CHECK(ret,-1,"msgrcv");
    printf("you are %ld, msg = %s\n", type, msg.mtext);
}

proc文件系统

我们来介绍一下这个东西,我们在根目录下打开可以看见该文件目录,cd进行展示会发现很多文件大小都是0:
在这里插入图片描述
这是因为proc文件系统不是一个真正的磁盘系统,而是一个伪文件系统。

proc是process的缩写,所以proc文件系统的内容是操作系统的运行状态在文件系统中的映射。

这样做的好处是可以像修改文件一样去修改操作系统的属性。

proc文件目录下面的数字开头的内容其实对应的就是OS中的每一个进程,数字代表进程的PID号。

在这里插入图片描述
我们还可以进入这个sys文件目录下,即系统进程下去查看很多信息:
在这里插入图片描述
这里面有个kernel内核信息,我们可以在内核文件夹里去修改很多的内容,比如改共享内存的内容啊改消息队列的内容啊之类的。

信号

信号是一种软件层面的异步事件机制。

信号可能是进程发给进程的,也有可能是操作系统发给进程的。

而与之对应的在硬件层面的异步事件机制是中断。

使用man 7 signal命令查看信号手册:
在这里插入图片描述

信号默认行为

在这里插入图片描述

从上到下分别是:终止、忽略、终止并生成core、暂停、恢复。

在这里插入图片描述
可以看见上面的每个信号都会对应一个默认的行为。

我们学习这一节的内容,就是为了实现更改默认的信号行为,所以上面的内容只是铺垫。

信号产生的时机

在这里插入图片描述
进程或者操作系统或者硬件产生信号,然后递送到目标进程,中间会有一个传送的时间间隔,我们接下来的事情不会去管产生信号的行为,而是去修改传送信号的时间以及目标进程递送信号的行为。

当信号产生时

在这里插入图片描述

更改默认的信号行为

现在我们试图让信号递送时不再执行默认操作,而是调用一个函数。

这里我们可以用signal系统调用看看,用man 2 signal命令查看其man手册:
在这里插入图片描述
这个系统调用的作用是用来注册一个信号处理行为,注册的含义是等到信号到来时才会调用。
简单使用代码测试一下:
在这里插入图片描述

低速系统调用

低速系统调用是指可能陷入永久等待的系统调用。

在这里插入图片描述
signal特点之一就是一次注册,永久生效,如何改变这种永久生效的效果为让注册只生效一次?
可以使用SIG_DFL,设置其为默认的行为模式:
在这里插入图片描述
特点之二时,递送A时,会将A假如mask,其它信号不会加入mask,且会自动重启低速系统调用。

为了更好的控制上述特点,我们可以使用sigaction来替代signal:
在这里插入图片描述
虽然sigaction可以更好的控制信号,但是也更加复杂了,主要体现在其参数结构体的设计上:
在这里插入图片描述
简单测试:

#include <43func.h>
void sigFunc(int num){
    printf("before, num = %d\n", num);
    sleep(3);
    printf("after, num = %d\n", num);
}
void sigFunc3(int num, siginfo_t *siginfo , void * p){
    printf("num = %d\n", num);
    printf("sender pid = %d\n", siginfo->si_pid);
}
int main(){
    struct sigaction act;
    memset(&act,0,sizeof(act));
    act.sa_handler = sigFunc;
    //act.sa_sigaction = sigFunc3;
    //act.sa_flags = SA_RESTART|SA_SIGINFO|SA_RESETHAND;
    //act.sa_flags = SA_RESTART|SA_NODEFER;
    act.sa_flags = SA_RESTART;
    sigaddset(&act.sa_mask,SIGQUIT);
    int ret = sigaction(SIGINT,&act,NULL);
    ERROR_CHECK(ret,-1,"sigaction");
    //ret = sigaction(SIGQUIT,&act,NULL);
    //ERROR_CHECK(ret,-1,"sigaction");
    char buf[100] = {0};
    read(STDIN_FILENO,buf,sizeof(buf));
    printf("buf = %s\n", buf);
    //while (1)
    //{
    //}
    
}

sa_mask

在这里插入图片描述

sa_mask用来指定递送过程中的额外屏蔽信号

在这里插入图片描述

sigprocmask实现全程阻塞

在这里插入图片描述
简单测试:
在这里插入图片描述
运行结果:
在这里插入图片描述

获取pending集合

在这里插入图片描述

pause等待信号

在这里插入图片描述

kill发送信号

在这里插入图片描述

简单代码测试:
在这里插入图片描述
运行结果:
在这里插入图片描述
使用raise系统调用可以给自己发信号:
在这里插入图片描述
简单的代码测试:
在这里插入图片描述
运行效果:
在这里插入图片描述
在实现有序退出时该函数还是有用的。

alarm系统调用:定闹钟

在这里插入图片描述

可以简单的使用一下:
在这里插入图片描述

时钟

在这里插入图片描述

在这里插入图片描述
简单使用:
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1118536.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

springboot生成二维码的正确姿势-附视频附源码

二维码的原理是什么&#xff0c;如何保证不重复&#xff1f;你有没有想过这样一件事&#xff0c;二维码是实现原理是什么&#xff1f;如何保证各个平台的二维码是唯一的&#xff1f;就算你的程序停止运行&#xff0c;但是你的二维码依然存在。设计上要保证唯一性&#xff0c;比…

ubuntu启动模式介绍以及如何进入单用户模式和恢复模式

Ubuntu操作系统提供了多种启动模式&#xff0c;每种模式都有不同的用途和功能。下面将深入介绍Ubuntu的几种启动模式&#xff1a; 正常启动模式&#xff08;Normal boot&#xff09;&#xff1a;这是默认的启动模式&#xff0c;也是大多数用户使用的模式。在正常启动模式下&am…

Linux 救援模式

Linux突然坏了 第三次坏了 第一次是找不到盘&#xff0c;修复好了 第二次是找不到卷&#xff0c;但是能启动&#xff0c;启动界面选择救援模式&#xff0c;可以正常使用 第三次&#xff0c;尝试修复卷&#xff0c;启动后&#xff0c;找不到文件系统了&#xff0c;只能从光盘…

【C++】继承和多态常见的问题

一、概念考查 1、下面哪种面向对象的方法可以让你变得富有( A ) A. 继承 B. 封装 C. 多态 D. 抽象 继承机制是面向对象程序设计使代码可以复用的最重要手段&#xff0c;继承是类设计层次的复用。 2、( D )是面向对象程序设计语…

【MySQL】数据库数据类型

文章目录 1. 整体概要2. 数值类型(有符号) tinyint 创建表(无符号) tinyint 创建表bit类型float 类型(无符号)floatdecimal 3. 二进制类型char类型varchar类型 4. 日期时间日期时间类型 5. string 类型enum类型和set类型enum类型和set类型的查找在枚举中的查找在set中的查找 1.…

MOSFET(五):DrMos

一、简介 DrMos&#xff08;Driver MOSFET&#xff09;技术是 Intel 于2004年推出的服务器主板节能技术&#xff0c;即把 2 个MOSFET和 1 个MOS驱动器 三合一&#xff0c;集成在一个封装中。集成后的 DrMos 面积是分离MOSFET的 &#xff0c;功率密度是其 倍&#xff0c;通过搭…

华为eNSP配置专题-BGP路由协议的配置

文章目录 华为eNSP配置专题-BGP路由协议的配置0、概要介绍1、前置环境1.1、宿主机1.2、eNSP模拟器 2、基本环境搭建2.1、终端构成和连接2.2、终端的基本配置 3、OSPF配置4、BGP配置4.1、BGP的基本配置4.2、BGP中路由的宣告4.3、BGP的监控 5、让PC1和PC2互通5.1、将BGP的路由引入…

【鸿蒙软件开发】ArkTS常用组件之Button

文章目录 前言一、创建按钮1.1 Button创建接口介绍1.2 创建正常的按钮&#xff0c;不包括子组件1.3 创建正常的按钮&#xff0c;包括子组件1.4 按钮的不同样式胶囊按钮&#xff08;默认类型&#xff09;圆形按钮普通按钮 二、添加事件2.1 .onClick事件添加事件 三、什么时候使用…

【十四】记一次MySQL宕机恢复过程,MySQL INNODB 损坏恢复

记一次MySQL宕机恢复过程 简介&#xff1a;一个业务数据库疏于运维管理&#xff0c;突然在今天崩溃宕机了&#xff0c;真是让人抓狂&#xff0c;上面也不知道积累了多久的数据&#xff0c;平时也没有定期做好备份&#xff0c;这下岂不是瞎了啊&#xff0c;经过不断的收集信息和…

Base 编码家族:Base16 编码

文章目录 参考环境Base 编码Base 的含义计数系统编码系统 为什么需要 Base 编码&#xff1f;ASCII 编码 Base16 编码概念Base16 字符集 Base16 编码原理编码 Base16 编码特点体积膨胀 参考 项目描述搜索引擎Bing、GoogleAI 大模型文心一言、通义千问、讯飞星火认知大模型、Cha…

Git Bash(一)Windows下安装及使用

目录 一、简介1.1 什么是Git&#xff1f;1.2 Git 的主要特点1.3 什么是 Git Bash&#xff1f; 二、下载三、安装3.1 同意协议3.2 选择安装位置3.3 其他配置&#xff08;【Next】 即可&#xff09;3.4 安装完毕3.5 打开 Git Bash 官网地址&#xff1a; https://www.git-scm.com/…

【C++】特殊类实现

一、请设计一个类&#xff0c;不能被拷贝 拷贝只会放生在两个场景中&#xff1a;拷贝构造函数以及赋值运算符重载&#xff0c;因此想要让一个类禁止拷贝&#xff0c; 只需让该类不能调用拷贝构造函数以及赋值运算符重载即可。 C98 将拷贝构造函数与赋值运算符重载只声明不定义…

聚类分析 | Python密度聚类(DBSCAN)

密度聚类是一种无需预先指定聚类数量的聚类方法&#xff0c;它依赖于数据点之间的密度关系来自动识别聚类结构。 本文中&#xff0c;演示如何使用密度聚类算法&#xff0c;具体是DBSCAN&#xff08;Density-Based Spatial Clustering of Applications with Noise&#xff09;来…

磁盘清理 | 已经卸载的软件还出现在应用和功能里怎么办?

一句话总结解决方法&#xff1a; 安装Geek Uninstaller,删除卸载残留。 问题描述&#xff1a; 最近磁盘满了&#xff0c;需要删除一些平时不常用的软件&#xff0c;但是发现一个问题。就是已经删除的软件&#xff0c;仍然会出现在“应用与功能”中。并且显示卸载图标为灰色&am…

基于PHP的线上购物商城,MySQL数据库,PHPstudy,原生PHP,前台用户+后台管理,完美运行,有一万五千字论文。

目录 演示视频 基本介绍 论文截图 功能结构 系统截图 演示视频 基本介绍 基于PHP的线上购物商城&#xff0c;MySQL数据库&#xff0c;PHPstudy&#xff0c;原生PHP&#xff0c;前台用户后台管理&#xff0c;完美运行&#xff0c;有一万五千字论文。 现如今,购物网站是商业…

用Python进行websocket接口测试

这篇文章主要介绍了用Python进行websocket接口测试&#xff0c;帮助大家更好的理解和使用python&#xff0c;感兴趣的朋友可以了解下 我们在做接口测试时&#xff0c;除了常见的http接口&#xff0c;还有一种比较多见&#xff0c;就是socket接口&#xff0c;今天讲解下怎么用P…

(一)docker:建立oracle数据库

前言&#xff0c;整个安装过程主要根据docker-images/OracleDatabase/SingleInstance /README.md &#xff0c;里边对如何制作容器讲的比较清楚&#xff0c;唯一问题就是都是英文&#xff0c;可以使用谷歌浏览器自动翻译成中文&#xff0c;自己再对照英文相互参照来制作提前准备…

P1 缓冲池管理

文章目录 Task1 LRU-K 替换策略Task2 缓冲池管理Task3 读/写页面保护 Task1 LRU-K 替换策略 LRU-K算法&#xff1a;当访问次数达到K次后&#xff0c;将数据索引从历史队列移到缓存队列中&#xff08;缓存队列时间降序&#xff09;&#xff1b;缓存数据队列中被访问后重新排序&…

Python--循环中的两大关键词 break 与 continue

在Python循环中&#xff0c;经常会遇到两个常见的关键词&#xff1a;break 与 continue break&#xff1a;代表终止整个循环结构 continue&#xff1a;代表中止当前本次循环&#xff0c;继续下一次循环 break&#xff1a; 英 /breɪk/ v. 打破&#xff0c;打碎&#xff0c…

c语言练习95:练习使用双向链表(实现增删改查)

练习使用双向链表&#xff08;实现增删改查&#xff09; 是指针指向了一块被释放的空间 解决方案&#xff1a; plistNULL List.h #pragma once #define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<stdlib.h> #include<assert.h> #include<…