偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

玩透Linux信號(hào)機(jī)制

系統(tǒng) Linux
syscall進(jìn)入內(nèi)核,把該干的事情干完,就執(zhí)行sysret返回用戶(hù)態(tài)。返回到哪里?rcx中內(nèi)存地址的位置,這個(gè)位置就是信號(hào)處理函數(shù)。執(zhí)行完信號(hào)處理函數(shù),pop出棧底元素,跳過(guò)去,這個(gè)位置就是CPU執(zhí)行syscall進(jìn)入內(nèi)核后面的哪一行代碼的內(nèi)存地址,從而實(shí)現(xiàn)接著執(zhí)行。

1.信號(hào)處理函數(shù)

如果我們想捕捉進(jìn)程的這兩個(gè)信號(hào):SIGCHLD、SIGCONT,用到函數(shù)sigaction

圖片圖片

Linux內(nèi)核提供了多少種信號(hào)呢?shell終端運(yùn)行shell -l即可看到,64種。我們代碼中捕捉的兩個(gè)信號(hào),分別是17號(hào)、18號(hào)信號(hào),這兩個(gè)數(shù)字記一下,等下查看內(nèi)核數(shù)據(jù)能看到

圖片圖片

先在內(nèi)核中找到我們注冊(cè)的信號(hào)處理函數(shù),再說(shuō)底層實(shí)現(xiàn)原理。這個(gè)你得自己寫(xiě)內(nèi)核驅(qū)動(dòng)程序,市面上沒(méi)有任何工具可以讓你看

圖片圖片

找到了。再確定一下我們通過(guò)函數(shù)sigaction注冊(cè)的信號(hào)處理函數(shù)地址是不是這兩個(gè),一毛一樣

圖片圖片

接下來(lái)說(shuō)說(shuō)Linux內(nèi)核是如何存儲(chǔ)我們注冊(cè)的信號(hào)處理函數(shù)

struct task_struct {
    struct sighand_struct		*sighand;
    ……
}

struct sighand_struct {
	spinlock_t		siglock;
	refcount_t		count;
	wait_queue_head_t	signalfd_wqh;
	struct k_sigaction	action[_NSIG];
};

struct k_sigaction {
	struct sigaction sa;
#ifdef __ARCH_HAS_KA_RESTORER
	__sigrestore_t ka_restorer;
#endif
};

struct sigaction {
#ifndef __ARCH_HAS_IRIX_SIGACTION
	__sighandler_t	sa_handler;
	unsigned long	sa_flags;
#else
	unsigned int	sa_flags;
	__sighandler_t	sa_handler;
#endif
#ifdef __ARCH_HAS_SA_RESTORER
	__sigrestore_t sa_restorer;
#endif
	sigset_t	sa_mask;	/* mask last for extensibility */
};

Linux內(nèi)核中,每個(gè)進(jìn)程對(duì)應(yīng)一個(gè)task_struct實(shí)例,里面有個(gè)屬性sighand就是用來(lái)存儲(chǔ)你使用函數(shù)sigaction注冊(cè)的信號(hào)處理函數(shù),具體存儲(chǔ)在sighand_struct的action數(shù)組中,數(shù)組的索引就是信號(hào)的編號(hào):1-64,數(shù)組的值是k_sigaction實(shí)例,真正存放信號(hào)處理函數(shù)的地方是sigaction.sa_handler

所以如果你想查看Linux內(nèi)核中,某個(gè)進(jìn)程注冊(cè)的所有信號(hào)處理函數(shù),代碼這樣寫(xiě)即可

圖片圖片

至此,我們寫(xiě)代碼注冊(cè)的信號(hào)處理函數(shù),在內(nèi)核中如何存儲(chǔ)的,就徹底搞明白了。那內(nèi)核是何時(shí)、怎么調(diào)用這個(gè)函數(shù)的呢?接著走……

2.kill-18

比如我們通過(guò)kill -18向進(jìn)程18226發(fā)送信號(hào),中間發(fā)生了什么?我就不貼源碼了,直接單步調(diào)試內(nèi)核,貼調(diào)用棧吧

