Linux操作系統之信號:信號的產生

前言:

上篇文章我們大致講解了信號的有關概念,為大家引入了信號的知識點。但光知道那些是遠遠不夠的。

本篇文章,我將會為大家自己的講解一下信號的產生的五種方式,希望對大家有所幫助。

一、鍵盤(硬件)產生信號

回顧

我們上文曾經說過,當我們在前臺運行的一個進程時,尤其是while控制的死循環程序,我們可以通過按下ctrl + c按鍵來終止進程。

這是因為我們通過系統按鍵給進程發送了二號信號SIGINT,并殺死了該進程:

其中從34-64的信號我們這里不予關注,也用不上。我們只談論前面31中信號。?

我們當時通過signal系統調用接口,來自定義了對二號信號的處理方式handler。

其實忘記說的是,void handler(int signumber)中的參數signumber,其實就是該信號的編號。

而信號每一個信號如SIGINT,他其實都是一個宏定義:

?我們可以通過man 7 signal,來查看更多信號的內容:

2號信號的默認處理方式是Term,也就是終止進程,另外,Core也是默認終止進程。


如果我們把一個進程的所有信號的默認處理方式都變成handler,會發什么什么情況呢?


#include <iostream>
#include <unistd.h>
#include<sys/types.h>
#include<signal.h>void handler(int signumber)
{std::cout<<"捕獲到信號"<<signumber<<",開始執行自定義處理方法"<<std::endl;
}int main()
{for(int i=1;i<=31;++i){signal(i,handler);}while(true){std::cout<<"I am "<<getpid()<<" , I am waiting signal!"<<std::endl;sleep(3);}return 0;
}

難不成,所有信號都干不掉這個進程了嗎??

這個問題,信號設計者自然也會想到,所以在信號中,就有幾位信號的默認處理方式是無法改變的,九號信號就是這樣。


信號位圖

我們在按下鍵盤時,鍵盤會把信息交給操作系統,操作系統才向進程發送信號。

操作系統憑什么能收到鍵盤的信息啊?

:因為我們之前說過,操作系統是所有硬件的管理者,這一點我們在操作系統的那一章節說過。

同學們,我們說處理信號是在合適的時候而不是立即處理,那么,我們就必須把收到的信號保存起來。否則你不能及時處理,要等待,就會丟失信號。

那么進程是如何保管自己收到的信號的呢?

答案是:PCB

我們之前說每一個信號都有一個編號,并且編號剛好對應1-31,同學們,這你想到了什么?

:是不是位圖啊!!

所以,在每一個進程的PCB中,存在一個位圖,對應的比特位的0/1,就代表的是否收到對應的信號:

所以我們可以知道,發送信號,其實本質上不是發送,而是寫入!!

寫入信號!!:OS修改對應的進程的PCB中的信號位圖:0->1。

同樣的,我們說每一個信號都有一個對應的默認處理方法。這個方法又是怎么保存的呢?

參考一下我們的struct file中的操作表,在我們的task_struct中,也存在一個函數指針數組,分別存儲對應下標的信號的默認處理方法:

?


?硬件中斷初體驗

那么,操作系統是怎么知道鍵盤上有數據的呢?
難不成要操作系統一直在詢問這些硬件嗎?要知道,操作系統可是很忙的,基本什么事情,都有它的參與。

自然不會由操作系統主動去詢問,這就跟你平時在公司上班,上頭直接把任務分配給你,你完成了要給別人說一樣。操作系統就是這個老板,硬件就是員工。

當按下鼠標,鼠標就會產生硬件中斷,在馮諾依曼體系的幫助下,告訴操作系統我已經準備好了。

?

至此,操作系統就不用主動去知道鍵盤是否有數據,他只需要等別人告訴他。

這樣,就實現了硬件與操作系統的并行執行。

操作系統通過中斷管理所有硬件。那他內部管理進程,想模擬硬件的行為,于是就有了信號


二、指令

我們之前講過,當我們想要對一個進程發送信號,我們只需要知道這個進程的pid,于是我們可以通過kill的系統指令,來給這個進程發送指定編號的信號:

所以我們這里就不再過多復述了。

三、系統調用

那么kill指令是怎么實現的呢?

它是根據kill系統調用來實現的:
?

第一個參數就是對應進程pid,第二個參數就是發送信號的編號或者宏。

至此,我們可以模擬實現一個我們的mykill程序:

#include <iostream>
#include <unistd.h>
#include<sys/types.h>
#include<signal.h>int main(int argc,char* argv[])
{if(argc!=3){std::cerr<<"Usage:" << argv[0] << " -signumber pid" << std::endl;return 1;}int n=::kill(atoi(argv[2]),atoi(argv[1]));return n;
}


