setjmp和longjmp的實現原理與應用

來源:互聯網
上載者:User

在討論setjmp的實現原理之前,我們先看一個setjmp和longjmp的例子:
#include <stdio.h>
#include <stdlib.h>
#include <setjmp.h>

static jmp_buf jmpbuf_1;

int g_a = 0;

void test

int main

ret = setjmp;

if

else if

else if

test;

return 0;
}

下面是測試代碼在不同的編譯最佳化條件下的執行結果:
wangyao@wangyao-laptop:~/Test/sys$ gcc longjmp.c -O2
wangyao@wangyao-laptop:~/Test/sys$ ./a.out 1
Orig setjmp
Return From longjmp 1
Global_a: 1111
Local_a: 1
wangyao@wangyao-laptop:~/Test/sys$ ./a.out 2
Orig setjmp
Return From longjmp 2
Global_a: 1111
Local_a: 1
wangyao@wangyao-laptop:~/Test/sys$ gcc longjmp.c
wangyao@wangyao-laptop:~/Test/sys$ ./a.out 1
Orig setjmp
Return From longjmp 1
Global_a: 1111
Local_a: 2222
wangyao@wangyao-laptop:~/Test/sys$ ./a.out 2
Orig setjmp
Return From longjmp 2
Global_a: 1111
Local_a: 2222

man 3 longjmp中提到:
The values of automatic variables are unspecified after a call to longjmp if they meet all the following criteria:
  they are local to the function that made the corresponding setjmp call;
  their values are changed between the calls to setjmp and longjmp; and
  they are not declared as volatile.

posix中沒有定義自動變數在調用longjmp後的動作。

在調用longjmp後,自動變數、全域變臉、寄存器變數、靜態變數和易失變數的變化是不同的。全域變數、靜態變數和易失變數的分配要麼是在data段,要麼是在bss段中,環境切換對它們基本上沒有什麼影響;但是對於棧和寄存器來講,環境切換就有很大的影響。因此對於一些自動變數、寄存器變數就可能會在longjmp的時候發生變化,當然這個還與編譯時間候的最佳化條件有關。

下面再來討論setjmp和longjmp的實現原理,這裡我們只關注x86架構的實現,具體來講是i386的實現。

首先,回想一下x86架構下面,函數調用過程棧的變化情況:
在x86架構下,調用函數的時候,首先自右向左將壓棧,(早期的gcc版本是採用push $變數的方法;後期gcc使用的是sub $num,esp 然後mov $變數,esp+$num的方法)
接下來就是call func了,call func的動作是,將下一指令地址壓棧,jmp到func的地址執行。
func剛開始就執行一個push ebp,將原先的棧基址儲存下來。

setjmp具體的實現在:
glibc-2.7/sysdeps/i386/setjmp.S
將寄存器中的值儲存到jmp_buf中。
ENTRY )
ENTER

movl JMPBUF, %eax
CHECK_BOUNDS_BOTH_WIDE , $JB_SIZE)

/ Save registers. /
movl %ebx,
movl %esi,
movl %edi,
leal JMPBUF, %ecx / Save SP as it will be after we return. /
#ifdef PTR_MANGLE
PTR_MANGLE
#endif
movl %ecx,
movl PCOFF, %ecx / Save PC we are returning to now. /
#ifdef PTR_MANGLE
PTR_MANGLE
#endif
movl %ecx,
LEAVE / pop frame pointer to prepare for tail-call. /
movl %ebp,  / Save caller‘s frame pointer. /

#if defined NOT_IN_libc && defined IS_IN_rtld
/ In ld.so we never save the signal mask. /
xorl %eax, %eax
ret
#else
/ Make a tail call to __sigjmp_save; it takes the same args. /
jmp __sigjmp_save
#endif

longjmp的具體實現在:
glibc-2.7/sysdeps/i386/__longjmp.S
將jmp_buf中的值恢複到相應的寄存器中,並將longjmp的第2個參數作為傳回值返回,由於longjmp中將寄存器的eip設定回setjmp時候的值。longjmp的傳回值在程式邏輯上就是setjmp的傳回值了。
#ifdef PTR_DEMANGLE
movl JBUF, %eax / User‘s jmp_buf in %eax. /
CHECK_BOUNDS_BOTH_WIDE , $JB_SIZE)

/ Save the return address now. /
movl , %edx
/ Get the stack pointer. /
movl , %ecx
PTR_DEMANGLE
PTR_DEMANGLE
cfi_def_cfa
cfi_register
cfi_register
cfi_offset
cfi_offset
cfi_offset
cfi_offset
/ Restore registers. /
movl , %ebx
movl , %esi
movl , %edi
movl , %ebp
cfi_restore
cfi_restore
cfi_restore
cfi_restore

movl VAL, %eax / Second argument is return value. /
movl %ecx, %esp
#else
movl JBUF, %ecx / User‘s jmp_buf in %ecx. /
CHECK_BOUNDS_BOTH_WIDE , $JB_SIZE)

movl VAL, %eax / Second argument is return value. /
/ Save the return address now. /
movl , %edx
/ Restore registers. /
movl , %ebx
movl , %esi
movl , %edi
movl , %ebp
movl , %esp
#endif
/ Jump to saved PC. /
jmp %edx
END )

-----------------------------------------------------------------------------
對於setjmp和longjmp的使用還應該注意一些問題:
setjmp和longjmp函數,這兩個函數在跳轉時會帶訊號屏蔽字跳轉,在訊號處理常式中使用longjmp會導致後來產生的這種訊號被屏蔽,無法調用此種訊號的訊號處理函數。
POSIX.1 也沒有具體說明setjmp和longjmp對訊號屏蔽字的作用,而是定義了兩個新函數: sigsetjmp和siglongjmp。
sigsetjmp在參數為非0的時候,會儲存進程的當前訊號屏蔽字;在調用siglongjmp的時候,再恢複儲存的訊號屏蔽字。

一段示範siglongjmp的代碼:
#include <signal.h>
#include <setjmp.h>
#include <stdio.h>
#include <stdlib.h>

/ Jump buffer /
static sigjmp_buf jmpbuf;

/ Signal handler /
static void myfunc

int main

else

/ JUMP /
printf;

return 0;
}
運行結果為:
wangyao@wangyao-laptop:~/Test/sys$ ./a.out
SIGQUIT
I‘m jumped
I‘m here

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.