自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

系統(tǒng)調(diào)用,讓世界轉(zhuǎn)起來!

系統(tǒng) Linux
這篇文章講解系統(tǒng)調(diào)用,系統(tǒng)調(diào)用與調(diào)用一個(gè)庫有何區(qū)別,以及在操作系統(tǒng)/應(yīng)用程序接口上的刺探工具。如果徹底了解了應(yīng)用程序借助操作系統(tǒng)發(fā)生的哪些事情,那么就可以將一個(gè)不可能解決的問題轉(zhuǎn)變成一個(gè)快速而有趣的難題。

 [[229002]]

我其實(shí)不想將它分解開給你看,用戶應(yīng)用程序其實(shí)就是一個(gè)可憐的甕中大腦brain in a vat

它與外部世界的每個(gè)交流都要在內(nèi)核的幫助下通過系統(tǒng)調(diào)用才能完成。一個(gè)應(yīng)用程序要想保存一個(gè)文件、寫到終端、或者打開一個(gè) TCP 連接,內(nèi)核都要參與。應(yīng)用程序是被內(nèi)核高度懷疑的:認(rèn)為它到處充斥著 bug,甚至是個(gè)充滿邪惡想法的腦子。

這些系統(tǒng)調(diào)用是從一個(gè)應(yīng)用程序到內(nèi)核的函數(shù)調(diào)用。出于安全考慮,它們使用了特定的機(jī)制,實(shí)際上你只是調(diào)用了內(nèi)核的 API。“系統(tǒng)調(diào)用system call”這個(gè)術(shù)語指的是調(diào)用由內(nèi)核提供的特定功能(比如,系統(tǒng)調(diào)用 open())或者是調(diào)用途徑。你也可以簡稱為:syscall。

這篇文章講解系統(tǒng)調(diào)用,系統(tǒng)調(diào)用與調(diào)用一個(gè)庫有何區(qū)別,以及在操作系統(tǒng)/應(yīng)用程序接口上的刺探工具。如果徹底了解了應(yīng)用程序借助操作系統(tǒng)發(fā)生的哪些事情,那么就可以將一個(gè)不可能解決的問題轉(zhuǎn)變成一個(gè)快速而有趣的難題。

那么,下圖是一個(gè)運(yùn)行著的應(yīng)用程序,一個(gè)用戶進(jìn)程:

它有一個(gè)私有的 虛擬地址空間—— 它自己的內(nèi)存沙箱。整個(gè)系統(tǒng)都在它的地址空間中(即上面比喻的那個(gè)“甕”),程序的二進(jìn)制文件加上它所使用的庫全部都 被映射到內(nèi)存中。內(nèi)核自身也映射為地址空間的一部分。

下面是我們程序 pid 的代碼,它通過 getpid(2) 直接獲取了其進(jìn)程 id:

  1. #include <sys/types.h>
  2. #include <unistd.h>
  3. #include <stdio.h>
  4.  
  5. int main()
  6. {
  7. pid_t p = getpid();
  8. printf("%d\n", p);
  9. }

pid.c download

在 Linux 中,一個(gè)進(jìn)程并不是一出生就知道它的 PID。要想知道它的 PID,它必須去詢問內(nèi)核,因此,這個(gè)詢問請求也是一個(gè)系統(tǒng)調(diào)用:

它的***步是開始于調(diào)用 C 庫的 getpid(),它是系統(tǒng)調(diào)用的一個(gè)封裝。當(dāng)你調(diào)用一些函數(shù)時(shí),比如,open(2)、read(2) 之類,你是在調(diào)用這些封裝。其實(shí),對于大多數(shù)編程語言在這一塊的原生方法,最終都是在 libc 中完成的。

