生活随笔
收集整理的這篇文章主要介紹了
linux下的系统调用函数到内核函数的追踪
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
Original from:?http://blog.chinaunix.net/uid-28458801-id-3468966.html
使用的 glibc : glibc-2.17
使用的 linux kernel :linux-3.2.07
系統(tǒng)調(diào)用是內(nèi)核向用戶進(jìn)程提供服務(wù)的唯一方法,應(yīng)用程序調(diào)用操作系統(tǒng)提供的功能模塊(函數(shù))。 用戶程序通過(guò)系統(tǒng)調(diào)用從用戶態(tài)(user mode)切換到核心態(tài)(kernel mode ),從而可以訪問(wèn) 相應(yīng)的資源。這樣做的好處是: 為用戶空間提供了一種硬件的抽象接口,使編程更加容易。 有利于系統(tǒng)安全。 有利于每個(gè)進(jìn)程度運(yùn)行在虛擬系統(tǒng)中,接口統(tǒng)一有利于移植。
運(yùn)行模式、地址空間、上下文
:
運(yùn)行模式(mode)
Linux 使用了其中的兩個(gè):
特權(quán)級(jí)0和特權(quán)級(jí)3
?,即
內(nèi)核模式(kernel mode) 和用戶模式(user mode )
地址空間(space )
a)每個(gè)進(jìn)程的虛擬地址空間可以劃分為兩個(gè)部分:用
戶空間和內(nèi)核空間
b)在用戶態(tài)下只能訪問(wèn)用戶空間;而在核心態(tài)下,既可以訪問(wèn)用戶空間,又可以訪問(wèn)內(nèi)核空間。?
c)內(nèi)核空間在每個(gè)進(jìn)程的虛擬地址空間中都是固定的
(虛擬地址為3G~4G的地址空間
)。
上下文(context )
一個(gè)進(jìn)程的上下文可以分為三個(gè)部分:用戶級(jí)上下文、寄存器上下文以及系統(tǒng)級(jí)上下文。
a)用戶級(jí)上下文:正文、數(shù)據(jù)、用戶棧以及共享存儲(chǔ)區(qū);
b)寄存器上下文:通用寄存器、程序寄存器(IP )、處理機(jī)狀態(tài)寄存器(EFLAGS)、棧指針(ESP);
c)系統(tǒng)級(jí)上下文:進(jìn)程控制塊task_struct 、內(nèi)存管理信息(mm_struct 、vm_area_struct、pgd 、pmd、
?? pte 等)、核心棧等。
系統(tǒng)調(diào)用、API和C 庫(kù)
?
a)Linux 的
應(yīng)用編程接口(API)
遵循POSIX標(biāo)準(zhǔn)
?
b)
Linux 的系統(tǒng)調(diào)用作為c庫(kù)的一部分提供
。c庫(kù)中實(shí)現(xiàn)了Linux 的主要API,
?? 包括標(biāo)準(zhǔn)c庫(kù)函數(shù)和系統(tǒng)調(diào)用。
?
c)
應(yīng)用編程接口(API)
其實(shí)是一組函數(shù)定義,這些函數(shù)說(shuō)明了如何獲得一個(gè)給定的服務(wù);
?? 而
系統(tǒng)調(diào)用
是通過(guò)軟中斷向內(nèi)核發(fā)出一個(gè)明確的請(qǐng)求,
每個(gè)系統(tǒng)調(diào)用對(duì)應(yīng)一個(gè)封裝例程 ? (wrapper routine,唯一目的就是發(fā)布系統(tǒng)調(diào)用)
。一些API應(yīng)用了封裝例程。
????? @a@ API還包含各種編程接口,如:C庫(kù)函數(shù)、OpenGL 編程接口等
?
d)
系統(tǒng)調(diào)用的實(shí)現(xiàn)是在內(nèi)核完成的,而用戶態(tài)的函數(shù)是在函數(shù)庫(kù)中實(shí)現(xiàn)的
系統(tǒng)調(diào)用與操作系統(tǒng)命令
a)
操作系統(tǒng)命令
相對(duì)應(yīng)用編程接口更高一層,每個(gè)操作系統(tǒng)命令都是一個(gè)可執(zhí)行程序,
?? 比如ls 、hostname 等,
?
b)
操作系統(tǒng)命令的實(shí)現(xiàn)調(diào)用了系統(tǒng)調(diào)用
?
c)通過(guò)?
strace
?命令可以查看操作系統(tǒng)命令所調(diào)用的系統(tǒng)調(diào)用,如:
?? strace ls ?? strace hostname
?系統(tǒng)調(diào)用與內(nèi)核函數(shù)
a)
內(nèi)核函數(shù)
在形式上與普通函數(shù)一樣,但它是在內(nèi)核實(shí)現(xiàn)的,需要滿足一些內(nèi)核編程的要求
?
b)系統(tǒng)調(diào)用是用戶進(jìn)程進(jìn)入內(nèi)核的接口層,它本身并非內(nèi)核函數(shù),但它是由內(nèi)核函數(shù)實(shí)現(xiàn)的
?
c)進(jìn)入內(nèi)核后,不同的系統(tǒng)調(diào)用會(huì)找到各自對(duì)應(yīng)的內(nèi)核函數(shù),
?? 這些
內(nèi)核函數(shù)被稱(chēng)為系統(tǒng)調(diào)用的“服務(wù)例程 ”
系統(tǒng)調(diào)用處理程序及服務(wù)例程
a)當(dāng)用戶態(tài)的進(jìn)程調(diào)用一個(gè)系統(tǒng)調(diào)用時(shí),CPU切換
到內(nèi)核態(tài)并開(kāi)始執(zhí)行一個(gè)內(nèi)核函數(shù)
?
b)系統(tǒng)調(diào)用處理程序執(zhí)行下列操作:
?? @a@ 在內(nèi)核棧
保存大多數(shù)寄存器的內(nèi)容
?
?? @b@ 調(diào)用名為
系統(tǒng)調(diào)用服務(wù)例程
(system call service routine)的相應(yīng)的C函數(shù)來(lái)
?????
處理系統(tǒng)調(diào)用
?
?? @c@ 通過(guò)ret_from_sys_call(? ) 函數(shù)從系統(tǒng)調(diào)用返回
系統(tǒng)調(diào)用流程
系統(tǒng)調(diào)用中參數(shù)傳遞
a)每個(gè)系統(tǒng)調(diào)用至少有一個(gè)參數(shù),即通過(guò)
?eax 寄存器傳遞來(lái)的系統(tǒng)調(diào)用號(hào)
?
b)用寄存器傳遞參數(shù)
必須滿足兩個(gè)條件
:?
?? @a@?
每個(gè)參數(shù)的長(zhǎng)度不能超過(guò)寄存器的長(zhǎng)度
?
?? @b@?
參數(shù)的個(gè)數(shù)不能超過(guò)6 個(gè)(包括eax 中傳遞的系統(tǒng)調(diào)用號(hào))
,否則,
???????
用一個(gè)單獨(dú)的寄存器指向進(jìn)程地址空間中這些參數(shù)值所在的一個(gè)內(nèi)存區(qū)
?
c)在少數(shù)情況下,系統(tǒng)調(diào)用不使用任何參數(shù)
?
d)服務(wù)例程的
返回值必須寫(xiě)到eax 寄存器
中
很多系統(tǒng)調(diào)用需要不止一個(gè)參數(shù)
普通C函數(shù)的參數(shù)傳遞是通過(guò)把參數(shù)值寫(xiě)入堆棧(用戶態(tài)堆棧或內(nèi)核態(tài)堆棧)來(lái)實(shí)現(xiàn)的。 但因?yàn)橄到y(tǒng)調(diào)用是一種特殊函數(shù),它由用戶態(tài)進(jìn)入了內(nèi)核態(tài),所以既不能使用用戶態(tài)的堆棧 也不能直接使用內(nèi)核態(tài)堆棧
在int $0x80匯編指令之前,系統(tǒng)調(diào)用的參數(shù)被寫(xiě)入CPU的寄存器。然后,在進(jìn)入內(nèi)核態(tài)調(diào)用系統(tǒng)調(diào)用服務(wù)例程之前,內(nèi)核再把存放在CPU寄存器中的參數(shù)拷貝到內(nèi)核態(tài)堆棧中。因?yàn)楫吘狗?wù)例程是C函數(shù),它還是要到堆棧中去尋找參數(shù)的
?
系統(tǒng)調(diào)用小結(jié)
程序執(zhí)行系統(tǒng)調(diào)用大致可歸結(jié)為以下幾個(gè)步驟:
?
1、程序調(diào)用libc 庫(kù)的封裝函數(shù)。 ? 2、調(diào)用軟中斷int 0x80? 進(jìn)入內(nèi)核。 ? 3、在內(nèi)核中首先執(zhí)行system_call 函數(shù)(首先將系統(tǒng)調(diào)用號(hào)(eax)和可以 ??? 用到的所有CPU寄存器保存到相應(yīng)的堆棧中(由SAVE_ALL完成) ), ?? 接著根據(jù)系統(tǒng)調(diào)用號(hào)在系統(tǒng)調(diào)用表中查找到對(duì)應(yīng)的系統(tǒng)調(diào)用服務(wù)例程。 ? 4、執(zhí)行該服務(wù)例程。 ? 5、執(zhí)行完畢后,轉(zhuǎn)入ret_from_sys_call 例程,從系統(tǒng)調(diào)用返回
在深入討論內(nèi)核和用戶空間庫(kù)如何實(shí)現(xiàn)系統(tǒng)調(diào)用的技術(shù)細(xì)節(jié)之前,
簡(jiǎn)要看一下內(nèi)核以系統(tǒng)調(diào)用形式實(shí)際提供的各個(gè)函數(shù)是很有用處的。
每個(gè)系統(tǒng)調(diào)用都通過(guò)一個(gè)符號(hào)常數(shù)標(biāo)識(shí),符號(hào)常數(shù)的定義是平臺(tái)相關(guān)的,
在內(nèi)核源碼?
<include/asm_xx/unistd.h>
?中指定,
XX
表示平臺(tái)相關(guān),有些是?
asm_arch
,有的是
?asm_generic?
等
用于實(shí)現(xiàn)系統(tǒng)調(diào)用的處理程序函數(shù),在形式上有如下幾個(gè)共同的特性:
1,
每個(gè)函數(shù)的名稱(chēng)前綴都是 sys_?
,將該函數(shù)唯一地標(biāo)識(shí)為一個(gè)系統(tǒng)調(diào)用,
?? 更精確的說(shuō),標(biāo)識(shí)為一個(gè)系統(tǒng)調(diào)用的處理程序函數(shù)。
2,
所有的處理程序函數(shù)都最多接收 5 個(gè)參數(shù)
。
否則,
???
用一個(gè)單獨(dú)的寄存器指向進(jìn)程? 地址空間中這些參數(shù)值所在的一個(gè)內(nèi)存區(qū)即可
3,
所有的系統(tǒng)調(diào)用都在內(nèi)核態(tài)執(zhí)行
。
系統(tǒng)調(diào)用由內(nèi)核分配的一個(gè)編號(hào)唯一標(biāo)識(shí)。
所有的系統(tǒng)調(diào)用都由一處中樞代碼處理,根據(jù)調(diào)用編號(hào)和一個(gè)靜態(tài)表,將調(diào)用分派到具體的函數(shù)。
傳遞的參數(shù)也是由中樞代碼處理,這樣參數(shù)的傳遞獨(dú)立于實(shí)際的系統(tǒng)調(diào)用。
從用戶態(tài)到內(nèi)核態(tài),以及調(diào)用分派和參數(shù)傳遞,都是由匯編語(yǔ)言代碼實(shí)現(xiàn)的。
為容許用戶態(tài)和內(nèi)核態(tài)之間的切換,用戶進(jìn)程必須通過(guò)一條專(zhuān)用的機(jī)器指令,引起處理器/內(nèi)核
對(duì)該進(jìn)程的關(guān)注,這需要 C 標(biāo)準(zhǔn)庫(kù)的協(xié)助。內(nèi)核也必須提供一個(gè)例程,來(lái)滿足切換請(qǐng)求并執(zhí)行
相關(guān)操作。該例程不能在用戶空間中實(shí)現(xiàn),因?yàn)槠渲行枰獔?zhí)行普通應(yīng)用程序不允許執(zhí)行的命令。
系統(tǒng)調(diào)用表 <linux/arch/arm/kernel/calls.S> (armV7)
點(diǎn)擊(此處 )折疊或打開(kāi)
/* * linux/arch/arm/kernel/calls.S * * Copyright (C) 1995-2005 Russell King * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This file is included thrice in entry-common.S */ /* 0 */ CALL(sys_restart_syscall) CALL(sys_exit) CALL(sys_fork_wrapper) CALL(sys_read) CALL(sys_write) /* 5 */ CALL(sys_open) CALL(sys_close) CALL(sys_ni_syscall) /* was sys_waitpid */ CALL(sys_creat) CALL(sys_link) /* 10 */ CALL(sys_unlink) CALL(sys_execve_wrapper) CALL(sys_chdir) CALL(OBSOLETE(sys_time)) /* used by libc4 */
以系統(tǒng)調(diào)用 open() 函數(shù)為例:
1,X86 平臺(tái): 1,用戶空間
?? 1,函數(shù) open() 的聲明 ????? @1@ 在使用 open()函數(shù)時(shí),要 include <fcntl.h>
點(diǎn)擊(此處 )折疊或打開(kāi)? <glibc-2.17\include\fcntl.h>
#ifndef _FCNTL_H #include <io/fcntl.h> #ifndef _ISOMAC / * ?Now ?define the internal interfaces. ?* / extern?int ?__open64?( const ?char?* __file, ?int ?__oflag, ?. . . ) ; libc_hidden_proto?( __open64) extern?int ?__libc_open64?( const ?char?* file, ?int ?oflag, ?. . . ) ; extern?int ?__libc_open?( const ?char?* file, ?int ?oflag, ?. . . ) ; libc_hidden_proto?( __libc_open) extern?int ?__libc_creat?( const ?char?* file, ?mode_t mode) ; extern?int ?__libc_fcntl?( int ?fd, ?int ?cmd, ?. . . ) ; . . .
點(diǎn)擊(此處 )折疊或打開(kāi)?? <glibc-2.17\io\fcntl.h>
. . . / * ?Open FILE?and ?return a new file descriptor?for ?it, ?or ?- 1?on ?error . ???OFLAG determines the type of access used. ?If ?O_CREAT?is ?on ?OFLAG, ???the third argument?is ?taken as a `mode_t' , ?the mode of the created file. ???This?function ?is ?a cancellation point?and ?therefore?not ?marked with ???__THROW. ?* / #ifndef __USE_FILE_OFFSET64 extern?int ?open?( const ?char?* __file, ?int ?__oflag, ?. . . ) ?__nonnull?( ( 1) ) ; #else # ifdef __REDIRECT extern?int ?__REDIRECT?( open, ?( const ?char?* __file, ?int ?__oflag, ?. . . ) , ?open64) ?????__nonnull?( ( 1) ) ; #?else # define open open64 # endif #endif #ifdef __USE_LARGEFILE64 extern?int ?open64?( const ?char?* __file, ?int ?__oflag, ?. . . ) ?__nonnull?( ( 1) ) ; #endif . . .
點(diǎn)擊(此處 )折疊或打開(kāi)
/ * ?Define a macro which expands inline into the wrapper code?for ?a system ???call . ?* / # undef INLINE_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; ?} )
2,內(nèi)核空間
(1)系統(tǒng)啟動(dòng)時(shí),對(duì)INT 0x80進(jìn)行一定的初始化。
使用匯編子程序setup_idt(linux/arch/i386/kernel/head.S)初始化idt表(中斷描述符表),這時(shí)所有的入口函數(shù)偏移地址都被設(shè)為ignore_int ,如下圖所示。
2)用戶程序需要系統(tǒng)提供服務(wù)的時(shí)候,會(huì)通過(guò)系統(tǒng)調(diào)用產(chǎn)生一個(gè)int 0x80的軟中斷,就會(huì)進(jìn)入到系統(tǒng)調(diào)用的入口函數(shù),入口函數(shù)存放在以下文件當(dāng)中
點(diǎn)擊(此處 )折疊或打開(kāi)??? <arch\x86\kernel\entry_32.S>
ENTRY( system_call) ? ??? RING0_INT_FRAME # cant unwind into user?space ?anyway? ? ? pushl?% eax ? ?? # save orig_eax ,將系統(tǒng)調(diào)用號(hào)壓入棧中 ??? CFI_ADJUST_CFA_OFFSET 4? ??? SAVE_ALL ? ? ? #將寄存器的值壓入堆棧當(dāng)中,壓入堆棧的順序?qū)?yīng)著結(jié)構(gòu)體struct pt_regs , ?????????????????? #當(dāng)出棧的時(shí)候,就將這些值傳遞到結(jié)構(gòu)體struct pt_regs里面的成員, ?????????????????? #從而實(shí)現(xiàn)匯編代碼向C程序傳遞參數(shù) ? ??? GET_THREAD_INFO( % ebp) ? ????????????????? # system?call ?tracing?in ?operation?/ ?emulation? ?????????????? #GET_THREAD_INFO宏獲得當(dāng)前進(jìn)程的thread_info結(jié)構(gòu)的地址,獲取當(dāng)前進(jìn)程的信息。 ?????????????? #thread_inof結(jié)構(gòu)中flag字段的_TIF_SYSCALL_TRACE或_TIF_SYSCALL_AUDIT? ?????????????? #被置1。如果發(fā)生被跟蹤的情況則轉(zhuǎn)向相應(yīng)的處理命令處。? ??? testl $_TIF_WORK_SYSCALL_ENTRY, TI_flags( % ebp) ? ?? jnz syscall_trace_entry??? #比較結(jié)果不為零的時(shí)候跳轉(zhuǎn)。? ????????????????????????????? #對(duì)用戶態(tài)進(jìn)程傳遞過(guò)來(lái)的系統(tǒng)調(diào)用號(hào)的合法性進(jìn)行檢查 ????????????????????????????? #如果不合法則跳到syscall_badsys標(biāo)記的命令處。? ?? cmpl $( nr_syscalls) , ?% eax? ?? jae syscall_badsys ? ? ? ? #比較結(jié)果大于或者等于最大的系統(tǒng)調(diào)用號(hào)的時(shí)候跳轉(zhuǎn),不合法? ????????????????????????????? #合法則跳轉(zhuǎn)到相應(yīng)系統(tǒng)調(diào)用號(hào)所對(duì)應(yīng)的服務(wù)例程當(dāng)中,? ????????????????????????????? #也就是在sys_call_table表中找到了相應(yīng)的函數(shù)入口點(diǎn)。? ?????????? #由于sys_call_table表的表項(xiàng)占4字節(jié)字節(jié)字節(jié)字節(jié),因此獲得服務(wù)例程指針的具體方法 ?????????? #是將由eax保存的系統(tǒng)調(diào)用號(hào)乘以4再與sys_call_table表的基址相加。? syscall_call: ? ??? call ?* sys_call_table( , % eax, 4) ? ? ? movl?% eax, PT_EAX( % esp) ??? # store the return value 將保存的結(jié)果返回。
點(diǎn)擊(此處 )折疊或打開(kāi)?? <arch\x86\include\asm\ptrace.h>
struct pt_regs?{ ????unsigned long bx; ????unsigned long cx; ????unsigned long dx; ????unsigned long si; ????unsigned long di; ????unsigned long bp; ????unsigned long ax; ????unsigned long ds; ????unsigned long es; ????unsigned long fs; ????unsigned long gs; ????unsigned long orig_ax; ????unsigned long ip; ????unsigned long cs; ????unsigned long flags; ????unsigned long sp; ????unsigned long ss; } ;
MicrosoftInternetExplorer402DocumentNotSpecified7.8Normal0
接下來(lái),會(huì)進(jìn)入到系統(tǒng)調(diào)用表查找到系統(tǒng)調(diào)用服務(wù)程序的入口函數(shù)的地址,再進(jìn)行跳轉(zhuǎn),
整個(gè)過(guò)程如下圖所示:
??
MicrosoftInternetExplorer402DocumentNotSpecified7.8Normal0
1,系統(tǒng)調(diào)用號(hào):
點(diǎn)擊(此處 )折疊或打開(kāi)?????? <arch\x86\include\asm\unistd_32.h>
#ifndef _ASM_X86_UNISTD_32_H #define _ASM_X86_UNISTD_32_H / * ?* ?This file contains the system?call ?numbers. ?* / #define __NR_restart_syscall 0 #define __NR_exit???????? 1 #define __NR_fork???????? 2 #define __NR_read???????? 3 #define __NR_write???????? 4 #define __NR_open???????? 5 #define __NR_close???????? 6 #define __NR_waitpid???????? 7
MicrosoftInternetExplorer402DocumentNotSpecified7.8Normal02,系統(tǒng)調(diào)用原型:
點(diǎn)擊(此處 )折疊或打開(kāi)??? <include\linux\syscalls.h>
asmlinkage long sys_open( const ?char __user?* filename, ????????????????int ?flags, ?int ?mode) ;
???
MicrosoftInternetExplorer402DocumentNotSpecified7.8Normal0
其中這里使用了一個(gè)宏 asmlinkage?,我們?cè)倏匆幌滤谙到y(tǒng)里的定義:
點(diǎn)擊(此處 )折疊或打開(kāi)??? <arch\x86\include\asm\linkage.h>
#ifdef CONFIG_X86_32 #define asmlinkage CPP_ASMLINKAGE __attribute__( ( regparm( 0) ) )
MicrosoftInternetExplorer402DocumentNotSpecified7.8Normal0
后面的?__attribute__((regparm(0))) 表示的是不通過(guò)寄存器來(lái)傳遞參數(shù),通過(guò)棧來(lái)傳遞
??? 所以系統(tǒng)調(diào)用的入口函數(shù)里面參數(shù)的傳遞:
點(diǎn)擊(此處 )折疊或打開(kāi)???? <arch\x86\kernel\entry_32.S>
ENTRY( system_call) ????SAVE_ALL ????#將寄存器的值壓入堆棧當(dāng)中,壓入堆棧的順序?qū)?yīng)著結(jié)構(gòu)體struct pt_regs , ???????????????? #當(dāng)出棧的時(shí)候,就將這些值傳遞到結(jié)構(gòu)體struct pt_regs里面的成員, ???????????????? #從而實(shí)現(xiàn)從匯編代碼向C程序傳遞參數(shù)。
??? 定義 SAVE_ALL 是將參數(shù)壓到堆棧中,然后通過(guò)堆棧來(lái)進(jìn)行參數(shù)的傳遞
3,獲取系統(tǒng)調(diào)用入口函數(shù):
點(diǎn)擊(此處 )折疊或打開(kāi)??? <arch\x86\kernel\entry_32.S>
syscall_call: ????call ?* sys_call_table( , % eax, 4)
sys_call_table 每一項(xiàng)占用4個(gè)字節(jié)。system_call函數(shù)可以讀取 eax 寄存器,獲取當(dāng)前系統(tǒng)調(diào)用的
系統(tǒng)調(diào)用號(hào),將其乘以 4 生成偏移地址,然后以 sys_call_table 為基址,基址加上偏移地址
所指向的內(nèi)容,既是應(yīng)該調(diào)用的服務(wù)程序的地址。
點(diǎn)擊(此處 )折疊或打開(kāi)???????? <arch\x86\kernel\syscall_table_32.S>
ENTRY( sys_call_table) ????. long sys_restart_syscall?/ * ?0?- ?old?"setup()" ?system?call , ?used?for ?restarting?* / ????. long sys_exit ????. long ptregs_fork ????. long sys_read ????. long sys_write ????. long sys_open?/ * ?5?* / ????. long sys_close ????. long sys_waitpid ????. long sys_creat ????. long sys_link ????. long sys_unlink?/ * ?10?* /
?? 在本例中,sys_open 是系統(tǒng)調(diào)用服務(wù)程序的入口地址
4,調(diào)用系統(tǒng)調(diào)用函數(shù):(在新的內(nèi)核中,函數(shù)的實(shí)現(xiàn)并不是直接通過(guò) sys_xxx 函數(shù),
?????????????????????? 而是通過(guò)一個(gè)宏的封裝)
sys_open -> do_sys_open -> do_filp_open ->do_last-> nameidata_to_filp -> __dentry_open?
點(diǎn)擊(此處 )折疊或打開(kāi)????????? <fs/open.c>
SYSCALL_DEFINE3( open, ?const ?char __user?* , ?filename, ?int , ?flags, ?int , ?mode) { ????long ret; ????if ?( force_o_largefile( ) ) ????????flags?| = ?O_LARGEFILE; ????ret?= ?do_sys_open( AT_FDCWD, ?filename, ?flags, ?mode) ; ????/ * ?avoid REGPARM breakage?on ?x86: ?* / ????asmlinkage_protect( 3, ?ret, ?filename, ?flags, ?mode) ; ????return ret; }
其中宏 SYSCALL_DEFINE3 定義如下:
點(diǎn)擊(此處 )折疊或打開(kāi)?????? <include\linux\syscalls.h>
#ifdef CONFIG_FTRACE_SYSCALLS #define SYSCALL_DEFINE0( sname) ????????????????????\ ????SYSCALL_TRACE_ENTER_EVENT( _##sname) ; ????????????\ ????SYSCALL_TRACE_EXIT_EVENT( _##sname) ; ????????????\ ????static struct syscall_metadata __used????????????\ ???? __syscall_meta__##sname?= ?{ ????????????????\ ????????. name ????????= ?"sys_" # sname, ????????????\ ????????. syscall_nr????= ?- 1, ????/ * ?Filled?in ?at boot?* / ????\ ????????. nb_args ????= ?0, ????????????????\ ????????. enter_event????= ?& event_enter__##sname, ????\ ????????. exit_event????= ?& event_exit__##sname, ????????\ ????????. enter_fields????= ?LIST_HEAD_INIT( __syscall_meta__##sname. enter_fields) , ?\ ????} ; ????????????????????????????\ ????static struct syscall_metadata __used????????????\ ???? __attribute__( ( section( "__syscalls_metadata" ) ) ) ????\ ?????* __p_syscall_meta_##sname?= ?& __syscall_meta__##sname; ????\ ????asmlinkage long sys_##sname( void) #else #define SYSCALL_DEFINE0( name) ???? asmlinkage long sys_##name( void) #endif #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__)
在本例中,結(jié)合 sys_open 的定義:
點(diǎn)擊(此處 )折疊或打開(kāi)??? <include\linux\syscalls.h>
asmlinkage long sys_open( const ?char __user?* filename, ????????????????int ?flags, ?int ?mode) ;
可以知道,SYSCALL_DEFINE3 中的數(shù)字 3 表示這個(gè)函數(shù)需要傳遞 3 個(gè)參數(shù)。
其中 “##”表示宏中字符直接,即:
SYSCALL_DEFINEx(3,_open,__VA_ARGS__)
其中 SYSCALL_DEFINEx 定義如下:
點(diǎn)擊(此處 )折疊或打開(kāi)???? <include\linux\syscalls.h>
#define SYSCALL_DEFINEx( x, ?sname, ?. . . ) ????????????????\ ????__SYSCALL_DEFINEx( x, ?sname, ?__VA_ARGS__)
其中 __SYSCALL_DEFINEx 定義如下:
點(diǎn)擊(此處 )折疊或打開(kāi)????? <include\linux\syscalls.h>
#define __SYSCALL_DEFINEx( x, ?name, ?. . . ) ????????????????????\ ????asmlinkage long sys##name( __SC_DECL##x( __VA_ARGS__) ) ; ????????\ ????static inline long SYSC##name( __SC_DECL##x( __VA_ARGS__) ) ; ????\ ????asmlinkage long SyS##name( __SC_LONG##x( __VA_ARGS__) ) ????????\ ????{ ????????????????????????????????\ ????????__SC_TEST##x( __VA_ARGS__) ; ????????????????\ ????????return?( long) ?SYSC##name( __SC_CAST##x( __VA_ARGS__) ) ; ????\ ????} ????????????????????????????????\ ????SYSCALL_ALIAS( sys##name, ?SyS##name) ; ????????????????\ ????static inline long SYSC##name( __SC_DECL##x( __VA_ARGS__) )
在本例中如下:
點(diǎn)擊(此處 )折疊或打開(kāi)????? <include\linux\syscalls.h>
#define __SYSCALL_DEFINEx( 3, ?_open, ?. . . ) ????????????????????\ ????asmlinkage long sys_open ( __SC_DECL3 ( __VA_ARGS__) ) ; ????????\ ????static inline long SYSC_ open ( __SC_DECL3 ( __VA_ARGS__) ) ; ????\ ????asmlinkage long SyS_open ( __SC_LONG3 ( __VA_ARGS__) ) ????????\ ????{ ????????????????????????????????\ ????????__SC_TEST3 ( __VA_ARGS__) ; ????????????????\ ????????return?( long) ?SYSC_open ( __SC_CAST3 ( __VA_ARGS__) ) ; ????\ ????} ????????????????????????????????\ ????SYSCALL_ALIAS( sys_open , ?SyS_open ) ; ????????????????\ ????static inline long SYSC_open ( __SC_DECL3 ( __VA_ARGS__) )
MicrosoftInternetExplorer402DocumentNotSpecified7.8Normal0
當(dāng)我們自己定義一個(gè)不需要傳遞參數(shù)的系統(tǒng)調(diào)用的時(shí)候,可以這樣定義我們的函數(shù):
SYSCALL_DEFINE0(mycall)
{
printk("This?is?my_sys_call\n");
return?0;
}
5,對(duì)調(diào)用的函數(shù)進(jìn)行解析
點(diǎn)擊(此處 )折疊或打開(kāi)????????? <fs/open.c>
SYSCALL_DEFINE3( open, ?const ?char __user?* , ?filename, ?int , ?flags, ?int , ?mode) { ????long ret; ????if ?( force_o_largefile( ) ) ????????flags?| = ?O_LARGEFILE; ????ret?= ?do_sys_open( AT_FDCWD, ?filename, ?flags, ?mode) ; ????/ * ?avoid REGPARM breakage?on ?x86: ?* / ????asmlinkage_protect( 3, ?ret, ?filename, ?flags, ?mode) ; ????return ret; }
@a1@? open的核心處理在函數(shù) do_sys_open() 中
點(diǎn)擊(此處 )折疊或打開(kāi)?????? <fs/open.h>
long do_sys_open( int ?dfd, ?const ?char __user?* filename, ?int ?flags, ?int ?mode) { ????struct open_flags op; ????int ?lookup?= ?build_open_flags( flags, ?mode, ?& op) ; ??? /*獲取文件名,getname()函數(shù)內(nèi)部首先創(chuàng)建存取文件名的內(nèi)存空間, ????? 然后從用戶空間把文件名拷貝到內(nèi)存空間來(lái)*/ ????char?* tmp?= ?getname( filename) ; ?? ????int ?fd?= ?PTR_ERR( tmp) ; ????if ?( ! IS_ERR( tmp) ) ?{ /*獲取一個(gè)可用的 fd,該函數(shù)通過(guò)調(diào)用 alloc_fd() 函數(shù)從 fd_table 中獲取一個(gè)可用的 fd, ?*并做一些簡(jiǎn)單的初始化。 ?*需要注意的是:文件描述符 fd,只對(duì)本進(jìn)程有效,也就是說(shuō)這個(gè) fd 只在該進(jìn)程中可見(jiàn), ????????????? 在別的進(jìn)程中可能不沒(méi)有使用或是表示別的文件。 ?*/ ????????fd?= ?get_unused_fd_flags( flags) ; ????????if ?( fd?> = ?0) ?{ /*文件描述符 fd 獲取成功,則打開(kāi)文件,創(chuàng)建一個(gè) file 對(duì)象 ?*/ ????????????struct file?* f?= ?do_filp_open( dfd, ?tmp, ?& op, ?lookup) ; ????????????if ?( IS_ERR( f) ) ?{ ??????????????? //打開(kāi)失敗,釋放 fd ????????????????put_unused_fd( fd) ; ????????????????fd?= ?PTR_ERR( f) ; ????????????} ?else ?{ /*如果文件已經(jīng)打開(kāi),根據(jù) inode 所指定的信息進(jìn)行打開(kāi)函數(shù),函數(shù)(參數(shù)為 f)將該文件加入到 ?*文件監(jiān)控的系統(tǒng)中。該系統(tǒng)是用來(lái)監(jiān)控文件被打開(kāi),創(chuàng)建,讀寫(xiě),關(guān)閉,修改等操作的。 ?*/ ????????????????fsnotify_open( f) ; /*將文件指針安裝在 fd 數(shù)組中, ?*將 struct file *f 加入到 fd 索引位置處 的數(shù)組中。在后續(xù)過(guò)程中,有對(duì)這個(gè)文件描述符操作的話, ?*就會(huì)通過(guò)查找該數(shù)組得到對(duì)應(yīng)的文件結(jié)構(gòu),然后進(jìn)行相關(guān)的操作 ?*/ ????????????????fd_install( fd, ?f) ; ????????????} ????????} ??????? //釋放放置從用戶空間拷貝過(guò)來(lái)的文件名的存儲(chǔ)空間 ????????putname( tmp) ; ????} ????return fd; }
點(diǎn)擊(此處 )折疊或打開(kāi)??? <include/linux/namei.h>
struct nameidata?{ ????struct path????path; ?? //當(dāng)前目錄的數(shù)據(jù)結(jié)構(gòu) ????struct qstr????last;?? //用以保存當(dāng)前目錄的名稱(chēng) ????struct path????root; ????struct inode????* inode; ?/ * ?path. dentry. d_inode?* / ????unsigned?int ????flags; ????unsigned????seq; ????int ????????last_type; ????unsigned????depth; ???? //連接文件的深度 ????char?* saved_names[ MAX_NESTED_LINKS?+ ?1] ; ????/ * ?Intent data?* / ????union?{ ????????struct open_intent open; ????} ?intent; } ;
do_filp_open()解析:
點(diǎn)擊(此處 )折疊或打開(kāi)??????? <fs/namei.c>
struct file?* do_filp_open( int ?dfd, ?const ?char?* pathname, ????????const ?struct open_flags?* op, ?int ?flags) { ????struct nameidata nd; ????struct file?* filp; ??? /*根據(jù)目錄打開(kāi)文件 ???? */ ????filp?= ?path_openat ( dfd, ?pathname, ?& nd, ?op, ?flags?| ?LOOKUP_RCU) ; ????if ?( unlikely( filp?= = ?ERR_PTR( - ECHILD) ) ) ????????filp?= ?path_openat( dfd, ?pathname, ?& nd, ?op, ?flags) ; ????if ?( unlikely( filp?= = ?ERR_PTR( - ESTALE) ) ) ????????filp?= ?path_openat( dfd, ?pathname, ?& nd, ?op, ?flags?| ?LOOKUP_REVAL) ; ????return filp; }
path_openat():
點(diǎn)擊(此處 )折疊或打開(kāi)??????? <fs/namei.c>
static struct file?* path_openat( int ?dfd, ?const ?char?* pathname, ????????struct nameidata?* nd, ?const ?struct open_flags?* op, ?int ?flags) { ????struct file?* base?= ?NULL ; ????struct file?* filp; ????struct path path; ????int ?error ; ... ????current- > total_link_count?= ?0; ??? //對(duì)路徑進(jìn)行解析 ????error = link_path_walk(pathname, nd); ????if ?( unlikely( error ) ) ????????goto out_filp; ????filp?= ?do_last( nd, ?& path, ?op, ?pathname) ; ... }
link_path_walk():
點(diǎn)擊(此處 )折疊或打開(kāi)????????? <fs/namei.c>
/ * ?* ?Name resolution. ?* ?This?is ?the basic name resolution?function , ?turning a pathname into ?* ?the final dentry. ?We expect?' base' ?to ?be positive?and ?a directory. ?* ?* ?Returns 0?and ?nd will have valid dentry?and ?mnt?on ?success. ?* ?Returns?error ?and ?drops reference?to ?input namei data?on ?failure. ?* / static?int ?link_path_walk( const ?char?* name, ?struct nameidata?* nd) { ????struct path?next ; ????int ?err ; ???? ... ??????? //查找文件 ???? err = walk_component(nd, &next, &this, type, LOOKUP_FOLLOW); ????????if ?( err ?< ?0) ????????????return?err ; ... }
walk_component
():
點(diǎn)擊(此處 )折疊或打開(kāi)????????? <fs/namei.c>
static inline?int ?walk_component( struct nameidata?* nd, ?struct path?* path, ????????struct qstr?* name, ?int ?type, ?int ?follow) { ????struct inode?* inode; ????int ?err ; ????/ * ?????* ?"." ?and ?".." ?are special?- ?".." ?especially so because it has ?????* ?to ?be able?to ?know about the current root directory?and ?????* ?parent relationships. ?????* / ... ??? //查找文件的具體實(shí)現(xiàn) ????err = do_lookup(nd, name, path, &inode); ????if ?( unlikely( err ) ) ?{ ????????terminate_walk( nd) ; ????????return?err ; ????} ? ... }
最后根據(jù)條件打開(kāi)設(shè)備:
點(diǎn)擊(此處 )折疊或打開(kāi)??????? <fs/namei.c>
/ * ?* ?Handle the last?step ?of open( ) ?* / static struct file?* do_last( struct nameidata?* nd, ?struct path?* path, ?????????????const ?struct open_flags?* op, ?const ?char?* pathname) { ????struct dentry?* dir?= ?nd- > path. dentry; ????struct dentry?* dentry; ????int ?open_flag?= ?op- > open_flag; ????int ?will_truncate?= ?open_flag?& ?O_TRUNC; ????int ?want_write?= ?0; ????int ?acc_mode?= ?op- > acc_mode; ????struct file?* filp; ????int ?error ; ????nd- > flags?& = ?~ LOOKUP_PARENT; ????nd- > flags?| = ?op- > intent; . . . common: ????error ?= ?may_open( & nd- > path, ?acc_mode, ?open_flag) ; ????if ?( error ) ????????goto?exit ; ??? //打開(kāi)設(shè)備 ????filp = nameidata_to_filp(nd); . . . }
點(diǎn)擊(此處 )折疊或打開(kāi)
/ * * ?* ?nameidata_to_filp?- ?convert a nameidata?to ?an open filp. ?* ?@nd: ?pointer?to ?nameidata ?* ?@flags: ?open flags ?* ?* ?Note that this?function ?destroys the original nameidata ?* / struct file?* nameidata_to_filp( struct nameidata?* nd) { ????const ?struct cred?* cred?= ?current_cred( ) ; ????struct file?* filp; ????/ * ?Pick up the filp from the open intent?* / ????filp?= ?nd- > intent. open. file; ????nd- > intent. open. file?= ?NULL ; ????/ * ?Has the filesystem initialised the file?for ?us? ?* / ????if ?( filp- > f_path. dentry?= = ?NULL ) ?{ ????????path_get( & nd- > path) ; ??????? /*填充一個(gè) struct file 結(jié)構(gòu),打開(kāi)設(shè)備的具體實(shí)現(xiàn) ???????? */ ?filp = __dentry_open(nd->path.dentry, nd->path.mnt, filp, ?????????????? NULL, cred); ????} ????return filp; }
點(diǎn)擊(此處 )折疊或打開(kāi)
static struct file?* __dentry_open( struct dentry?* dentry, ?struct vfsmount?* mnt, ????????????????????struct file?* f, ????????????????????int ?( * open) ( struct inode?* , ?struct file?* ) , ????????????????????const ?struct cred?* cred) { ????static?const ?struct file_operations empty_fops?= ?{ } ; ????struct inode?* inode; ????int ?error ; ????f- > f_mode?= ?OPEN_FMODE( f- > f_flags) ?| ?FMODE_LSEEK?| ????????????????FMODE_PREAD?| ?FMODE_PWRITE; ????if ?( unlikely( f- > f_flags?& ?O_PATH) ) ????????f- > f_mode?= ?FMODE_PATH; . . . ????if ?( ! open?& & ?f- > f_op) ??????????//調(diào)用 def_chr_fops里的open函數(shù),即chrdev_dev ????????open = f->f_op->open; ?? ????if ?( open) ?{ ????????error = open(inode, f); ???//執(zhí)行該設(shè)備的 open 函數(shù) ????????if ?( error ) ????????????goto cleanup_all; ????} . . . }
總結(jié)
以上是生活随笔 為你收集整理的linux下的系统调用函数到内核函数的追踪 的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
如果覺(jué)得生活随笔 網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔 推薦給好友。