ToB企服应用市场:ToB评测及商务社交产业平台

标题: Linux:进程信号(一.认识信号、信号的产生及深层理解、Term与Core) [打印本页]

作者: 渣渣兔    时间: 2024-7-25 11:53
标题: Linux:进程信号(一.认识信号、信号的产生及深层理解、Term与Core)
上次竣事了进程间通讯的知识先容:Linux:进程间通讯(二.共享内存详细解说以及小项目使用和相关指令、消息队列、信号量


  

1.认识信号

**概念:**在Linux系统中,进程之间可以通过信号进行通讯,实现异步信息的发送和接收。
信号是Linux系统中一种轻量级的通讯机制,用于关照进程发生了某种事件或异常情况。进程可以发送信号给其他进程,也可以接收来自其他进程或系统的信号。
   
  可以使用kill -l来查看信号

   
  进程看待信号方式

   异步发送指的是信号是由其他用户或进程产生的,而接收信号的进程在信号到达之前大概一直在处理自己的使命
  
2.信号的产生

2.1信号的处理的方式 — signal()函数


signal()函数是Linux系统中用于注册信号处理函数的函数。它的原型如下:
  1. #include <signal.h>
  2. typedef void (*sighandler_t)(int);
  3. sighandler_t signal(int signum, sighandler_t handler);//sighandler_t是个函数指针
复制代码
这个函数接受两个参数:signum表示要捕获的信号编号,handler表示要注册的信号处理函数。
   
  完备的表述应该是:定义一个处理SIGINT信号的处理函数,并通过signal()函数将这个处理函数注册到SIGINT信号上。当进程收到SIGINT信号时,系统会调用注册的处理函数来处理该信号。
  2.2kill指令产生信号

kill指令是用于向进程发送信号的命令。通过kill命令,可以向指定进程发送差异类型的信号,例如SIGTERM、SIGKILL等。这些信号可以触发进程中注册的信号处理函数,大概直接停止进程的执行。
kill命令的基本语法为:
  1. kill [options] <PID>
复制代码
<ID>是要发送信号的目标进程的进程ID。可以通过ps命令或其他方式获取目标进程的进程ID。
  1. #include <iostream>
  2. #include <unistd.h>
  3. #include <sys/types.h>
  4. using namespace std;
  5. int main()
  6. {
  7.     while(true)
  8.     {
  9.         cout << "I'm a process, pid:" << getpid() << endl;
  10.         sleep(2);//我们写个死循环,每隔两秒打印一下
  11.     }
  12.     return 0;
  13. }
复制代码

2.3键盘产生信号

验证:
  1. #include <iostream>
  2. #include <unistd.h>
  3. #include <sys/types.h>
  4. #include <signal.h>
  5. using namespace std;
  6. void handler(int signum)
  7. {
  8.     cout << "got a signal, number is : " << signum << endl;
  9. }
  10. int main()
  11. {
  12.     signal(SIGINT, handler);  // 对2号信号SIGINT处理
  13.     signal(SIGQUIT, handler); // 对号信号SIGQUIT处理
  14.     while(true)
  15.     {
  16.         cout << "I'm a process, pid:" << getpid() << endl;
  17.         sleep(2);
  18.     }
  19.     return 0;
  20. }
复制代码

2.4系统调用发送信号 —kill系统调用、raise()和abort()库函数

kill是一个常见的系统调用,用于向指定的进程发送信号。通过kill系统调用,一个进程可以向另一个进程发送差异类型的信号,从而实现进程之间的通讯和控制。
kill系统调用的原型如下:
  1. #include <sys/types.h>
  2. #include <signal.h>
  3. int kill(pid_t pid, int sig);
复制代码

kill系统调用的返回值为0表示乐成发送信号,-1表示发送信号失败,并且在这种情况下,可以通过errno全局变量获取具体的错误信息。
我们可以利用这个,来实现一个kill指令
  1. #include <iostream>
  2. #include <unistd.h>
  3. #include <sys/types.h>
  4. #include <signal.h>
  5. #include <cerrno>
  6. #include <cstring>
  7. using namespace std;
  8. int main(int argc, char *argv[])
  9. {
  10.     if (argc != 3)
  11.     {
  12.         cout << "Usage: kill -signum pid" << endl;
  13.         return 1;
  14.     }
  15.     int signum = stoi(argv[1] + 1); // 获取信号的数字
  16.     int pid = stoi(argv[2]);        // 获取pid
  17.     int n = kill(pid, signum);
  18.     if (n == -1)
  19.     {
  20.         cerr << "kill failed: " << strerror(errno) << endl;
  21.     }
  22.     return 0;
  23. }
复制代码
  可以对任意进程发送任意的信号
     给自己放指定信号(6号SIGABRT)
  2.5软件条件产生信号

读端关闭其文件描述符并且不再读取数据时,如果写端继续向管道写入数据,操作系统会发送一个SIGPIPE信号给写端进程。默认情况下,这个信号会停止写端进程。SIGPIPE信号是一个用于处理管道写端在写操作时无读端接收的情况的信号。
  1. #include <unistd.h>
  2. unsigned int alarm(unsigned int seconds);
复制代码
调用alarm函数可以设定一个闹钟,也就是告诉内核在seconds秒之后给当前进程发SIGALRM信号, 该信号的默认处理动作是停止当前进程。
函数的返回值是0大概是从前设定的闹钟时间还余下的秒数
   在Linux系统中,SIGALRM信号的默认行为是停止进程。当步伐设置一个定时器并在定时器到期时产生SIGALRM信号时,如果步伐没有显式地捕获和处理这个信号,那么默认情况下操作系统会停止该进程。
  alarm(0),代表取消闹钟:alarm(0)函数会扫除之前设置的定时器,并返回剩余的定时器时间(如果有的话)而且不会再触发SIGALRM信号
  怎么理解软件条件:
   软件条件是指软件层面上的一种异常情况或特定的条件,通常由软件中断信号触发,用来关照进程某种特定的事件已经发生
  布局体与堆等数据布局都是软件,也有条件触发
  

2.6异常产生信号


3.信号产生的深层理解

键盘产生信号


那么现在又有问题了,什么叫做解释成为信号,什么叫做发送给进程?
信号暂时生存在那里呢?
   进程的PCB中,使用位图布局来存1到31号的信号:比特位的位置来表示信号编号,比特位的01来表示是否收到指定的信号
  那么发送信号本质上是写入信号:
  

    task_struct是内核数据布局,只有OS有能力写入。我们用户只能使用系统调用。以是,无论信号产生的方式有多少种,最终都是OS在进程中写入信号的
  异常产生信号



   但如果我们自定义处理里,没有进行exit()退出,那么就会一直打印
  因为,寄存器中的数据都是进程的上下文,CPU一直在进行进程的调度,那么就涉及到进程上下文的生存和恢复,因为我们没有进行退出操作,以是每次恢复后,异常还是存在。
  

   最终信号一定都是OS进行写入进程中的信号位图中
  总结一下:
   
  4.Term与Core


   需要注意的是云服务器默认关闭了core file的选项:因为如果步伐瓦解是由于某种未知的错误或条件触发的,并且这个问题没有得到及时办理,那么核心转储(core dump)文件大概会不停生成,占用大量的磁盘空间
  ulimit -a 是一个在 Linux中用于显示当前 shell 会话的资源限定的命令。ulimit 命令允许用户设置或查看各种 shell 和进程资源限定。这些限定可以资助防止系统资源的滥用,如 CPU 时间、文件大小、打开的文件描述符数量等。
  当你运行 ulimit -a 时,它会列出所有当前设置的资源限定。以下是一些常见的 ulimit 资源和它们的描述:
  
  如果想要修改某个限定,可以使用 ulimit 命令加上相应的选项和新的限定值。
  例如,要设置最大打开文件描述符数量为 4096,你可以运行 ulimit -n 4096。但是请注意,这些限定通常只影响当前 shell 会话和由该 shell 启动的子进程。它们不会永久地改变系统配置。
  我们想要产生core文件的话:ulimit -c选项设置core file的大小
  core文件


Core文件是Linux系统下的内核转储文件,当步伐瓦解时由操作系统生成,主要用于对步伐进行调试
当步伐出现内存越界、段错误(Segmentation Fault)或其他异常情况导致瓦解时,操作系统会中断该进程,并将当前内存状态、寄存器状态、堆栈指针、内存管理信息以及各个函数使用堆栈信息等生存到Core文件中。如许,步伐员就可以通过读取和分析Core文件来找出步伐瓦解的原因和位置,从而进行调试和修复。
Core文件的存在是为了资助步伐员更好地理解和办理步伐瓦解的问题。由于Core文件包罗了步伐瓦解时的详细内存状态信息,因此它对于调试复杂的内存问题、并发问题以及系统调用等问题非常有用。同时,由于Core文件是在步伐瓦解时自动生成的,因此它也可以作为一种自动记载步伐瓦解信息的机制,方便步伐员进行事后分析和排查。
但是,由于Core文件大概包罗大量的内存数据,因此它大概会占用较大的磁盘空间。在不需要进行调试或分析的情况下,可以通过修改操作系统的配置来克制生成Core文件或将其生存到其他位置。

  1. #include <iostream>
  2. #include <sys/types.h>
  3. #include <sys/wait.h>
  4. #include <signal.h>
  5. using namespace std;
  6. int main()
  7. {
  8.     pid_t id = fork();
  9.     if (id == 0)
  10.     {
  11.         // child
  12.         int a = 10;
  13.         a /= 0;
  14.         exit(1);
  15.     }
  16.     // father
  17.     int status = 0;
  18.     pid_t rid = waitpid(id, &status, 0);
  19.     cout << "exit code:" << ((status >> 8) & 0xff) << endl;
  20.     cout << "exit signal:" << (status & 0x7f) << endl;
  21.     cout << "core dump:" << ((status >> 7) & 0x1) << endl;
  22.     return 0;
  23. }
复制代码


今天也是到这里了,(存货太多慢慢发了)。学了网络部门的要赶快做项目了

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4