圖片圖片

這里看到的只是內(nèi)核態(tài)的調(diào)用棧,用戶(hù)態(tài)的,kill命令底層調(diào)用的就是glibc庫(kù)中的kill函數(shù),而kill函數(shù)則是通過(guò)syscall+kill的內(nèi)核調(diào)用號(hào),進(jìn)入內(nèi)核,調(diào)用相關(guān)函數(shù)

圖片圖片

關(guān)于用戶(hù)態(tài)切內(nèi)核態(tài),CPU提供了四個(gè)門(mén)、兩個(gè)快速調(diào)用,以前的實(shí)現(xiàn)方式是0x80中斷門(mén),現(xiàn)在都是走syscall快速調(diào)用。如果你非科班,或者沒(méi)學(xué)過(guò)操作系統(tǒng),應(yīng)該沒(méi)聽(tīng)過(guò)這個(gè),或者對(duì)這個(gè)沒(méi)概念。建議非科班出身的小伙伴,一定要把操作系統(tǒng)補(bǔ)一下

那這個(gè)信號(hào)在內(nèi)核中是如何存儲(chǔ)的呢?核心邏輯在send_signal中,我就不貼代碼了,直接說(shuō)它做了什么吧

struct task_struct {
    struct signal_struct		*signal;
    sigset_t			blocked;
    ……
}

struct signal_struct {
    /* shared signal handling: */
	struct sigpending	shared_pending;
    ……
}

struct sigpending {
	struct list_head list;
	sigset_t signal;
};

struct sigqueue {
	struct list_head list;
	int flags;
	kernel_siginfo_t info;
	struct user_struct *user;
};

進(jìn)入內(nèi)核的時(shí)候,信號(hào)會(huì)被包裝成kernel_siginfo

圖片圖片

真正與進(jìn)程關(guān)聯(lián)起來(lái)的步驟是,將kernel_siginfo再包裝成sigqueue,然后將sigqueue實(shí)例掛到sigpending中,進(jìn)程結(jié)構(gòu)體task_struct中有個(gè)屬性shared_pending就是鏈表頭,有點(diǎn)抽象,看圖

圖片圖片

順便說(shuō)一下,Linux內(nèi)核中的64種信號(hào)分成兩個(gè)陣營(yíng):可靠信號(hào)與不可靠信號(hào),可靠信號(hào)又叫實(shí)時(shí)信號(hào),不可靠信號(hào)又叫非實(shí)時(shí)信號(hào)

圖片圖片

實(shí)時(shí)與非實(shí)時(shí),表達(dá)的不是立刻去做的意思,是指信號(hào)不會(huì)丟失。這名字起的真讓人容易產(chǎn)生誤解。Linux內(nèi)核中的很多函數(shù)名也是,比如get_signal,它里面做了很多重要的事情,我看代碼的時(shí)候以為就是去信息相關(guān)信息

嗯,差不多就這些。至此,信號(hào)在內(nèi)核中是如何存儲(chǔ)的就清晰了。那進(jìn)程何時(shí)處理信號(hào),Linux內(nèi)核是如何設(shè)計(jì)的呢?

3.信號(hào)處理

如果這塊由你來(lái)設(shè)計(jì),你會(huì)怎么做?不知道?好吧……

Linux內(nèi)核是如何設(shè)計(jì)的呢?它的設(shè)計(jì)是在進(jìn)程由內(nèi)核態(tài)返回用戶(hù)態(tài)的路徑上實(shí)現(xiàn)的。為什么要這么做呢?

因?yàn)橐嫒葸\(yùn)行用戶(hù)態(tài)注冊(cè)的信號(hào)處理函數(shù),這個(gè)節(jié)點(diǎn)是最優(yōu)選擇。反正都要進(jìn)入用戶(hù)態(tài)執(zhí)行,在這之前,順便把信號(hào)處理函數(shù)執(zhí)行了。這里要怎么實(shí)現(xiàn)呢?改線程棧結(jié)構(gòu),你如果學(xué)了我講的匯編,你就知道要怎么改了。

