《Linux C编程实战》笔记:信号的发送

news2024/9/30 23:32:01

信号的发送主要由函数kill、raise、sigqueue、alarm、setitimer以及abort来完成

kill函数

kill函数用来发送信号给指定的进程。

#include<sys/types.h>
#include<signal.h>
int kill(pid_t pid,int sig);

该函数的行为与第一个参数pid有关,第二个参数sig表示信号编号。

  • 如果pid是正数,则发送信号sig给进程号为pid的进程
  • 如果pid为0,则发送信号sig给当前进程所属的进程组里的所有进程
  • 如果pid为-1,则把信号sig广播至系统除1号进程(init进程)和自身以外的所有进程
  • 如果pid是比-1还小的负数,则发送信号sig给属于进程组-pid(也就是pid的绝对值) 的所有进程
  • 如果参数sig是0,则kill仍执行正常的错误检查,但不发送信号。可以利用这一点来确定某新城是否有权向另外一个进程发送信号。如果向一个并不存在的进程发送空信号,则kill返回-1,errno被设置为ESRCH

函数执行成功返回0,当有错误发生时返回-1,错误代码存入errno。

注意:只有具有root权限的进程才能向其他任意进程发送信号,非root权限的进程只能向属于同一个组或同一个用户的进程发送信号。

示例程序1

该示例程序实现了自己的kill命令。在shell中kill命令也是用来发送信号的,我们用代码仿照shell的kill命令,但是不支持-l选项(显示信号编号)

//本程序只支持按信号的编号发送信号
#include<stdio.h>
#include<sys/types.h>
#include<signal.h>
#include<stdlib.h>
#include<string.h>
int main(int argc,char **argv){
    int i,j;
    int signum=SIGTERM;//默认发送SIGTERM
    pid_t pid;
    //首先检查参数,要么是只有一个参数,也就是pid,要么是三个参数,也就是 -s signum pid
    if(argc!=2&&argc!=4){
        printf("Usage:./my_kill <-s signum>[PID]\n");
        exit(0);
    }
    for(i=1;i<argc;i++){
        //如果有-s,找到-s后面的参数,它就是signum
        if(!strcmp(argv[i],"-s")){
            signum=atoi(argv[i+1]);
            break;
        }
    }
    if(argc==2){
        //如果只有一个参数,它就是pid
        pid=atoi(argv[1]);
    }else{
        for(j=1;j<argc;j++){
            //i是在-s,所以j=i+2的时候就指向的是pid
            if(j!=i&&j!=i+1){
                pid=atoi(argv[j]);
                break;
            }
        }
    }
    if(kill(pid,signum)<0){
        perror("kill");
        exit(1);
    }
    return 0;
}

运行的方式:./my_kill -s 2(这是signum) 2568(这是pid)

这个例子可以配合之前几节写的信号接收的程序使用来测试

raise函数

raise函数是ANSI C而非POSIX标准定义的,用来给调用它的进程发送信号。

#include<signal.h>
int raise(int sig);

示例程序2

这里用raise函数写一个自发自收的信号

#include<stdio.h>
#include<sys/types.h>
#include<signal.h>
#include<stdlib.h>
#include<string.h>
void process(int signum){
    printf("recv!\n");
}
int main(int argc,char **argv){
    signal(SIGINT,process);
    raise(SIGINT);
    return 0;
}

这都很简单,就不解释了。

sigqueue函数

这个函数支持信号带有参数,从而可以与函数sigaction配合使用

#include<signal.h>
int sigqueue(pid_t pid,int sig,const union sigval value);

sigqueue的另一个与kill的不同点是它不能给一组进程发送信号

参数value是一个union共用体,定义如下

union sigval{
    int sival_int;
    void *sival_ptr;
};

union的特点就是只能是其中一个。也就是说信号携带的要么是一个整型值,要么是一个void型指针。当接收进程的信号处理函数是由sigaction设置的并且设置了SA_SIGINFO标准,接收进程可以从siginfo_t结构的si_value域取得信号发送时携带的参数

成功执行返回0,发生错误返回-1,错误码存到errno里。

程序3

这个函数书上竟然没有给示例,我尝试自己写一个demo来测试一下。

首先是接收方的程序,设置一下sigaction

#include<stdio.h>
#include<sys/types.h>
#include<signal.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
void receive_process(int signum,siginfo_t *info_ptr,void *unused_ptr){
    printf("recv!\n");
    printf("data is:%d\n",info_ptr->si_value.sival_int);
}
int main(int argc,char **argv){
    printf("my pid:%d\n",getpid());
    struct sigaction act;
    act.sa_flags=SA_SIGINFO;
    act.sa_sigaction=receive_process;
    sigaction(SIGINT,&act,nullptr);
    while(1);
    return 0;
}

