如何追蹤fp的系統調用過程

爲了能夠分析fp從用戶態到內核態在整個操作系統中的調用流程,可以通過strace這個命令來進行分析。
首先,寫出一個使用標準庫函數對文件進行讀寫操作的程序:

#include <stdio.h>
#include <string.h>
#define FILENAME ("/home/tl/UNIX-programme/self-practice/file.txt")
#define BUF ("This is CSDN\n")
int main()
{
	int length = 0;
	FILE *fp = NULL;
	fp = fopen(FILENAME , "a+");
	length = fwrite(BUF, 1, strlen(BUF), fp);
	printf("length=%d\n",length);
	fclose(fp);
	return 0;
}

接着使用strace命令將上述程序發出的所有系統調用的列表寫到log.txt中:

strace -o log.txt ./fp

log.txt的內容如下:

execve("./fp", ["./fp"], [/* 39 vars */]) = 0
brk(0)                                  = 0x80b5000
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
mmap2(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb774f000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY)      = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=54092, ...}) = 0
mmap2(NULL, 54092, PROT_READ, MAP_PRIVATE, 3, 0) = 0xb7741000
close(3)                                = 0
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
open("/lib/libc.so.6", O_RDONLY)        = 3
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0@n\1\0004\0\0\0"..., 512) = 512
fstat64(3, {st_mode=S_IFREG|0755, st_size=1421892, ...}) = 0
mmap2(NULL, 1427880, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7c0000
mmap2(0x917000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x157) = 0x917000
mmap2(0x91a000, 10664, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x91a000
close(3)                                = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7740000
set_thread_area({entry_number:-1 -> 6, base_addr:0xb77406c0, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}) = 0
mprotect(0x917000, 8192, PROT_READ)     = 0
mprotect(0x8049000, 4096, PROT_READ)    = 0
mprotect(0xb87000, 4096, PROT_READ)     = 0
munmap(0xb7741000, 54092)               = 0
brk(0)                                  = 0x80b5000
brk(0x80d6000)                          = 0x80d6000
open("/home/tl/UNIX-programme/self-practice/file.txt", O_RDWR|O_CREAT|O_APPEND, 0666) = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=13, ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb774e000
fstat64(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb774d000
write(1, "length=13\n", 10)             = 10
write(3, "This is CSDN\n", 13)          = 13
close(3)                                = 0
munmap(0xb774e000, 4096)                = 0
exit_group(0)                           = ?

很多系統調用是由啓動和運行應用程序所需的框架代碼生成的。
mmap2和unmap負責管理應用程序使用的動態內存區域。
malloc用於在進程堆區域分配內存,內部執行了brk系統調用。
三個直接使用的系統調用open、read和close,會轉爲相應的內核函數的調用。下面來看一下參數在內核空間和用戶空間傳遞的過程,時間順序如下:
在這裏插入圖片描述
當用戶空間的open等函數進行內核空間後,開始運行用於實現系統調用的處理程序函數,這些函數的名稱前綴爲sys_。內核將控制權轉移給處理程序例程後,控制流進入平臺無關的代碼,比如read函數在系統調用函數sys_read之後,會將控制權傳遞給一個更通用的內核輔助函數vfs_read()函數,這個vfs_read是不依賴與特定CPU或體系結構的通用文件操作函數。處理完在返回結果時,無需特別的操作,簡單的return後接返回值即可。
再來分析觸發系統調用的過程是是如何實現的:

  • 從用戶態切換到核心態,以及調用分派和參數傳遞,都是由彙編語言代碼實現的。不同的平臺使用不同的彙編語言方法來執行系統調用,
    在IA-32系統上,使用彙編語言指令int $0x80來引發軟件中斷128。
    在ARM系統上,通過SWI指令可以引發中斷/異常向量控制號128,將控制權轉移給內核,使ARM從用戶模式進入管理模式,即Linux操作系統從用戶態進入內核態;此時,保存CPSR至SPSR、保存R15(PC)至R14(LR),強制R15-PC(程序計數器)從0x0000 0008處取指令,內核系統調用處理函數vector_SWI()。
  • 在應用程序藉助於標準庫切換到核心態後,內核需要查找與該系統調用匹配的處理程序函數,並向處理函數提供傳遞的參數。sys_call_table表中保存了一組指向處理程序例程的函數指針,可用於查找處理程序。
  • 從核心態返回給用戶態,通過返回碼來通知用戶應用程序。儘管內核儘可能保持內核空間和用戶空間的獨立,但是有時候內核代碼必須訪問用戶應用程序的虛擬內存,但是內核不能簡單的反引用用戶空間的指針,而必須採用特定的函數,確保內存區已經在物理內存中。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章