1. 程式人生 > >Linux下的訊號(一)----訊號的基本概念與產生

Linux下的訊號(一)----訊號的基本概念與產生

一,訊號的基本概念

1,什麼是訊號?
日常生活中,當我們走到馬路上時,看到的綠燈是一種訊號,它能提示我們怎樣安全的過馬路。又比如,新學期開始學校給每個班發的課表也是一種訊號,它能提示同學們在適當的時間地點去上相應的課程而不是虛度光陰……生活中其實我們忽略了很多訊號,正是由於這些訊號的存在,才使得我們的生活方便而有序。
總結一下你會發現訊號是什麼,訊號就是當你看到它是知道它是什麼,並且知道看到訊號之後應該做什麼,至於你遵不遵守就是你自己的事了, 計算機中的訊號也不例外。

2,計算機中的訊號
同日常生活中的訊號一樣,計算機在收到訊號之後,並不一定會立即處理它,它會將收到的訊號記錄在其相應程序的PCB中的訊號部分,等待合適的時間再去處理它。換句話說,一個程序是否收到訊號,需要檢視其程序PCB中的訊號資訊,給程序發訊號實則是向程序PCB中寫入訊號資訊。同時,我們的作業系統是很智慧的,當任何一個程序接收到任何一個訊號時,作業系統會自動地知道各訊號應作何處理。
檢視系統定義的訊號列表:kill -l
這裡寫圖片描述


