中文字幕在线观看,亚洲а∨天堂久久精品9966,亚洲成a人片在线观看你懂的,亚洲av成人片无码网站,亚洲国产精品无码久久久五月天

程序是怎么執(zhí)行的

2018-07-20    來源:編程學(xué)習(xí)網(wǎng)

容器云強(qiáng)勢上線!快速搭建集群,上萬Linux鏡像隨意使用

  Docker是一個(gè)建立在操作系統(tǒng)+編譯器基礎(chǔ)之上的系統(tǒng),所以了解操作系統(tǒng),編譯器以及程序運(yùn)行機(jī)制對(duì)我們理解Docker來說非常重要。本文是一個(gè)自己的體會(huì),有很多不精確的地方,目的是希望大家多關(guān)注低層,多修煉內(nèi)功,多讀好書。

  一直想寫篇文章來說明在程序運(yùn)行過程中操作系統(tǒng)都干了些什么事。下面我試著說明:

  首先,任何程序都是有格式的,所謂無規(guī)矩不成方圓,任何美的,精巧的事物都是精密組織的,程序也一樣。我之前用的最多的是c#與java,有趣的是,當(dāng)時(shí)很多人嘲笑java與c#們一直在用腳本寫程序,大概在他們眼里c與c++才是真正的程序。但是,現(xiàn)實(shí)就是現(xiàn)實(shí),其實(shí)我們都是在一個(gè)叫做虛擬機(jī)的程序下寫托管代碼,它掌握著程序的編譯,鏈接,加載,映射與最終執(zhí)行與終止。它就是操作系統(tǒng),準(zhǔn)確的講是操作系統(tǒng)+編譯器。他們是真正的元虛擬機(jī)。

  然后我來解釋下如何運(yùn)行一個(gè)程序:

  程序是精巧與復(fù)雜的,熟悉它以后你也會(huì)覺得它是脆弱的,因?yàn)橹灰幸粋(gè)bit發(fā)生錯(cuò)誤,整個(gè)系統(tǒng)就會(huì)崩潰。這個(gè)系統(tǒng)就是執(zhí)行文件格式,在linux下叫elf(executable linkable format)而windows下叫pe(portable execute)。我想寫操作系統(tǒng)第一步就是制定這個(gè)規(guī)則,不然一切都沒有規(guī)律。所以我想linus牛,但是ken tomason有過之而無不及,畢竟你是在人家基礎(chǔ)之上發(fā)展而來的,計(jì)算機(jī)世界就是如此沒辦法,誰讓你在人家下面呢?

  我以linux系統(tǒng)為例,簡單講講程序由編譯 鏈接裝載與執(zhí)行。elf文件格式分為很多段—section,總體分為只讀可執(zhí)行的代碼段與可讀可寫的數(shù)據(jù)段。.txt就是典型的代碼段,.data .rodata .symbl .rel .got .plt都是數(shù)據(jù)段。那么,編譯器負(fù)責(zé)將程序員寫的程序,編譯成elf文件,代碼,注視,代碼行對(duì)應(yīng)機(jī)器碼信息,就是調(diào)試信息啦會(huì)進(jìn)去.txt .code .comment .debug段,常量與靜態(tài)變量進(jìn)入.data .rodata .bss。接下來,編譯器將引用的頭文件中的代碼(特指靜態(tài)編譯)與引用的glibc中的庫函數(shù)打包(鏈接)到整個(gè)可執(zhí)行文件中,然后在elf文件中設(shè)置文件頭信息,如段表位置,程序入口位置等信息。當(dāng)然,這里不得不提的是符號(hào)表,與重定位表,他們是整個(gè)程序最終能跑起來的關(guān)鍵。gcc是靠符號(hào),或者說程序是靠符號(hào)來鏈接的,不管是函數(shù)還是變量,都是符號(hào)而已,所以從側(cè)面講,寫程序跟寫文章沒啥區(qū)別。程序就像個(gè)圖書館,每個(gè)函數(shù)與變量都是書,鏈接程序好比在圖書館看書,當(dāng)你看到一個(gè)點(diǎn)時(shí),就會(huì)叫你去某某位置拿另一本書,翻到特定位置開始繼續(xù)讀,如果沒找到就會(huì)爆出鏈接錯(cuò)誤。而重定位表就是一次性講所有對(duì)需要跳轉(zhuǎn)的位置進(jìn)行更改,以確保程序中不存在沒有拿到手的書。

  好,現(xiàn)在程序已經(jīng)鏈接好了,接下來就是操作系統(tǒng)進(jìn)行裝載與執(zhí)行了。當(dāng)然這是靜態(tài)的鏈接,動(dòng)態(tài)鏈接會(huì)稍微復(fù)雜,會(huì)寫很多,這里不討論。操作系統(tǒng)會(huì)打開elf文件的裝載視圖,它能根據(jù)裝載視圖的段表—segment這跟section在中文都是段,沒辦法!這個(gè)視圖是將數(shù)據(jù)與代碼分開的,相似section鏈接在一起,所以數(shù)量也比section少很多,目的是在裝載時(shí)節(jié)約內(nèi)存。因?yàn),段映射到?nèi)存是要地址對(duì)齊的,如按照地址4096(一般簇大小為4k)整除來對(duì)齊,這樣做是有好處的,能減少內(nèi)存碎片,加快磁盤讀寫速度,磁盤最小扇區(qū)512byte,所以整數(shù)倍讀取能少一次尋址,當(dāng)然效率更高。這在游戲引擎,數(shù)據(jù)庫設(shè)計(jì)領(lǐng)域比較多見,畢竟io是最大瓶頸,所以再這程序時(shí)也要考慮對(duì)象占用內(nèi)存大小是否是操作系統(tǒng)最小簇的整數(shù)倍來判斷一個(gè)程序是否是高人所做。

  回來,操作系統(tǒng)會(huì)最先讀取可執(zhí)行的文件頭,因?yàn)槔锩嬗羞\(yùn)行程序的信息,如段表位置,程序入口,程序類型等。對(duì)于操作系統(tǒng)最重要的是段表與程序入口。其中段表就是elf中有多少段,每個(gè)段在文件中的偏移,入口則是常說得main函數(shù)的虛擬地址。這里就出現(xiàn)一個(gè)問題,程序非得以main函數(shù)開始嗎?其實(shí)看出來了,不用!只是gcc認(rèn)定符號(hào)main為c語言的入口,其他程序照抄罷了,當(dāng)然你可以加入編譯條件更改入口即可。gcc是stallman寫的,他是個(gè)黑客,全世界只要運(yùn)行c的地方,他都能黑,呵呵。

  好了,操作系統(tǒng)在讀取可執(zhí)行程序頭時(shí)做了三件事:1.創(chuàng)建虛擬內(nèi)存空間來容納一個(gè)進(jìn)程,2.根據(jù)文件頭內(nèi)容建立程序虛擬內(nèi)存地址與elf文件的映射關(guān)系表,vma(virtual memory area)結(jié)構(gòu),3.初始化程序的?臻g與堆空間。下面解釋下這三個(gè)過程。

  1,虛擬內(nèi)存。虛擬內(nèi)存是編譯器與操作系統(tǒng)的一個(gè)約定。任何程序在編譯無鏈接時(shí)得地址都是虛擬地址。為什么要用虛擬地址這個(gè)問題說來話長。話說在很久以前,大家都很窮,都沒內(nèi)存,但是要運(yùn)行的程序很多,系統(tǒng)不可能為每個(gè)程序分配單獨(dú)的內(nèi)存,同時(shí)領(lǐng)導(dǎo)還要求同時(shí)所有程序都要運(yùn)行,咋辦呢?辦法總比問題多,咱可以分時(shí)嘛,你上完cpu我再上,但是大家各自在用cpu時(shí),其他只能看著,直到一個(gè)人說"下一個(gè)",這個(gè)人不管在干嘛都得放棄,讓其他人用cpu。這樣對(duì)所有人都公平,而且每個(gè)人在用cpu是能感覺到cpu只被它獨(dú)有,用戶體驗(yàn)還挺好。所以一次解決可所有問題。而,這個(gè)組織人,就是那個(gè)喊“下一個(gè)”的家伙就是操作系統(tǒng)。那,說這么多,跟虛擬地址有啥關(guān)系呢?其實(shí)仔細(xì)想想如果大家都是用物理地址,而彼此在運(yùn)行時(shí)都獨(dú)占系統(tǒng)資源,那前一個(gè)程序修改了我的數(shù)據(jù)咋辦,得了,都由操作系統(tǒng)說了算吧,它做內(nèi)存映射的維護(hù),大家都用統(tǒng)一的地址空間,但是運(yùn)行時(shí)映射到不同的物理內(nèi)存互不干擾來。所以你可以看到所有l(wèi)inux程序都從相同的虛擬地址開始執(zhí)行。

  2.建立內(nèi)存到文件得映射。我們知道,程序都不是一次性加載到內(nèi)存的,而是一段段的,這是由著名的copy on write規(guī)則約束而來的。而這一段也是規(guī)定好大小的一般是操作系統(tǒng)簇的大小,也叫一頁。當(dāng)程序運(yùn)行過程中發(fā)現(xiàn)某個(gè)數(shù)據(jù)在內(nèi)存中沒有則會(huì)報(bào)一個(gè)頁讀取錯(cuò)誤,并觸發(fā)操作系統(tǒng)的缺頁中斷。這時(shí)就要靠操作系統(tǒng)通過讀取elf文件頭建立的從文件系統(tǒng)到虛擬內(nèi)存的映射來獲取了。它等于是程序運(yùn)行時(shí)到程序得一個(gè)索引結(jié)構(gòu),存儲(chǔ)了運(yùn)行時(shí)程序虛擬內(nèi)存地址到文件地址的對(duì)應(yīng)表。

  3.好了,第三步最簡單,就是操作系統(tǒng)載人main函數(shù)后面跟的那個(gè)char argc與char*argv了。他們是程序啟動(dòng)參數(shù)。還要載入程序運(yùn)行的環(huán)境變量,棧空間,堆空間,也就是靜態(tài)數(shù)據(jù)與全局變量部分。然后把程序執(zhí)行寄存器指向程序開始的地方。開始執(zhí)行!看似簡單,但是很復(fù)雜的過程開始了!

  好了,這就是簡單的程序如何被操作系統(tǒng)執(zhí)行的簡單描述,當(dāng)然這只是靜態(tài)鏈接程序的加載,動(dòng)態(tài)鏈接稍微復(fù)雜點(diǎn)。原理差不多,呵呵。

標(biāo)簽: linux 代碼 腳本 數(shù)據(jù)庫

版權(quán)申明:本站文章部分自網(wǎng)絡(luò),如有侵權(quán),請(qǐng)聯(lián)系:west999com@outlook.com
特別注意:本站所有轉(zhuǎn)載文章言論不代表本站觀點(diǎn)!
本站所提供的圖片等素材,版權(quán)歸原作者所有,如需使用,請(qǐng)與原作者聯(lián)系。

上一篇:10個(gè)所需的IT技能 助你職場成功

下一篇:20個(gè)非常有用的Java程序片段