1.使用者進程使用如open();的C庫函數。
2從庫函數(open)中使用"int 0x80"或是syscall命令進入system_call。
glibc 中的部分源碼 //sysdeps/unix/sysv/linux/x86_64/sysdeps.hINLINE_SYSCALL--->INTERNAL_SYSCALL--->INTERNAL_SYSCALL_NCS--->syscall. # define INLINE_SYSCALL(name, nr, args...) \ ({ \ unsigned long int resultvar = INTERNAL_SYSCALL (name, , nr, args); \ if (__builtin_expect (INTERNAL_SYSCALL_ERROR_P (resultvar, ), 0)) \ { \ __set_errno (INTERNAL_SYSCALL_ERRNO (resultvar, )); \ resultvar = (unsigned long int) -1; \ } \ (long int) resultvar; }) # define INTERNAL_SYSCALL(name, err, nr, args...) \ INTERNAL_SYSCALL_NCS (__NR_##name, err, nr, ##args) # define INTERNAL_SYSCALL_NCS(name, err, nr, args...) \ ({ \ unsigned long int resultvar; \ LOAD_ARGS_##nr (args) \ LOAD_REGS_##nr \ asm volatile ( \ "syscall\n\t" \//x86_64的關鍵指令syscall. : "=a" (resultvar) \ : "0" (name) ASM_ARGS_##nr : "memory", "cc", "r11", "cx"); \ (long int) resultvar; })
|
有一個問題:在i386的CPU上,我們知道linux在初始化時為中斷號0x80準備了一個中斷處理函數(trap_init-->set_system_trap_gate(SYSCALL_VECTOR, &system_call);)。當發生int 0x80時,CPU通過一系列的動作(這個硬體動作,其它網文介紹很多),到達中斷處理函數。那麼在x86_64的CPU上,syscall指令能跳到哪去呢?CPU是如何處理這個指令的呢?需要OS為這個指令準備哪些資訊?還有它又是怎麼回來的?
3 CPU完成一系列切換的動作。
4.進入中斷處理函數,更準確的說法是進入核心。
在arch/x86/kernel/entry_64.S#455或是在arch/x86/kernel/entry_32.S#L498(在entry_32.s為例)
ENTRY(system_call)
499 RING0_INT_FRAME # can't unwind into user space anyway
500 pushl_cfi %eax # save orig_eax
501 SAVE_ALL
502 GET_THREAD_INFO(%ebp)
503 # system call tracing in operation / emulation
504 testl $_TIF_WORK_SYSCALL_ENTRY,TI_flags(%ebp)
505 jnz syscall_trace_entry
506 cmpl $(nr_syscalls), %eax
507 jae syscall_badsys
508syscall_call:
509 call *sys_call_table(,%eax,4)
510 movl %eax,PT_EAX(%esp) # store the return value
511syscall_exit:
512 LOCKDEP_SYS_EXIT
513 DISABLE_INTERRUPTS(CLBR_ANY) # make sure we don't miss an interrupt
514 # setting need_resched or sigpending
515 # between sampling and the iret
516 TRACE_IRQS_OFF
517 movl TI_flags(%ebp), %ecx
518 testl $_TIF_ALLWORK_MASK, %ecx # current->work
519 jne syscall_exit_work
當進入了system_call之後。
SAVE_ALL:???
。。。。
進入sys_call_table.xxx 中。
sys_call_table的定義在哪裡,它的引用在哪裡?為什麼在核心和x86中的很多檔案中都有它????
sys_call_table的定義:(在 linux/arch/x86/kernel/syscall_table_32.S)。
ENTRY(sys_call_table) 2 .long sys_restart_syscall /* 0 - old "setup()" system call, used for restarting */
3 .long sys_exit
4 .long ptregs_fork
5 .long sys_read
6 .long sys_write
7 .long sys_open /* 5 */
8
這樣就可以找到核心定義的sys_function函數了。
5.運行各種各樣的系統調用的函數。
那麼各種各樣的sys_function都是定義在哪裡呢?例:
SYSCALL_DEFINE3(semop, int, semid, struct sembuf __user *, tsops,unsigned, nsops)
{函數實體。
。。。。。
。。。。。
}
在一些檔案中,我們可以找到以上這樣的語句。
在syscalls.h中定義如下:
#define SYSCALL_DEFINE1(name, ...) SYSCALL_DEFINEx(1, _##name, __VA_ARGS__)#define SYSCALL_DEFINE2(name, ...) SYSCALL_DEFINEx(2, _##name, __VA_ARGS__)#define SYSCALL_DEFINE3(name, ...) SYSCALL_DEFINEx(3, _##name, __VA_ARGS__)#define SYSCALL_DEFINE4(name, ...) SYSCALL_DEFINEx(4, _##name, __VA_ARGS__)#define SYSCALL_DEFINE5(name, ...) SYSCALL_DEFINEx(5, _##name, __VA_ARGS__)#define SYSCALL_DEFINE6(name, ...) SYSCALL_DEFINEx(6, _##name, __VA_ARGS__)
它由一系列的宏,逐步定義的,如何做的??
這些宏的具體定義的內涵是什嗎?
SYSCALL_DEFINE3(semop,int,semid,......)這個宏,把"semop"的函數,定義成
sys_semop(int semid,struct sembuf __user* tsops,unsigned nsops);
這個sys_semop函數就是sys_call_table中的一部分了。
這樣使用者進程在使用系統調用時,read,write,或是semget,semop時,就可以調用到相應的sys_xxxx函數了。
附:cond_syscall解釋/* * "Conditional" syscalls * * What we want is __attribute__((weak,alias("sys_ni_syscall"))), * but it doesn't work on all toolchains, so we just do it by hand */#ifndef cond_syscall#define cond_syscall(x) asm(".weak\t" #x "\n\t.set\t" #x ",sys_ni_syscall")#endif在sys_ni.c中:cond_syscall(sys_fanotify_mark);
cond_syscall定義如下:
/*
* "Conditional" syscalls
*
* What we want is __attribute__((weak,alias("sys_ni_syscall"))),
* but it doesn't work on all toolchains, so we just do it by hand */
#ifndef cond_syscall#define cond_syscall(x) asm(".weak\t" #x "\n\t.set\t" #x ",sys_ni_syscall")
#endif
asm(".weak\t" #x "\n\t.set\t" #x ",sys_ni_syscall")分析如下:以上嵌入式彙編翻譯成彙編代碼如下:
.weak #x .set #x,sys_ni_syscall
以上的含義是:在編譯時間,告訴編譯器,當沒有符號x時,就用sys_ni_syscall這個符號來代替符號x.
在http://www.acsu.buffalo.edu/~charngda/cc.html中說明如下:
#pragma weak symbol1=symbol2
Declare symbol1 as a weak alias of symbol2. Equivalently, one can use
#pragma weak symbol1=symbol2 定義 symbol1作為symbol2的弱別名,與如下代碼等效。
__asm__(".weak symbol1"); //定義symbol1是弱符號。
__asm__(".set symbol1,symbol2"); //把symbol1與symbol2聯絡起來。
A better way to achieve this is through the "weak, alias" function attributes.
還有一種更好的實現“弱,別名”函數屬性的方式。