当前位置: 首页 > news >正文

烟台做网站的公司企业文化墙

烟台做网站的公司,企业文化墙,网站标题修改,网站开发的形式有哪些目录 前言#xff1a; 1、进程信号基本概念 1.1、什么是信号#xff1f; 1.2、信号的作用 2、键盘键入 2.1、ctrlc 终止前台进程 2.1.1、signal 注册执行动作 3、系统调用 3.1、kill 函数 3.2、模拟实现 myKill 3.3、raise 函数 3.4、abort 函数 4、软件条件信号…目录 前言 1、进程信号基本概念 1.1、什么是信号 1.2、信号的作用 2、键盘键入 2.1、ctrlc 终止前台进程 2.1.1、signal 注册执行动作 3、系统调用 3.1、kill 函数 3.2、模拟实现 myKill 3.3、raise 函数 3.4、abort 函数 4、软件条件信号产生发送的第三种方式软件条件 4.1、alarm 设置闹钟 5、硬件异常 5.1、除 0 导致异常 5.2、状态寄存器 5.3、野指针导致异常 6.核心转储  6.2、打开与关闭核心转储 6.3、核心转储的作用 总结 前言 在 Linux 中进程具有独立性进程在运行后可能 “放飞自我”这是不利于管理的于是需要一种约定俗成的方式来控制进程的运行这就是 进程信号本文将会从什么是进程信号开篇讲述各种进程信号的产生方式及作用 正文 1、进程信号基本概念 1.1、什么是信号 信号 是信息传递的承载方式一种信号往往代表着一种执行动作比如 鸡叫  天快亮了闹钟  起床、完成任务红绿灯  红灯停绿灯行 当然这些都是生活中的 信号当产生这些 信号 时我们会立马想到对应的 动作 这是因为 我们认识并能处理这些信号 我们能进行处理是因为受过教育学习了执行动作但对进程来说它可没有接受过九年义务教育也不知道什么时候该干什么事 于是程序员们给操作系统植入了一批 指令一个指令表示一种特殊动作而这些指令就是 信号进程信号 kill -l这些就是当前系统中的 进程信号一共 62 个其中 1~31 号信号为 普通信号学习目标用于 分时操作系统剩下的 34~64 号信号为 实时信号用于 实时操作系统 分时操作系统根据时间片实行公平调度适用于个人电脑实时操作系统高响应适合任务较少、需要快速处理的平台比如汽车车机、火箭发射控制台 1.2、信号的作用 早在 《Linux进程学习【进程状态】》 我们就已经使用过 信号 了比如 kill -9 pid 终止进程运行kill -19 pid 暂停进程运行kill -18 pid 恢复进程运行 就连常用的 ctrlc 和 ctrld 热键本质上也是 信号 这么多信号其对应功能是什么呢 可以通过 man 7 signal 进行查询 man 7 signal简单总结一下1~31 号信号对应的功能如下表格内容引用自 2021dragon Linux中的31个普通信号 注意 其中的 9 号 和 19 号信号是非常特殊的不能修改其默认动作 1.3、信号的基本认知 进程信号由 信号编号 执行动作 构成一个信号对应一种动作对于进程来说动作无非就这几种终止进程、暂停进程、恢复进程3 个信号就够用了啊为什么要搞这么多信号 创造信号的目的不只是控制进程还要便于管理进程进程的终止原因有很多种如果一概而论的话对于问题分析是非常不友好的所以才会将信号细分化搞出这么多信号目的就是为了方便定位、分析、解决问题并且 普通信号 就 31 个这就是意味着所有普通信号都可以存储在一个 int 中表示是否收到该信号信号的保存 所以信号被细化了不同的信号对应不同的执行动作虽然大部分最终都是终止进程 进程的执行动作是可修改的默认为系统预设的 默认动作 默认动作忽略自定义动作 所以我们可以 更改信号的执行动作后面会专门讲信号处理相关内容 信号有这么多个并且多个进程可以同时产生多个信号操作系统为了管理先描述、再组织在 PCB 中增加了 信号相关的数据结构signal_struct在这个结构体中必然存在一个 位图结构 uint32_t signals 存储 1~31 号信号的有无信息 //信号结构体源码部分 struct signal_struct {atomic_t sigcnt;atomic_t live;int nr_threads;wait_queue_head_t wait_chldexit; /* for wait4() *//* current thread group signal load-balancing target: */struct task_struct *curr_target;/* shared signal handling: */struct sigpending shared_pending;/* thread group exit support */int group_exit_code;/* overloaded:* - notify group_exit_task when -count is equal to notify_count* - everyone except group_exit_task is stopped during signal delivery* of fatal signals, group_exit_task processes the signal.*/int notify_count;struct task_struct *group_exit_task;/* thread group stop support, overloads group_exit_code too */int group_stop_count;unsigned int flags; /* see SIGNAL_* flags below *//** PR_SET_CHILD_SUBREAPER marks a process, like a service* manager, to re-parent orphan (double-forking) child processes* to this process instead of init. The service manager is* able to receive SIGCHLD signals and is able to investigate* the process until it calls wait(). All children of this* process will inherit a flag if they should look for a* child_subreaper process at exit.*/unsigned int is_child_subreaper:1;unsigned int has_child_subreaper:1;//…… };1.信号是执行的动作的信息载体程序员在设计进程的时候早就已经设计了其对信号的识别能力 2.信号对于进程来说是异步的随时可能产生如果信号产生时进程在处理优先级更高的事情那么信号就不能被立即处理此时进程需要保存信号后续再处理 3.进程可以将 多个信号 或 还未处理 的信号存储在 signal_struct 这个结构体中具体信号编号存储在 uint32_t signals 这个位图结构中 4.所谓的 “发送” 信号其实就是写入信号修改进程中位图结构中对应的比特位由 0 置为 1表示该信号产生了 5.signal_struct 属于内核数据结构只能由 操作系统 进行同一修改无论信号是如何产生的最终都需要借助 操作系统 进行发送 6.信号并不是立即处理的它会在合适的时间段进行统一处理 本文讲解的就是 信号产生 部分相关知识下面正式开始学习 信号产生  2、键盘键入 信号产生发送的第一种方式键盘键入 通俗来说就是命令行操作 2.1、ctrlc 终止前台进程 系统卡死遇到过吧程序死循环遇到过吧这些都是比较常见的问题当发生这些问题时我们可以通过 键盘键入 ctrl c 发出 2号信号终止前台进程的运行 下面是一段死循环代码 #include iostream #include unistd.h using namespace std;int main() {while(true){cout 我是一个进程我正在运行…… PID: getpid() endl;sleep(1);}return 0; }运行程序后会一直循环打印此时如果想要终止进程可以直接按 ctrl c 发出 2 号信号终止前台进程  此时发出了一个 2 号信号 SIGINT 终止了该进程的运行 如何证明呢如何证明按 ctrl c 发出的是 2 号信号呢 证明自有方法前面说过一个信号配有一个执行动作并且执行动作是可以修改的需要用到 signal 函数属于 信号处理 部分的内容这里需要提前用一下 2.1.1、signal 注册执行动作 signal 函数可以用来 修改信号的执行动作也叫注册自定义执行动作 signal 调用成功返回上一个执行方法的值其实就是下标后面介绍失败则返回 SIG_ERR并设置错误码 返回值可以不用关注重点在于 signal 的参数 参数1 待操作信号的编号 参数2 待注册的新方法 参数1 就是信号编号为 int单纯地传递 信号名也是可以的因为信号名其实就是信号编号的宏定义 参数2 是一个函数指针意味着需要传递一个 参数为 int返回值为空的函数对象  参数 int 是执行动作的信号编号 void handler(int) //其中的函数名可以自定义显然signal 函数是一个 回调函数当信号发出时会去调用相应的函数也就是执行相应的动作 我们先对 2 号信号注册新动作在尝试按下 ctrl c看看它发出的究竟是不是 2 号信号 #include iostream #include signal.h #include unistd.h using namespace std;void handler(int signo) {cout 当前 signo 号信号正在尝试执行相应的动作 endl; }int main() {//给 2 号信号注册新方法signal(2, handler);while(true){cout 我是一个进程我正在运行…… PID: getpid() endl;sleep(1);}return 0; }当我们修改 2 号信号的执行动作后再次按下 ctrl c 尝试终止前台进程结果失败了执行动作变成了我们注册的新动作 这足以证明 ctrl c 就是在给前台进程发出 2 号信号ctrl c 失效后可以通过 ctrl \ 终止进程发出的是 3 号信号3 号信号在发出后会生成 核心转储 文件 普通信号只有 31 个如果把所有普通信号的执行动作都改了会发生什么呢难道会得到一个有着 金刚不坏 之身的进程吗 #include iostream #include signal.h #include unistd.h using namespace std;void handler(int signo) {cout 当前 signo 号信号正在尝试执行相应的动作 endl; }int main() {//给所有普通信号注册新方法for(int i 1; i 32; i)signal(i, handler);while(true){cout 我是一个进程我正在运行…… PID: getpid() endl;sleep(1);}return 0; }大部分信号的执行动作都被修改了但 9 号信号没有因为 9 号信号是 SIGKILL专门用于杀死进程只要是进程他都能干掉 19 号信号 SIGSTOP 也无法修改执行动作所以前面说过9 号 SIGKILL 和 19 号 SIGSTOP 信号是很特殊的经过特殊设计不能修改其执行动作 2.2、硬件中断 当我们从键盘按下 ctrl c 时发生了这些事CPU 获取到键盘 “按下” 的信号调用键盘相应的 “方法” 从键盘中读取数据读取数据后解析然后发出 3 号信号 其中 CPU 捕获键盘 “按下” 信号的操作称为 硬件中断 CPU 中有很多的针脚不同的硬件对应着不同的针脚每一个针脚都有自己的编号硬件与针脚一对一相连并通过 中断控制器比如 8259进行控制当我们按下键盘后 中断控制器首先给 CPU 发送信息包括键盘对应的针脚号 然后 CPU 将获取到的针脚号中断号写入 寄存器 中 最后根据 寄存器 里的 中断号去 中断向量表 中查表找到对应硬件的方法执行它的读取方法就行了 这样 CPU 就知道是 键盘 发出的信号然后就会去调用 键盘 的执行方法通过键盘的读取方法读取到 ctrl c 这个信息转化后就是 2 号信号执行终止前台进程的动作 键盘被按下 和 键盘哪些位置被按下 是不一样的 首先键盘先按下CPU 确定对应的读取方法 其次才是通过 读取方法 从键盘中读取数据 注键盘读取方法如何进行读取这是驱动的事我们不用关心 硬件中断 的流程与 进程信号 的流程雷同同样是 先检测到信号然后再去执行相应的动作不过此时发送的是 中断信号执行的是 调用相应方法罢了 信号 与 动作 的设计方式很实用操作系统只需要关注是否有信号发出发出后去中断向量表中调用相应的方法即可不用管硬件是什么样、如何变化做到了 操作系统 与 硬件 间的解耦 3、系统调用 除了可以通过 键盘键入 发送信号外还可以通过直接调用 系统接口 发送信号毕竟 bash 也是一个进程本质上就是在进行程序替换而已 3.1、kill 函数 信号的发送主要是通过 kill 函数进行发送 返回值成功返回 0失败返回 -1 并设置错误码 参数1待操作进程的 PID 参数2待发送的信号 下面来简单用一下程序运行 5 秒后自己把自己杀死 #include iostream #include signal.h #include unistd.h using namespace std;int main() {int n 1;while (true){cout 我是一个进程已经运行了 n 秒 PID: getpid() endl;sleep(1);n;if (n 5)kill(getpid(), SIGKILL);}return 0; }kill 函数当然也可以发送其他信号这里就不一一展示了其实命令行中的 kill 命令就是对 kill 函数的封装kill -信号编号 -PID 其中的参数2、3不正是 kill 函数所需要的参数吗所以我们可以尝试自己搞一个 myKill 命令 3.2、模拟实现 myKill 这里就直接利用 命令行参数 简单实现了 #include iostream #include string #include signal.husing namespace std;void Usage(string proc) {// 打印使用信息cout \tUsage: \n\t;cout proc 信号编号 目标进程 endl;exit(2); }int main(int argc, char *argv[]) {// 参数个数要严格限制if (argc ! 3){Usage(argv[0]);}//获取两个参数int signo atoi(argv[1]);int pid atoi(argv[2]);//执行信号发送kill(pid, signo);return 0; }下面随便跑一个进程然后用自己写的 myKill 命令给进程发信号 我们可以把这个程序改造下改成进程替换的方式让后将自己写的命令进行安装就能像 kill 一样直接使用了 3.3、raise 函数 发送信号的还有一个 raise 函数这个函数比较奇怪只能 自己给自己发信号 返回值成功返回 0失败返回 非0 就只有一个参数待发送的信号 可以这样理解raise 是对 kill 函数的封装每次传递的都是自己的 PID #include iostream #include signal.h #include unistd.h using namespace std;int main() {int n 1;while (true){cout 我是一个进程已经运行了 n 秒 PID: getpid() endl;sleep(1);n;if (n 5)raise(SIGKILL); //自己杀死自己 }return 0; } 3.4、abort 函数 abort 是 C 语言提供的一个函数它的作用是 给自己发送 6 号 SIGABRT 信号 没有返回值也没有参数 值得一提的是abort 函数即使在修改执行动作后最后仍然会发送 6 号信号 #include iostream #include signal.h #include unistd.h using namespace std;void handler(int signo) {cout 收到了 signo 号信号已执行新动作 endl; }int main() {signal(6, handler);// signal(SIGABRT, handler); //这种写法也是可以的int n 1;while (true){cout 我是一个进程已经运行了 n 秒 PID: getpid() endl;sleep(1);n;if (n 5)abort();}return 0; }即使执行了我们新注册的方法abort 最后仍然会发出 6 号信号终止进程 同样是终止进程C语言 还提供了一个更好用的函数exit()所以 abort 用的比较少了解即可 总的来说系统调用中举例的这三个函数关系是kill 包含 raiseraise 包含 abort作用范围是在逐渐缩小的 4、软件条件信号产生发送的第三种方式软件条件 其实这种方式我们之前就接触过了管道读写时如果读端关闭那么操作系统会发送信号终止写端这个就是 软件条件 引发的信号发送发出的是 13 号 SIGPIPE 信号 4.1、alarm 设置闹钟 系统为我们提供了 闹钟报警alarm这个 闹钟 可不是用来起床的而是用来 定时 的 返回值如果上一个闹钟还有剩余时间则返回剩余时间否则返回 0 参数想要设定的时间单位是秒 当时间到达闹钟中的预设时间时闹钟会响并且发送 14 号 SIGALRM 信号 比如这样 #include iostream #include signal.h #include unistd.h using namespace std;int main() {alarm(5); //设定一个五秒后的闹钟int n 1;while (true){cout 我是一个进程已经运行了 n 秒 PID: getpid() endl;sleep(1);n;}return 0; }#include iostream #include signal.h #include unistd.h using namespace std;void handler(int signo) {cout 收到了 signo 号信号已执行新动作 endl;int n alarm(10);cout 上一个闹钟剩余时间: n endl; }int main() {signal(SIGALRM, handler);alarm(10); //设定一个十秒后的闹钟while(true){cout 我是一个进程我正在运行…… PID: getpid() endl;sleep(1);};return 0; }系统中不止一个闹钟所以 OS 需要 先描述再组织将这些闹钟管理起来 可以借助闹钟简单测试一下当前服务器的算力 5、硬件异常 最后一种产生发送信号的方式是硬件异常 所谓 硬件异常 其实就是我们在写程序最常遇到的各种报错比如 除 0、野指针 5.1、除 0 导致异常 先来看一段简单的错误代码 #include iostream using namespace std;int main() {int n 10;n / 0;return 0; }显然是会报错的是毕竟 0 不能作为常数  根据报错信息可以推测出此时发送的是 8 号 SIGFPE 信号浮点异常 让我们通过 signal 更改 8 号信号的执行动作尝试逆天改命让 除 0 合法 #include iostream #include signal.h #include unistd.h using namespace std;void handler(int signo) {cout 虽然除 0 了但我不终止进程 endl; }int main() {signal(SIGFPE, handler);int n 10;n / 0;return 0; }结果一直在死循环似的发送信号明明只发生了一次 除 0 行为 想要明白背后的原理需要先认识一下 状态寄存器 5.2、状态寄存器 在 CPU 中存在很多 寄存器其中大部分主要用来存储数据信息用于运算除此之外还存在一种特殊的 寄存器 》 状态寄存器这个 寄存器 专门用来检测当前进程是否出现错误行为如果有就会把 状态寄存器位图结构中对应的比特位置 1意味着出现了 异常 当操作系统检测到 状态寄存器 出现异常时会根据其中的值向出现异常的进程 轮询式 的发送信号目的就是让进程退出 比如上面的 除 0 代码发生异常后CPU 将 状态寄存器 修改变成 异常状态操作系统检测到 异常 后会向进程发送 8 号信号即使我们修改了 8 号信号的执行动作但 因为状态寄存器仍然处于异常状态所以操作系统才会不断发送 8 号信号所以才会死循环式的打印 能让 状态寄存器 变为 异常 的都不是小问题需要立即终止进程然后寻找、解决问题 毕竟如果让 除 0 变为合法那最终的结果是多少呢所以操作系统才会不断发送信号目的就是 终止进程的运行 5.3、野指针导致异常 除了 除 0 异常外还有一个 臭名昭著 的异常野指针问题 #include iostream using namespace std;int main() {int* ptr nullptr;*ptr 10;return 0; }Segmentation fault 段错误 这是每个 C/C 程序猿都会遇到的问题因为太容易触发了出现段错误问题时操作系统会发送 11号 SIGSEGV 信号终止进程可以通过修改执行动作验证这里不再演示 那么 野指针 问题是如何引发的呢 借用一下 共享内存 中的图~ 野指针问题主要分为两类 指向不该指向的空间权限不匹配比如只读的区域偏要去写 共识在执行 *ptr 10 这句代码时首先会进行 虚拟地址 - 真实物理地址 之间的转换 指向不该指向的空间这很好理解就是页表没有将 这块虚拟地址空间 与 真实物理地址空间 建立映射关系此时进行访问时 MMU 识别到异常于是 MMU 直接报错操作系统识别到 MMU 异常后向对应的进程发出终止信号 权限不匹配页表中除了保存映射关系外还会保存该区域的权限情况比如 是否命中 / RW 等权限当发生操作与权限不匹配时比如 nullptr 只允许读取并不允许其他行为此时解引用就会触发 MMU 异常操作系统识别到后同样会对对应的进程发出终止信号 页表中的属性 是否命中RW 权限UK 权限不必关心 注MMU 是内存管理单元主要负责 虚拟地址 与 物理地址 间的转换工作同时还会识别各种异常行为 一旦引发硬件层面的问题操作系统会直接发信号立即终止进程 到目前为止我们学习了很多信号分别对应着不同的情况其中有些信号还反映了异常信息所以将信号进行细分还是很有必要的 6.核心转储  对于某些信号来说当终止进程后需要进行 core dump产生核心转储文件 比如3号 SIGQUIT、4号 SIGILL、5号 SIGTRAP、6号 SIGABRT、7号 SIGBUS、8号 SIGFPE、11号 SIGSEGV、24号 SIGXCPU、25号 SIGXFSZ、31号 SIGSYS 都是可以产生核心转储文件的 不同信号的动作Action Trem - 单纯终止进程Core - 先发生核心转储生成核心转储文件前提是此功能已打开再终止进程 但在前面的学习中我们用过 3、6、8、11 号信号都没有发现 核心转储 文件啊 难道是我们的环境有问题吗 确实当前环境确实有问题因为它是 云服务器而 云服务器 中默认是关闭核心转储功能的 6.2、打开与关闭核心转储 通过指令 ulimit -a 查看当前系统中的资源限制情况 ulimit -a可以看到当前系统中的核心转储文件大小为 0即不生成核心转储文件 通过指令手动设置核心转储文件大小 ulimit -c 1024现在可以生成核心转储文件了 就拿之前的 野指针 代码测试因为它发送的是 11 号信号会产生 core dump 文件 核心转储文件是很大的而有很多信号都会产生核心转储文件所以云服务器一般默认是关闭的 云服务器上是可以部署服务的一般程序发生错误后会立即重启 如果打开了核心转储一旦程序 不断挂掉、又不断重启那么必然会产生大量的核心转储文件当文件足够多时磁盘被挤满导致系统 IO 异常最终会导致整个服务器挂掉的 还有一个重要问题是 core 文件中可能包含用户密码等敏感信息不安全 关闭核心转储很简单设置为 0 就好了 ulimit -c 06.3、核心转储的作用 如此大的核心转储文件有什么用呢 答案是 调试 没错核心转储文件可以调试并且直接从出错的地方开始调试 这种调试方式叫做 事后调试 调试方法 gcc / g 编译时加上 -g 生成可调试文件运行程序生成 core-dump 文件gdb 程序 进入调试模式core-file core.file 利用核心转储文件快速定位至出错的地方 之前在 进程创建、控制、等待 中我们谈到了 当进程异常退出时被信号终止不再设置退出码而是设置 core dump 位 及 终止信号 也就是说父进程可以借此判断子进程是否产生了 核心转储 文件 总结 信号产生部分就到此下一篇信号保存
http://www.pierceye.com/news/150231/