第二個系統調用就是raise:raise 函數可以給當前進程發送指定的信號(就是自己給自己發信號)。

還有一個系統調用時abort:abort 函數使當前進程接收到信號而異常終?。?

不難發現,后面兩個調用的作用都是進行了特化,可以猜測他們的底層都調用了kill。


四、軟件條件

我們當時在講匿名管道的時候,提到過:

管道讀端關閉,如果此時寫端還想寫入

操作系統就會直接終止該寫端進程:這其實就是發送的13號信號

這歌案例就是軟件條件不具備,你不具備寫入的條件,于是要發送信號。

除了13信號外,還有一個14信號SIGALRM,這就涉及到了一個系統調用:alarm鬧鐘

?alarm()?是 Unix/Linux 系統提供的?定時器函數,用于在指定時間后向當前進程發送?SIGALRM?信號(默認行為是終止進程)。它屬于?<unistd.h>?頭文件,常用于實現超時控制或周期性任務。這個鬧鐘是一次性的,你設置一次alarm函數,就會設置一個鬧鐘?

我們運行程序:

#include <iostream>
#include <unistd.h>
#include <signal.h>int main()
{int count = 0;alarm(1);while (true){std::cout << "count : "<< count << std::endl;count++;}return 0;
}

就知道了一秒內的循環次數

?我們一般會搭配上signal,使用自己的處理方法,這樣就能實現一下特殊的代碼:

#include <iostream>
#include <unistd.h>
#include <signal.h>int count = 0;
void handler(int signumber)
{std::cout << "count : " << count << std::endl;exit(0);
}
int main()
{signal(SIGALRM, handler);alarm(1);while (true){count++;}return 0;
}

誒,為什么后面這個代碼while循環了這么多次呢?不都是一秒鐘的鬧鐘嗎??

這是因為:cout是阻塞式I/O操作,涉及用戶態到內核態的切換,以及終端設備的輸出

這些操作非常耗時,一秒鐘的大部分時間花在 I/O 上,而非?count++

而后面的操作就只涉及了count++,最后才會打印輸出。


如果我們想設置重復鬧鐘呢?

就需要循環調用了:

#include <iostream>
#include <unistd.h>
#include <signal.h>
#include <vector>
#include <functional>using func_t = std::function<void()>;int gcount = 0;
std::vector<func_t> gfuncs;// 把信號 更換 成為 硬件中斷
void hanlder(int signo)
{for (auto &f : gfuncs){f();}std::cout << "gcount : " << gcount << std::endl;alarm(1);
}int main()
{gfuncs.push_back([](){ std::cout << "我是一個內核刷新操作" << std::endl; });gfuncs.push_back([](){ std::cout << "我是一個檢測進程時間片的操作,如果時間片到了,我會切換進程" << std::endl; });gfuncs.push_back([](){ std::cout << "我是一個內存管理操作,定期清理操作系統內部的內存碎片" << std::endl; });alarm(1); // 一次性的鬧鐘,超時alarm會自動被取消signal(SIGALRM, hanlder);while (true){pause();std::cout << "我醒來了..." << std::endl;gcount++;}
}

?

如果時間才間隔快點,把信號更換為硬件中斷,就是我們操作系統的運行原理


?五、異常

我們都知道,當我們的代碼出現除0或者使用野指針時,進程就會直接終止掉。

關于野指針我們在虛擬地址空間的時候曾經提到過:這是因為在頁表上找該虛擬地址的映射關系時,找不到,或者權限不夠,所以會被信號終止。

那么除零呢?

當我們出現除0異常時,會發送8信號(野指針是11)給我們的進程:

我們寫以下代碼:

#include <iostream>
#include <unistd.h>
#include <signal.h>
#include <vector>
#include <functional>void handler(int num)
{std::cout<<"捕獲到信號"<<num<<std::endl;
}int main()
{signal(11,handler);signal(8,handler);int a=10;a/=0;int*p=nullptr;*p=10;while(true){std::cout<<"hello world"<<std::endl;};return 0;
}

運行結果會無限打印:捕獲到信號8.

這是為什么呢?

在我們的CPU上:

我們的操作系統需要知道CPU內部是否出錯,(CPU也是一個硬件),就存在一個狀態寄存器,負責判斷此次的運算結果是否范圍溢出等問題。(標記位為0表示正常,為1表示溢出)

當我們不終止進程,使用自己的處理方法,由于進程會輪循調度的原因,保存上下文信息,不終止進程就會一直調度該進程,發現溢出->發送信號,不斷重復該過程