然后是发送方的程序

#include<stdio.h>
#include<sys/types.h>
#include<signal.h>
#include<stdlib.h>
#include<unistd.h>
int main(int argc,char **argv){
    pid_t pid=atoi(argv[1]);
    int data=atoi(argv[2]);
    union sigval value;
    value.sival_int=data;
    sigqueue(pid,SIGINT,value);
    return 0;
}

运行结果如图

完美的发送和接收,证明讲的确实没问题。

我在上一节里写的si_value的类型是sigval_t,我去源码里翻了下, sigval_t和union sigval是一样的,只是重新命名了。 

sigaction不会写的可以看我之前写的文章《Linux C编程实战》笔记:信号的捕捉和处理-CSDN博客

alarm函数

alarm函数可以用来设置定时器,定时器超时将产生SIGALRM信号给调用进程。

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

经过seconds秒后,内核将给调用该函数的进程发送SIGALRM信号,如果seconds为0,则不再发送SIGALRM信号。最新一次调用alarm函数将取消之前一次的设定。

注意:alarm只设定为发送一次信号,如果要多次发送,就要对alarm进行多次调用。

示例程序4

该示例程序模拟网络命令ping的功能。

其实我也不知道ping是干啥的...反正跟着敲吧。

#include<stdio.h>
#include<sys/types.h>
#include<signal.h>
#include<stdlib.h>
#include<unistd.h>
void send_ip(){
    printf("send a icmp echo request packet\n");
}
void recv_ip(){
    while(1);
}
void handler_sigalarm(int signo){
    send_ip();
    alarm(2);
}
int main(int argc,char **argv){
    signal(SIGALRM,handler_sigalarm);
    raise(SIGALRM);//触发一个SIGALRM信号给本进程
    recv_ip();
    return 0;
}

其实就是一直在发这段话罢了。整个程序也很好懂,就不多讲了。

getitimer/setitimer函数

与alarm函数一样,setitimer函数也是用来设置定时器的,且alarm和setitimer使用的是同一个定时器,因此会相互影响。setitimer要比alarm具有更多的功能。

#include<sys/time.h>
int getitimer(int which,struct itimerval *value);
int setitimer(int which,const struct itimerval *value,struct itimerval *ovalue);

第一个参数which用来指定使用哪一个定时器,根据参数which可单独设定每个定时器,定时器的种类如下:

 参数value用来指定定时器的时间,结构struct itimerval的定义如下:

struct itimerval{
    struct timeval it_interval;
    struct timeval it_value;
}
//解释一下setitimer里这个结构体的使用,首先定时器时间是it_value,这个计完后会发一个信号,之后
//it_value会重新变为it_interval,并且之后一直以it_interval为间隔循环计时,计到0也会发送信号
struct timeval{
    long tv_sec;//秒数
    long tv_usec;//微秒
}

对于函数getitimer,如果存在由which指定的定时器,则将剩余时间保存在it_value中,该定时器的初始值保存在it_interval中;如果不存在指定类型的定时器,则将value置为0返回。执行成功返回0,当有错误发生时则返回-1,错误代码存入errno中。

示例程序5

#include<stdio.h>
#include<sys/time.h>
#include<signal.h>
#include<stdlib.h>
#include<unistd.h>
//信号处理程序
void handler_sigtimer(int signo){
    switch (signo)
    {
    case SIGALRM:
        printf("recv SIGALRM\n");
        break;
    case SIGPROF:
        printf("recv SIGPROF\n");
        break;
    default:
        break;
    }
}
int main(int argc,char **argv){
    struct itimerval value;
    //安装信号处理函数
    signal(SIGALRM,handler_sigtimer);
    signal(SIGPROF,handler_sigtimer);
    //初始化value结构
    value.it_value.tv_sec=1;    //第一次1秒触发信号
    value.it_value.tv_usec=0;
    value.it_interval.tv_sec=5;    //第二次之后都是5秒触发信号
    value.it_interval.tv_usec=0;
    //设置定时器
    setitimer(ITIMER_REAL,&value,nullptr);
    setitimer(ITIMER_PROF,&value,nullptr);
    while(1);
    return 0;
}

