Linux多进程和多线程(四)进程间通讯-定时器信号和子进程退出信号

news2024/11/14 19:08:00
  • 多进程(四)
    • 定时器信号
    • alarm()函数
    • 示例
    • alarm()函数的限制
    • 定时器信号的实现原理
    • setitimer()函数
    • setitimer()和alarm()函数的区别
      • setitimer()
        • old_value参数的示例
      • 对比alarm()
      • 区别总结:
  • 子进程退出信号
    • 示例:

多进程(四)

定时器信号

SIGALRM 信号是用来通知进程的定时器到期的。它是一个非可靠信号,即使进程捕获了它也不能保证定时器一定会到期。

定时器信号的使用场景:

  1. 定时器信号可以用来实现计时器功能。
  2. 定时器信号可以用来实现超时重试功能。
  3. 定时器信号可以用来实现定时任务功能。

在这里插入图片描述

alarm()函数

alarm()函数用来设置一个定时器,单位是秒。当定时器到期时,会向进程发送SIGALRM信号。
函数原型:

unsigned int alarm(unsigned int seconds);

返回值:

  • 如果成功设置定时器,则返回之前的定时器值(以秒为单位)。
  • 如果定时器未设置,则返回0。

参数说明:

  • seconds:定时器的秒数。

要点:

定时器的定时任务由内核完成, alarm 函数值负责设置定时时间, 并告诉内核启动定时器
当定时时间超时后,内核会向进程发出 SIGALRM 信号

示例

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <signal.h>
#include <unistd.h>
#include <sys/wait.h>

//信号处理函数
void do_sig_alarm(int sig);
int main(){
    unsigned int ret;

    ret= alarm(5);//设置定时器,5秒,由内核来进行定时,时间结束后会发送SIGALRM信号
    printf("定时器返回: %d\n", ret);//ret为0,前一次设置定时器或者定时器时间已经用完

//    ret= alarm(5);//设置定时器,5秒,由内核来进行定时
//    printf("定时器返回: %d\n", ret);//ret为5

    __sighandler_t r;//信号处理函数的返回值
    r=signal(SIGALRM,do_sig_alarm);
    if(r==SIG_ERR){//出错处理
        perror("signal");//出错处理
        exit(1);//退出程序
    }
    pause();//暂停程序,等待定时器超时,时间结束后会发送SIGALRM信号 默认是结束进程


    return 0;
}

//信号处理函数
void do_sig_alarm(int sig){
    printf("收到定时器超时信号 : %d\n", sig);
}

alarm()函数的限制

  • 定时器信号是非可靠信号,即使进程捕获了它也不能保证定时器一定会到期。
  • 定时器信号只对进程有效,对子进程不起作用。
  • 定时器信号只对定时器设置的时间有效,不会影响到其他的定时器。

定时器信号的实现原理

  • 定时器信号是由内核来进行定时,并向进程发送SIGALRM信号。
  • 定时器信号的实现依赖于系统调用alarm()函数。
  • 定时器信号的实现原理是,内核在进程的进程控制块(PCB)中设置一个定时器,并将定时器的到期时间写入到进程的用户态的定时器中。
  • 当定时器到期时,内核向进程发送SIGALRM信号,进程捕获到信号后,可以执行定时器到期的任务。

setitimer()函数

setitimer()函数用来设置一个定时器,单位是秒。当定时器到期时,会向进程发送SIGALRM信号。


setitimer()和alarm()函数的区别

setitimer() 和 alarm() 函数在操作系统中都用于设置定时器,但它们有一些显著的区别:

setitimer()

setitimer() 函数更为灵活和精细,它可以设置三种类型的定时器,分别是:

ITIMER_REAL:实际时间定时器,当定时器到期时,会发送 SIGALRM 信号。

ITIMER_VIRTUAL:与进程在用户态下执行时间相关,当进程在用户态下运行时间到期时,会发送 SIGVTALRM 信号。

