do signel 函式的理解

2021-07-29 03:51:28 字數 3009 閱讀 9496

在   中記錄了對signal()函式和sigaction()函式的分析,這裡分析使用者程式進行系統呼叫(int 0x80)時中斷處理程式對訊號的處理。在程序每次呼叫系統呼叫時,若該程序已收到訊號,則該函式就會把訊號的處理控制代碼(即對應的訊號處理函式)插入到使用者程式堆疊中。這樣,在當前系統呼叫結束返回後就會立刻執行訊號控制代碼程式,然後再繼續執行使用者的程式,如下圖所示

在使用者程式呼叫系統呼叫剛進入核心時,該程序的核心態堆疊上會由cpu 自動壓入如圖5-8 中所示的內容,也即:使用者程式的ss 和esp 以及使用者程式中下一條指令的執行點位置cs 和eip。

在處理完此次指定的系統呼叫功能並準備呼叫do_signal()時,核心態堆疊中的內容見圖5-9 中左邊所示。因此do_signal()的引數即是這些在核心態堆疊上的內容。

在處理完兩個預設訊號控制代碼(sig_ign 和sig_dfl)之後,若使用者自定義了訊號處理程式(訊號控制代碼sa_handler),do_signal()開始準備把使用者自定義的控制代碼插入使用者態堆疊中。它首先把核心態堆疊中原使用者程式的返回執行點指標eip 儲存為old_eip,然後將該eip 替換成指向自定義控制代碼sa_handler,也即讓圖中核心態堆疊中的eip 指向sa_handler。接下來通過把核心態中儲存的「原esp」減去longs 值,把使用者態堆疊向下擴充套件了7 或8 個長字空間。最後把核心堆疊上的一些暫存器內容複製到了這個空間中,見圖中右邊所示。總共往使用者態堆疊上放置了7 到8 個值,我們現在來說明這些值的含義以及放置這些值的原因。old_eip 即是原使用者程式的返回位址,是在核心堆疊上eip 被替換成訊號控制代碼位址之前保留下來的。eflags、edx 和ecx 是原使用者程式在呼叫系統呼叫之前的值,基本上也是呼叫系統呼叫的引數,在系統呼叫返回後仍然需要恢復這些使用者程式的暫存器值。eax 中儲存有系統呼叫的返回值。如果所處理的訊號還允許收到本身,則堆疊上還存放有該程序的阻塞碼blocked。下乙個是訊號signr 值。

總體程式執行流程如下:

(1)使用者程式執行系統呼叫,進入核心態;

(2)在核心態執行系統呼叫函式後,執行do_signal()函式,由於核心堆疊中eip被修改為處理函式的位址,故而系統呼叫函式返回時直接到訊號處理函式中執行**,此時為使用者態,注意,此時為使用者態;

(3)訊號處理函式執行完後,通過ret 指令,cpu 會把控制權移交給sa_restorer 所指向的恢復程式去執行。而sa_restorer 程式會做一些使用者態堆疊的清理工作;

(4)最後通過sa_restorer 的ret 指令彈出原使用者程式的eip(也即堆疊上的old_eip),返回去執行使用者程式。

源**分析:

void do_signal(long signr,long eax, long ebx, long ecx, long edx,

long fs, long es, long ds,

long eip, long cs, long eflags,

unsigned long * esp, long ss)

// 如果該訊號控制代碼只需使用一次,則將該控制代碼置空(該訊號控制代碼已經儲存在sa_handler 指標中)。

if (sa->sa_flags & sa_oneshot)

sa->sa_handler = null;

// 下面這段**將訊號處理控制代碼插入到使用者堆疊中,同時也將sa_restorer,signr,程序遮蔽碼(如果

// sa_nomask 沒置位),eax,ecx,edx 作為引數以及原呼叫系統呼叫的程式返回指標及標誌暫存器值

// 壓入堆疊。因此在本次系統呼叫中斷(0x80)返回使用者程式時會首先執行使用者的訊號控制代碼程式,然後

// 再繼續執行使用者程式。

// 將使用者呼叫系統呼叫的**指標eip 指向該訊號處理控制代碼

*(&eip) = sa_handler;

// 如果允許訊號自己的處理控制代碼收到訊號自己,則也需要將程序的阻塞碼壓入堆疊。

// 注意,這裡longs 的結果應該選擇(7*4):(8*4),因為堆疊是以4 位元組為單位操作的。

longs = (sa->sa_flags & sa_nomask)?7:8;

// 將原呼叫程式的使用者堆疊指標向下擴充套件7(或8)個長字(用來存放呼叫訊號控制代碼的引數等),

// 並檢查記憶體使用情況(例如如果記憶體超界則分配新頁等)。

*(&esp) -= longs;

verify_area(esp,longs*4);

// 在使用者堆疊中從下到上存放sa_restorer, 訊號signr, 遮蔽碼blocked(如果sa_nomask 置位),

// eax, ecx, edx, eflags 和使用者程式原**指標。

tmp_esp=esp;

put_fs_long((long) sa->sa_restorer,tmp_esp++);

put_fs_long(signr,tmp_esp++);

if (!(sa->sa_flags & sa_nomask))

put_fs_long(current->blocked,tmp_esp++);

put_fs_long(eax,tmp_esp++);

put_fs_long(ecx,tmp_esp++);

put_fs_long(edx,tmp_esp++);

put_fs_long(eflags,tmp_esp++);

put_fs_long(old_eip,tmp_esp++);//將原返回位址壓入使用者態堆疊

current->blocked |= sa->sa_mask; // 程序阻塞碼(遮蔽碼)添上sa_mask 中的碼位。

}文中**於linux核心完全注釋(修正版v1.9.5)

函式指標的理解。

static const struct imx fb videomode imxfb find mode struct imxfb info fbi return null imxfb find mode是函式名,呼叫函式後得到乙個指向結構體struct imx fb videomode的指標 位址...

虛函式的理解

includeusing namespace std class a virtual a virtual void print class b public a b void print int main 之前對虛函式一直一知半解,現在把總結的內容記錄下來。c 的物件導向的乙個特徵就是多型,即一切用...

gradient 函式的理解

梯度 變化 參考量 gradient 是求數值梯度函式的命令。fx,fy gradient x 其中fx為其水平方向上的梯度,fy為其垂直方向上的梯度,fx的第一列元素為原矩陣第二列與第一列元素之差,fx的第二列元素為原矩陣第三列與第一列元素之差除以2,以此類推 fx i,j f i,j 1 f i...