程序设置了两个定时器ITIMER_REAL和ITIMER_PROF。系统经过1秒后,将触发一个SIGALRM信号,以后每5秒触发一个SIGALRM信号。按照程序执行时消耗的时间以及内核因本程序消耗的时间来计时。第一次经过一秒后将触发一个SIGPROF信号,以后每5秒触发一个SIGPROF信号。

abort函数

#include<stdlib.h>
void abort(void);

如果进程设置了信号处理函数以捕获SIGABRT信号,且信号处理函数不返回(如使用longjmp),则abort()不能终止进程。abort()终止进程时,所有打开的流(如i/o流、文件流)均会被刷新和关闭。如果进程设置了SIGABRT被阻塞或忽略,abort()将覆盖这种设置。

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

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

相关文章

幻兽帕鲁安装和开服教学

《幻兽帕鲁》游戏热度异常火爆&#xff0c;很多玩家想下载《幻兽帕鲁》和朋友玩&#xff0c;但不知道在哪里能够下载到&#xff0c;下面请看《幻兽帕鲁》下载安装教学&#xff0c;希望能够帮助大家。 幻兽帕鲁》目前仅在PC上的Steam平台发售&#xff0c;可以登录Steam搜索“幻…

Unity 解决异步分发方案

很多程序&#xff0c;包括游戏、小程序、一些AR、VR的程序&#xff0c;因为客户端体量太大&#xff0c;更新频繁都涉及到远程热更新的问题&#xff0c;解决这类问题的思路基本上是客户端解决主要功能&#xff0c;资源类放置在服务器。 下面记录下&#xff1a; 1.CDN或者云轻量…

AI嵌入式K210项目(18)-卷积人工神经网络硬件加速器 KPU

文章目录 前言一、K210的KPU二、实验过程总结 前言 K210内置了丰富的加速器&#xff0c;包括神经网络处理器 (KPU)&#xff0c;AES(高级加密加速器)&#xff0c;APU 麦克风阵列语音数据加速计算处理器&#xff0c;现场可编程 IO 阵列 (FPIOA)&#xff0c;数字摄像头接口 (DVP)…

源码篇--Redis 五种数据类型

文章目录 前言一、 字符串类型&#xff1a;1.1 字符串的编码格式&#xff1a;1.1.1 raw 编码格式:1.1.2 empstr编码格式:1.1.3 int 编码格式:1.1.4 字符串存储结构展示: 二、 list类型&#xff1a;2.1 List 底层数据支持&#xff1a;2.2 List 源码实现&#xff1a;2.3 List 结构…

水经微图系列产品新功能盘点!

水经微图&#xff0c;简称“微图”。 我们曾在直播中分享过微图APP苹果版的功能&#xff0c;本周四晚19:30我们将在另一个视频号分享盘点微图APP安卓版的详细功能&#xff0c;以及Web版近期上线的新功能功能。 微图APP安卓版 我们在《水经微图安卓版APP正式上线》一文中&…

ABAP SQL CDSView Entity中使用正则RegEx表达式(Regular Expressions)

