現在大家都在談設計,談架構,談框架,套用一句流行語:「
雖然聽不懂他們在說什麼,但是感覺很牛的樣子。 」
誠然。在軟體日益龐大的今天,先古時代單打獨鬥的天才程式設計師幾乎銷聲匿跡了,程式設計師的成功大多是團隊的成功,團隊合作無非也是分層,分模組,架構設計完了就是用各種框架,高內聚,低耦合云云。
很少有人聽到有人在討論編譯,連線,演算法,資料結構之類的東西。
程式設計師越來越像軟體集成員了。
我還是喜歡自下而上的學習方法。前天看了本書《程式設計師的自我修養——
編譯,連線和庫》。又明白了一些底層的東西。準確講應該是作業系統和高階語言(應用程式)之間那層。這本書很好。(只有在買書的時候,我才能深刻的體會到知識就是金錢。
65塊錢啊。才
450多頁。)大一的時候借了,
「 雖然看不懂作者在講什麼,但是感覺很牛的樣子
「,大二的時候也借了,依舊是
「 雖然看不懂作者在講什麼,但是感覺很牛的樣子
」,前幾天在圖書館溜達。又一次和它邂逅。遂收入囊中,借來看看
。雖然不至於看懂,但令我欣慰的是終於知道作者是講什麼的了。
初中時候覺得,寫軟體的人好厲害啊。高中的時候覺得,寫作業系統的人好厲害啊。大一的時候覺得,寫編譯器的人真牛啊。大二的時候想,發明語言的才是真正的大牛。現在想想,只能對以前的想法呵呵一笑。
john resig
22歲寫了
jquery
。唉~
各種***。
先說一點編譯的東西吧。
編譯過程:
1.預編譯
prepressing
展開巨集#define
,載入庫檔案
#include
等。2.
編譯compilation
高階語言編譯成組合語言,這個過程會有編譯器優化。
3.彙編
assembly
組合語言編譯成二進位制**。這個過程是最簡單的。
4.連線
linking
位址和空間分配,符號決議,重定位。
個人覺得編譯和連線過程是最複雜的。
編譯:因為現代cpu
已經很繁雜,各種功能很多。暫且不說
cisc
和 risc
。x86
,x86_64
,ia64
,mips
,arm
等等架構的
cpu,還有亂序發射,流水線優化,多核優化,暫存器優化,
cacha
優化等等一堆亂七八糟的東西。可謂眼花繚亂。記得
gcc支援
50+種
cpu平台的編譯。恐怖的一公尺。亂序發射,編譯器,
cache
優化等各種
cpu的特性,也增加了程式併發多執行緒編寫的難度。
連線:動態連線,靜態連線,可重入,位址重定向,個人感覺也是比較難的東西。
再穿插點x86
和彙編的東西。
8086
處理器有
20跟位址線,但是只有
16跟資料線。所以可以定位
1m的記憶體空間,但是只能訪問
64k的位址,為了解決這個問題,
intel
的工程師很聰明的用了乙個方法,段
+偏移量 這種記憶體訪問方式。寫彙編的時候,一般是這樣開頭的。
code segment,
data segment
,stack segment
三個段。我以前一直以為沒有作業系統的時候,這三個段就依次匯入到
8086
的三個段裡面。真無知啊。
馮諾依曼果然是大神。沒有計算機的時候就設計了計算機的靈魂。資料和指令分離。試想一下在混沌初開的時候,如果資料和指令是混雜在一起的話,那將是多麼恐怖的事情。不過只要是乙個正常的人,寫彙編的時候就會很自然的把指令和資料分離。
假如不分離的話,乙個指令執行完了,後面乙個是資料,要怎麼區分?cpu
會不會把資料當做指令去執行?
解決方法之一是記憶體裡面加標誌位,0
是可執行指令,
1是不可執行的資料。無疑這會造成記憶體浪費。
另一種方法是按照 指令-資料-
指令-資料-
指令-資料 交叉存放(當然,也可以1指令
n資料,或者n指令
1資料)方式存放,
cpu根據週期來判斷記憶體內容是資料還是指令,這會出現很嚴重的問題,
1.程式指令和資料的比例不是固定的。
2.又會增加
cpu
上面乙個問題就很麻煩了,姑且放在一邊不考慮,如果程式設計師無意寫錯了指令,跳轉到了乙個存放資料的記憶體或者不小心修改了指令(現在的指令都是唯讀的,這也就是為什麼每次除錯的時候要編譯整個程式)怎麼辦,那從那以後所有的程式都是壞死的(起碼是不可預料的),現在我們寫程式,錯一句就那麼一句錯了,不會影響到剩下的,如果一句錯了,剩下的都跟著亂了,那將是多麼恐怖的事情。
所以,乙個正常的人會把指令和資料分離的。馮大神果然是大神。
指令和資料混雜的壞處可謂多多,指令和資料分離有什麼好處呢?可也是謂多多。 1.
現代cpu
的cache
都是指令
cache
和資料cache
分離的,想象一下,資料和指令分離了,指令
cache
和資料cache
也分離了,那麼指令和資料的
cache
命中率一下子提高了
50%,乙個小時的程式半個小時就能執行完。這還沒算上面兩個問題的記憶體節約以及相容性問題。
2.指令是唯讀的,避免了程式設計師無意識地對指令區域記憶體的修改。避免產生了很多錯誤。
3.指令和資料分離,所以指令可以重複利用。也就是後面將要討論的靜態連線以及動態鏈結的東西。
綜上所述,計算機要指令和資料分離。我估計馮大神的**也就這麼寫的內容吧。改天到網上找來膜拜一下。
馮骨開天之後,程式猿們看到了光明。沐浴在神的思想之中,程式猿們感到了快樂。可是有些程式猿不滿足於神既有優化,於是有猿想:「資料中也有唯讀的和可修改的,我是不是可以把資料也分下段?這樣,我寫程式時如果手誤修改了唯讀的內容,那麼程式會報告錯誤。這該是多麼美妙的事情啊。」於是就有了那個經典的程式:
#include
int main(int argc ,char* argv)
好多懂一點的c
語言新手可能會以為輸出結果是
"aello"
,但是實際結果是:
linux:
core dump
吐核,我一直覺得吐核這個翻譯是個很
cute
的翻譯。其實也是段錯誤,陷入核心。終止程式,把控制權交給核心。應該是個
cpu異常中斷或者錯誤中斷。
windows 下面好像是段錯誤。既對唯讀段進行賦值操作的段錯誤。
所以,正確的寫法是:
#include
int main(int argc ,char* argv)
應該用字串陣列。字串編譯完了以後是放在資料段的唯讀區域的。而字串陣列是放在程式上下文中。既**段。(有同學可能會問,這不是資料麼。怎麼去了**段?額。這個我也很有點迷茫。)但是執行到printf("$s",str);
的時候,是進行了
n次壓棧操作,一方面儲存當前暫存器(程式上下文)以及其他要儲存的資訊,另一方面把引數(
str指標)壓棧,傳遞給
printf
函式。這個過程有個準業術語,叫呼叫慣例(引數傳遞方式-
#include
char *str = "hello";
*str = 'a';
int main(int argc ,char* argv)
則"hello"
和 str
是放在資料段裡了。
剛才說到分段,無到有是馮大神指出的光明道路,那麼有到多則是無數程式猿的智慧型總結。
自從有程式猿發現可以根據實際需求來再次分段之後,各種分段就如雨後春筍般破土而出。
比如注釋資訊段,除錯段,編譯器資訊段,目標平台段,動態連線資訊段,符號雜湊表段,等等。linux
下面有檢視可執行檔案(以及
so動態
/binutils
。windows
馬克思告訴我們,事物總是不斷發展的。各種段之後,慢慢的記憶體大了,有了記憶體管理的概念,分段的好處是記憶體可以充分利用。比如1m
的記憶體劃分為
512個段,記憶體利用更充分了。再後來慢慢的有了
os的概念,於是又有了堆(
heap
)段的概念。又有了分頁方式的記憶體管理,每次分配給程式的記憶體顆粒更小了,所以記憶體利用率得到了極大的提高。既:生產力氣得到了極大的發展。
乙個小故事
從前有乙個叫馬里的小女孩,她四歲的時候,天使飛到她家來看她,天使問她,馬里,你長大了,有什麼願望要我幫你實現嗎?馬里說,我想在我20歲生日的時候找到乙個男朋友,他叫汗斯,他要有長長的頭髮,他要會彈結他,會唱歌,我們會生4個孩子,都是女孩,她們都要去學校學跳芭蕾。馬里長大了,在她17歲的時候,她真的遇...
乙個關於博士的小故事
下面是我看到的乙個小故事,也許已經很舊了,但是很有教育意義,覺得不錯就發上來了。有乙個博士分到一家研究所,成為學歷最高的乙個人。有一天他到單位後面的小池塘去釣魚,正好正副所長在他的一左一右,也在釣魚。他只是微微點了點頭,這兩個本科生,有啥好聊的呢?不一會兒,正所長放下釣竿,伸伸懶腰,蹭蹭蹭從水面上如...
有關敬業的乙個小故事
新年伊始,筆者所在公司天濟大藥房副總就輪流著在各門店培訓員工,今天輪到我們後勤人員,講的是關於如何忠誠服務於企業 如何敬業的問題,以及現代職場新思維,當中插入了乙個故事讓筆者感觸頗深,想把大概的故事情節給記錄下來,加深影響。有兩個年輕人,咱們就暫且用a君 和 b 君替代吧。他們都是從同一家學校同一類...