?

?野指針越界訪問也是類似的形式,當我們虛擬地址轉化為物理地址成功時,會把地址存儲在CR3中,如果失敗,就會存儲在CR2中,這樣我們就知道出錯了。


六、Core與Term

Core與Term都是終止進程,但是Core還做了一些特殊的處理。

如果是core的終止進程,在終止后會幫我們形成一個debug文件,通常是corecore.pid文件。

我們可以通過一些命令來看到出錯的信息來調試。(或者gdb)

這里我就不在贅述,感興趣的可以了解一下。

總結:

今天我們詳細講解的信號產生的五種方式,希望對大家有所幫助!!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/914498.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/914498.shtml
英文地址,請注明出處:http://en.pswp.cn/news/914498.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

pdf拆分

文章目錄 背景目標實現下載 背景 好不容易下載的1000頁行業報告&#xff0c;領導非要按章節拆分成20份&#xff01;學術論文合集需要按作者拆分投稿&#xff0c;手動分頁到懷疑人生…客戶發來加密合同&#xff0c;要求每5頁生成獨立文檔&#xff0c;格式還不能亂&#xff01; …

vue3使用mermaid生成圖表,并可編輯

效果圖實際代碼<template><div class"mermaid-container" style"z-index: 99999" ref"wrapperRef"><!-- 控制欄 --><div class"control-bar"><div class"control-bar-flex control-bar-tab-wrap"…

tcp/quic 的滑動窗口

一、滑動窗口 rwnd&#xff1a; 接收端窗口&#xff0c;接收方在每次發送ACK確認報文時&#xff0c;會包含一個 rwnd (Receive Window Size) 字段&#xff0c;指明自己當前剩余的接收緩沖區大小&#xff08;即可用窗口&#xff09;&#xff0c;這里是否是socket的接收緩沖區&am…

JVM監控及診斷工具-命令行篇

18.1. 概述 性能診斷是軟件工程師在日常工作中需要經常面對和解決的問題&#xff0c;在用戶體驗至上的今天&#xff0c;解決好應用的性能問題能帶來非常大的收益。 Java 作為最流行的編程語言之一&#xff0c;其應用性能診斷一直受到業界廣泛關注。可能造成 Java 應用出現性能…

Jenkins 版本升級與插件問題深度復盤:從 2.443 到 2.504.3 及功能恢復全解析

前言&#xff1a;問題溯源與升級必要性 在 Jenkins 持續集成體系中&#xff0c;插件生態是其強大功能的核心驅動力。然而&#xff0c;某次例行維護中&#xff0c;團隊對 Jenkins 2.443 環境的插件進行批量升級后&#xff0c;意外觸發連鎖反應 &#xff1a; SSH Server 插件功能…

Ribbon實戰

一、前置知識 1.1 負載均衡定義 負載均衡指的是將網絡請求通過不同的算法分配到不同的服務器上的技術&#xff0c;從而提升系統的性能。 1.2 負載均衡工具 負載均衡工具可以分分為客戶端負載均衡工具和服務端負載均衡工具&#xff0c;它們的區別如下。 表1-1 負載均衡工具…

cs285學習筆記(一):課程總覽

根據 Fall 2023 學期的官方課程日程&#xff0c;這里是 CS?285 全課程的 Lecture 大綱及內容摘要&#xff0c;詳細對應周次和主題&#xff0c;方便你快速定位每節課要點、相關作業與視頻資源 &#x1f3af; 官方課程地址 YouTobe 視頻地址 blibli視頻(帶中文字幕) &#x…

OkHttp SSE 完整總結(最終版)

1. SSE 基礎概念 什么是 SSE&#xff1f; SSE&#xff08;Server-Sent Events&#xff09;是一種 Web 標準&#xff0c;允許服務器向客戶端推送實時數據。 核心特點 單向通信&#xff1a;服務器 → 客戶端 基于 HTTP 協議&#xff1a;使用 GET 請求 長連接&#xff1a;連…

聚寬sql數據庫傳遞

自建數據庫從聚寬到Q-MT自動化交易實戰 從接觸聚寬以來一直都是手動跟單&#xff0c;在網上看到許多大佬的自動交易文章&#xff0c;心里也不禁十分癢癢。百說不如一練&#xff0c;千講不如實干。經過一番努力&#xff0c;終于成功實盤了&#xff0c;效果還可以&#xff0c;幾…

es里為什么node和shard不是一對一的關系

提問&#xff1a; 既然多個shard會被分配到同一個node上&#xff0c;那么為什么不把多個shard合并成一個然后存在當前node上呢&#xff0c;簡而言之也就是讓node和shard形成一對一的關系呢 &#xff1f;非常好的問題&#xff0c;這正是理解Elasticsearch分片&#xff08;shard…