ITIMER_PROF:与进程在用户态和内核态下执行时间相关,当定时器到期时,会分别发送 SIGPROF 信号。

它的函数原型如下:

#include <sys/time.h>
int setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value);
参数解释:
which:选择哪种定时器(ITIMER_REAL、ITIMER_VIRTUAL 或 ITIMER_PROF)。
new_value:指向一个 itimerval 结构,该结构指定定时器的初始响应时间和周期性时间间隔。
old_value:指向一个 itimerval 结构,如果不是 NULL,将返回先前的定时器设置。

old_value是可选参数,如果不为 NULL,则函数调用成功后,old_value 结构将包含先前的定时器设置。

itimerval结构体定义如下:
struct itimerval {
     struct timeval it_interval; /* 间隔时间 */
     struct timeval it_value;    /* 到期时间 */
 };

 timeval结构体定义如下:
 struct timeval {
     time_t tv_sec;    /* 秒 */
     suseconds_t tv_usec; /* 微秒 */
 };

示例

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <signal.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/time.h>

void do_sig_alarm(int sig);

int main(){
    struct itimerval itv;//定义定时器结构体
    itv.it_value.tv_sec=3;//设置定时器时间为5秒
    itv.it_value.tv_usec=0;//设置微秒为0
    itv.it_interval.tv_sec=5;//设置定时器间隔时间为5秒
    itv.it_interval.tv_usec=0;//设置微秒为0


    if(setitimer(ITIMER_REAL,&itv,NULL)!=0){//设置定时器
        perror("setitimer");
        exit(1);
    }
    __sighandler_t r;//信号处理函数的返回值
    r=signal(SIGALRM,do_sig_alarm);
    if(r==SIG_ERR){//出错处理
        perror("signal");//出错处理
        exit(1);//退出程序
    }
    while(1) {
        pause();//暂停程序等待信号
    }
    return 0;
}

void do_sig_alarm(int sig){
    printf("收到定时器超时信号 : %d\n", sig);
}
old_value参数的示例

old_value参数用于返回setitimer()函数调用之前的定时器设置。这对于获取定时器的先前状态或恢复定时器的原始状态非常有用

示例程序,演示如何使用old_value参数来保存和恢复定时器的原始状态:

#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <sys/time.h>

// 定时器处理函数
void timer_handler(int signum) {
    printf("Timer expired\n");
}

int main() {
    struct itimerval timer, old_timer;

    // 设置信号处理程序
    signal(SIGALRM, timer_handler);

    // 初始化新定时器设置
    timer.it_value.tv_sec = 5;        // 定时器第一次触发的时间(秒)
    timer.it_value.tv_usec = 0;       // 定时器第一次触发的时间(微秒)
    timer.it_interval.tv_sec = 1;     // 定时器周期性触发的时间间隔(秒)
    timer.it_interval.tv_usec = 0;    // 定时器周期性触发的时间间隔(微秒)

    // 设置新的定时器,并获取旧的定时器设置
    if (setitimer(ITIMER_REAL, &timer, &old_timer) == -1) {
        perror("setitimer");
        return 1;
    }

    // 打印旧的定时器设置
    printf("Old timer:\n");
    printf("it_value: %ld sec, %ld usec\n", (long)old_timer.it_value.tv_sec, (long)old_timer.it_value.tv_usec);
    printf("it_interval: %ld sec, %ld usec\n", (long)old_timer.it_interval.tv_sec, (long)old_timer.it_interval.tv_usec);

    // 等待定时器触发
    while (1) {
        pause(); // 让进程一直等待信号打断
    }

    return 0;
}

运行结果:

程序运行时会输出旧定时器的设置,并在5秒后第一次触发定时器,然后每秒触发一次,输出"Timer expired"。

通过这种方式,你可以使用old_value参数来保存和恢复以前的定时器设置。

对比alarm()