同樣,不貼代碼了,直接單步調(diào)試Linux內(nèi)核看吧!

圖片圖片

函數(shù)do_signal就是信號(hào)處理的核心函數(shù)

接下來(lái)詳細(xì)分析代碼層面實(shí)現(xiàn),如果你不會(huì)匯編,你可能就看不懂了

4.執(zhí)行信號(hào)處理函數(shù)

什么時(shí)候CPU會(huì)由用戶(hù)態(tài)進(jìn)入內(nèi)核態(tài)呢?發(fā)生中斷、異常,還有比較常見(jiàn)的:系統(tǒng)調(diào)用。比如write函數(shù)

圖片圖片

當(dāng)CPU執(zhí)行syscall指令,CPU就進(jìn)入Linux內(nèi)核中了,這時(shí)候用戶(hù)態(tài)的棧比如是這樣(我就畫(huà)關(guān)鍵信息了哦)

圖片圖片

如果有信號(hào)需要處理的時(shí)候,這時(shí)候用戶(hù)又設(shè)置了信號(hào)處理函數(shù),那內(nèi)核在回用戶(hù)態(tài)前會(huì)把棧改成這樣

圖片圖片

會(huì)把rcx設(shè)置為信號(hào)處理函數(shù)的地址,syscall進(jìn)入內(nèi)核,配套的返回指令是sysret,會(huì)返回到rcx中內(nèi)存地址的位置執(zhí)行代碼。所有的函數(shù)的最后一條指令都是ret,會(huì)pop出棧頂元素,并跳轉(zhuǎn)到那個(gè)內(nèi)存地址開(kāi)始執(zhí)行代碼。

syscall進(jìn)入內(nèi)核,把該干的事情干完,就執(zhí)行sysret返回用戶(hù)態(tài)。返回到哪里?rcx中內(nèi)存地址的位置,這個(gè)位置就是信號(hào)處理函數(shù)。執(zhí)行完信號(hào)處理函數(shù),pop出棧底元素,跳過(guò)去,這個(gè)位置就是CPU執(zhí)行syscall進(jìn)入內(nèi)核后面的哪一行代碼的內(nèi)存地址,從而實(shí)現(xiàn)接著執(zhí)行。這樣就完成了進(jìn)入用戶(hù)態(tài),順便執(zhí)行信號(hào)處理函數(shù)的動(dòng)作。怎么樣,是不是特別有智慧!

責(zé)任編輯:武曉燕 來(lái)源: 硬核子牙
相關(guān)推薦

2011-07-05 18:40:19

QT 信號(hào) 機(jī)制

2011-07-05 18:32:52

QT 信號(hào) 機(jī)制

2025-02-03 07:00:00

2021-12-10 00:01:53

Vsync信號(hào)機(jī)制

2022-11-03 07:35:47

OS內(nèi)核異步

2017-09-14 09:40:32

PythonUbuntu信號(hào)機(jī)制

2017-01-16 15:05:17

Linux信號(hào)機(jī)制分析

2017-01-16 14:48:42

Linux信號(hào)機(jī)制分析

2025-06-25 06:18:46

Linux多線程機(jī)制

2011-06-09 09:45:35

Linux QT 信號(hào)

2022-10-08 08:34:34

JVM加載機(jī)制代碼

2024-07-25 11:53:53

2010-09-09 14:15:08

無(wú)線網(wǎng)絡(luò)信號(hào)

2011-06-23 14:40:13

Qt 信號(hào)

2010-12-27 10:13:05

PROC文件系統(tǒng)

2025-07-14 00:40:00

Node.js代碼信號(hào)

2009-11-23 19:47:57

ibmdwLinux

2024-05-08 13:41:31

OpenAIA/B測(cè)試

2011-06-13 10:21:25

QT 信號(hào) 槽機(jī)制

2016-08-16 08:26:19

Linuxsignalsigaction
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)