Linux教程

Linux--进程信号

本文主要是介绍Linux--进程信号,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

进程信号

  • Linux下信号种类
    • 1.1 信号产生
    • 1.2 信号注册
    • 1.3 信号注销
    • 1.4 信号处理
      • 自定义处理方式的信号捕捉流程
    • 1.5 阻塞
  • 2. 信号的基本应用
    • 2.1 SIGCHILD
    • 2.2 SIGPIPE
  • 3. 关键字volatile
  • 4. 函数的可重入与不可重入

  • 信号量不是信号!
  • 信号是一种中断机制,或者说是一种事件通知机制,本文讲述的是软件中断
  • 通过信号通知进程发生了某个事件,打断进程当前的操作,去处理事件。
  • 一个信号对应一个事件,信号必须能够被识别。

Linux下信号种类

  • 使用kill-l 命令进行查看,62种
  • 1~31:非可靠信号,借鉴Unix而来,
  • 33~64:可靠信号,扩充,
  • 信号的生命周期:产生,注册,注销,处理

1.1 信号产生

  • 硬件产生:Ctrl+c(中断当前操作), Ctrl+\, Ctrl+z,
  • 软件产生:kill命令发送一个信号给进程;例如:9号信号(SIGKILL)强制杀死;kill -sigid pid
  • kill杀死进程的原理是给进程发送一个终止信号,进程处理信号的方式就是退出进程。
  • int kill(pid_t pid, int sig); --给指定进程发生指定信号
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<signal.h>
int main(int argc, char *argv[])
{
	while(1)
	{
		printf("废寝忘食?\n");
		sleep(1);
		kill(getpid(), 9);//给指定进程发生指定信号
	}
}

在这里插入图片描述

  • int raise(int fig); --给调用进程发生指定信号
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<signal.h>
int main(int argc, char *argv[])
{
	while(1)
	{
		printf("废寝忘食?\n");
		sleep(1);
		raise(9);//给调用进程发生指定信号
	}
}

在这里插入图片描述

  • unsigned int alarm(unsigned int seconds); --设置一个定时器
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<signal.h>
void test(int no)
{
	printf("该吃了\n");
	alarm(3);
}
int main(int argc, char *argv[])
{
	signal(SIGINT, sigcb);
	alarm(3);
	while(1)
	{
		printf("废寝忘食?\n");
		sleep(1);
	}
}

在这里插入图片描述
在这里插入图片描述

  • void abort(void); --引起异常进程终止
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<signal.h>
int main(int argc, char *argv[])
{
	while(1)
	{
		printf("废寝忘食?\n");
		sleep(1);
		abort();//引起异常进程终止
	}
}

在这里插入图片描述

1.2 信号注册

  • 让进程知道自己收到了哪个信号。
  • 修改pcb中的未决信号集合位图,并添加信号信息节点
  • pending:未决信号集合,没有被处理的信号集合,是一个位图,用于标记有哪些信号待处理。
  • sigqueue:双向链表,用于添加信号信息节点,形态的节点有多少个,就表示有多少个相同的信号待处理。
  • 非可靠:若信号没有注册,则注册,已经注册则什么都不做;
  • 可靠:不管信号有没有注册,都会注册一次。

1.3 信号注销

  • 删除信号痕迹(删除节点+修改位图)
  • 非可靠信号:删除节点后,直接重置位图;
  • 可靠信号:删除一个信息节点后,确定没有相同节点了才会重置位图。

1.4 信号处理

  • 就是打断进程当前操作,然后执行信号的处理函数,执行完毕之后回到原来的主控流程继续运行

处理方式:

  • 默认处理方式:执行默认的处理函数;

  • 忽略处理方式:信号依然会注册只是处理方式变为空操作;

  • 自定义处理方式:自己定义信号处理函数,修改信号的处理函数指针。

  • sighandler_t signal(int signum, sighandler_t handler);

  • signum:要修改的信号;

  • handler:传入新的处理方式;SIG_DFL/SIG_IGN/自定义函数。

  • typedef void (*sighandler_t)(int);

  • 返回值:成功返回原来的处理方式;失败返回SIG_ERR。