封裝為這些基本的操作系統(tǒng) API 提供了方便,這樣可以保持內(nèi)核的簡潔。所有的內(nèi)核代碼運(yùn)行在特權(quán)模式下,有 bug 的內(nèi)核代碼行將會(huì)產(chǎn)生致命的后果。能在用戶模式下做的任何事情都應(yīng)該在用戶模式中完成。由庫來提供友好的方法和想要的參數(shù)處理,像 printf(3) 這樣。

我們拿一個(gè) web API 進(jìn)行比較,內(nèi)核的封裝方式可以類比為構(gòu)建一個(gè)盡可能簡單的 HTTP 接口去提供服務(wù),然后提供特定語言的庫及輔助方法?;蛘咭部赡苡幸恍┚彺?,這就是 libc 的 getpid() 所做的:***調(diào)用時(shí),它真實(shí)地去執(zhí)行了一個(gè)系統(tǒng)調(diào)用,然后,它緩存了 PID,這樣就可以避免后續(xù)調(diào)用時(shí)的系統(tǒng)調(diào)用開銷。

一旦封裝完成,它做的***件事就是進(jìn)入了超空間hyperspace:內(nèi)核。這種轉(zhuǎn)換機(jī)制因處理器架構(gòu)設(shè)計(jì)不同而不同。在 Intel 處理器中,參數(shù)和 系統(tǒng)調(diào)用號加載到寄存器中的,然后,運(yùn)行一個(gè) 指令 將 CPU 置于 特權(quán)模式 中,并立即將控制權(quán)轉(zhuǎn)移到內(nèi)核中的全局系統(tǒng)調(diào)用 入口。如果你對這些細(xì)節(jié)感興趣,David Drysdale 在 LWN 上有兩篇非常好的文章(其一,其二)。

內(nèi)核然后使用這個(gè)系統(tǒng)調(diào)用號作為進(jìn)入 sys_call_table 的一個(gè) 索引,它是一個(gè)函數(shù)指針到每個(gè)系統(tǒng)調(diào)用實(shí)現(xiàn)的數(shù)組。在這里,調(diào)用了 sys_getpid

在 Linux 中,系統(tǒng)調(diào)用大多數(shù)都實(shí)現(xiàn)為架構(gòu)無關(guān)的 C 函數(shù),有時(shí)候這樣做 很瑣碎,但是通過內(nèi)核優(yōu)秀的設(shè)計(jì),系統(tǒng)調(diào)用機(jī)制被嚴(yán)格隔離。它們是工作在一般數(shù)據(jù)結(jié)構(gòu)中的普通代碼。嗯,除了完全偏執(zhí)的參數(shù)校驗(yàn)以外。

一旦它們的工作完成,它們就會(huì)正常返回,然后,架構(gòu)特定的代碼會(huì)接手轉(zhuǎn)回到用戶模式,封裝將在那里繼續(xù)做一些后續(xù)處理工作。在我們的例子中,getpid(2) 現(xiàn)在緩存了由內(nèi)核返回的 PID。如果內(nèi)核返回了一個(gè)錯(cuò)誤,另外的封裝可以去設(shè)置全局 errno 變量。這些細(xì)節(jié)可以讓你知道 GNU 是怎么處理的。

如果你想要原生的調(diào)用,glibc 提供了 syscall(2) 函數(shù),它可以不通過封裝來產(chǎn)生一個(gè)系統(tǒng)調(diào)用。你也可以通過它來做一個(gè)你自己的封裝。這對一個(gè) C 庫來說,既不神奇,也不特殊。