相关文章:

  • 网站主办者单位有效证件电子件是什么怎么做免费的产品网站
  • 设计素材网站好融资吗网站设计需要需要用
  • 北京品牌营销的服务机构sem和seo有什么区别
  • 注册企业网站上海人才中心档案托管
  • 建设银行的网站为什么登不上公司员工培训方案
  • 网站形式wordpress 顶部工具栏
  • 网站前后台修改wordpress用户密码
  • 微信 公司网站 怎么做手机端视频网站模板下载
  • 何为响应式网站太原自助建站
  • 网站建设方案书怎么写安徽和住房建设厅网站
  • 北京市住房和城乡建设厅官方网站重庆百度seo整站优化
  • 备案ip 查询网站查询网站河南建筑职业技术学院
  • 均安公司网站建设免费建手机个人网站
  • 南京做网站的网络公司排名wordpress发邮件更新
  • 抽奖的网站怎么做美食类网站模板
  • 自己建一个网站难吗网络安全行业公司排名
  • 做招聘的h5用哪个网站企业网站需要多大空间
  • 织梦 公司网站模板html5网站开发的源码
  • 晋江网站建设公司电脑培训网
  • 电子商务网站开发的题网站关键词排名怎么提升
  • 在百度网站备案查询上显示未备案是什么意思wordpress资源分享主题
  • 夏县做网站郑州做商城网站
  • 网站首页推荐网络服务提供者发现用户利用其网络服务对未成年
  • 中外网站建设区别微信软文是什么意思
  • 苏州网站建设极简幕枫卫浴网站建设
  • 优秀企业网站欣赏网站的备案怎么处理
  • 怎样做古玩网站毕业设计开题报告网站开发
  • 西安网站 建设app注册推广
  • 丹徒网站建设公司代理公司注册价格
  • 网站建站建设网站中国商标商标查询网