1. 正则表达式测试程序 DEMO_REGEXDEMO_REGEX_TOY 2. ABAP SQL & CDSView Entity支持正则语法的场景 SQL函数语法作用执行逻辑返回类型CDS View EntitiesABAP SQLLIKE_REGEXPRLIKE_REGEXPR( PCRE pcre, VALUE sql_exp1[, CASE_SENSIT…

Unity Mask合批情况验证

1.首先是两个Mask完全重合的情况下 每张图片使用的image都来自同一个图集 发现彼此之间是没有合批的&#xff0c;但是每个Mask内部是实现了合批的 经过计算此种情况的visiableList&#xff1a;mask1&#xff0c;IM1&#xff0c;IM2&#xff0c;mask2&#xff0c;IM3&#xf…

Centos7 两种方式安装 MySQL5.7 步骤 yum 、本地 tar 文件

一、使用 yum 源方式安装 1、卸载系统自带 mariadb MariaDB Server 是最流行的开源 关系型数据库 之一。它由 MySQL 的原始开发者制作&#xff0c;并保证保持开源。 在 CentOS 7 中默认安装有 MariaDB 可忽略&#xff0c;安装完成之后可以直接覆盖掉 MariaDB。 查看并卸载系统…

如何在阿里云提交使用工单

有时候大家在使用阿里云的服务时候&#xff0c;可能会遇到一些问题&#xff0c;或许是云服务器如何升级了如何改套餐啊之类的&#xff0c;亦或者是域名ICP备案啊看进度啊等等问题&#xff0c;遇到问题怎么办不要慌。我们可以使用阿里云的工单系统&#xff0c;阿里云工单系统可以…

虚拟机网络配置及Moba工具的使用

A、设置IP和网关 1、设置IP [roothadoop00 ~]# vi /etc/sysconfig/network-scripts/ifcfg-eth0 &#xff08;修改如下标红内容&#xff0c;没有的就添加&#xff09; DEVICEeth0 HWADDR08:00:27:BD:9D:B5 #不用改 TYPEEthernet UUID53e4e4b6-9724-43ab-9da7-68792e611031…

Confluence6+mysql5.7破j安装避坑详细记录

目录 一、前言 二、下载与安装 1、版本和安装环境 2、安装数据库 3、配置数据库 4、安装confluence 三、Pj confluence 1、选择语言和产品安装 2、Pj 3、上传mysql驱动 4、重启Confluence服务继续安装 四、Confluence重启卸载方法 重启方法 方法一 方法二 卸载…

acwing 动态规划dp 0 1背包问题

前言 hello小伙伴们&#xff0c;最近由于个人放假原因颓废了一段时间很长时间没有更新CSDN的内容了&#xff0c;唉&#xff0c;毕竟懂得都懂寒暑假静下心来学习的难度远比在学校里大的多。 但是&#xff0c;也不是毫无办法克服&#xff0c;今天我来了我们当地的一家自习室来学习…

《WebKit 技术内幕》学习之四(3): 资源加载和网络栈

3. 网络栈 3.1 WebKit的网络设施 WebKit的资源加载其实是交由各个移植来实现的&#xff0c;所以WebCore其实并没有什么特别的基础设施&#xff0c;每个移植的网络实现是非常不一样的。 从WebKit的代码结构中可以看出&#xff0c;网络部分代码的确比较少的&#xff0c;它们都在…

Mybatis 全局配置文件(三)

文章目录 第一章&#xff1a;概述第二章&#xff1a;properties (了解)第三章&#xff1a;settings第四章&#xff1a;typeAliases (别名处理器)第五章&#xff1a;typeHandlers (类型处理器)第六章&#xff1a;plugins(插件)第七章&#xff1a;environments (环境)第八章&…

k8s-基础知识(Service,NodePort,CusterIP,NameSpace,资源限制)

Node Node 是 Pod 真正运行的主机&#xff0c;可以是物理机&#xff0c;也可以是虚拟机。 Annotations 原文链接 Annotations 是 key/value 形式附加于对象的注解。不同于 Labels 用于标志和选择对象&#xff0c;Annotations 则是用来记录一些附加信息&#xff0c;用来辅助应…

深入理解JS语法与变量

深入理解JS语法与变量 前言初识JavaScriptJavaScript的语言风格和特性 JavaScript的书写位置认识输出语句学会处理报错REPL 环境 变量定义变量改变变量变量的合法命名变量的默认值变量的常见错误等号表示赋值同时声明多个变量 变量声明提升注意事项 结语 前言 在现代Web开发中…

Vue构建项目断点调试过程问题总结

Vue构建项目断点调试过程问题总结 问题背景 前端开发过程中&#xff0c;碰到问题时需要debug&#xff0c;快速分析和解决问题。一般除了console.log的方式打印日志外&#xff0c;更方便直观的方式就是打断点debug。本文对vue项目debug过程可能碰到的问题进行总结&#xff0c;…

Kafka(二)原理详解

一 、kafka核心总控制器&#xff08;Controller&#xff09; 在Kafka集群中会有一个或者多个broker&#xff0c;其中有一个broker会被选举为控制器&#xff08;Kafka Controller&#xff09;&#xff0c;它负责管理整个集群中所有分区和副本的状态。 作用&#xff1a;leader副…

FireAlpaca:轻量级、免费的Mac/Win绘图软件,让你的创意如火燃烧!

FireAlpaca是一款轻量级、免费的绘图软件&#xff0c;适用于Mac和Win系统&#xff0c;让你的创作过程更加快捷、简便。无论是绘制漫画、插图、设计作品还是进行简单的图片编辑&#xff0c;FireAlpaca都能满足你的需求。 首先&#xff0c;FireAlpaca具有直观友好的用户界面&…

国辰智企资产管理系统:实现资产精细化管理的首选

在市场竞争日益激烈、金融环境不断变化的背景下&#xff0c;有效的资产管理已成为企业保持竞争优势和实现财务目标的关键。传统资产管理方法已显不足以适应现代经济环境的快速变化。为了迎接这一挑战&#xff0c;越来越多企业纷纷采用先进的资产管理系统&#xff0c;以提高效率…