這種系統(tǒng)調(diào)用的設(shè)計(jì)影響是很深遠(yuǎn)的。我們從一個(gè)非常有用的 strace(1) 開始,這個(gè)工具可以用來監(jiān)視 Linux 進(jìn)程的系統(tǒng)調(diào)用(在 Mac 上,參見 dtruss(1m) 和神奇的 dtrace;在 Windows 中,參見 sysinternals)。這是對 pid 程序的跟蹤:

  1. ~/code/x86-os$ strace ./pid
  2.  
  3. execve("./pid", ["./pid"], [/* 20 vars */]) = 0
  4. brk(0) = 0x9aa0000
  5. access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
  6. mmap2(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7767000
  7. access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
  8. open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
  9. fstat64(3, {st_mode=S_IFREG|0644, st_size=18056, ...}) = 0
  10. mmap2(NULL, 18056, PROT_READ, MAP_PRIVATE, 3, 0) = 0xb7762000
  11. close(3) = 0
  12.  
  13. [...snip...]
  14.  
  15. getpid() = 14678
  16. fstat64(1, {st_mode=S_IFCHR|0600, st_rdev=makedev(136, 1), ...}) = 0
  17. mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7766000
  18. write(1, "14678\n", 614678
  19. ) = 6
  20. exit_group(6) = ?

輸出的每一行都顯示了一個(gè)系統(tǒng)調(diào)用、它的參數(shù),以及返回值。如果你在一個(gè)循環(huán)中將 getpid(2) 運(yùn)行 1000 次,你就會(huì)發(fā)現(xiàn)始終只有一個(gè) getpid() 系統(tǒng)調(diào)用,因?yàn)椋?PID 已經(jīng)被緩存了。我們也可以看到在格式化輸出字符串之后,printf(3) 調(diào)用了 write(2)