自定义处理方式的信号捕捉流程

程序运行:

  • 当程序运行的都是我们自己写的代码和访问的都是自己的变量则程序运行在用户态;
  • 若程序运行要访问内核空间或者说要完成内核中的功能就需要切换到内核态运行;
  • 程序运行从用户态切换到内核态的方式:系统调用接口,中断,异常
  • 可以使程序切换到内核态:memcpy、fwrite、read、int a = 10/0;

1.5 阻塞

  • 信号依然可以注册,只是暂时阻止信号被处理。
  • 在pcb中还有一个信号集合–阻塞集合;哪个信号在这个集合中被标记,则表示这个信号要阻塞,收到了这个信号则暂时不去处理。
  • int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
  • how:操作类型–要对阻塞集合进行的操作;
  • SIG_BLOCK:将set集合中的信号添加到阻塞集合;block |= set
  • SIG_UNBLOCK:从阻塞集合中移除set中的信号; block &= ~block
  • SIG_SETMASK:将set集合中的信号设置为阻塞集合;block = set
  • oldset :用于保存修改前阻塞集合中的数据,以便于能够还原。
  • 修改指定信号的处理方式为自定义,能够感受到收到了某个信号;signal
  • 阻塞所有信号;sigprocmask,sigset_t;
  • 让程序的运行停下来,向进程发送信号(非可靠、可靠);getchar
  • 让程序继续向下运行,解除阻塞,查看信号的处理结果;sigprocmask
  • int sigemptyset(sigset_t *set); 清空set集合
  • int sigfillset(sigset_t *set); 填充所有信号到set集合
  • int sigaddset(sigset_t *set, int signum); 添加指定信号到set集合
  • int sigdelset(sigset_t *set, int signum); 从set集合删除指定信号
  • int sigismember(const sigset_t *set, int signum); 判断信号是否在集合中
  • 在所有信号中有两个信号比较特殊:SIGKILL -9 / SIGSTOP -19 – 这两个信号不能被阻塞,不能被修改处理方式,不能被忽略
  • 进程无法被杀死的情况:僵尸进程、信号被阻塞或者自定义忽略、进程使通知状态

2. 信号的基本应用

2.1 SIGCHILD

  • SIGCHILD :子进程退出之后给父进程发送的信号
    · SIGCHLD信号的默认处理方式,就是什么都不做;之前使用waitpid避免出现僵尸进程。
  • 自定义SIGCHLD信号的处理方式,在信号回调时调用waitpid。
  • SIGCHLD信号是一个非可靠信号,有可能会丢失事件,
    · while(waitpid(-1, NULL, WNOHANG) > 0;
    ` waitpid:返回值大于0表示有子进程退出;等于0表示没有子进程退出。
  • signal(SIGCHLD, SIG_IGN);–显式的忽略处理.

2.2 SIGPIPE

  • SIGPIPE:管道所有读端被关闭之后继续写入触发异常对应的信号
    · SIGPIPE默认的处理方式式退出进程
    · 若不想退出进程,则需要自定义处理

3. 关键字volatile

  • 作用:修饰一个变量,保持变量的内存可见性;
  • 修饰变量后,cup每次访问变量数据都需要重新从内存中加载数据,防止编译器过度优化。
    在这里插入图片描述
    在这里插入图片描述

4. 函数的可重入与不可重入

  • 函数的重入:一个函数在多个执行流程中,重复进入一个函数进行执行。
  • 可重入函数:一个函数重入之后不会出现预期之外的结果;
  • 不可重入函数:一旦函数重入之后,有可能造成数据二义或者逻辑混乱。
  • 判断基准:在一个函数中是否对一个全局数据进行了不受保护的非原子操作。
这篇关于Linux--进程信号的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!