alarm() 函数是一个简单的工具,用于设置一个秒级别的定时器,
当这个定时器到期时,操作系统会发送一个 SIGALRM 信号给调用进程。
它只有一种模式,而且只能设置一个定时器。

它的函数原型如下:

#include <unistd.h>
unsigned int alarm(unsigned int seconds);

seconds:指定定时器的时间(秒)。

区别总结:

定时器类型:

setitimer() 支持三种定时器类型(实际时间、用户态时间、用户态和内核态时间),更灵活。
alarm() 只支持实际时间定时器。

精度:

setitimer() 支持微秒级精度。

alarm() 只支持秒级精度。

支持多个定时器:

setitimer() 可以同时设置和管理多个不同类型的定时器。

alarm() 只能设置一个定时器,后续调用会覆盖之前的定时器。

子进程退出信号

在使⽤ wait() 函数时,由于阻塞或者⾮阻塞都⾮常消耗资源,并且在阻塞情况下,⽗
进程不能执⾏其他逻辑

⼦进程退出是异步事件, 可以利⽤在⼦进程退出时,会⾃动给⽗进程发送 SIGCHLD 信号
在这里插入图片描述

示例:

void do_sig_child(int sig);

int main(){
    pid_t cpid;//子进程id


    __sighandler_t r;//信号处理函数的返回值
    r=signal(SIGCHLD,do_sig_child);
    if(r==SIG_ERR){//出错处理
        perror("signal");//出错处理
        exit(1);//退出程序
    }


    cpid=fork();//创建子进程
    if(cpid==-1){//出错处理
        perror("fork");//出错处理
        exit(1);//退出程序
    }else if(cpid==0){//子进程
        printf("子进程pid: %d\n", getpid());
        sleep(5);//睡眠5秒
        exit(999);//退出子进程
    }else{//父进程
        printf("父进程pid: %d\n", getpid());
        //父进程不会因为等待子进程而阻塞,会继续往下执行
        while (1){

        }
        printf("父进程结束\n");
    }

    return 0;

}
//子进程退出后会发送SIGCHLD, SIGCHLD默认处理方式是忽略的,内核现在会调用这个函数
void do_sig_child(int sig){
    printf("SIGCHLD处理函数: %s\n", strsignal(sig));//打印 : SIGCHLD处理函数: Child exited
    wait(NULL  );//不会阻塞会立即释放子进程资源
    //通常情况下,当一个子进程终止时,它会进入一种称为“僵尸进程”的状态,
    // 保留一些必要的信息以便父进程能够获取其终止状态。
    // 如果父进程不调用 wait 或 waitpid 来获取该信息并释放资源,
    // 那么僵尸进程将一直存在,直到父进程终止。这会导致资源泄漏问题
}


后会发送SIGCHLD, SIGCHLD默认处理方式是忽略的,内核现在会调用这个函数
void do_sig_child(int sig){
    printf("SIGCHLD处理函数: %s\n", strsignal(sig));//打印 : SIGCHLD处理函数: Child exited
    wait(NULL  );//不会阻塞会立即释放子进程资源
    //通常情况下,当一个子进程终止时,它会进入一种称为“僵尸进程”的状态,
    // 保留一些必要的信息以便父进程能够获取其终止状态。
    // 如果父进程不调用 wait 或 waitpid 来获取该信息并释放资源,
    // 那么僵尸进程将一直存在,直到父进程终止。这会导致资源泄漏问题
}


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

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

相关文章

ctfshow web 36d 练手赛

不知所措.jpg 没啥用然后测试了网站可以使用php伪达到目的 ?filephp://filter/convert.base64-encode/resourcetest/../index.<?php error_reporting(0); $file$_GET[file]; $file$file.php; echo $file."<br />"; if(preg_match(/test/is,$file)){inclu…

统一视频接入平台LntonCVS视频监控平台具体功能介绍