strace 可以開始一個(gè)新進(jìn)程,也可以附加到一個(gè)已經(jīng)運(yùn)行的進(jìn)程上。你可以通過不同程序的系統(tǒng)調(diào)用學(xué)到很多的東西。例如,sshd 守護(hù)進(jìn)程一天都在干什么?

  1. ~/code/x86-os$ ps ax | grep sshd
  2. 12218 ? Ss 0:00 /usr/sbin/sshd -D
  3.  
  4. ~/code/x86-os$ sudo strace -p 12218
  5. Process 12218 attached - interrupt to quit
  6. select(7, [3 4], NULL, NULL, NULL
  7.  
  8. [
  9. ... nothing happens ...
  10. No fun, it's just waiting for a connection using select(2)
  11. If we wait long enough, we might see new keys being generated and so on, but
  12. let's attach again, tell strace to follow forks (-f), and connect via SSH
  13. ]
  14.  
  15. ~/code/x86-os$ sudo strace -p 12218 -f
  16.  
  17. [lots of calls happen during an SSH login, only a few shown]
  18.  
  19. [pid 14692] read(3, "-----BEGIN RSA PRIVATE KEY-----\n"..., 1024) = 1024
  20. [pid 14692] open("/usr/share/ssh/blacklist.RSA-2048", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
  21. [pid 14692] open("/etc/ssh/blacklist.RSA-2048", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
  22. [pid 14692] open("/etc/ssh/ssh_host_dsa_key", O_RDONLY|O_LARGEFILE) = 3
  23. [pid 14692] open("/etc/protocols", O_RDONLY|O_CLOEXEC) = 4
  24. [pid 14692] read(4, "# Internet (IP) protocols\n#\n# Up"..., 4096) = 2933
  25. [pid 14692] open("/etc/hosts.allow", O_RDONLY) = 4
  26. [pid 14692] open("/lib/i386-linux-gnu/libnss_dns.so.2", O_RDONLY|O_CLOEXEC) = 4
  27. [pid 14692] stat64("/etc/pam.d", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
  28. [pid 14692] open("/etc/pam.d/common-password", O_RDONLY|O_LARGEFILE) = 8
  29. [pid 14692] open("/etc/pam.d/other", O_RDONLY|O_LARGEFILE) = 4

看懂 SSH 的調(diào)用是塊難啃的骨頭,但是,如果搞懂它你就學(xué)會(huì)了跟蹤。能夠看到應(yīng)用程序打開的是哪個(gè)文件是有用的(“這個(gè)配置是從哪里來的?”)。如果你有一個(gè)出現(xiàn)錯(cuò)誤的進(jìn)程,你可以 strace 它,然后去看它通過系統(tǒng)調(diào)用做了什么?當(dāng)一些應(yīng)用程序意外退出而沒有提供適當(dāng)?shù)腻e(cuò)誤信息時(shí),你可以去檢查它是否有系統(tǒng)調(diào)用失敗。你也可以使用過濾器,查看每個(gè)調(diào)用的次數(shù),等等:

  1. ~/code/x86-os$ strace -T -e trace=recv curl -silent www.google.com. > /dev/null
  2.  
  3. recv(3, "HTTP/1.1 200 OK\r\nDate: Wed, 05 N"..., 16384, 0) = 4164 <0.000007>
  4. recv(3, "fl a{color:#36c}a:visited{color:"..., 16384, 0) = 2776 <0.000005>
  5. recv(3, "adient(top,#4d90fe,#4787ed);filt"..., 16384, 0) = 4164 <0.000007>
  6. recv(3, "gbar.up.spd(b,d,1,!0);break;case"..., 16384, 0) = 2776 <0.000006>
  7. recv(3, "$),a.i.G(!0)),window.gbar.up.sl("..., 16384, 0) = 1388 <0.000004>
  8. recv(3, "margin:0;padding:5px 8px 0 6px;v"..., 16384, 0) = 1388 <0.000007>
  9. recv(3, "){window.setTimeout(function(){v"..., 16384, 0) = 1484 <0.000006>

我鼓勵(lì)你在你的操作系統(tǒng)中的試驗(yàn)這些工具。把它們用好會(huì)讓你覺得自己有超能力。

但是,足夠有用的東西,往往要讓我們深入到它的設(shè)計(jì)中。我們可以看到那些用戶空間中的應(yīng)用程序是被嚴(yán)格限制在它自己的虛擬地址空間里,運(yùn)行在 Ring 3(非特權(quán)模式)中。一般來說,只涉及到計(jì)算和內(nèi)存訪問的任務(wù)是不需要請求系統(tǒng)調(diào)用的。例如,像 strlen(3)memcpy(3) 這樣的 C 庫函數(shù)并不需要內(nèi)核去做什么。這些都是在應(yīng)用程序內(nèi)部發(fā)生的事。

C 庫函數(shù)的 man 頁面所在的節(jié)(即圓括號里的 23)也提供了線索。節(jié) 2 是用于系統(tǒng)調(diào)用封裝,而節(jié) 3 包含了其它 C 庫函數(shù)。但是,正如我們在 printf(3) 中所看到的,庫函數(shù)最終可以產(chǎn)生一個(gè)或者多個(gè)系統(tǒng)調(diào)用。

如果你對此感到好奇,這里是 Linux (也有 Filippo 的列表)和 Windows 的全部系統(tǒng)調(diào)用列表。它們各自有大約 310 和 460 個(gè)系統(tǒng)調(diào)用。看這些系統(tǒng)調(diào)用是非常有趣的,因?yàn)椋鼈兇砹?em>軟件在現(xiàn)代的計(jì)算機(jī)上能夠做什么。另外,你還可能在這里找到與進(jìn)程間通訊和性能相關(guān)的“寶藏”。這是一個(gè)“不懂 Unix 的人注定最終還要重新發(fā)明一個(gè)蹩腳的 Unix ” 的地方。(LCTT 譯注:原文 “Those who do not understand Unix are condemned to reinvent it,poorly。” 這句話是 Henry Spencer 的名言,反映了 Unix 的設(shè)計(jì)哲學(xué),它的一些理念和文化是一種技術(shù)發(fā)展的必須結(jié)果,看似糟糕卻無法超越。)

與 CPU 周期相比,許多系統(tǒng)調(diào)用花很長的時(shí)間去執(zhí)行任務(wù),例如,從一個(gè)硬盤驅(qū)動(dòng)器中讀取內(nèi)容。在這種情況下,調(diào)用進(jìn)程在底層的工作完成之前一直處于休眠狀態(tài)。因?yàn)?,CPU 運(yùn)行的非常快,一般的程序都因?yàn)?I/O 的限制在它的生命周期的大部分時(shí)間處于休眠狀態(tài),等待系統(tǒng)調(diào)用返回。相反,如果你跟蹤一個(gè)計(jì)算密集型任務(wù),你經(jīng)常會(huì)看到?jīng)]有任何的系統(tǒng)調(diào)用參與其中。在這種情況下,top(1) 將顯示大量的 CPU 使用。

