Linux C++通讯架构【二】:进程和信号

终端(pts)与进程的关系

  1. pts就是一个bash(命令解释器),一个shell,一个可执行程序,在/bin/bash目录下,终端如果关闭,终端上的进程也就关闭了;
  • 比如执行一个可执行程序,不断往A终端printf数据,然后新开一个终端B,通过ps -la命令可以看到这个printf的进程,但是当A终端关闭后,ps查看进程发现这个printf的进程不在了

因为这个printf进程属于一个进程组,同一个进程组的进程组id相同,都是bash进程的子进程 (ps命令查看父进程可知),所以xshell其实并不是远程控制,而是一个实际的远程终端

202305232131940
  1. 会话(session):一个或多个进程组的集合。一个bash上有多个进程组,一般bash的所有进程都属于一个session,但也可能一个bash上有多个session

记录个命令:ps -eo pid,ppid,sid,tty,pgrp,comm | grep -E 'bash|PID|nginx' 其中,-o参数表示自定义展示哪些列,sid表示sessionid,tty表示终端(0,1,2是啥意思),pgrp表示进程组,-E表示正则匹配

  1. bash: shell的一种,linux默认采用bash这种shell,也就是说bash是一个可执行程序,主要作用是把用户输入的命令翻译给操作系统(命令解释器)。在/bin/bash目录,输入exit就可以logout这个终端

终端与信号

当终端要退出时,系统会发送SIGHUP信号(终端断开信号),给session_leader(一般就是bash进程,也可以调系统函数自己设置),bash进程收到SIGHUP信号后, 会把这个信号发送给所有sessionid相同的进程(如nginx子进程),然后再给自己发送SIGHUP信号。

strace信号跟踪工具:可以跟踪程序执行时进程的系统调用,以及所收到的信号,如在其他终端执行命令: sudo strace -e trace=signal -p 1359附着1359这个bash进程,然后关掉这个bash终端,查看输出

  • 怎么让bash退出,Nginx不退出
  • Nginx拦截操作系统的sighup信号:nginx启动时,加一行代码signal(SIGHUP,SIG_IGN);,SIG_IGN表示忽略某一种信号
    • 这种情况由于bash父进程死了,将变成孤儿进程由Init进程接管,也就是ppid变成1了
  • 设置Nginx的sessionid,新建一个session,和bash不在同一个会话里:nginx启动时,加一行代码setsid();
    • 进程组组长不能setid(),这在创建守护进程会提到。解决方法,fork一个子进程
    • setsid命令,可改变一个已启动进程的sessionid
#include<signal.h>

int main(int argc, char* const *argv){
    pid_t pid;
    printf("内容输出");
    pid=fork();   // 系统函数,子进程会从fork()调用之后开始执行
    if(pid<0){
        printf("子进程设置出错")
    }else if(pid==0){   // 子进程
        setsid();  // 子进程新建一个不同的session4
    }else{   // 父进程
        // signal(SIGHUP,SIG_IGN);
    }
    // ....逻辑代码
}

后台运行:

  • nginx执行自己的程序,终端可以继续处理其他的;
  • fg切换到前台,终端输入的指令Nginx都不会例会了
  • 后台运行可以往终端打印东西

信号

信号都是突发事件,异步发生,也称为软件中断,进程收到信号,操作系统都会知道

记录一条命令:find / -name "signal.h" | xargs grep -in "SIGHUP",其中,xargs表示找到文件后,将文件内容传输给grep进行查找 信号产生:

  • 某个进程发送给另一个进程或自己(Nginx热升级)
  • 内核发送给某个进程 :
    • ctrl+c(中断信号),kill命令
    • 内存访问异常,硬件通知内核,内核通知进程 信号都以SIG开头,如SIGHUP,信号都是一些正整数常量(signal.h中的宏定义,从1开始)

kill

我们在关闭bash时,bash会用kill命令发送一个SIGTERM信号,关闭nginx进程

用来发送信号,信号9定义为SIGKILL,kill -9 pid表示给进程发送一个SIGKILL的信号

然后,kill -1 pid表示挂起这个进程(信号1定义为SIGHUP),然而,神奇的是,这个进程直接被杀掉了 =》 这是由于操作系统给进程发送信号后,如果进程没有对这个信号做出处理,操作系统默认会杀死这个进程(之所以将信号发送命令命名为kill,就是因为绝大部分操作系统发送的信号的缺省操作就是杀死)

同理,kill pid也可以杀死进程

kill的常用参数:

202305232256846

内核态和用户态

malloc()和printf(),会先切换到内核态,再切换回用户态。其实printf内部就会调用malloc()

什么时候会从用户态切到内核态:

  • 系统调用:malloc()
  • 异常事件:比如来了个信号
  • 外围设备中断

信号集:

Linux支持60多种信号,信号集是一种数据结构,0011000...,可简单理解为64多个bit(其实是一个sigset_t结构)

typedef struct{
    unsigned long sig[2];   // 一个long4bit,两个正好8bit
    // 相关api自行百度
}sigset_t;

来一个信号对应位置置为1,如果本来就为1,说明同类型信号正在被处理,不能被打断,得排队。 如果一个信号处理函数正在处理其他信号,后续相同的信号统统归结为1次,并被阻塞到上个信号处理完。 信号屏蔽:一个进程有一个信号集,我们可以把要屏蔽的信号位设为1,就能实现信号阻塞了(sigprocmask()函数)。

全部评论

相关推荐

点赞 收藏 评论
分享
牛客网
牛客企业服务