程序员的自我修养学习笔记——第一章
從helloworld說(shuō)起:
#include <stdio.h>
int main()
{
printf("Hello,World\n");
return 0;
}
你能回答如下問(wèn)題嗎?
·程序?yàn)槭裁匆?jīng)過(guò)編譯才能運(yùn)行?
·編譯器把C語(yǔ)言轉(zhuǎn)化為可執(zhí)行的機(jī)器碼的過(guò)程做了什么,怎么做的
·最后編譯出來(lái)的可執(zhí)行文件里面是什么?除了機(jī)器碼之外還有什么?它們是怎么存放,怎么組織的?
·#include<stdio.h>是什么意思?把stdio.h包含進(jìn)來(lái)意味著什么?C語(yǔ)言庫(kù)又是什么?它怎么實(shí)現(xiàn)的?
·不同的編譯器(VC,GCC)和不同的硬件平臺(tái)(x86,SPARC,MIPS,ARM),以及不同的操作系統(tǒng)(Windows,Linux,Unix,Solaris),最終編譯出來(lái)的結(jié)果一樣嗎?為什么?
·Hello World程序是怎么運(yùn)行起來(lái)?操作系統(tǒng)是怎么裝載它的?它從哪兒開(kāi)始執(zhí)行,到哪兒結(jié)束?main函數(shù)之前發(fā)生了什么?main函數(shù)之后又發(fā)生了什么?
·如果沒(méi)有操作系統(tǒng),Hello World可以運(yùn)行嗎?如果要在一臺(tái)沒(méi)有操作系統(tǒng)的機(jī)器上運(yùn)行Hello World需要什么?應(yīng)該怎樣實(shí)現(xiàn)?
·printf是怎么實(shí)現(xiàn)的?它為什么可以由不定數(shù)量的參數(shù)?為什么它能夠在終端上輸出字符串
·Hello World程序運(yùn)行時(shí),它在內(nèi)存中是什么樣子的?
萬(wàn)變不離其宗:
為了協(xié)調(diào)CPU、內(nèi)存和高速的圖形設(shè)備,人們?cè)O(shè)計(jì)了一個(gè)高速的北橋芯片,以便它們之間能夠高速的交換數(shù)據(jù)。磁盤(pán)、USB、鼠標(biāo)等低速設(shè)備都連接在南橋上。將它們匯總之后連接到北橋上。
CPU的頻率被限制在了4GHz上,為了進(jìn)一步提高CPU速率,采用多核。SMP(Symmetrical Multi-Processing)——對(duì)稱多核處理器
系統(tǒng)軟件可以分成兩塊:一塊是平臺(tái)性的,如操作系統(tǒng)、內(nèi)核、驅(qū)動(dòng)程序、運(yùn)行庫(kù)等;另一塊是用于程序開(kāi)發(fā)的,如編譯器、匯編器、鏈接工具盒開(kāi)發(fā)庫(kù)。
系統(tǒng)調(diào)用的接口在實(shí)現(xiàn)中往往以軟中斷的方式提供,Linux使用0x80號(hào)中斷作為系統(tǒng)調(diào)用接口,Windows使用0x2E號(hào)中斷作為系統(tǒng)調(diào)用接口。
為了高效的利用CPU資源,采用了躲到程序技術(shù)
搶占式的操作系統(tǒng)可以強(qiáng)制剝奪CPU資源并且分配給它們認(rèn)為目前最需要的進(jìn)程。
文件系統(tǒng)保存了文件的存儲(chǔ)結(jié)構(gòu),負(fù)責(zé)維護(hù)這些數(shù)據(jù)結(jié)構(gòu)并且保證磁盤(pán)中的扇區(qū)能夠有效地組織和利用。
內(nèi)存不夠怎么辦:
早期的操作系統(tǒng),程序運(yùn)行時(shí)訪問(wèn)的地址都是物理地址。
內(nèi)存線性分配的缺點(diǎn):
地址空間不隔離,惡意程序很容易改寫(xiě)其他程序的內(nèi)存數(shù)據(jù)
內(nèi)存使用效率低,大量的數(shù)據(jù)換入換出,導(dǎo)致效率十分低下
程序運(yùn)行地址的不確定,有時(shí)候地址需要重定位
解決上面這些問(wèn)題,可以通過(guò)增加一個(gè)中間層來(lái)實(shí)現(xiàn)。我們通過(guò)把程序給出的地址看作是一種虛擬地址,然后通過(guò)某種映射機(jī)制,將這個(gè)虛擬地址轉(zhuǎn)換成實(shí)際的物理地址。
地址隔離:每個(gè)進(jìn)程都有自己獨(dú)立的虛擬地址空間,而且每個(gè)進(jìn)程只能訪問(wèn)自己的地址空間,這樣就有效地做到了進(jìn)程的隔離
分段的基本思路就是把一段與程序所需要的內(nèi)存空間大小的虛擬空間映射到某個(gè)地址空間。
通過(guò)分段解決了問(wèn)題1、3,實(shí)現(xiàn)了地址隔離,不需要對(duì)程序進(jìn)行地址重映射,因?yàn)樵诔绦騿T操作的是虛擬地址。
根據(jù)程序的局部性原理,當(dāng)一個(gè)程序在運(yùn)行時(shí),在某個(gè)時(shí)間段內(nèi),它只是頻繁地用到了一小部分?jǐn)?shù)據(jù),很多數(shù)據(jù)在一個(gè)時(shí)間段內(nèi)都是不會(huì)被用到的。我們可以使用更小的內(nèi)存 分割和映射粒度,這種方法就分頁(yè),達(dá)到提高了內(nèi)存的使用率
虛擬存儲(chǔ)的實(shí)現(xiàn)需要依靠硬件的支持,通過(guò)一個(gè)叫MMU的部件來(lái)進(jìn)行頁(yè)映射。
虛擬地址到物理地址的轉(zhuǎn)換過(guò)程:
線程,有時(shí)被稱為輕量級(jí)進(jìn)程(Lightweight Process,LWP),是程序執(zhí)行流的最小單元。一個(gè)標(biāo)準(zhǔn)的線程由線程ID,當(dāng)前指令指針(PC),寄存器集合和堆棧組成。另外,線程是進(jìn)程中的一個(gè)實(shí)體,是被系統(tǒng)獨(dú)立調(diào)度和分派的基本單位,線程自己不擁有系統(tǒng)資源,只擁有一點(diǎn)在運(yùn)行中必不可少的資源,但它可與同屬一個(gè)進(jìn)程的其它線程共享進(jìn)程所擁有的全部資源。一個(gè)線程可以創(chuàng)建和撤消另一個(gè)線程,同一進(jìn)程中的多個(gè)線程之間可以并發(fā)執(zhí)行。由于線程之間的相互制約,致使線程在運(yùn)行中呈現(xiàn)出間斷性。線程也有就緒、阻塞和運(yùn)行三種基本狀態(tài)。每一個(gè)程序都至少有一個(gè)線程,那就是程序本身。
引入線程的好處:
1 創(chuàng)建一個(gè)新線程花費(fèi)的時(shí)間少。
2 兩個(gè)線程(在同一進(jìn)程中的)的切換時(shí)間少。
3 由于同一個(gè)進(jìn)程內(nèi)的線程共享內(nèi)存和文件,所以線程之間互相通信不必調(diào)用內(nèi)核。
4 線程能獨(dú)立執(zhí)行,能充分利用和發(fā)揮處理機(jī)與外圍設(shè)備并行工作的能力。
線程的訪問(wèn)權(quán)限:
線程的優(yōu)先級(jí)改變一般有三種方式:
·用戶指定優(yōu)先級(jí)
·根據(jù)進(jìn)入等待狀態(tài)的頻繁程度提升或降低優(yōu)先級(jí)
·長(zhǎng)時(shí)間得不到執(zhí)行而被提升優(yōu)先級(jí)
線程在用盡時(shí)間片之后會(huì)被強(qiáng)制剝奪繼續(xù)執(zhí)行的權(quán)利,進(jìn)入到就緒狀態(tài),這個(gè)過(guò)程就叫做"搶占"(Preemption)
Linux下的多線程:
Linux將所有的執(zhí)行實(shí)體(無(wú)論進(jìn)程還是線程)都稱為任務(wù),每個(gè)任務(wù)概念上都類似于一個(gè)單線程實(shí)體,都成為任務(wù)(Task)
同步:是指在一個(gè)線程訪問(wèn)數(shù)據(jù)未結(jié)束的時(shí)候,其他線程不得對(duì)同一個(gè)數(shù)據(jù)進(jìn)行訪問(wèn)。
同步最常用的方法是“鎖”。
二元信號(hào)量是最簡(jiǎn)單的鎖,只有兩種狀態(tài):占用、與非占用
多元信號(hào)量,簡(jiǎn)稱為信號(hào)量。一個(gè)初值為N的信號(hào)量允許N個(gè)線程并發(fā)訪問(wèn)。
互斥信號(hào)量:和二元信號(hào)量類似,
區(qū)別:一般的信號(hào)量可以被系統(tǒng)中的一個(gè)線程獲取之后由另一個(gè)線程釋放,而互斥信號(hào)量則要求哪個(gè)線程獲取了互斥信號(hào)量,哪個(gè)線程就要負(fù)責(zé)釋放掉這個(gè)鎖。
臨界區(qū):是一種比互斥信號(hào)量更嚴(yán)格的同步手段,獲取臨界區(qū)的鎖稱為進(jìn)入臨界區(qū),釋放稱為離開(kāi)臨界區(qū)。
區(qū)別:臨界區(qū)和互斥信號(hào)量的區(qū)別在于互斥信號(hào)量在系統(tǒng)的任何進(jìn)程里面都是可見(jiàn)的,也就是說(shuō),一個(gè)進(jìn)程創(chuàng)建了一個(gè)互斥量或信號(hào)量,另一個(gè)進(jìn)程試圖去獲取該所是合法的。然而,臨界區(qū)的作用范圍僅限于本進(jìn)程,其他進(jìn)程無(wú)法獲取該鎖。
讀寫(xiě)鎖
條件變量
條件變量與互斥鎖,信號(hào)量的區(qū)別
1.互斥鎖必須總是由給它上鎖的線程解鎖,信號(hào)量的掛出即不必由執(zhí)行過(guò)它的等待操作的同一進(jìn)程執(zhí)行。一個(gè)線程可以等待某個(gè)給定信號(hào)燈,而另一個(gè)線程可以掛出該信號(hào)燈。
2.互斥鎖要么鎖住,要么被解開(kāi)(二值狀態(tài),類型二值信號(hào)量)。
3.由于信號(hào)量有一個(gè)與之關(guān)聯(lián)的狀態(tài)(它的計(jì)數(shù)值),信號(hào)量掛出操作總是被記住。然而當(dāng)向一個(gè)條件變量發(fā)送信號(hào)時(shí),如果沒(méi)有線程等待在該條件變量上,那么該信號(hào)將丟失。
4.互斥鎖是為了上鎖而設(shè)計(jì)的,條件變量是為了等待而設(shè)計(jì)的,信號(hào)燈即可用于上鎖,也可用于等待,因而可能導(dǎo)致更多的開(kāi)銷和更高的復(fù)雜性。
一個(gè)函數(shù)要可重入必須具備以下特點(diǎn):
·不使用任何(局部)靜態(tài)或全局的非const變量
·不返回任何(局部)靜態(tài)或全局的非const變量的指針
·僅依賴于調(diào)用方提供的參數(shù)
·不依賴任何單個(gè)資源的鎖(mutex等)
·不調(diào)用任何不可重入的函數(shù)
上鎖不一定能實(shí)現(xiàn)線程安全,因?yàn)榫幾g器可能做出過(guò)度的優(yōu)化。
編譯器為了提高x的訪問(wèn)速度,把x放到了某個(gè)寄存器里面,那么我們知道不同線程的寄存器是各自獨(dú)立的。因此可能上鎖也不管用。
可以使用volatile關(guān)鍵字防止編譯器過(guò)度優(yōu)化:
·阻止編譯器為了提高速度將一個(gè)變量緩存到寄存器內(nèi)而不寫(xiě)回
·阻止編譯器調(diào)整操作volatile變量的指令順序
以下部分出自:
源文檔 <http://hi.baidu.com/yibobin/blog/item/e1b13cfa1a335bd1b58f317e.html>
c++new的兩個(gè)步驟:分配內(nèi)存;調(diào)用構(gòu)造函數(shù)
// Singleton《設(shè)計(jì)模式:可復(fù)用面向?qū)ο筌浖A(chǔ)》
volatile T* pInst=0;
T* GetInstance()
{
// 雙層if讓lock的開(kāi)銷降低到最小
if (pInst==NULL)
{
lock();
if(pInst==NULL)
/*
1.分配內(nèi)存
2.在內(nèi)存的位置上調(diào)用構(gòu)造函數(shù)
3.將內(nèi)存的地址賦值給pInst
*/
pInst=new T;
/*
上面第2,3步順序可以顛倒,也就是說(shuō)
pInst的值已經(jīng)不是NULL,但對(duì)象仍然沒(méi)有構(gòu)造完畢,這時(shí)另一個(gè)GetInstance的并發(fā)調(diào)用此時(shí)第一個(gè)if里為false
這時(shí)就會(huì)范圍尚未構(gòu)造完全的對(duì)象地址pInst以提供給用戶使用
*/
unlock();
}
return pInst;
}
/* 阻止換序:可以加barrier指令阻止CPU將該指令之前的指令交換到barrier之后。
T* tenp=new T;
barrier();
pInst=tenp;
*/
make it simple, make it happen
總結(jié)
以上是生活随笔為你收集整理的程序员的自我修养学习笔记——第一章的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: windows常用快捷键
- 下一篇: loadrunner--设置代理录制