淺談npm,cnpm,pnpm,npx,nvm,yarn之間的區別

首先做一個基本的分類 名稱描述npm,cnpm,yarn,pnpm都是Javascript包管理器nvm是Node.js版本控制器npx命令行工具 I.npm,cnpm,yarn,pnpm npm (Node Package Manager) npm是Node.js默認的包管理器&#xff0c;隨Node.js的安裝會一起安裝。使用npm可以安裝&#xff0c;發布&…

滑動窗口-76.最小覆蓋子串-力扣(LeetCode)

一、題目解析1.不符合要求則返回空串("")2.子串中重復字符的數量要不少于t中該字符的數量二、算法原理解法1&#xff1a;暴力枚舉哈希表這里的暴力枚舉也可以優化&#xff0c;即在包含t中元素處枚舉&#xff0c;如在A、B和C處開始枚舉&#xff0c;減少不必要的枚舉 解…

從零構建搜索引擎 build demo search engine from scratch

從零構建搜索引擎 build demo search engine from scratch 我們每天都會使用搜索引擎&#xff1a;打開google等搜索引擎&#xff0c;輸入關鍵詞&#xff0c;檢索出結果&#xff0c;這是一次搜索&#xff1b;當打開歷史記錄旁邊的&#x1f50d;按鈕&#xff0c;輸入關鍵詞&#…

pytorch小記(二十九):深入解析 PyTorch 中的 `torch.clip`(及其別名 `torch.clamp`)

pytorch小記&#xff08;二十九&#xff09;&#xff1a;深入解析 PyTorch 中的 torch.clip&#xff08;及其別名 torch.clamp&#xff09;深入解析 PyTorch 中的 torch.clip&#xff08;及其別名 torch.clamp&#xff09;一、函數簽名二、簡單示例三、廣播支持四、與 Autograd…

快速分頁wpf

/*沒有在xaml設置上下文window.context是因為 命名空間一直對應不上 所以在xaml.cs 里面綁定*/ <Window x:Class"DataGrid.views.MainWindow"xmlns"http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x"http://schemas.microsoft…

如何徹底禁用 Chrome 自動更新

如何徹底禁用 Chrome 自動更新 隨著谷歌將 Chrome 瀏覽器版本升級至 138&#xff0c;它即將徹底拋棄對 Manifest V2 擴展的支持。許多用戶希望將瀏覽器版本鎖定在 138&#xff0c;以繼續使用 uBlock Origin、Tampermonkey 等常用擴展。 本文總結了四種有效方法&#xff0c;幫助…

流批一體的“奧卡姆剃刀”:Apache Cloudberry 增量物化視圖應用解析

引言&#xff1a;流批一體&#xff0c;理想與現實的鴻溝 在數據驅動的今天&#xff0c;“實時”二字仿佛擁有魔力&#xff0c;驅使著無數企業投身于流批一體架構的建設浪潮中。我們渴望實時洞察業務變化&#xff0c;實時響應用戶需求。以 Apache Flink 為代表的流處理引擎&…

C# 入門教程(三):詳解字段、屬性、索引器及各類參數與擴展方法

文章目錄一、字段、屬性、索引器、常量1.字段2.屬性2.1 什么是屬性2.2 屬性的聲明2.3 屬性與字段的關系3 索引器4. 常量二、傳值 輸出 引用 數組 具名 可選參數&#xff0c;擴展方法2.1 傳值參數2.1.1 值類型 傳參2.1.2 引用類型 傳參2.2 引用參數2.2.1 引用參數-值類型 傳參2.…

《美術教育研究》是什么級別的期刊?是正規期刊嗎?能評職稱嗎?

?問題解答&#xff1a;問&#xff1a;《美術教育研究》是不是核心期刊&#xff1f;答&#xff1a;不是&#xff0c;是知網收錄的第一批認定學術期刊。問&#xff1a;《美術教育研究》級別&#xff1f;答&#xff1a;省級。主管單位&#xff1a; 安徽出版集團有限責任公司 主辦…

每日算法刷題Day47:7.13:leetcode 復習完滑動窗口一章,用時2h30min

思考: 遇到子數組/子字符串可以考慮能不能用滑動窗口&#xff0c; 定長:逆向思維,答案不定 最大長度/最小長度:一般求長度 越長越合法/越短越合法/恰好:一般求數量 主要思考窗口條件成立&#xff0c; 判斷條件是符合窗口條件(最小長度/越長越合法還是不符合(最大長度/越短越合法…