IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 系统运维 -> Linux:信号 | 信号发送 | core dump文件 | 信号设置 | 信号处理 | 可重入函数 | volatile | SIGCHLD信号 -> 正文阅读

[系统运维]Linux:信号 | 信号发送 | core dump文件 | 信号设置 | 信号处理 | 可重入函数 | volatile | SIGCHLD信号


全文约 3036 字,预计阅读时长: 9分钟


信号

  • 信号是进程之间事件异步通知的一种方式,属于软中断。过程:信号产生,信号识别,处理处理。
  • 每个信号都有一个编号和一个宏定义名称,这些宏定义可以在signal.h中找到,例如其中有定义 #define SIGINT 2
    • Ctrl-C 产生的信号只能发给前台进程。 Shell可以同时运行一个前台进程和任意多个后台进程,
    • 一个命令后面加个&可以放到后台运行,这样Shell不必等待进程结束就可以接受新的命令,启动新的进程。
  • 前台进程在运行过程中用户随时可能按下 Ctrl-C 而产生一个信号,也就是说该进程的用户空间代码执行到任何地方都有可能收到 SIGINT 信号而终止,所以信号相对于进程的控制流程来说是异步(Asynchronous)的。
  • 对于相当一部分信号而言,当进程收到的时候,默认的处理动作是终止当前进程。
    • 对进程而言,有一些信号不能被捕捉和忽略,如: kill -9
    • 进程收到信号,不是立即处理的,而是在合适的时候。
  • 信号的处理:
    • 默认方式(部分终止进程,部分有特定的功能)
    • 忽略信号
    • 自定义方式:捕捉信号
  • 站在语言角度:程序崩溃;站在系统角度,进程受到了信号。

信号发送

  • 信号的产生有如下的方式:
    • kill 命令产生 kill -l 1—31普通信号;34—64实时信号,响应要求级别特别强的信号,一旦发出进程必须响应。
    • 键盘产生
    • 由软件条件产生信号:闹钟
    • 程序异常 、硬件异常产生
      • 当你的进程触发错误时,基本都有对应的软硬件监控,cpu下的状态寄存器,内存和页表mmu等,会被OS识别到,然后给目标进程发送信号,来达到终止进程的目的。
  • 信号的产生,在进程的运行的任何时间点都可以产生,有可能进程正在做更重要的事情。
    • 因为信号不是立即处理的,所以信号在进程的PCB里保存着。
    • 对进程而言:是否有信号、是什么信号
      • 存储方式:位图,无符号整形,1在比特位中的位置意味着是哪个信号,有没有1意味着有没有信号。
    • 是谁发的,如何发;直接简介通过OS向进程发信号。
      • 发送信号的本质,相当于写对应进程的PCB的位图。因为OS是进程的管理者,OS是由这个能力和义务的。
  • 由软件条件产生信号:闹钟定时终止
#include <unistd.h>
unsigned int alarm(unsigned int seconds);
//调用alarm函数可以设定一个闹钟,也就是告诉内核在seconds秒之后给当前进程发SIGALRM信号, 该信号的默认处理动作是终止当前进程。
----//一秒内可以加多少次。
int a =0;
void add(int signo)
{
	cout<<signo<<endl;
	cout<<a<<endl;
	exit(1);
}
int main()
{
	for(int i =1,i<32;i++)
	{
		signal(i,add);
	}
	alarm(1);
	while(true)
	{
		a++;
	}
	return 0;
}
  • 系统调用产生信号:
void abort(void;
int raise(int sig);
int kill(pid_t pid, int sig);

---//kill测试  kill 9 1231231
int main(int argc,char* argv[])
{
	kill(atoi(argv[2]),atoi(argv[1]));
}

  • MakeFile
CC=g++
LDFLAGS=-std=c++11 -g  //标准
Src=mysignal.cc  
Bin=mysignal

$(Bin):$(Src)
	$(CC) -o $@ $^ $(LDFLAGS)

.PHONY:clean
clean:
	rm -f $(Bin)

core dump文件

  • 核心转储,OS将进程运行时的核心数据dump到磁盘上,方便用户调试使用。快速定位BUG,标注了错误出现在了哪一行。
  • 一般而言,核心转储是关闭的。自己写出来的错误才会有core dump 标志位设置,不是所有的信号都设置
ulimit -a
ulimit -c 1024 //打开core转储开关生成文件
gdb mytest
gdb core-file core.....
  • 查看core dump标志位是否设置
---//子进程崩溃 父进程收集
int main()
{
	...
	int s=0;
	pid_t  ret = waitpid(-1,&s,0);
	if(ret>0)
	{
		cout<<((s>>7)&0x1)<<endl;
	}
0x7F7个高电平,1个低电平 0111 1111
}

信号设置

在这里插入图片描述

  • bolock 位图:代表是否哪种信号阻塞(屏蔽)
  • pending 位图表示:有哪种未决信号
  • sighandler数组表示:对应信号的处理方式(递达)。
    • 默认(终止等)
    • 忽略
    • 自定义捕捉,由用户提供。
  • sigset_t系统提供的数据类型,用来存储或设置位图中的信号,称为信号集。
  • 修改设置位图中的标志位,需要一系列系统提供的信号集操作函数
#include <signal.h>
int sigemptyset(sigset_t *set);  ---初始化,位图中标志全部请0
int sigfillset(sigset_t *set);   --全部置1
int sigaddset (sigset_t *set, int signo);	---指定位置设置1
int sigdelset(sigset_t *set, int signo);	---指定位置设置0
int sigismember(const sigset_t *set, int signo);   ---判断特定信号是否被设置

----都是成功返回0,出错返回-1
  • sigprocmask设置阻塞信号集
    • int sigprocmask(int how, const sigset_t *set, sigset_t *oset);
    • 参数:oset:非空指针,返回修改之前的信号屏蔽字。 set:非空指针,则更改进程的信号屏蔽字;
    • how:指示如何更改,参数的可选值:
      • SIG_BLOCK:将set信号添加到阻塞位图中。
      • SIG_UNBLOCK:解除阻塞信号set
      • SIG_SETMAXK:将当前信号屏蔽字设置成 set 信号。
      • 如果调用sigprocmask解除了对当前若干个未决信号的阻塞,则在sigprocmask返回前,至少将其中一个信号递达。
  • sigpending读取当前进程的未决信号集,通过set参数传出int sigpending(sigset_t *set);
  1. 阻塞 2号信号
  2. 不断获取pending信号集,并打印
  3. 发送2号信号给进程
  4. 过一段时间,解除对2号信号的阻塞
  5. 2号信号立马会被递达,执行默认动作。
  6. 依旧打印pending未决信号集
void show_pending(sigset_t *pending)
{
    for(int i = 1; i <= 31; i++){
        if(sigismember(pending, i)){	//判断信号在不在集合里
            cout << "1";
        }
        else{
            cout <<"0";
        }
    }
    cout << endl;
}

int main()
{
    sigset_t in;
    sigemptyset(&in);
    sigaddset(&in, 2); //set 2 signo block, user stack
    sigprocmask(SIG_SETMASK, &in, NULL); //kernel 2 block
    int count = 0;
    sigset_t pending;
    while(true){
        sigpending(&pending);
        show_pending(&pending);
        sleep(1);
        if(count == 20){
            sigprocmask(SIG_SETMASK, NULL, &in); //恢复之后,2号信号立马递达,并且执行默认动作!!!
            cout << "my: ";
            show_pending(&in);
            cout << "recover default: ";
            show_pending(&out);
        }
        count++;
    }
  • Linux下:常规信号在递达之前产生多次只计一次,而实时信号在递达之前产生多次可以依次放在一个队列里。

信号处理

  • 进行信号递达的时间:从内核态返回用户态时,尝试信号检测与捕捉执行。
  • 内核态与用户态:
    ??进程的地址空间0-3G是用户空间,3-4G是内核空间;操作系统提供的系统调用接口内核空间中,而内核空间中的代码数据在物理内存上放着,因此有一个内核级页表维护这个映射关系;再由于进程有多个,操作系统只有一个,因此内核级页表只有一个,且是共享的。
    ??用户自己的代码数据,通过接口访问内核代码数据,系统会自动进行身份切换,进入内核空间进行一系列操作。此时进程在用户空间的状态就叫用户态,在内核空间的状态叫做内核态。CPU中存在一个与 权限相关的寄存器数据标识所处的状态。
  • 故操作系统设计时:OS从内核态切换至用户态,会检测信号集是否需要被处理。
    在这里插入图片描述
  • 当去执行自定义信号捕捉的方法时,是需要切换至用户态的。因为内核态权限时很高的,如果此时有人利用这个bug会去进行大量危险的操作,进行程序替换等,破坏系统或用户的数据等。
  • 递达的处理方法一般有三种:
    • 默认(大部分终止进程)
    • 忽略
    • 自定义信号捕捉:signal()sigaction
  • signal:捕获进程递达的信号,进行怎样的处理
    • void (*signal(int sig, void (*func)(int)))(int)
    • 参数:sig – 在信号处理程序中作为变量使用的常量信号码,有些特定选项(异常终止、除0或算术溢出、野指针等)
      • SIGINT:中断信号常用,由用户产生;也就是kill -l列表里的 1 — 31 的普通信号。
    • func – 一个指向函数的指针,也可以是下面预定义函数之一:
      • SIG_DFL :默认的信号处理程序,大部分默认终止进程。
      • SIG_IGN :忽视信号。
void handler(int signo)
{
    std::cout << "get a signal: " << signo << std::endl;
    exit(0);
}
int main()
{
    for(int i=1; i < 32; i++){
        signal(i, handler);//会调用handler函数进行信号处理。
        
        //signal(i, SIG_IGN); ///SIG_ING 代表捕获到忽略 i 信号
   }
.....
  • sigaction:你想捕获哪一个信号,结构体:你想怎么处理这个信号,返回老的信号捕捉方法。
    • int sigaction(int signo, const struct sigaction *act, struct sigaction *oact);
      • signo是指定信号的编号。
      • act指针非空,则根据 ac t修改该信号的处理动作。
      • oact指针非 空,则通过oact传出该信号原来的处理动作,不需要可以设置为NULL。
  • act和oact指向sigaction结构体:
 struct sigaction {
               void     (*sa_handler)(int); //自定义的捕捉方法
               void     (*sa_sigaction)(int, siginfo_t *, void *);  //实时信号处理用
               sigset_t   sa_mask;//自动屏蔽另外一些信号
               int        sa_flags;//设为0
               void     (*sa_restorer)(void);//0
           };
---//使用
struct sigaction s1;
s1.sa_handler = handler;
s1.sa_flags =0;
sigemptyset(s1.sa_mask);
s1.sa_sigaction=NULL;
S1.sa_restorer = NULL;
sigaction(SIGINT,&act,NULL);
void handler(int signo).....

可重入函数

  • 当前运行进程收到信号的处理方法,而此时进程收到信号进行递达处理,之前正在运行的函数栈帧销毁,造成资源泄露等。
  • 一个函数符合以下条件之一则是不可重入的:
    • 调用了malloc或free,因为malloc也是用全局链表来管理堆的。
    • 调用了标准I/O库函数。标准I/O库的很多实现都以不可重入的方式使用全局数据结构。

volatile

  1. 告诉编译器不要将内存中的变量优化到cpu的寄存器中,cpu找数据时,去内存里找。解决寄存器和内存数据不一致的问题。
  2. 保持内存的可见性,告知编译器,被该关键字修饰的变量,不允许被优化,对该变量的任何操作,都必须在真实的内存中进行操作.
  3. 使用:volatile int flag = 0;

SIGCHLD信号

  • waitwaitpid函数清理僵尸进程,父进程可以阻塞等待子进程结束,也可以非阻 塞地查询是否有子进程结束等待清理(也就是轮询的方式)。两种方式各有缺点。
  • 其实子进程在终止时会给父进程发SIGCHLD信号,该信号的默认处理动作是忽略,父进程可以自 定义SIGCHLD信号的处理函数,调用wait清理子进程即可。
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
void handler(int sig)
{
	 pid_t id;
	 while( (id = waitpid(-1, NULL, WNOHANG)) > 0){
	 printf("wait child success: %d\n", id);
}
	 printf("child is quit! %d\n", getpid());
}
int main()
{
	 signal(SIGCHLD, handler);
		 pid_t cid;
		 if((cid = fork()) == 0){//child
		 printf("child : %d\n", getpid());
		 sleep(3);
		 exit(1);
	 }
 while(1){
	 	printf("father proc is doing some thing!\n");
		sleep(1);//在休眠时会被提前唤醒。
	 }
 return 0;
}
  • 是否需要wait子进程:
    • 僵尸进程的内存泄漏
    • 是否需要获得子进程的退出码:不关心就算了。
  系统运维 最新文章
配置小型公司网络WLAN基本业务(AC通过三层
如何在交付运维过程中建立风险底线意识,提
快速传输大文件,怎么通过网络传大文件给对
从游戏服务端角度分析移动同步(状态同步)
MySQL使用MyCat实现分库分表
如何用DWDM射频光纤技术实现200公里外的站点
国内顺畅下载k8s.gcr.io的镜像
自动化测试appium
ctfshow ssrf
Linux操作系统学习之实用指令(Centos7/8均
上一篇文章      下一篇文章      查看所有文章
加:2022-08-06 11:16:52  更:2022-08-06 11:20:19 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/15 11:39:38-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码