LntonCVS视频监控平台是一款基于H5技术开发的安防视频监控解决方案&#xff0c;专为全球范围内不同品牌、协议及设备类型的监控产品设计。该平台提供了统一接入管理&#xff0c;支持标准的H5播放接口&#xff0c;使其他应用平台能够快速集成视频功能。无论开发环境、操作系统或…

24-7-6-读书笔记(八)-《蒙田随笔集》[法]蒙田 [译]潘丽珍

文章目录 《蒙田随笔集》阅读笔记记录总结 《蒙田随笔集》 《蒙田随笔集》蒙田&#xff08;1533-1592&#xff09;&#xff0c;是个大神人&#xff0c;这本书就是250页的样子&#xff0c;但是却看了好长好长时间&#xff0c;体会还是挺深的&#xff0c;但看的也是不大仔细&…

《第一行代码》小结

文章目录 一. Android总览1. 系统架构2. 开发环境3. 在红米手机上运行4. 项目资源详解4.1 整体结构4.2 res文件4.3 build.gradle文件 二. Activity0. 常用方法小结1. 创建一个Activity 一. Android总览 1. 系统架构 应用层&#xff1a;所有安装在手机上的应用程序 应用框架层&…

vb.netcad二开自学笔记3:启动与销毁

Imports Autodesk.AutoCAD.ApplicationServicesImports Autodesk.AutoCAD.EditorInputImports Autodesk.AutoCAD.RuntimePublic Class WellcomCADImplements IExtensionApplicationPublic Sub Initialize() Implements IExtensionApplication.InitializeMsgBox("net程序已…

字节跳动与南开联合开源 StoryDiffusion:一键生成漫画和视频故事的神器!完全免费!

大家好&#xff0c;我是程序员X小鹿&#xff0c;前互联网大厂程序员&#xff0c;自由职业2年&#xff0c;也一名 AIGC 爱好者&#xff0c;持续分享更多前沿的「AI 工具」和「AI副业玩法」&#xff0c;欢迎一起交流~ 漫画&#xff0c;是多少人童年的回忆啊&#xff01; 记得小学…

Sahi+Yolov10

一、前言 了解到Sahi&#xff0c;是通过切图&#xff0c;实现提高小目标的检测效果。sahi 目前支持yolo5\yolo8\mmdet\detection2 等等算法&#xff0c;本篇主要通过实验onnx加载模型的方式使sahi支持yolov10。 二、代码 &#xff08;1&#xff09;转换模型 首先使用 conda创…

EtherCAT转Profinet网关配置说明第一讲:配置软件安装及介绍

网关XD-ECPNS20为EtherCAT转Profinet协议网关&#xff0c;使EtherCAT协议和Profinet协议两种工业实时以太网网络之间双向传输 IO 数据。适用于具有EtherCAT协议网络与Profinet协议网络跨越网络界限进行数据交换的解决方案。 本网关通过上位机来进行配置。 首先安装上位机软件 一…

DP:二维费用背包问题

文章目录 &#x1f3b5;二维费用背包问题&#x1f3b6;引言&#x1f3b6;问题定义&#x1f3b6;动态规划思想&#x1f3b6;状态定义和状态转移方程&#x1f3b6;初始条件和边界情况 &#x1f3b5;例题&#x1f3b6;1.一和零&#x1f3b6;2.盈利计划 &#x1f3b5;总结 &#x1…

Explore Synapse

rm -r dp-203 -f git clone https://github.com/MicrosoftLearning/dp-203-azure-data-engineer dp-203 cd dp-203/Allfiles/labs/01 ./setup.ps1 -- This is auto-generated code SELECTTOP 100 * FROMOPENROWSET(BULK https://datalakexxxxxxx.dfs.core.windows.net/fil…

单/多线程--协程--异步爬虫

免责声明:本文仅做技术交流与学习... 目录 了解进程和线程 单个线程(主线程)在执行 多线程 线程池 协程(爬虫多用) 假异步:(同步) 真异步: 爬虫代码模版 异步-爬虫 同步效果--19秒 异步效果--7秒 了解进程和线程 ​ # --------------------> # ------> # …

Opencv的基本操作(一)图像的读取显示存储及几何图形的绘制

文件的读取、显示、存取 cv2.imread(imagepath,IMREAD.xxx) 读取图像cv2.imshow(窗口名称,mat图片) 显示图像cv2.imwrite(保存的位置,img) 保存图像 # 1. 读取图像 原始图片路径&#xff0c;图片读取模式 cv2.imread(imagepath,IMREAD.xxx)cv2.IMREAD_COLOR 彩色模式读取 cv2…

PostgreSQL 如何优化存储过程的执行效率?

文章目录 一、查询优化1. 正确使用索引2. 避免不必要的全表扫描3. 使用合适的连接方式4. 优化子查询 二、参数传递1. 避免传递大对象2. 参数类型匹配 三、减少数据量处理1. 限制返回结果集2. 提前筛选数据 四、优化逻辑结构1. 分解复杂的存储过程2. 避免过度使用游标 五、事务处…

贵州建筑三类人员安全员2024年考试最新题库练习题

一、单选题 1.建设工程安全管理的方针是&#xff08;&#xff09;。 A.安全第一&#xff0c;预防为主&#xff0c;综合治理 B.质量第一&#xff0c;兼顾安全 C.安全至上 D.安全责任重于泰山 答案&#xff1a;A 2.安全生产管理的根本目的是&#xff08;&#xff09;。 A.…

zerotier-one自建根服务器方法五

一、简介 前面几篇文章已经写完了自己建立服务器的方法&#xff0c;今天写一下我在使用过程中遇到的问题和解决方法。 二、准备工作 准备一个有公网IP的云主机。 要稳定性、安全性、不差钱的可以使用阿里、腾讯等大厂的云服务器。 本人穷屌丝一枚&#xff0c;所以我用的是免…

数据结构1:C++实现边长数组

数组作为线性表的一种&#xff0c;具有内存连续这一特点&#xff0c;可以通过下标访问元素&#xff0c;并且下标访问的时间复杂的是O(1)&#xff0c;在数组的末尾插入和删除元素的时间复杂度同样是O(1)&#xff0c;我们使用C实现一个简单的边长数组。 数据结构定义 class Arr…

CentOS 7.9 停止维护(2024-6-30)后可用在线yum源 —— 筑梦之路

众所周知&#xff0c;centos 7 在2024年6月30日&#xff0c;生命周期结束&#xff0c;官方不再进行支持维护&#xff0c;而很多环境一时之间无法完全更新替换操作系统&#xff0c;因此对于yum源还是需要的&#xff0c;特别是对于互联网环境来说&#xff0c;在线yum源使用方便很…

第6章 选课学习:需求分析,添加选课,支付,支付通知,在线学习

1 模块需求分析 1.1 模块介绍 本模块实现了学生选课、下单支付、学习的整体流程。 网站的课程有免费和收费两种&#xff0c;对于免费课程学生选课后可直接学习&#xff0c;对于收费课程学生需要下单且支付成功方可选课、学习。 选课&#xff1a;是将课程加入我的课程表的过…

关于HTTP的攻击实验

实验原理&#xff1a;1. 根据ARP中间人攻击&#xff0c;获取 用户和服务器之间的数据2. 将获取到的数据 通过一定的技术来复原&#xff0c;进而获取用户的信息或者 相关权限实验拓扑图 将 kali 的网卡改为桥接模式&#xff0c;查看Kali和本机的ip 启动ettercap&#xff0c;…

CANopen协议开发梳理总结笔记教程

0、提醒 CANOpen使用时&#xff0c;需要清楚什么是大端和小端&#xff0c;这对于CANOpen数据发送及解析时&#xff0c;有很大的帮助。且学习开发CANOpen时&#xff0c;需要具备一定的CAN基础。 1、CANOpen协议介绍 ①、什么是CANOpen协议 CANOpen协议是一种架构在控制局域网络…