注意列表中不是64個訊號而是62個訊號。
1-31:普通訊號
34-64:實時訊號
———-以下內容只針對普通訊號進行講解———-
每個訊號都有一個巨集名稱和一個訊號編號,這些巨集定義可以在signal.h中找到,這些訊號各自在什麼條件下產生,預設的處理動作是什麼,在signal(7)中都有詳細說明:
檢視命令:man 7 signal
這裡寫圖片描述
各種訊號產生的原因:
[http://blog.sina.com.cn/s/blog_7ee076050101bz5v.html]

3,普通訊號的儲存:
試想一下,如果你是作業系統的設計人員,你會用什麼來儲存這31個訊號呢?當然,設計人員都很聰明,他們採用的是點陣圖儲存

,每一個bit位儲存一個相應訊號,31個訊號只需4個位元組(僅一個整形的大小)即可儲存,bit位的位置表示訊號的編號,bit位的值則用來表示是否收到該訊號(0—-未收到該訊號,1—-收到該訊號)。
訊號量的本質就是修改PCB中管理訊號變數中的某個bit位。

4,訊號產生的主要條件
背景知識:
前臺程序:基本不用和使用者互動,優先順序稍微低一些(執行前臺程序: ./test.c)
後臺程序:需要和使用者進行互動,需要較高的相應速度,優先級別高(執行後臺程序: ./test.c &)

1〉使用者在終端按下某些組合鍵時,終端驅動程式會發送訊號給前臺程序。例如:
Ctrl-C產生SIGINT訊號(2號訊號,終止前臺程序),
Ctrl-\產生SIGQUIT訊號(3號訊號,捕捉訊號),
Ctrl-Z產生SIGTSTP訊號(20號訊號,可使前臺程序停止)。
2〉硬體異常產生訊號,這些條件由硬體檢測到並通知核心,然後核心向當前程序傳送適當的訊號


例如:當前程序執行了除以0的指令,CPU的運算單元會產生異常,核心將這個異常解釋 為SIGFPE訊號傳送給程序。再比如當前程序訪問了非法記憶體地址,,MMU(記憶體管理單元)會產生異常,核心將這個異常解釋SIGSEGV訊號傳送給程序。
3〉一個程序呼叫kill(2)函式可以傳送訊號給另一個程序
可以用kill(1)命令傳送訊號給某個程序,kill(1)命令也是呼叫kill(2)函式實現的,如果不明確指定訊號則傳送SIGTERM訊號,該訊號的預設處理動作是終止程序。當核心檢測到某種軟體條件發生時也可以通過訊號通知程序,例如鬧鐘超時產生SIGALRM訊號,向讀端已關閉的管道寫資料時產生SIGPIPE訊號。 如果不想按預設動作處理訊號,使用者程式可以呼叫sigaction(2)函式告訴核心如何處理某種訊號。

5,處理訊號的方法
1〉忽略此訊號(大多數訊號都可以使用該方法處理)。
特例:SIGKILL , SIGSTOP
原因:它們向超級使用者提供一種使程序終止或停止的方法,同時,如果忽略某些由硬體異常產生的訊號,則程序的行為是未定義的。
2〉執行該信浩的預設處理動作(與訊號的種類有關,大多數訊號會直接終止該程序)。
3〉用訊號捕捉函式為該訊號指定自定義動作。
特例:不能捕捉SIGKILL , SIGSTOP訊號
原因:為了防止非法使用者的惡意入侵使得程序永遠殺不掉。
訊號捕捉函式:修改訊號的預設動作,有些訊號是不能被捕捉的,如9號訊號SIGKILL等。

#include <signal.h>

       typedef void (*sighandler_t)(int);

       sighandler_t signal(int signum, sighandler_t handler);

引數:signum:訊號編號 handler:指向怎樣捕捉該訊號的函式
返回值:signal函式的返回值是一個函式指標,成功返回返回以前的處理 配置,失敗返回錯誤碼對應的錯誤提示。
makefile:

 signal:signal.c
  2     gcc -o signal signal.c
  3 .PHONY:clean
  4 clean:
  5     rm -f signal

signal.c:

#include<unistd.h>
#include<stdio.h>
#include<signal.h>

void myhandler(int signo)
{
    printf("got SIGINT\n");
}
int main()
{
   signal(2,myhandler);//捕捉2號訊號SIGINT:Ctrl C
   while(1)
   {
       sleep(1);
   }
   return 0;
}

執行結果:
捕捉2號訊號:
這裡寫圖片描述

殺死程序(此時ctrl C不再能夠結束程序):
這裡寫圖片描述

程序結束:
這裡寫圖片描述

改進:signal.c

#include<unistd.h>
#include<stdio.h>
#include<signal.h>

typedef void (*sighandler_t)(int);//函式指標
sighandler_t old_handler = NULL;

void myhandler(int signo)
{
      printf("got SIGINT\n");//第一次按ctrlC捕捉2號訊號SIGINT
      signal(2, old_handler);//恢復預設的處理,再按ctrl C的話,就會終止程式
}
int main()
{
   old_handler = signal(2,myhandler);//捕捉2號訊號SIGINT:Ctrl C
   while(1)
   {
       sleep(1);
   }
   return 0;
}

執行結果:
這裡寫圖片描述

二,產生訊號

1,通過終端按鍵產生訊號
上面說過,SIGINT(ctrl C)的預設處理動作是終止程序,SIGQUIT(ctrl )的預設處理動作是終止程序並且Core Dump,那麼什麼是Core Dump呢?
Core Dump:核心轉儲。當一個程序要異常終止時,可以選擇把程序的使用者空間記憶體資料全部儲存到磁碟上,檔名通常是core,這叫做Core Dump。
程序異常終止通常是因為有Bug,比如非法記憶體訪問導致段錯誤,事後可以用偵錯程式檢查core檔案以查清錯誤原因,這叫做Post-mortem Debug(事後除錯)。一個程序允許產生多大的core檔案取決於程序的Resource Limit(這個資訊儲存 在PCB中)。預設是不允許產生core檔案的,因為core檔案中可能包含使用者密碼等敏感資訊,不安全。在開發除錯階段可以用ulimit命令改變這個限制,允許產生core檔案。
程式碼驗證:
1>首先用ulimit命令改變Shell程序的Resource Limit,允許core檔案最大為1024K:ulimit -c 1024
這裡寫圖片描述
2>然後寫一個死迴圈程式:
makefile:

   .PHONY: all
   all:signal sig
   signal:signal.c
       gcc -o [email protected] $^
   sig:sig.c
       gcc -o [email protected] $^
   .PHONY:clean
   clean:
       rm -f signal sig

sig.c:

 #include<stdio.h>
 #include<sys/types.h>
 #include<unistd.h>

 int main()
 {
     printf("pid is %d\n",getpid());
     while(1);
     return 0;
 }

執行結果:
這裡寫圖片描述
ulimit命令改變了Shell程序的Resource Limit,test程序的PCB由Shell程序複製而來,所以也具 有和Shell程序相同的Resource Limit值,這樣就可以產生Core Dump了。

2,軟硬體異常產生訊號
軟硬體異常訊號 其他訊號 SIGCHLD or SIGCLD 子程序結束時, 父程序會收到這個訊號。 如果父程序沒有處理這個訊號,也沒有等待(wait)子程序,子程序雖然終止,但是還會在核心程序表中佔有表項,這時的子程序稱為殭屍程序。這種情 況我們應該避免(父程序或者忽略SIGCHILD訊號,或者捕捉它,或者wait它派生的子程序,或者父程序先終止,這時子程序的終止自動由init程序 來接管)。

3,呼叫系統函式想程序發訊號
1〉首先在後臺執行死迴圈程式,然後用kill命令給它發SIGSEGV訊號。
這裡寫圖片描述
3603是sig程序的id。之所以要再次回車才顯示“段錯誤”是因為在3603程終止掉 之前已經回到了Shell提示符等待使用者輸入下一條令,Shell不希望“段錯誤”資訊和使用者的輸入交錯在一起,所以等使用者輸入命令之後才顯示。
指定某種訊號的kill命令可以有多種寫 法,上面的命令還可以寫成:
kill -SIGSEGV 3603或kill -11 3603, 11是訊號SIGSEGV的編號。
以往遇 到的段錯誤都是由非法記憶體訪問產生的,而這個程式本身沒錯,給它發SIGSEGV也能產生段錯誤。

kill命令是呼叫kill函式實現的。kill函式可以給一個指定的程序傳送指定的訊號。
raise函式可以給當前程序傳送指定的訊號(自己給自己發訊號)。

#include <signal.h>
int kill(pid_t pid, int signo);//給任意程序發訊號
int raise(int signo);//自己給自己發任意訊號

這兩個函式都是成功返回0,錯誤返回-1。

kill函式例項:
makefile:

  1 .PHONY: all
  2 all: signal sig kill
  3 signal:signal.c
  4     gcc -o [email protected] $^
  5 sig:sig.c
  6     gcc -o [email protected] $^
  7 kill:kill.c
  8     gcc -o [email protected] $^
  9 .PHONY:clean
 10 clean:
 11     rm -f signal sig kill

kill.c:

  7 #include<stdio.h>
  8 #include<signal.h>
  9 #include<stdlib.h>
 10 
 11 static void usage(const char* proc)//.kill用法
 12 {
 13     printf("usage:%s sig pid\n",proc);
 14 }
 15 int main(int argc,char* argv[])
 16 {
 17     if(argc != 3)
 18     {
 19         usage(argv[0]);
 20         return 1;
 21     }
 22     int pid = atoi(argv[2]);//程序號
 23     int sig = atoi(argv[1]);//訊號編號
 24     kill(pid,sig);//用當前程序給pid號程序傳送sig號訊號
 25     return 0;
 26 }

1〉首先在前臺執行上面的死迴圈程式
這裡寫圖片描述

2〉使用kill函式向死迴圈程序傳送2號訊號終止該程序
這裡寫圖片描述

這裡寫圖片描述

raise函式的例項:
makefile:

  1 .PHONY: all
  2 all: signal sig kill raise
  3 signal:signal.c
  4     gcc -o [email protected] $^
  5 sig:sig.c
  6     gcc -o [email protected] $^
  7 kill:kill.c
  8     gcc -o [email protected] $^
  9 raise:raise.c
 10     gcc -o [email protected] $^
 11 .PHONY:clean
 12 clean:
 13     rm -f signal sig kill raise

raise.c:

  7 #include<stdio.h>
  8 #include<signal.h>
  9 #include<stdlib.h>
 10 #include<unistd.h>
 11 
 12 void myhandler()
 13 {
 14     printf("this is a raise test:pid is %d\n",getpid());
 15 }
 16 int main()
 17 {
 18     signal(2,myhandler);
 19     while(1)
 20     {
 21         raise(2);//給自己發2號訊號
 22         sleep(1);
 23     }
 24     return 0;
 25 }

執行中用kill命令殺死該程序:kill -9 4009
這裡寫圖片描述

abort函式使當前程序接收到訊號而異常終止。

#include <stdlib.h>
void abort(void);//自己給自己發終止訊號

就像exit函式一樣,abort函式總是會成功的,所以沒有返回值。
makefile:

  1 .PHONY: all
  2 all: signal sig kill raise abort
  3 signal:signal.c
  4     gcc -o [email protected] $^
  5 sig:sig.c
  6     gcc -o [email protected] $^
  7 kill:kill.c
  8     gcc -o [email protected] $^
  9 raise:raise.c
 10     gcc -o [email protected] $^
 11 abort:abort.c
 12     gcc -o [email protected] $^
 13 .PHONY:clean
 14 clean:
 15     rm -f signal sig kill raise abort

abort.c:

  7 #include<stdio.h>
  8 #include<unistd.h>
  9 #include<stdlib.h>
 10 #include<signal.h>
 11 
 12 int count = 0;
 13 void myhandler(int sig)
 14 {
 15     printf("count is %d,sig is:%d\n",count++,sig);
 16 }
 17 int main()
 18 {
 19     for(int i = 1; i <= 31; ++i)
 20     {
 21         signal(i,myhandler);
 22     }
 23     while(1)
 24     {
 25         sleep(1);
 26         abort();//使當前程序接收到訊號而異常止
 27     }
 28     return 0;
 29 }

這裡寫圖片描述

4,由軟體條件產生訊號
由軟體條件產生的訊號:SIGPIPE,SIGALRM(主要介紹)
alarm函式可以設定一個鬧鐘,也就是告訴核心在seconds秒之後給當前程序發SIGALRM訊號, 該訊號的預設處理動作是終止當前程序。

#include <unistd.h>
unsigned int alarm(unsigned int seconds);

返回值:是0或者是以前設定的鬧鐘時間還餘下 的秒數
如果seconds值為0,表示取消以前設定的鬧鐘,函式的返回值仍然是以前設定的鬧鐘時間還餘下的秒數。
makefile:

  1 .PHONY: all
  2 all: signal sig kill raise abort alarm
  3 signal:signal.c
  4     gcc -o [email protected] $^
  5 sig:sig.c
  6     gcc -o [email protected] $^
  7 kill:kill.c
  8     gcc -o [email protected] $^
  9 raise:raise.c
 10     gcc -o [email protected] $^
 11 abort:abort.c
 12     gcc -o [email protected] $^
 13 alarm:alarm.c
 14     gcc -o [email protected] $^
 15 .PHONY:clean
 16 clean:
 17     rm -f signal sig kill raise abort alarm

alarm.c:

  7 #include<stdio.h>
  8 #include<signal.h>
  9 #include<stdlib.h>
 10 #include<unistd.h>
 11 
 12 int count = 0;
 13 void myhandler()
 14 {
 15     printf("count is %d\n",count);
 16     exit(1);
 17 }
 18 int main()
 19 {
 20     signal(SIGALRM,myhandler);
 21     alarm(1);//設定鬧鐘
 22     while(1)
 23     {
 24         count++;
 25     }
 26     return 0;
 27 }

這個程式的作用是1秒鐘之內不停地數數,1秒鐘到了就被SIGALRM訊號終止。
這裡寫圖片描述

相關推薦

iOS/OS X記憶體管理基本概念原理

CSDN移動將持續為您優選移動開發的精華內容,共同探討移動開發的技術熱點話題,涵蓋移動應用、開發工具、移動遊戲及引擎、智慧硬體、物聯網等方方面面。如果您想投稿、尋求《近匠》報道,或給文章挑錯,歡迎傳送郵件至tangxy#csdn.net(請把#改成@)。  在Objective-C的記憶體管理中,其實就

Linux訊號----訊號基本概念產生

一,訊號的基本概念 1,什麼是訊號? 日常生活中,當我們走到馬路上時,看到的綠燈是一種訊號,它能提示我們怎樣安全的過馬路。又比如,新學期開始學校給每個班發的課表也是一種訊號,它能提示同學們在適當的時間地點去上相應的課程而不是虛度光陰……生活中其

Linuxnodejs:安裝和使用

一、安裝 一共有三種安裝方式,由於其他兩種不太方便,因此只介紹第一種二進位制檔案安裝。 1、首先官網下載nodejs安裝壓縮包。 2、下載完成後用ftp上傳到Linux任意資料夾即可。我的是/home/chuan/chuansoft。然後進入到

RabbitMQ學習之Linux安裝

CentOS 6.2 64bit 安裝erlang及RabbitMQ Server 1、作業系統環境(CentOS 6.2 64bit) [[email protected] ~]# cat /etc/issue CentOS release 6.2 (Final) Kernel \r on

Python爬蟲基本概念

popu 通用 字符 spider dai 自身 部分 螞蟻 people 網絡爬蟲的定義 網絡爬蟲(Web Spider。又被稱為網頁蜘蛛。網絡機器人,又稱為網頁追逐者),是一種依照一定的規則,自己主動的抓取萬維網信息的程序或者腳本。另外一些不常使用

redis使用基礎 ——Redis基本概述安裝配置

錯誤信息 並不是 make load 消息隊列 一個 多少 通過 功能 redis使用基礎(一) ——Redis基本概述與安裝配置 (轉載請附上本文鏈接——linhxx) 一、特性 1、存儲方式 Redis采用Key-Value類型進行存儲,數據存

【C++併發實戰】併發基本概念

  什麼是併發 併發,最簡單的理解就是,兩個或者以上的活動同時進行。舉個比較實際的例子,你可以手腳並用,兩隻手做不同的動作等等。 在計算機中的“併發”,是指一個系統可以同時執行多個獨立的活動。在以前大多數計算機都只有一個處理單元(或者核心),這種計算機在同一時刻只能執行一個任務,任務

概率論基礎知識概率論基本概念

概率論 0. 前言 本文主要旨在對概率論的基礎概念與知識進行概要的總結,以便於使用到時可以參考。 概率論是數理統計的基礎,也是很多機器學習模型的支撐,概率論在機器學習中佔主要地位,因為概率論為機器學習演算法的正確性提供了理論依據。 1. 概率論的基本概念 1.

Linux 高可用HA叢集基本概念詳解

目錄 十二、總結 一、高可用叢集的定義   高可用叢集,英文原文為High Availability Cluster,簡稱HACluster,簡單的說,叢集(cluster)就是一組計算機,它們作為一個整體向用戶提供一組網路資

各種音視訊編解碼學習詳解之 編解碼學習筆記基本概念

最近在研究音視訊編解碼這一塊兒,看到@bitbit大神寫的【各種音視訊編解碼學習詳解】這篇文章,非常感謝,佩服的五體投地。奈何大神這邊文章太長,在這裡我把它分解很多小的篇幅,方便閱讀。大神部落格傳送門:https://www.cnblogs.com/skyofbitbit/p/3651270.htm

JVM調優總結基本概念

一、資料型別   Java虛擬機器中,資料型別可以分為兩類:基本型別和引用型別。   基本型別的變數儲存原始值,即:他代表的值就是數值本身; 而引用型別的變數儲存引用值。“引用值”代表了某個物件的引用,而不是物件本身,物件本身存放在這個引用值所表示的地址的位置。

Netty 入門基本元件執行緒模型

  Netty 的學習內容主要是圍繞 TCP 和 Java NIO 這兩個點展開的,後文中所有的內容如果沒有特殊說明,那麼所指的內容都是與這兩點相關的。由於 Netty 是基於 Java NIO 的 API 之上構建的網路通訊框架,Java NIO 中的幾個元件,都能在 Netty 中找到對應的封裝。下面我們

VxWorks6.6 pcPentium BSP 使用說明基本概念

“VxWorks6.6 BSP 使用說明”將釋出pcPentium和idp945兩個系列的BSP的使用說明。每個系列約5篇文章。之後還將釋出由這兩個官方提供的BSP的實戰移植方法。 本說明適用範圍 p

手勢識別--手勢基本概念和ChaLearn Gesture Challenge

以下轉自: 像點選(clicks)是GUI平臺的核心,輕點(taps)是觸控平臺的核心那樣,手勢(gestures)是Kinect應用程式的核心。和圖形使用者介面中的數字互動不同,手勢是現實生活中存在的動作。如果沒有電腦我們就不需要滑鼠,但是沒了Kinect,手

GCD教程基本概念

什麼是GCD? Grand Central Dispatch或者GCD,是一套低層API,提供了一種新的方法來進行併發程式編寫。從基本功能上講,GCD有點像NSOperationQueue,他們都允許程式將任務切分為多個單一任務然後提交至工作佇列來併發地或者序列地執行。

MySQL-ProxySQL中介軟體| ProxySQL基本概念

目錄     MySQL-ProxySQL中介軟體(一)| ProxySQL基本概念: https://www.cnblogs.com/SQLServer2012/p/10972593.html     MySQL-ProxySQL中介軟體(二)| Adm

Druid.io系列基本概念架構

在介紹Druid架構之前,我們先結合有關OLAP的基本原理來理解Druid中的一些基本概念。 1 資料 以圖3.1為例,結合我們在第一章中介紹的OLAP基本概念,按列的型別上述資料可以分成以下三類: 時間序列(Timestamp),Druid既是記憶

Linux訊號

訊號的概述     訊號是事件發生時對程序的通知機制,也成為軟體中斷,是程序之間通訊的方式之一。訊號分為兩大類,一組用於核心向程序通知事件,構成所謂的傳統或標準訊號;另一組由實時訊號構成。     訊號因某些事件而產生,訊號產生

Linux訊號——子程序的非同步等待方式

1.訊號    訊號(是一種軟體中斷)是由使用者、系統或者程序傳送給目標程序的資訊,以通知目標程序某個狀態的改變或系統異常。 2.訊號的產生 (1)前臺程序,使用者可以通過輸入特殊終端字元來給它傳送訊

java程式設計師菜鳥進階十三linux基礎入門vmvare安裝linux RedHat圖解超詳細篇

對於linux,我從大二就想學習一下,但一直苦於無從下手,所以一直拖到現在,鑑於筆者瞭解很多人在linux入門的困難在何處,所以我認為本套入門基礎文章還是挺適合想學習linux的朋友,本系列文章大約十篇文章左右,近期會不斷更新下來,沒有linux基礎但又想學習linux的朋友可以關注一下本系列