在一個(gè)系統(tǒng)調(diào)用中的開銷可能會(huì)是一個(gè)問題。例如,固態(tài)硬盤比普通硬盤要快很多,但是,操作系統(tǒng)的開銷可能比 I/O 操作本身的開銷 更加昂貴。執(zhí)行大量讀寫操作的程序可能就是操作系統(tǒng)開銷的瓶頸所在。向量化 I/O 對此有一些幫助。因此要做 文件的內(nèi)存映射,它允許一個(gè)程序僅訪問內(nèi)存就可以讀或?qū)懘疟P文件。類似的映射也存在于像視頻卡這樣的地方。最終,云計(jì)算的經(jīng)濟(jì)性可能導(dǎo)致內(nèi)核消除或最小化用戶模式/內(nèi)核模式的切換。

最終,系統(tǒng)調(diào)用還有益于系統(tǒng)安全。一是,無論如何來歷不明的一個(gè)二進(jìn)制程序,你都可以通過觀察它的系統(tǒng)調(diào)用來檢查它的行為。這種方式可能用于去檢測惡意程序。例如,我們可以記錄一個(gè)未知程序的系統(tǒng)調(diào)用的策略,并對它的異常行為進(jìn)行報(bào)警,或者對程序調(diào)用指定一個(gè)白名單,這樣就可以讓漏洞利用變得更加困難。在這個(gè)領(lǐng)域,我們有大量的研究,和許多工具,但是沒有“殺手級”的解決方案。

這就是系統(tǒng)調(diào)用。很抱歉這篇文章有點(diǎn)長,我希望它對你有用。接下來的時(shí)間,我將寫更多(短的)文章,也可以在 RSSTwitter 關(guān)注我。這篇文章獻(xiàn)給 glorious Clube Atlético Mineiro。

責(zé)任編輯:龐桂玉 來源: Linux中國
相關(guān)推薦

2023-04-23 11:27:04

云計(jì)算

2010-06-12 17:07:17

TCP IP協(xié)議

2009-10-20 11:12:26

綜合布線系統(tǒng)

2010-06-18 10:13:17

虛擬機(jī)消失

2018-03-18 08:28:04

數(shù)據(jù)中心運(yùn)維組織架構(gòu)數(shù)據(jù)中心

2024-09-18 15:24:23

飛輪數(shù)據(jù)大模型系統(tǒng)

2024-03-13 08:21:53

冒泡排序動(dòng)畫

2013-06-18 10:21:43

云計(jì)算云服務(wù)公共云服務(wù)

2021-11-19 07:55:17

存儲(chǔ)鏡像部署

2013-12-18 13:17:56

多核CPU

2010-08-19 14:49:28

OSPFv3

2019-03-26 09:28:05

大數(shù)據(jù)部署架構(gòu)

2021-01-22 14:03:34

Flutter系統(tǒng)鴻蒙

2021-08-04 10:40:20

混合IT數(shù)字業(yè)務(wù)CIO

2018-08-21 21:30:32

云計(jì)算公有云數(shù)據(jù)中心

2010-03-17 08:55:10

ASP.NETWeb Server

2016-06-27 15:55:15

移動(dòng)

2017-01-11 14:19:26

JVM源碼All

2011-05-24 15:29:05

程序CC++

2012-07-01 03:23:31

JBuilder
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號