亚洲欧美第一页_禁久久精品乱码_粉嫩av一区二区三区免费野_久草精品视频

? 歡迎來到蟲蟲下載站! | ?? 資源下載 ?? 資源專輯 ?? 關于我們
? 蟲蟲下載站

?? 漫談兼容內核之一:reactos怎樣實現系統調用.txt

?? 漫談系統內核內幕 收集得很辛苦 呵呵 大家快下在吧
?? TXT
?? 第 1 頁 / 共 3 頁
字號:
漫談兼容內核之一:ReactOS怎樣實現系統調用

[align=center][size=5][b]漫談兼容內核之一:ReactOS怎樣實現系統調用[/b][/size][/align]
[align=center] 毛德操[/align]

    有網友在論壇上發貼,要求我談談ReactOS是怎樣實現系統調用的。另一方面,我上次已經談到兼容內核應該如何實現Windows系統調用的問題,接著談談ReactOS怎樣實現系統調用倒也順理成章,所以這一次就來談談這個話題。不過這顯然不屬于“漫談Wine”的范疇,也確實沒有必要再來個“漫談ReactOS”,因此決定把除Wine以外的話題都納入“漫談兼容內核”。
    ReactOS這個項目的目標是要開發出一個開源的Windows。不言而喻,它要實現的系統調用就是Windows的那一套系統調用,也就是要忠實地實現Windows系統調用界面。本文要說的不是Windows系統調用界面本身,而是ReactOS怎樣實現這個界面,主要是說說用戶空間的應用程序怎樣進入/退出內核、即系統空間,怎樣調用定義于這個界面的函數。實際上,ReactOS正是通過“int 0x2e”指令進入內核、實現系統調用的。雖然ReactOS并不是Windows,它的作者們也未必看到過Windows的源代碼;但是我相信,ReactOS的代碼、至少是這方面的代碼,與“正本”Windows的代碼應該非常接近,要有也只是細節上的差別。
    下面以系統調用NtReadFile()為例,按“自頂向下”的方式,一方面說明怎樣閱讀ReactOS的代碼,一方面說明ReacOS是怎樣實現系統調用的。

    首先,Windows應用程序應該通過Win32 API調用這個接口所定義的庫函數,這些庫函數基本上都是在“動態連接庫”、即DLL中實現的。例如,ReadFile()就是在Win32 API中定義的一個庫函數。實現這個庫函數的可執行程序在Windows的“系統DLL”之一kernel32.dll中,有興趣的讀者可以在Windows上用一個工具depends.exe打開kernel32.dll,就可以看到這個DLL的導出函數表中有ReadFile()。另一方面,在微軟的VC開發環境(Visual Studio)中、以及Win2k DDK中,都有個“頭文件”winbase.h,里面有ReadFile()的接口定義:

[code]
WINBASEAPI
BOOL
WINAPI
ReadFile(
    IN HANDLE hFile,
    OUT LPVOID lpBuffer,
    IN DWORD nNumberOfBytesToRead,
    OUT LPDWORD lpNumberOfBytesRead,
    IN LPOVERLAPPED lpOverlapped
    );

    函數名前面的關鍵詞WINAPI表示這是個定義于Win32 API的函數。
    在ReactOS的代碼中同樣也有winbase.h,這在目錄reactos/w32api/include中:

[code]
BOOL WINAPI ReadFile(HANDLE, PVOID, DWORD, PDWORD, LPOVERLAPPED);[/code]
[/code]

    顯然,這二者實際上是相同的(要不然就不兼容了)。當然,微軟沒有公開這個函數的代碼,但是ReactOS為之提供了一個開源的實現,其代碼在reactos/lib/kernel32/file/rw.c中。
[code]
BOOL STDCALL
ReadFile( HANDLE hFile,  LPVOID lpBuffer,  DWORD nNumberOfBytesToRead,
         LPDWORD lpNumberOfBytesRead,  LPOVERLAPPED lpOverLapped )
{
   ……

   errCode = NtReadFile(hFile,
       hEvent,
       NULL,
       NULL,
       IoStatusBlock,
       lpBuffer,
       nNumberOfBytesToRead,
       ptrOffset,
       NULL);

   ……
   return(TRUE);
}
[/code]
    我們在這里只關心NtReadFile(),所以略去了別的代碼。
    如前所述,NtReadFile()是Windows的一個系統調用,內核中有個函數就叫NtReadFile(),它的實現在ntoskrnl.exe中(這是Windows內核的核心部分),這也可以用depends.exe打開ntoskrnl.exe察看。ReactOS代碼中對內核函數NtReadFile()的定義在reactos/include/ntos/zw.h中,同樣的定義也出現在reactos/w32api/include/ddk/winddk.h中:
[code]
NTSTATUS
STDCALL
NtReadFile(
   IN HANDLE FileHandle,
   IN HANDLE Event OPTIONAL,
   IN PIO_APC_ROUTINE UserApcRoutine OPTIONAL,
   IN PVOID UserApcContext OPTIONAL,
   OUT PIO_STATUS_BLOCK IoStatusBlock,
   OUT PVOID Buffer,
   IN ULONG BufferLength,
   IN PLARGE_INTEGER ByteOffset OPTIONAL,
   IN PULONG Key OPTIONAL 
   );
[/code]
    而相應的實現則在reactos/ntoskrnl/io/rw.c中。
    表面上看這似乎挺正常,ReadFile()調用NtReadFile(),reactos/ntoskrnl/io/rw.c則為其提供了被調用的NtReadFile()。可是仔細一想就不對了。這ReadFile()是在用戶空間運行的,而reactos/ntoskrnl/io/rw.c中的代碼卻是在內核中,是在系統空間。難道用戶空間的程序竟能如此這般地直接調用內核中的函數嗎?如果那樣的話,那還要什么陷阱門、調用門這些機制呢?再說,編譯的時候又怎樣把它們連接起來呢?
    這么一想,就可以斷定這里面另有奧妙。仔細一查,原來還另有一個NtReadFile(),在msvc6/iface/native/syscall/Debug/zw.c中:
[code]
__declspec(naked) __stdcall
NtReadFile(int dummy0, int dummy1, int dummy2) 
{
    __asm {
    push ebp
    mov ebp, esp
    mov eax,152
    lea edx, 8[ebp]
    int 0x2E
    pop ebp
    ret 9
    }
}
[/code]
    原來,用戶空間也有一個NtReadFile(),正是這個函數在執行自陷指令“int 0x2e”。我們看一下這段匯編代碼。這里面的152就是NtReadFile()這個系統調用的調用號,所以當CPU自陷進入系統空間后寄存器eax持有具體的系統調用號。而寄存器edx,在執行了lea這條指令以后,則持有CPU在調用這個函數前夕的堆棧指針,實際上就是指向堆棧中調用參數的起點。在進行系統調用時如何傳遞參數這個問題上,Windows和Linux有著明顯的差別。我們知道,Linux是通過寄存器傳遞參數的,好處是效率比較高,但是參數的個數受到了限制,所以Linux系統調用的參數都很少,真有大量參數需要傳遞時就把它們組裝在數據結構中,而只傳遞數據結構指針。而Windows則通過堆棧傳遞參數。讀者在上面看到,ReadFile()在調用NtReadFile()時有9個參數,這9個參數都被壓入堆棧,而edx就指向堆棧中的這些參數的起點(地址最低處)。我們在這個函數中沒有看到對通過堆棧傳下來的參數有什么操作,也沒有看到往堆棧里增加別的參數,所以傳下來的9個參數被原封不動地傳了下去(作為int 0x2e自陷的參數)。這樣,當CPU自陷進入內核以后,edx仍指向用戶空間堆棧中的這些參數。當然,CPU進入內核以后的堆棧是系統空間堆棧,而不是用戶空間堆棧,所以需要用copy_from_user()一類的函數把這些參數從用戶空間拷貝過來,此時edx的值就可用作源指針。至于寄存器ebp,則用作調用這個函數時的“堆??蚣堋敝羔槨?    當內核完成了具體系統調用的操作,CPU返回到用戶空間時,下一條指令是“pop ebp”,即恢復上一層函數的堆??蚣苤羔?。然后,指令“ret 9”使CPU返回到上一層函數,同時調整堆棧指針,使其跳過堆棧上的9個調用參數。在“正宗”的x86匯編語言中,用在ret指令中的數值以字節為單位,所以應該是“ret 24h”,而這里卻是以4字節長字為單位,這顯然是因為用了不同的匯編工具。
    子程序的調用者可以把參數壓入堆棧,通過堆棧把參數傳遞給被調用者??墒?,當CPU從子程序返回時,由誰負責從堆棧中清除這些參數呢?顯然,要么就是由調用者負責,要么就是由被調用者負責,這里需要有個約定,使得調用者和被調用者取得一致。在上面NtReadFile()這個函數中,我們看到是由被調用者負起了這個責任、在調整堆棧指針。函數代碼前面的__stdcall就說明了這一點。同樣,在.h文件中對NtReadFile()的定義(申明)之前也加上了STDCALL,也是為了說明這個約定。“Undocumented Windows 2000 Secrets”這本書中(p51-53)對類似的各種約定有一大段說明,讀者可以參考。另一方面,在上面這個函數的代碼中,函數的調用參數是3個而不是9個。但是看一下代碼就可以知道這些參數根本就沒有被用到,而調用者、即前面的ReadFile()、也是按9個參數來調用NtReadFile()的。所以,這里的三個參數完全是虛設的,有沒有、或者有幾個、都無關緊要,難怪代碼中稱之為“dummy”。
    用戶空間的這個NtReadFile()向上代表著內核函數NtReadFile(),向下則代表著想要調用內核函數NtReadFile()的那個函數,在這里是ReadFile();但是它本身并不提供什么附加的功能,這樣的中間函數稱為“stub”。
    當然,ReactOS的這種做法很容易把讀者引入迷茫。相比之下,Linux的做法就比較清晰,例如應用程序調用的是庫函數write(),而內核中與之對應的函數則是sys_write()。
    那么為什么ReactOS要這么干呢?我只能猜測:
    (1).Windows的源代碼中就是這樣,例如用depends.exe在ntdll.dll和ntoskrnl.exe中都可看到有名為NtReadFile()的函數,而ReactOS的人就依葫蘆畫瓢。
    (2).作為一條開發路線,ReactOS可能在初期不劃分用戶空間和系統空間,所有的代碼全在同一個空間運行,所以應用程序可以直接調用內核中的函數。這樣,例如對文件系統的開發就可以簡單易行一些。然后,到一些主要的功能都開發出來以后,再來劃分用戶空間和系統空間,并且補上如何跨越空間這一層。從zw.c這個文件在native/syscall/Debug目錄下這個跡象看,ReactOS似乎正處于走出這一步的過程中。
    (3).ReactOS的作者們可能有意讓它也可以用于嵌入式系統。嵌入式系統往往不劃分用戶空間和系統空間,而把應用程序和內核連接在同一個可執行映像中。這樣,如果需要把代碼編譯成一個嵌入式系統,就不使用stub;而若要把代碼編譯成一個桌面系統,則可以在用戶空間加上stub并在內核中加上處理自陷指令“int 0x2e”的程序。

    在Windows中,stub函數NtReadFile()在ntdll.dll中。實際上,所有0x2e系統調用的stub函數都在這個DLL中。顯然,所有系統調用的stub函數具有相同的樣式,不同的只是系統調用號和參數的個數,所以ReactOS用一個工具來自動生成這些stub函數。這個工具的代碼在msvc6/iface/native/genntdll.c中,下面是一個片斷:
[code]
void write_syscall_stub(FILE* out, FILE* out3, char* name, char* name2,
   char* nr_args, unsigned int sys_call_idx)
{
int i;
int nArgBytes = atoi(nr_args);

#ifdef PARAMETERIZED_LIBS
  ……
#else
  fprintf(\"\\n\\t.global _%s\\n\\t\"\n",name);
  fprintf(out,"\".global _%s\\n\\t\"\n",name2);
  fprintf(out,"\"_%s:\\n\\t\"\n",name);
  fprintf(out,"\"_%s:\\n\\t\"\n",name2);
#endif
  fprintf(out,"\t\"pushl\t%%ebp\\n\\t\"\n");
  fprintf(out,"\t\"movl\t%%esp, %%ebp\\n\\t\"\n");
  fprintf(out,"\t\"mov\t$%d,%%eax\\n\\t\"\n",sys_call_idx);
  fprintf(out,"\t\"lea\t8(%%ebp),%%edx\\n\\t\"\n");
  fprintf(out,"\t\"int\t$0x2E\\n\\t\"\n");
  fprintf(out,"\t\"popl\t%%ebp\\n\\t\"\n");
  fprintf(out,"\t\"ret\t$%s\\n\\t\");\n\n",nr_args);
  ……
}
[/code]
    代碼中的’\t’表示TAB字符,讀者閱讀這段代碼應該沒有什么問題。這段代碼根據name、nr_args、sys_call_idx等參數為給定系統調用生成stub函數的匯編代碼。那么這些參數從何而來呢?在ReactOS代碼的reactos/tools/nci目錄下有個文件sysfuncs.lst,下面是從這個文件中摘出來的幾行:
[code]
NtAcceptConnectPort 6
NtAccessCheck 8
NtAccessCheckAndAuditAlarm 11
NtAddAtom 3
……
NtClose 1
……
NtReadFile 9
……
[/code]
    這里的NtAcceptConnectPort就是調用號為0的系統調用NtAcceptConnectPort(),它有6個參數。另一個系統調用NtClose()只有1個參數。而NtReadFile()有9個參數,并且正好是這個表中的第153行,所以調用號是152。

    用戶空間的程序一執行int 0x2e,CPU就自陷進入了系統空間。其間的物理過程這里就不多說了,有需要的讀者可參考“情景分析”或其它有關資料。我這里就從CPU怎樣進入int 0x2e的自陷處理程序說起。
    像別的中斷向量一樣,ReactOS在其初始化程序KeInitExceptions()中設置了int 0x2e的向量,這個函數的代碼在reactos/ntoskrnl/ke/i386/exp.c中:
[code]
VOID INIT_FUNCTION
KeInitExceptions(VOID)
/*
* FUNCTION: Initalize CPU exception handling
*/
{
   ……
   set_trap_gate(0, (ULONG)KiTrap0, 0);
   set_trap_gate(1, (ULONG)KiTrap1, 0);
   set_trap_gate(2, (ULONG)KiTrap2, 0);
   set_trap_gate(3, (ULONG)KiTrap3, 3);
   ……
   set_system_call_gate(0x2d,(int)interrupt_handler2d);

?? 快捷鍵說明

復制代碼 Ctrl + C
搜索代碼 Ctrl + F
全屏模式 F11
切換主題 Ctrl + Shift + D
顯示快捷鍵 ?
增大字號 Ctrl + =
減小字號 Ctrl + -
亚洲欧美第一页_禁久久精品乱码_粉嫩av一区二区三区免费野_久草精品视频
日韩欧美国产高清| 国内精品免费在线观看| 国产欧美日韩不卡| 欧美xxxxx牲另类人与| 欧美一二三区精品| 日韩一区二区麻豆国产| 欧美一激情一区二区三区| 欧美日韩视频在线一区二区| 欧美日韩久久久| 欧美日韩国产精品自在自线| 欧美男人的天堂一二区| 欧美一级淫片007| 精品国产123| 国产日本欧洲亚洲| ...中文天堂在线一区| 亚洲男人的天堂一区二区| 一区二区三区视频在线看| 亚洲bt欧美bt精品777| 日韩电影免费在线看| 精品一区二区久久| 国产成人高清视频| 欧美中文字幕一区| 在线成人午夜影院| 国产精品天干天干在观线| 亚洲色图20p| 午夜国产精品影院在线观看| 日韩成人av影视| 国产超碰在线一区| 在线观看91视频| 欧美一区二区三区精品| 中文一区在线播放| 亚洲国产美国国产综合一区二区| 麻豆精品一区二区av白丝在线| 国产在线精品一区二区三区不卡 | 91精品婷婷国产综合久久| 欧美成人欧美edvon| 国产精品欧美精品| 日日摸夜夜添夜夜添国产精品 | 成人看片黄a免费看在线| 欧美影视一区二区三区| 日韩免费高清视频| 成人免费一区二区三区视频| 轻轻草成人在线| 成人精品鲁一区一区二区| 91麻豆精品国产91| 亚洲欧美激情小说另类| 蜜桃传媒麻豆第一区在线观看| 成人午夜视频免费看| 欧美一区二区三区在线电影 | 粉嫩高潮美女一区二区三区| 91久久精品国产91性色tv| 精品三级av在线| 亚洲欧洲精品一区二区三区| 日本不卡免费在线视频| 91热门视频在线观看| 日韩免费视频线观看| 亚洲欧美精品午睡沙发| 精品在线播放免费| 91精品中文字幕一区二区三区 | 不卡在线观看av| 91精品国产综合久久精品| 国产精品午夜在线| 精品一区二区免费在线观看| 3d动漫精品啪啪一区二区竹菊| 欧美极品少妇xxxxⅹ高跟鞋 | 懂色av一区二区三区免费看| 欧美一区二区在线免费播放| 亚洲欧美日韩一区| 成人av网站在线观看| 日本一区二区视频在线| 国产在线精品一区二区夜色| 日韩欧美一级二级三级久久久 | 欧美电视剧在线看免费| 亚洲成av人综合在线观看| 欧美色区777第一页| 亚洲制服丝袜av| 色天使色偷偷av一区二区| 亚洲男人的天堂av| 欧美无乱码久久久免费午夜一区| 亚洲欧美另类图片小说| 色婷婷亚洲一区二区三区| 亚洲男同性恋视频| 欧美日韩精品免费观看视频| 性做久久久久久久免费看| 欧美日韩久久不卡| 麻豆精品视频在线观看| 久久久久久久电影| 成人免费视频视频在线观看免费| 国产精品成人午夜| 欧美中文一区二区三区| 午夜欧美2019年伦理| 日韩欧美国产一区在线观看| 国产一区视频网站| 中文字幕日本不卡| 欧美视频在线观看一区| 日韩1区2区日韩1区2区| 久久老女人爱爱| 99九九99九九九视频精品| 亚洲综合免费观看高清完整版| 欧美日韩亚洲综合| 国产真实乱偷精品视频免| 日韩欧美电影一二三| 久久成人精品无人区| 久久精品一区蜜桃臀影院| 日本精品免费观看高清观看| 香蕉av福利精品导航| 日韩精品中文字幕在线不卡尤物| 国产成人一级电影| 亚洲午夜精品17c| 日韩精品专区在线影院重磅| 懂色av中文一区二区三区| 亚洲午夜激情网页| 中文字幕av一区 二区| 51久久夜色精品国产麻豆| 成人性生交大合| 日韩精品久久久久久| 国产精品久久久久久久久久免费看| 色偷偷久久人人79超碰人人澡| 久久精品国产精品青草| 亚洲精品免费播放| 久久精品一区八戒影视| 欧美精品在欧美一区二区少妇| 国产精品一区二区视频| 午夜精品福利一区二区蜜股av| 中文字幕乱码亚洲精品一区| 欧美夫妻性生活| 精品国产污污免费网站入口| 91视视频在线观看入口直接观看www| 日本欧美一区二区在线观看| 一区精品在线播放| 久久蜜桃av一区精品变态类天堂 | 视频一区中文字幕| 国产精品蜜臀av| 久久你懂得1024| 欧美久久一区二区| 欧美三级电影在线看| 97久久精品人人做人人爽| 国产精品资源在线看| 麻豆高清免费国产一区| 亚洲成人手机在线| 夜夜嗨av一区二区三区四季av| 欧美国产一区在线| 国产亚洲精久久久久久| 日韩视频一区二区三区| 欧美精品v国产精品v日韩精品| 色八戒一区二区三区| 丁香六月久久综合狠狠色| 精品在线免费视频| 国产呦精品一区二区三区网站| 美女免费视频一区二区| 免费在线视频一区| 日韩激情在线观看| 日韩国产在线一| 日韩国产高清影视| 秋霞国产午夜精品免费视频| 午夜精品久久久久久久久久久| 亚洲第一福利视频在线| 日韩美女久久久| 亚洲精品国产一区二区三区四区在线| 国产精品精品国产色婷婷| 国产精品国产三级国产三级人妇| 国产婷婷精品av在线| 久久品道一品道久久精品| 久久人人超碰精品| 国产人成一区二区三区影院| 中文字幕电影一区| 最近日韩中文字幕| 亚洲www啪成人一区二区麻豆| 日韩精品一卡二卡三卡四卡无卡| 日本一道高清亚洲日美韩| 久久99精品久久久| 成人久久18免费网站麻豆| 色综合天天综合在线视频| 欧美日韩国产小视频在线观看| 在线不卡的av| 国产视频一区二区在线| 国产精品国产三级国产aⅴ中文| 亚洲人123区| 丝袜亚洲另类欧美| 国产美女精品一区二区三区| 99精品一区二区| 欧美丰满高潮xxxx喷水动漫| www国产成人| 亚洲日穴在线视频| 男男视频亚洲欧美| eeuss鲁片一区二区三区在线观看| 色综合天天综合网天天狠天天| 欧美一区二区播放| 国产精品国产精品国产专区不片| 亚洲一区免费观看| 国产专区欧美精品| 色噜噜偷拍精品综合在线| 日韩欧美不卡在线观看视频| 国产精品乱人伦一区二区| 亚洲国产欧美日韩另类综合| 丁香六月综合激情| 欧美va天堂va视频va在线| 亚洲欧美激情小说另类| 美国av一区二区| 色偷偷久久一区二区三区|