日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 运维知识 > linux >内容正文

linux

linux下的系统调用函数到内核函数的追踪

發(fā)布時(shí)間:2025/3/15 linux 21 豆豆
生活随笔 收集整理的這篇文章主要介紹了 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ò),歡迎將生活随笔推薦給好友。