亚洲va中文字幕无码,亚洲AV女人18毛片水真多,国产性爱在线观看亚洲黄色一级片 http://www.tkk7.com/tinysun/category/37798.htmlzh-cnThu, 23 Aug 2012 06:43:12 GMTThu, 23 Aug 2012 06:43:12 GMT60svn修訂版和最后修改的修訂版[轉(zhuǎn)]http://www.tkk7.com/tinysun/archive/2012/08/23/386100.html何克勤何克勤Thu, 23 Aug 2012 05:13:00 GMThttp://www.tkk7.com/tinysun/archive/2012/08/23/386100.htmlhttp://www.tkk7.com/tinysun/comments/386100.htmlhttp://www.tkk7.com/tinysun/archive/2012/08/23/386100.html#Feedback0http://www.tkk7.com/tinysun/comments/commentRss/386100.htmlhttp://www.tkk7.com/tinysun/services/trackbacks/386100.html

花了點(diǎn)時(shí)間把svnbook看了遍,對(duì)于svn有了個(gè)比較好的認(rèn)識(shí)。svn info時(shí),修訂版和最后修改的修訂版總是讓我感覺(jué)很困惑。要搞明白這個(gè)需要對(duì)下面幾個(gè)關(guān)鍵字有所了解。

HEAD:版本庫(kù)中的最新版本。

COMMITED:文件最后提交生成的版本號(hào)。

PREV:文件倒數(shù)第二次提交生成的版本號(hào)。

BASE:目錄簽出或者簽入生成的版本號(hào)。

HEAD、COMMITED和PREV比較好理解,BASE比較難于理解。假設(shè)一個(gè)目錄下有兩個(gè)文件configure.ac和Makefile.am,第一次將它們check out出來(lái)時(shí),會(huì)生成一個(gè)新的revision,這個(gè)便是BASE了。此時(shí)使用svn info configure.ac/Makefile.am可以發(fā)現(xiàn)它們的修訂版是一樣的,但是最后修改的修訂版不同。這里的修訂版對(duì)應(yīng)其實(shí)就是BASE,而最后修改的修訂版則是COMMITED。插一句,很多人很容易誤解為啥修訂版號(hào)和最后修改的修訂版號(hào)不一致。

若將configure.ac修改并check in,這個(gè)時(shí)候會(huì)生成一個(gè)新的revision,configure.ac的BASE和COMMITED的值相當(dāng)。而svn info Makefile.am,發(fā)現(xiàn)它的BASE和COMMITED沒(méi)有改變。svn up一下,發(fā)現(xiàn)Makefile.am的BASE會(huì)變成最新的,和configure.ac相同。

簽出代碼庫(kù)。

1[henshao@henshao ~/svn]$ svn co file:///Users/henshao/svn/dogg/learn_svn/ learn_svn2
2A    learn_svn2/trunk
3A    learn_svn2/trunk/configure.ac
4A    learn_svn2/trunk/Makefile.am
5Checked out revision 17.

顯示修訂版(BASE)和最后修改的修訂版(COMMITED)。

01[henshao@henshao ~/svn/learn_svn2/trunk]$ svn st -v
02                17       17 henshao      .
03                17       17 henshao      configure.ac
04                17       15 henshao      Makefile.am
05  
06[henshao@henshao ~/svn/learn_svn2/trunk]$ svn info Makefile.am
07Path: Makefile.am
08Name: Makefile.am
09URL: file:///Users/henshao/svn/dogg/learn_svn/trunk/Makefile.am
10Repository Root: file:///Users/henshao/svn/dogg
11Repository UUID: 7ee338c4-a6e3-468b-b576-d1b767dd90e2
12Revision: 17
13Node Kind: file
14Schedule: normal
15Last Changed Author: henshao
16Last Changed Rev: 15
17Last Changed Date: 2011-06-23 17:03:08 +0800 (四, 23  6 2011)
18Text Last Updated: 2011-06-23 18:37:50 +0800 (四, 23  6 2011)
19Checksum: 5b211a202b8ae001a86a557108d4989c

修改Makefile.am并簽入看看。

01[henshao@henshao ~/svn/learn_svn2/trunk]$ svn ci Makefile.am -m "LD_ADD add ssl library"
02Sending        Makefile.am
03Transmitting file data .
04Committed revision 18.
05  
06[henshao@henshao ~/svn/learn_svn2/trunk]$ svn info Makefile.am
07Path: Makefile.am
08Name: Makefile.am
09URL: file:///Users/henshao/svn/dogg/learn_svn/trunk/Makefile.am
10Repository Root: file:///Users/henshao/svn/dogg
11Repository UUID: 7ee338c4-a6e3-468b-b576-d1b767dd90e2
12Revision: 18
13Node Kind: file
14Schedule: normal
15Last Changed Author: henshao
16Last Changed Rev: 18
17Last Changed Date: 2011-06-23 18:41:41 +0800 (四, 23  6 2011)
18Text Last Updated: 2011-06-23 18:41:31 +0800 (四, 23  6 2011)
19Checksum: e4cc7bf424ff911c9619060a5f1c1030
20  
21[henshao@henshao ~/svn/learn_svn2/trunk]$ svn info configure.ac
22Path: configure.ac
23Name: configure.ac
24URL: file:///Users/henshao/svn/dogg/learn_svn/trunk/configure.ac
25Repository Root: file:///Users/henshao/svn/dogg
26Repository UUID: 7ee338c4-a6e3-468b-b576-d1b767dd90e2
27Revision: 17
28Node Kind: file
29Schedule: normal
30Last Changed Author: henshao
31Last Changed Rev: 17
32Last Changed Date: 2011-06-23 17:44:51 +0800 (四, 23  6 2011)
33Text Last Updated: 2011-06-23 18:37:50 +0800 (四, 23  6 2011)
34Checksum: 6b49ae8f3346120311e11843c23b0b00

svn update一下看看。

01[henshao@henshao ~/svn/learn_svn2/trunk]$ svn up
02At revision 18.
03  
04[henshao@henshao ~/svn/learn_svn2/trunk]$ svn info configure.ac
05Path: configure.ac
06Name: configure.ac
07URL: file:///Users/henshao/svn/dogg/learn_svn/trunk/configure.ac
08Repository Root: file:///Users/henshao/svn/dogg
09Repository UUID: 7ee338c4-a6e3-468b-b576-d1b767dd90e2
10Revision: 18
11Node Kind: file
12Schedule: normal
13Last Changed Author: henshao
14Last Changed Rev: 17
15Last Changed Date: 2011-06-23 17:44:51 +0800 (四, 23  6 2011)
16Text Last Updated: 2011-06-23 18:37:50 +0800 (四, 23  6 2011)
17Checksum: 6b49ae8f3346120311e11843c23b0b00
18  
19[henshao@henshao ~/svn/learn_svn2/trunk]$ svn st -v
20                18       18 henshao      .
21                18       17 henshao      configure.ac
22                18       18 henshao      Makefile.am

svn一個(gè)版本庫(kù)的revision是全局的,不管是在trunk還是branch,也不管使用merge合并代碼還是消除修改,簽入和簽出都會(huì)生成一個(gè)新的revision。當(dāng)項(xiàng)目中一個(gè)文件簽入時(shí)會(huì)導(dǎo)致別的文件的BASE暫時(shí)低于HEAD,但是一旦update,二者將保持一致。



]]>
linux分布式編譯distcc和ccache的部署 http://www.tkk7.com/tinysun/archive/2012/02/24/370682.html何克勤何克勤Fri, 24 Feb 2012 06:49:00 GMThttp://www.tkk7.com/tinysun/archive/2012/02/24/370682.htmlhttp://www.tkk7.com/tinysun/comments/370682.htmlhttp://www.tkk7.com/tinysun/archive/2012/02/24/370682.html#Feedback0http://www.tkk7.com/tinysun/comments/commentRss/370682.htmlhttp://www.tkk7.com/tinysun/services/trackbacks/370682.html

unset LANGUAGE
export LANG="en"
cd /home/kingsoft
mkdir distcc
cd distcc

 

 

rpm包用:rpm -ivh ...
bz2包用:tar -xvf ...
進(jìn)入distcc解壓后的目錄
./configure && make && make install
mkdir /usr/lib/distcc
mkdir /usr/lib/distcc/bin
cd /usr/lib/distcc/bin
ln -s /usr/local/bin/distcc gcc
ln -s /usr/local/bin/distcc cc
ln -s /usr/local/bin/distcc g++
ln -s /usr/local/bin/distcc c++

 


進(jìn)入ccache解壓目錄
./configure && make && make install

mkdir /Data
mkdir /Data/Cache
mkdir /Data/Cache/CCache
cd /Data/Cache
touch /var/log/distccd.log

vim ~/.bash_profile
把 /usr/lib/distcc/bin 加到PATH
并添加下面內(nèi)容
## ----- Distcc -----
#
DISTCC_HOSTS="localhost 192.168.1.1"
DISTCC_VERBOSE=1
DISTCC_LOG="/var/log/distcc.log"
export DISTCC_HOSTS PATH DISTCC_VERBOSE DISTCC_LOG
#
## ----- End -----

## ----- Ccache -----
#
# export CCACHE_DISABLE=1
CCACHE_DIR=/Data/Cache/CCache
CCACHE_LOGFILE=/Data/Cache/CCache.log
CCACHE_PREFIX="distcc"
CC="ccache gcc"
CXX="ccache g++"
export CCACHE_DIR CCACHE_LOGFILE CCACHE_PREFIX CC CXX
#
## ----- End -----


vim /etc/rc.local
distccd --daemon --allow 10.20.0.0/16


==========================================
啟動(dòng)監(jiān)控:distccd --daemon --allow 10.20.0.0/16
查看監(jiān)控:distccmon-text 1



]]>
網(wǎng)絡(luò)編程之nagle算法和TCP_NODELAY http://www.tkk7.com/tinysun/archive/2011/05/20/350659.html何克勤何克勤Fri, 20 May 2011 01:27:00 GMThttp://www.tkk7.com/tinysun/archive/2011/05/20/350659.htmlhttp://www.tkk7.com/tinysun/comments/350659.htmlhttp://www.tkk7.com/tinysun/archive/2011/05/20/350659.html#Feedback0http://www.tkk7.com/tinysun/comments/commentRss/350659.htmlhttp://www.tkk7.com/tinysun/services/trackbacks/350659.htmlTCP/IP協(xié)議中,無(wú)論發(fā)送多少數(shù)據(jù),總是要在數(shù)據(jù)前面加上協(xié)議頭,同時(shí),對(duì)方接收到數(shù)據(jù),也需要發(fā)送ACK表示確認(rèn)。為了盡可能的利用網(wǎng)絡(luò)帶寬,TCP總是希望盡可能的發(fā)送足夠大的數(shù)據(jù)。(一個(gè)連接會(huì)設(shè)置MSS參數(shù),因此,TCP/IP希望每次都能夠以MSS尺寸的數(shù)據(jù)塊來(lái)發(fā)送數(shù)據(jù))。
Nagle算法就是為了盡可能發(fā)送大塊數(shù)據(jù),避免網(wǎng)絡(luò)中充斥著許多小數(shù)據(jù)塊。

Nagle算法的基本定義是任意時(shí)刻,最多只能有一個(gè)未被確認(rèn)的小段。 所謂“小段”,指的是小于MSS尺寸的數(shù)據(jù)塊,所謂“未被確認(rèn)”,是指一個(gè)數(shù)據(jù)塊發(fā)送出去后,沒(méi)有收到對(duì)方發(fā)送的ACK確認(rèn)該數(shù)據(jù)已收到。

舉個(gè)例子,比如之前的blog中的實(shí)驗(yàn),一開(kāi)始client端調(diào)用socket的write操作將一個(gè)int型數(shù)據(jù)(稱為A塊)寫(xiě)入到網(wǎng)絡(luò)中,由于此時(shí)連接是空閑的(也就是說(shuō)還沒(méi)有未被確認(rèn)的小段),因此這個(gè)int型數(shù)據(jù)會(huì)被馬上發(fā)送到server端,接著,client端又調(diào)用write操作寫(xiě)入‘\r\n’(簡(jiǎn)稱B塊),這個(gè)時(shí)候,A塊的ACK沒(méi)有返回,所以可以認(rèn)為已經(jīng)存在了一個(gè)未被確認(rèn)的小段,所以B塊沒(méi)有立即被發(fā)送,一直等待A塊的ACK收到(大概40ms之后),B塊才被發(fā)送。整個(gè)過(guò)程如圖所示:

 

這里還隱藏了一個(gè)問(wèn)題,就是A塊數(shù)據(jù)的ACK為什么40ms之后才收到?這是因?yàn)門(mén)CP/IP中不僅僅有nagle算法,還有一個(gè)ACK延遲機(jī)制 。當(dāng)Server端收到數(shù)據(jù)之后,它并不會(huì)馬上向client端發(fā)送ACK,而是會(huì)將ACK的發(fā)送延遲一段時(shí)間(假設(shè)為t),它希望在t時(shí)間內(nèi)server端會(huì)向client端發(fā)送應(yīng)答數(shù)據(jù),這樣ACK就能夠和應(yīng)答數(shù)據(jù)一起發(fā)送,就像是應(yīng)答數(shù)據(jù)捎帶著ACK過(guò)去。在我之前的時(shí)間中,t大概就是40ms。這就解釋了為什么'\r\n'(B塊)總是在A塊之后40ms才發(fā)出。

如果你覺(jué)著nagle算法太搗亂了,那么可以通過(guò)設(shè)置TCP_NODELAY將其禁用 。當(dāng)然,更合理的方案還是應(yīng)該使用一次大數(shù)據(jù)的寫(xiě)操作,而不是多次小數(shù)據(jù)的寫(xiě)操作。


本文來(lái)自CSDN博客,轉(zhuǎn)載請(qǐng)標(biāo)明出處:http://blog.csdn.net/historyasamirror/archive/2011/05/15/6423235.aspx



]]>
理解可執(zhí)行程序的各種神器 http://www.tkk7.com/tinysun/archive/2011/01/19/343184.html何克勤何克勤Tue, 18 Jan 2011 16:05:00 GMThttp://www.tkk7.com/tinysun/archive/2011/01/19/343184.htmlhttp://www.tkk7.com/tinysun/comments/343184.htmlhttp://www.tkk7.com/tinysun/archive/2011/01/19/343184.html#Feedback0http://www.tkk7.com/tinysun/comments/commentRss/343184.htmlhttp://www.tkk7.com/tinysun/services/trackbacks/343184.html

ldd查看應(yīng)用程序鏈接了哪些動(dòng)態(tài)庫(kù)。

nm列出目標(biāo)文件中包含的符號(hào)信息。

size列出各個(gè)段的大小及總的大小。

strings列出文件中的字符串。

readelf讀取elf文件的完整結(jié)構(gòu)。

objdump導(dǎo)出目標(biāo)文件的相關(guān)信息(elf文件相關(guān)工具的源頭)。

gdb對(duì)文件的執(zhí)行過(guò)程進(jìn)行調(diào)試分析,設(shè)置斷點(diǎn)(b)、單步執(zhí)行(n)、函數(shù)調(diào)用追蹤(bt)、反匯編(disassemble)。

strace跟蹤程序中的系統(tǒng)調(diào)用及信號(hào)處理信息。

LD_DEBUG通過(guò)設(shè)置這個(gè)環(huán)境變量,可以方便的看到 loader 的加載過(guò)程(包括庫(kù)的加載,符號(hào)解析等過(guò)程),使用【LD_DEBUG=help 可執(zhí)行文件路徑】可查看使用幫助。

LD_PRELOAD環(huán)境變量指定的共享庫(kù)會(huì)被預(yù)先加載,如果出現(xiàn)重名的函數(shù),預(yù)先加載的函數(shù)將會(huì)被調(diào)用,如在預(yù)先加載的庫(kù)中包含自定義的puts函數(shù),則在執(zhí)行程序時(shí)將使用自定義版本的puts函數(shù),而不是libc庫(kù)中的puts函數(shù)。

proc文件系統(tǒng)中包含進(jìn)程的地址空間映射關(guān)系,具體查看/proc/進(jìn)程id/maps文件的內(nèi)容。

valgrind工具對(duì)可執(zhí)行程序文件進(jìn)行內(nèi)存檢查(還有cache模擬、調(diào)用過(guò)程跟蹤等功能),以避免內(nèi)存泄露等問(wèn)題。

addrline將可執(zhí)行文件中的地址轉(zhuǎn)換為其在源文件中對(duì)應(yīng)的位置(文件名:行號(hào))。



]]>
進(jìn)程上下文和中斷上下文http://www.tkk7.com/tinysun/archive/2010/11/07/337492.html何克勤何克勤Sun, 07 Nov 2010 15:59:00 GMThttp://www.tkk7.com/tinysun/archive/2010/11/07/337492.htmlhttp://www.tkk7.com/tinysun/comments/337492.htmlhttp://www.tkk7.com/tinysun/archive/2010/11/07/337492.html#Feedback0http://www.tkk7.com/tinysun/comments/commentRss/337492.htmlhttp://www.tkk7.com/tinysun/services/trackbacks/337492.html進(jìn)程上下文和中斷上下文是操作系統(tǒng)中很重要的兩個(gè)概念,這兩個(gè)概念在操作系統(tǒng)課程中不斷被提及,是最經(jīng)常接觸、看上去很懂但又說(shuō)不清楚到底怎么回事。造成這種局面的原因,可能是原來(lái)接觸到的操作系統(tǒng)課程的教學(xué)總停留在一種淺層次的理論層面上,沒(méi)有深入去研究。

處理器總處于以下?tīng)顟B(tài)中的一種:
1、內(nèi)核態(tài),運(yùn)行于進(jìn)程上下文,內(nèi)核代表進(jìn)程運(yùn)行于內(nèi)核空間;
2、內(nèi)核態(tài),運(yùn)行于中斷上下文,內(nèi)核代表硬件運(yùn)行于內(nèi)核空間;
3、用戶態(tài),運(yùn)行于用戶空間。

用戶空間的應(yīng)用程序,通過(guò)系統(tǒng)調(diào)用,進(jìn)入內(nèi)核空間。這個(gè)時(shí)候用戶空間的進(jìn)程要傳遞很多變量、參數(shù)的值給內(nèi)核,內(nèi)核態(tài)運(yùn)行的時(shí)候也要保存用戶進(jìn)程的一些寄存器值、變量等。所謂的“進(jìn)程上下文”,可以看作是用戶進(jìn)程傳遞給內(nèi)核的這些參數(shù)以及內(nèi)核要保存的那一整套的變量和寄存器值和當(dāng)時(shí)的環(huán)境等。

硬件通過(guò)觸發(fā)信號(hào),導(dǎo)致內(nèi)核調(diào)用中斷處理程序,進(jìn)入內(nèi)核空間。這個(gè)過(guò)程中,硬件的一些變量和參數(shù)也要傳遞給內(nèi)核,內(nèi)核通過(guò)這些參數(shù)進(jìn)行中斷處理。所謂的“中斷上下文”,其實(shí)也可以看作就是硬件傳遞過(guò)來(lái)的這些參數(shù)和內(nèi)核需要保存的一些其他環(huán)境(主要是當(dāng)前被打斷執(zhí)行的進(jìn)程環(huán)境)。


關(guān)于進(jìn)程上下文LINUX完全注釋中的一段話:

   當(dāng)一個(gè)進(jìn)程在執(zhí)行時(shí),CPU的所有寄存器中的值、進(jìn)程的狀態(tài)以及堆棧中的內(nèi)容被稱為該進(jìn)程的上下文。當(dāng)內(nèi)核需要切換到另一個(gè)進(jìn)程時(shí),它需要保存當(dāng)前進(jìn)程的所有狀態(tài),即保存當(dāng)前進(jìn)程的上下文,以便在再次執(zhí)行該進(jìn)程時(shí),能夠必得到切換時(shí)的狀態(tài)執(zhí)行下去。在LINUX中,當(dāng)前進(jìn)程上下文均保存在進(jìn)程的任務(wù)數(shù)據(jù)結(jié)構(gòu)中。在發(fā)生中斷時(shí),內(nèi)核就在被中斷進(jìn)程的上下文中,在內(nèi)核態(tài)下執(zhí)行中斷服務(wù)例程。但同時(shí)會(huì)保留所有需要用到的資源,以便中繼服務(wù)結(jié)束時(shí)能恢復(fù)被中斷進(jìn)程的執(zhí)行。


本文來(lái)自CSDN博客,轉(zhuǎn)載請(qǐng)標(biāo)明出處:http://blog.csdn.net/eroswang/archive/2007/11/28/1905830.aspx



]]>
Socket send函數(shù)和recv函數(shù)詳解http://www.tkk7.com/tinysun/archive/2010/10/20/335699.html何克勤何克勤Wed, 20 Oct 2010 08:21:00 GMThttp://www.tkk7.com/tinysun/archive/2010/10/20/335699.htmlhttp://www.tkk7.com/tinysun/comments/335699.htmlhttp://www.tkk7.com/tinysun/archive/2010/10/20/335699.html#Feedback0http://www.tkk7.com/tinysun/comments/commentRss/335699.htmlhttp://www.tkk7.com/tinysun/services/trackbacks/335699.html

int send( SOCKET s,      const char FAR *buf,      int len,      int flags );  

不論是客戶還是服務(wù)器應(yīng)用程序都用send函數(shù)來(lái)向TCP連接的另一端發(fā)送數(shù)據(jù)。

客戶程序一般用send函數(shù)向服務(wù)器發(fā)送請(qǐng)求,而服務(wù)器則通常用send函數(shù)來(lái)向客戶程序發(fā)送應(yīng)答。

該函數(shù)的第一個(gè)參數(shù)指定發(fā)送端套接字描述符;

第二個(gè)參數(shù)指明一個(gè)存放應(yīng)用程序要發(fā)送數(shù)據(jù)的緩沖區(qū);

第三個(gè)參數(shù)指明實(shí)際要發(fā)送的數(shù)據(jù)的字節(jié)數(shù);

第四個(gè)參數(shù)一般置0。

這里只描述同步Socket的send函數(shù)的執(zhí)行流程。當(dāng)調(diào)用該函數(shù)時(shí),send先比較待發(fā)送數(shù)據(jù)的長(zhǎng)度len和套接字s的發(fā)送緩沖的 長(zhǎng)度,如果len大于s的發(fā)送緩沖區(qū)的長(zhǎng)度,該函數(shù)返回SOCKET_ERROR;如果len小于或者等于s的發(fā)送緩沖區(qū)的長(zhǎng)度,那么send先檢查協(xié)議是否正在發(fā)送s的發(fā)送緩沖中的數(shù)據(jù),如果是就等待協(xié)議把數(shù)據(jù)發(fā)送完,如果協(xié)議還沒(méi)有開(kāi)始發(fā)送s的發(fā)送緩沖中的數(shù)據(jù)或者s的發(fā)送緩沖中沒(méi)有數(shù)據(jù),那么 send就比較s的發(fā)送緩沖區(qū)的剩余空間和len,如果len大于剩余空間大小send就一直等待協(xié)議把s的發(fā)送緩沖中的數(shù)據(jù)發(fā)送完,如果len小于剩余空間大小send就僅僅把buf中的數(shù)據(jù)copy到剩余空間里(注意并不是send把s的發(fā)送緩沖中的數(shù)據(jù)傳到連接的另一端的,而是協(xié)議傳的,send僅僅是把buf中的數(shù)據(jù)copy到s的發(fā)送緩沖區(qū)的剩余空間里)。如果send函數(shù)copy數(shù)據(jù)成功,就返回實(shí)際copy的字節(jié)數(shù),如果send在copy數(shù)據(jù)時(shí)出現(xiàn)錯(cuò)誤,那么send就返回SOCKET_ERROR;如果send在等待協(xié)議傳送數(shù)據(jù)時(shí)網(wǎng)絡(luò)斷開(kāi)的話,那么send函數(shù)也返回SOCKET_ERROR。

要注意send函數(shù)把buf中的數(shù)據(jù)成功copy到s的發(fā)送緩沖的剩余空間里后它就返回了,但是此時(shí)這些數(shù)據(jù)并不一定馬上被傳到連接的另一端如果協(xié)議在后續(xù)的傳送過(guò)程中出現(xiàn)網(wǎng)絡(luò)錯(cuò)誤的話,那么下一個(gè)Socket函數(shù)就會(huì)返回SOCKET_ERROR。(每一個(gè)除send外的Socket函數(shù)在執(zhí)行的最開(kāi)始總要先等待套接字的發(fā)送緩沖中的數(shù)據(jù)被協(xié)議傳送完畢才能繼續(xù),如果在等待時(shí)出現(xiàn)網(wǎng)絡(luò)錯(cuò)誤,那么該Socket函數(shù)就返回 SOCKET_ERROR)

注意:在Unix系統(tǒng)下,如果send在等待協(xié)議傳送數(shù)據(jù)時(shí)網(wǎng)絡(luò)斷開(kāi)的話,調(diào)用send的進(jìn)程會(huì)接收到一個(gè)SIGPIPE信號(hào),進(jìn)程對(duì)該信號(hào)的默認(rèn)處理是進(jìn)程終止。

通過(guò)測(cè)試發(fā)現(xiàn),異步socket的send函數(shù)在網(wǎng)絡(luò)剛剛斷開(kāi)時(shí)還能發(fā)送返回相應(yīng)的字節(jié)數(shù),同時(shí)使用select檢測(cè)也是可寫(xiě)的,但是過(guò)幾秒鐘之后,再send就會(huì)出錯(cuò)了,返回-1。select也不能檢測(cè)出可寫(xiě)了。

recv函數(shù)

int recv( SOCKET s,     char FAR *buf,      int len,     int flags     );   

不論是客戶還是服務(wù)器應(yīng)用程序都用recv函數(shù)從TCP連接的另一端接收數(shù)據(jù)。

該函數(shù)的第一個(gè)參數(shù)指定接收端套接字描述符;

第二個(gè)參數(shù)指明一個(gè)緩沖區(qū),該緩沖區(qū)用來(lái)存放recv函數(shù)接收到的數(shù)據(jù);

第三個(gè)參數(shù)指明buf的長(zhǎng)度;

第四個(gè)參數(shù)一般置0。

這里只描述同步Socket的recv函數(shù)的執(zhí)行流程。當(dāng)應(yīng)用程序調(diào)用recv函數(shù)時(shí),recv先等待s的發(fā)送緩沖中的數(shù)據(jù)被協(xié)議傳送完畢,如果協(xié)議在傳送s的發(fā)送緩沖中的數(shù)據(jù)時(shí)出現(xiàn)網(wǎng)絡(luò)錯(cuò)誤,那么recv函數(shù)返回SOCKET_ERROR,如果s的發(fā)送緩沖中沒(méi)有數(shù)據(jù)或者數(shù)據(jù)被協(xié)議成功發(fā)送完畢后,recv先檢查套接字s的接收緩沖區(qū),如果s接收緩沖區(qū)中沒(méi)有數(shù)據(jù)或者協(xié)議正在接收數(shù)據(jù),那么recv就一直等待,只到協(xié)議把數(shù)據(jù)接收完畢。當(dāng)協(xié)議把數(shù)據(jù)接收完畢,recv函數(shù)就把s的接收緩沖中的數(shù)據(jù)copy到buf中(注意協(xié)議接收到的數(shù)據(jù)可能大于buf的長(zhǎng)度,所以 在這種情況下要調(diào)用幾次recv函數(shù)才能把s的接收緩沖中的數(shù)據(jù)copy完。recv函數(shù)僅僅是copy數(shù)據(jù),真正的接收數(shù)據(jù)是協(xié)議來(lái)完成的),recv函數(shù)返回其實(shí)際copy的字節(jié)數(shù)。如果recv在copy時(shí)出錯(cuò),那么它返回SOCKET_ERROR;如果recv函數(shù)在等待協(xié)議接收數(shù)據(jù)時(shí)網(wǎng)絡(luò)中斷了,那么它返回0。

注意:在Unix系統(tǒng)下,如果recv函數(shù)在等待協(xié)議接收數(shù)據(jù)時(shí)網(wǎng)絡(luò)斷開(kāi)了,那么調(diào)用recv的進(jìn)程會(huì)接收到一個(gè)SIGPIPE信號(hào),進(jìn)程對(duì)該信號(hào)的默認(rèn)處理是進(jìn)程終止。



]]>
。。。。http://www.tkk7.com/tinysun/archive/2010/10/11/334455.html何克勤何克勤Mon, 11 Oct 2010 09:20:00 GMThttp://www.tkk7.com/tinysun/archive/2010/10/11/334455.htmlhttp://www.tkk7.com/tinysun/comments/334455.htmlhttp://www.tkk7.com/tinysun/archive/2010/10/11/334455.html#Feedback0http://www.tkk7.com/tinysun/comments/commentRss/334455.htmlhttp://www.tkk7.com/tinysun/services/trackbacks/334455.html http://blog.csdn.net/eroswang/category/322381.aspx?PageNumber=2

]]>
信號(hào)“未決”與“阻塞”http://www.tkk7.com/tinysun/archive/2010/10/11/334368.html何克勤何克勤Mon, 11 Oct 2010 07:47:00 GMThttp://www.tkk7.com/tinysun/archive/2010/10/11/334368.htmlhttp://www.tkk7.com/tinysun/comments/334368.htmlhttp://www.tkk7.com/tinysun/archive/2010/10/11/334368.html#Feedback0http://www.tkk7.com/tinysun/comments/commentRss/334368.htmlhttp://www.tkk7.com/tinysun/services/trackbacks/334368.html信號(hào)狀態(tài):
    信號(hào)的”未決“是一種狀態(tài),指的是從信號(hào)的產(chǎn)生到信號(hào)被處理前的這一段時(shí)間;信號(hào)的”阻塞“是一個(gè)開(kāi)關(guān)動(dòng)作,指的是阻止信號(hào)被處理,但不是阻止信號(hào)產(chǎn)生。
    APUE例題在sleep前用sigprocmask阻塞了退出信號(hào),然后sleep,然后在sleep的過(guò)程中產(chǎn)生一個(gè)退出信號(hào),但是此時(shí)退出信號(hào)被阻 塞過(guò),(中文的”阻塞”在這里容易被誤解為一種狀態(tài),實(shí)際上是一種類似于開(kāi)關(guān)的動(dòng)作,所以說(shuō)“被阻塞過(guò)”,而不是“被阻塞”)所以處于“未決”狀態(tài),在 sleep后又用sigprocmask關(guān)掉退出信號(hào)的阻塞開(kāi)關(guān),因?yàn)橹爱a(chǎn)生的退出信號(hào)一直處于未決狀態(tài),當(dāng)關(guān)上阻塞開(kāi)關(guān)后,馬上退出“未決”狀態(tài),得到處理,這一切發(fā)生在sigprocmask返回之前。

信號(hào)生命周期:
    對(duì)于一個(gè)完整的信號(hào)生命周期(從信號(hào)發(fā)送到相應(yīng)的處理函數(shù)執(zhí)行完畢)來(lái)說(shuō),可以分為三個(gè)重要的階段,這三個(gè)階段由四個(gè)重要事件來(lái)刻畫(huà):1.信號(hào)誕生;2. 信號(hào)在進(jìn)程中注冊(cè)完畢;3.信號(hào)在進(jìn)程中的注銷完畢;4.信號(hào)處理函數(shù)執(zhí)行完畢。相鄰兩個(gè)事件的時(shí)間間隔構(gòu)成信號(hào)生命周期的一個(gè)階段。
    下面闡述四個(gè)事件的實(shí)際意義:
1.信號(hào)"誕生"。信號(hào)的誕生指的是觸發(fā)信號(hào)的事件發(fā)生(如檢測(cè)到硬件異常、定時(shí)器超時(shí)以及調(diào)用信號(hào)發(fā)送函數(shù)kill()或sigqueue()等)。

2.信號(hào)在目標(biāo)進(jìn)程中"注冊(cè)";
    進(jìn)程的task_struct結(jié)構(gòu)中有關(guān)于本進(jìn)程中未決信號(hào)的數(shù)據(jù)成員:
struct sigpending pending;
struct sigpending
{
    struct sigqueue *head, **tail;
    sigset_t signal;
};
第一、第二個(gè)成員分別指向一個(gè)sigqueue類型的結(jié)構(gòu)鏈(稱之為"未決信號(hào)信息鏈")的首尾,第三個(gè)成員是進(jìn)程中所有未決信號(hào)集,信息鏈中的每個(gè)sigqueue結(jié)構(gòu)體刻畫(huà)一個(gè)特定信號(hào)所攜帶的信息,并指向下一個(gè)sigqueue結(jié)構(gòu):
struct sigqueue
{
    struct sigqueue *next;
    siginfo_t info;
};
    信號(hào)在進(jìn)程中注冊(cè)指的就是信號(hào)值加入到進(jìn)程的未決信號(hào)集中(sigpending結(jié)構(gòu)的第二個(gè)成員sigset_t signal),并且信號(hào)所攜帶的信息被保留到未決信號(hào)信息鏈的某個(gè)sigqueue結(jié)構(gòu)中。只要信號(hào)在進(jìn)程的未決信號(hào)集中,表明進(jìn)程已經(jīng)知道這些信號(hào)的 存在,但還沒(méi)來(lái)得及處理,或者該信號(hào)被進(jìn)程阻塞。
注:
    當(dāng)一個(gè)實(shí)時(shí)信號(hào)發(fā)送給一個(gè)進(jìn)程時(shí),不管該信號(hào)是否已經(jīng)在進(jìn)程中注冊(cè),都會(huì)被再注冊(cè)一次,因此,信號(hào)不會(huì)丟失,因此,實(shí)時(shí)信號(hào)又叫做"可靠信號(hào)"。這意味著同一個(gè)實(shí)時(shí)信號(hào)可以在同一個(gè)進(jìn)程的未決信號(hào)信息鏈中占有多個(gè)sigqueue結(jié)構(gòu)(進(jìn)程每收到一個(gè)實(shí)時(shí)信號(hào),都會(huì)為它分配一個(gè)結(jié)構(gòu)來(lái)登記該信號(hào)信息,并把該結(jié)構(gòu)添加在未決信號(hào)鏈尾,即所有誕生的實(shí)時(shí)信號(hào)都會(huì)在目標(biāo)進(jìn)程中注冊(cè));
當(dāng)一個(gè)非實(shí)時(shí)信號(hào)發(fā)送給一個(gè)進(jìn)程時(shí),如果該信號(hào)已經(jīng)在進(jìn)程中注冊(cè),則該信號(hào)將被丟棄,造成信號(hào)丟失。因此,非實(shí)時(shí)信號(hào)又叫做"不可靠信號(hào)"。 這意味著同一個(gè)非實(shí)時(shí)信號(hào)在進(jìn)程的未決信號(hào)信息鏈中,至多占有一個(gè)sigqueue結(jié)構(gòu)(一個(gè)非實(shí)時(shí)信號(hào)誕生后,(1)、如果發(fā)現(xiàn)相同的信號(hào)已經(jīng)在目標(biāo)結(jié) 構(gòu)中注冊(cè),則不再注冊(cè),對(duì)于進(jìn)程來(lái)說(shuō),相當(dāng)于不知道本次信號(hào)發(fā)生,信號(hào)丟失;(2)、如果進(jìn)程的未決信號(hào)中沒(méi)有相同信號(hào),則在進(jìn)程中注冊(cè)自己)。

3. 信號(hào)在進(jìn)程中的注銷。在目標(biāo)進(jìn)程執(zhí)行過(guò)程中,會(huì)檢測(cè)是否有信號(hào)等待處理(每次從系統(tǒng)空間返回到用戶空間時(shí)都做這樣的檢查)。如果存在未決信號(hào)等待處理且該 信號(hào)沒(méi)有被進(jìn)程阻塞,則在運(yùn)行相應(yīng)的信號(hào)處理函數(shù)前,進(jìn)程會(huì)把信號(hào)在未決信號(hào)鏈中占有的結(jié)構(gòu)卸掉。是否將信號(hào)從進(jìn)程未決信號(hào)集中刪除對(duì)于實(shí)時(shí)與非實(shí)時(shí)信號(hào) 是不同的。對(duì)于非實(shí)時(shí)信號(hào)來(lái)說(shuō),由于在未決信號(hào)信息鏈中最多只占用一個(gè)sigqueue結(jié)構(gòu),因此該結(jié)構(gòu)被釋放后,應(yīng)該把信號(hào)在進(jìn)程未決信號(hào)集中刪除(信 號(hào)注銷完畢);而對(duì)于實(shí)時(shí)信號(hào)來(lái)說(shuō),可能在未決信號(hào)信息鏈中占用多個(gè)sigqueue結(jié)構(gòu),因此應(yīng)該針對(duì)占用gqueue結(jié)構(gòu)的數(shù)目區(qū)別對(duì)待:如果只占用 一個(gè)sigqueue結(jié)構(gòu)(進(jìn)程只收到該信號(hào)一次),則應(yīng)該把信號(hào)在進(jìn)程的未決信號(hào)集中刪除(信號(hào)注銷完畢)。否則,不在進(jìn)程的未決信號(hào)集中刪除該信號(hào) (信號(hào)注銷完畢)。進(jìn)程在執(zhí)行信號(hào)相應(yīng)處理函數(shù)之前,首先要把信號(hào)在進(jìn)程中注銷。

4.信號(hào)生命終止。進(jìn)程注銷信號(hào)后,立即執(zhí)行相應(yīng)的信號(hào)處理函數(shù),執(zhí)行完畢后,信號(hào)的本次發(fā)送對(duì)進(jìn)程的影響徹底結(jié)束。
注:
1) 信號(hào)注冊(cè)與否,與發(fā)送信號(hào)的函數(shù)(如kill()或sigqueue()等)以及信號(hào)安裝函數(shù)(signal()及sigaction())無(wú)關(guān),只與信 號(hào)值有關(guān)(信號(hào)值小于SIGRTMIN的信號(hào)最多只注冊(cè)一次,信號(hào)值在SIGRTMIN及SIGRTMAX之間的信號(hào),只要被進(jìn)程接收到就被注冊(cè))。
2)在信號(hào)被注銷到相應(yīng)的信號(hào)處理函數(shù)執(zhí)行完畢這段時(shí)間內(nèi),如果進(jìn)程又收到同一信號(hào)多次,則對(duì)實(shí)時(shí)信號(hào)來(lái)說(shuō),每一次都會(huì)在進(jìn)程中注冊(cè);而對(duì)于非實(shí)時(shí)信號(hào)來(lái)說(shuō),無(wú)論收到多少次信號(hào),都會(huì)視為只收到一個(gè)信號(hào),只在進(jìn)程中注冊(cè)一次。


]]>
linux的內(nèi)存管理FAQhttp://www.tkk7.com/tinysun/archive/2010/09/30/333559.html何克勤何克勤Thu, 30 Sep 2010 08:22:00 GMThttp://www.tkk7.com/tinysun/archive/2010/09/30/333559.htmlhttp://www.tkk7.com/tinysun/comments/333559.htmlhttp://www.tkk7.com/tinysun/archive/2010/09/30/333559.html#Feedback0http://www.tkk7.com/tinysun/comments/commentRss/333559.htmlhttp://www.tkk7.com/tinysun/services/trackbacks/333559.html一、Linux內(nèi)存管理到底是分段還是分頁(yè)?

在實(shí)模式下沒(méi)有引入段式內(nèi)存管理模式前,程序都是使用絕對(duì)地址來(lái)進(jìn)行內(nèi)存操作的,這樣造成了程序的可移植性差等問(wèn)題。于 是在8086時(shí)代開(kāi)始引入段式內(nèi)存管理,段式管理引入了地址映射的概念,不但解決了可移植性問(wèn)題,也解決了16位ALU和段寄存器的可尋址限制,即支持更 大的內(nèi)存尋址.每個(gè)段的大小是64KB.

在實(shí)模式下,由于系統(tǒng)程序和用戶程序在訪問(wèn)內(nèi)存等資源時(shí)權(quán)限沒(méi)有區(qū)分,所以很可能造成操作系統(tǒng)出現(xiàn)異常.于是Intel在80286時(shí)代開(kāi)發(fā)出來(lái)了保護(hù)模式.

而Intel為了兼容80386前的處理器就規(guī)定:在IA(Intel Arch)32保護(hù)模式下,不能禁止分段,但是分頁(yè)是Optional的。/*When operating in protected mode, some form of segmentation must be used. There is no mode bit to disable segmentation. The use of paging, however, is optional.*/.

既然linux在內(nèi)存管理上不能禁止分段,那么linux又認(rèn)為分段會(huì)造成大量的內(nèi)存碎片,所以就通過(guò)將邏輯地址中的段基址置為0,加上偏移量形成線性地址,來(lái)簡(jiǎn)化段式管理內(nèi)存而使用分頁(yè)管理內(nèi)存的。

在IA32下,每頁(yè)是4K.

二、linux的三種類型內(nèi)存地址及關(guān)系是什么樣的?

分段                     分頁(yè)

邏輯地址————> 線性地址 —————->物理地址

在 x86 架構(gòu)中,內(nèi)存被劃分成 3 種類型的地址:

邏輯地址 (logical address) 由16位段選擇符:32位偏移量來(lái)表示,它有可能直接對(duì)應(yīng)于一個(gè)物理位置(Intel實(shí)模式下)。
線性地址 (linear address)或虛擬地址 是由段描述符中的32位段基址和邏輯地址中的32位偏移量相加構(gòu)成的。例如:0x08048394,由于linux把所有的段基址都置為0x00000000.所以線性地址=0x00000000+偏移量=邏輯地址,在不分頁(yè)的情況下:線性地址就直接是物理地址;

物理地址 (physical address) 是使用物理地址總線中的位表示的地址。內(nèi)存管理單元可以將邏輯地址轉(zhuǎn)換成物理地址。

三、為什么在32位的操作系統(tǒng)下每個(gè)進(jìn)程會(huì)有4G的內(nèi)存使用限制?

由于32位操作系統(tǒng)下,由于虛擬地址空間是2的32次方=4 294 967 296,通常的數(shù)據(jù)對(duì)象都以字節(jié)為單位,所以一個(gè)進(jìn)程的可尋址范圍就是4G內(nèi)存

四、為什么linux服務(wù)器是32位的還可以認(rèn)出來(lái)多于4G的內(nèi)存?

本來(lái)在IA32下,機(jī)器只能認(rèn)出4G的內(nèi)存,但I(xiàn)ntel為了讓32位的OS能支持更多的內(nèi)存。采取了PAE(Physical Address Extension, 物理地址擴(kuò)展)技術(shù).PAE的支持,不僅需要處理器的支持,OS的內(nèi)核也需要支持才可行(我們通常采用的RHEL3/4/5 kernel都支持PAE),也就是處理器內(nèi)部增加了PAE寄存器,用于記錄多出的4條地址總線,所以系統(tǒng)就是2^36=64G.

五、在32位的linux操作系統(tǒng)下,為什么linux系統(tǒng)認(rèn)出來(lái)的內(nèi)存大于4G,而我的程序每個(gè)進(jìn)程卻只能分配4G以下的內(nèi)存?

雖然Intel為其處理器為支持PAE增加了4條地址線,但因?yàn)樘摂M地址是32位的,所以單個(gè)進(jìn)程還是只能分配4G以下的內(nèi)存

Note: Linux can use up to 64 Gigabytes of physical memory on x86 systems. However, the address space of 32-bit x86 processors is only 4 Gigabytes large. Thus means that, if you have a large amount of physical memory, not all of it can be “permanently mapped” by the kernel. The physical memory that’s not permanently mapped is called “high memory”.

Redhat網(wǎng)站http://www.redhat.com/rhel/compare/

六、32位的RedHat linux”smp” kernel”hugemem” kernel中內(nèi)存支持方面有什么建議?

Smp kernel和hugemem kernel都支持PAE支持,也就意味著最大能支持64G的內(nèi)存。而RedHat建議當(dāng)你的物理內(nèi)存在16GB之內(nèi),用SMP” kernel,在16GB-64GB之間使用”Hugemem” kernel.這是因?yàn)樘摂M地址空間里有1G用于內(nèi)核空間,而3G用于用戶空間。而關(guān)健的一些數(shù)據(jù)結(jié)構(gòu)是存放在1G內(nèi)核空間的,在管理32G內(nèi)存當(dāng)中,需 要用到0.5G來(lái)用于管理這些物理內(nèi)存(容易觸發(fā)OOM killer).雖然32位OS下,內(nèi)核和用戶空間的比例都是1:3,但Hugemem打了一個(gè)補(bǔ)丁,使比例成為4G:4G,即使內(nèi)核空間和用戶空間相互 獨(dú)立,所以也會(huì)有性能上的損失,因?yàn)閼?yīng)用程序的運(yùn)行,通常會(huì)有內(nèi)核和用戶空間的切換。所以如果你的內(nèi)存大于16G,建議你使用64位的OS。

/* The “SMP” kernel supports a maximum of 16GB of main memory. Systems with more than 16GB of main memory use the “Hugemem” kernel. In certain workload scenarios it may be advantageous to use the “Hugemem” kernel on systems with more than 12GB of main memory. */

七、我們通常malloc4K內(nèi)存,到底是怎么對(duì)應(yīng)到物理內(nèi)存的?

在linux下,處理器在得到內(nèi)存的線性地址進(jìn)行內(nèi)存尋址時(shí),不是直接在內(nèi)存的物理地址里查找的,而是通過(guò)線性地址轉(zhuǎn)換到主內(nèi)存的物理地 址,TLB(Translation lookaside buffer,可以簡(jiǎn)單的理解為:一種存儲(chǔ)線性地址和物理地址的硬件高速緩沖器)就是負(fù)責(zé)將虛擬內(nèi)存地址翻譯成實(shí)際的物理內(nèi)存地址,而CPU尋址時(shí)會(huì)優(yōu)先 在TLB中進(jìn)行尋址。如果TLB里沒(méi)有,則從頁(yè)表里進(jìn)行線性地址到物理地址的轉(zhuǎn)換.hugepage能增加TLB的命中率,所以會(huì)在某些方面能大大提高系 統(tǒng)性能)。

1、邏輯地址轉(zhuǎn)線性地址

LL.jpg

a、首先根據(jù)指令的性質(zhì)來(lái)確定該使用哪一個(gè)段寄存器。
b、根據(jù)段寄存器的內(nèi)容,到GDT中找到相應(yīng)的“段描述結(jié)構(gòu)”

c、根據(jù)linux把所有的段基址都置為0×00000000.所以0×00000000+偏移量就是線性地址了.在不分頁(yè)的情況下:線性地址就直接是物理地址;
同時(shí),在上面過(guò)程中,由于有對(duì)訪問(wèn)權(quán)限的檢查,就實(shí)現(xiàn)了保護(hù)。

2、線性地址轉(zhuǎn)物理地址

在保護(hù)模式下,控制寄存器CR0的最高位PG位(PE位控制是否為保護(hù)模式)控制著分頁(yè)管理機(jī)制是否生效,如果PG=1,分頁(yè)機(jī)制生效,需通過(guò)頁(yè)表查找才能把線性地址轉(zhuǎn)換物理地址。如果PG=0,則分頁(yè)機(jī)制無(wú)效,線性地址就直接做為物理地址。

頁(yè)式內(nèi)存管理中,32位的線性地址劃分為三個(gè)部分:10位的頁(yè)目錄表下標(biāo)、10位的頁(yè)面表下標(biāo)、12位的頁(yè)內(nèi)偏移量。CPU增加了一個(gè)CR3寄存器存放指向當(dāng)前頁(yè)目錄表的指針。尋址方式就改為:
a、從CR3取得頁(yè)目錄表的基地址;
b、根據(jù)10位頁(yè)目錄表下標(biāo)和從CR3取得的基地址,得到相應(yīng)頁(yè)表的基地址;
c、根據(jù)10位頁(yè)面表下標(biāo)和b中得到的頁(yè)表基地址,從頁(yè)面表中取得相應(yīng)的頁(yè)面描述項(xiàng);
d、將頁(yè)面描述項(xiàng)中的頁(yè)面基地址和線性地址中的12位頁(yè)內(nèi)地址偏移相加,得到物理地址。
同時(shí),在地址轉(zhuǎn)換的過(guò)程中也有越界和權(quán)限的檢查,就不贅述了。

LP

注:傳統(tǒng)的32位操作系統(tǒng)采用的是兩級(jí)分頁(yè)模型

64位和PAE支持都采用了三級(jí)分頁(yè)模型



]]>
Linux slab 分配器剖析http://www.tkk7.com/tinysun/archive/2010/09/30/333537.html何克勤何克勤Thu, 30 Sep 2010 05:46:00 GMThttp://www.tkk7.com/tinysun/archive/2010/09/30/333537.htmlhttp://www.tkk7.com/tinysun/comments/333537.htmlhttp://www.tkk7.com/tinysun/archive/2010/09/30/333537.html#Feedback0http://www.tkk7.com/tinysun/comments/commentRss/333537.htmlhttp://www.tkk7.com/tinysun/services/trackbacks/333537.html

]]>
頻繁分配釋放內(nèi)存導(dǎo)致的性能問(wèn)題的分析http://www.tkk7.com/tinysun/archive/2010/09/30/333536.html何克勤何克勤Thu, 30 Sep 2010 05:35:00 GMThttp://www.tkk7.com/tinysun/archive/2010/09/30/333536.htmlhttp://www.tkk7.com/tinysun/comments/333536.htmlhttp://www.tkk7.com/tinysun/archive/2010/09/30/333536.html#Feedback0http://www.tkk7.com/tinysun/comments/commentRss/333536.htmlhttp://www.tkk7.com/tinysun/services/trackbacks/333536.html現(xiàn)象
1 壓力測(cè)試過(guò)程中,發(fā)現(xiàn)被測(cè)對(duì)象性能不夠理想,具體表現(xiàn)為:
進(jìn)程的系統(tǒng)態(tài)CPU消耗20,用戶態(tài)CPU消耗10,系統(tǒng)idle大約70
2 用ps -o majflt,minflt -C program命令查看,發(fā)現(xiàn)majflt每秒增量為0,而minflt每秒增量大于10000。

初步分析
majflt代表major fault,中文名叫大錯(cuò)誤,minflt代表minor fault,中文名叫小錯(cuò)誤。
這兩個(gè)數(shù)值表示一個(gè)進(jìn)程自啟動(dòng)以來(lái)所發(fā)生的缺頁(yè)中斷的次數(shù)。
當(dāng)一個(gè)進(jìn)程發(fā)生缺頁(yè)中斷的時(shí)候,進(jìn)程會(huì)陷入內(nèi)核態(tài),執(zhí)行以下操作:
檢查要訪問(wèn)的虛擬地址是否合法
查找/分配一個(gè)物理頁(yè)
填充物理頁(yè)內(nèi)容(讀取磁盤(pán),或者直接置0,或者啥也不干)
建立映射關(guān)系(虛擬地址到物理地址)
重新執(zhí)行發(fā)生缺頁(yè)中斷的那條指令
如果第3步,需要讀取磁盤(pán),那么這次缺頁(yè)中斷就是majflt,否則就是minflt。
此進(jìn)程minflt如此之高,一秒10000多次,不得不懷疑它跟進(jìn)程內(nèi)核態(tài)cpu消耗大有很大關(guān)系。

分析代碼
查看代碼,發(fā)現(xiàn)是這么寫(xiě)的:一個(gè)請(qǐng)求來(lái),用malloc分配2M內(nèi)存,請(qǐng)求結(jié)束后free這塊內(nèi)存。看日志,發(fā)現(xiàn)分配內(nèi)存語(yǔ)句耗時(shí)10us,平均一條請(qǐng)求處理耗時(shí)1000us 。 原因已找到!
雖然分配內(nèi)存語(yǔ)句的耗時(shí)在一條處理請(qǐng)求中耗時(shí)比重不大,但是這條語(yǔ)句嚴(yán)重影響了性能。要解釋清楚原因,需要先了解一下內(nèi)存分配的原理。

內(nèi)存分配的原理
從 操作系統(tǒng)角度來(lái)看,進(jìn)程分配內(nèi)存有兩種方式,分別由兩個(gè)系統(tǒng)調(diào)用完成:brk和mmap(不考慮共享內(nèi)存)。brk是將數(shù)據(jù)段(.data)的最高地址指 針_edata往高地址推,mmap是在進(jìn)程的虛擬地址空間中(一般是堆和棧中間)找一塊空閑的。這兩種方式分配的都是虛擬內(nèi)存,沒(méi)有分配物理內(nèi)存。在第 一次訪問(wèn)已分配的虛擬地址空間的時(shí)候,發(fā)生缺頁(yè)中斷,操作系統(tǒng)負(fù)責(zé)分配物理內(nèi)存,然后建立虛擬內(nèi)存和物理內(nèi)存之間的映射關(guān)系。
在標(biāo)準(zhǔn)C庫(kù)中,提供了malloc/free函數(shù)分配釋放內(nèi)存,這兩個(gè)函數(shù)底層是由brk,mmap,munmap這些系統(tǒng)調(diào)用實(shí)現(xiàn)的。
下面以一個(gè)例子來(lái)說(shuō)明內(nèi)存分配的原理:

1進(jìn)程啟動(dòng)的時(shí)候,其(虛擬)內(nèi)存空間的初始布局如圖1所示。其中,mmap內(nèi)存映射文件是在堆和棧的中間(例如libc-2.2.93.so,其它數(shù)據(jù)文件等),為了簡(jiǎn)單起見(jiàn),省略了內(nèi)存映射文件。_edata指針(glibc里面定義)指向數(shù)據(jù)段的最高地址。
2 進(jìn)程調(diào)用A=malloc(30K)以后,內(nèi)存空間如圖2:malloc函數(shù)會(huì)調(diào)用brk系統(tǒng)調(diào)用,將_edata指針往高地址推30K,就完成虛擬內(nèi)存 分配。你可能會(huì)問(wèn):只要把_edata+30K就完成內(nèi)存分配了?事實(shí)是這樣的,_edata+30K只是完成虛擬地址的分配,A這塊內(nèi)存現(xiàn)在還是沒(méi)有物 理頁(yè)與之對(duì)應(yīng)的,等到進(jìn)程第一次讀寫(xiě)A這塊內(nèi)存的時(shí)候,發(fā)生缺頁(yè)中斷,這個(gè)時(shí)候,內(nèi)核才分配A這塊內(nèi)存對(duì)應(yīng)的物理頁(yè)。也就是說(shuō),如果用malloc分配了 A這塊內(nèi)容,然后從來(lái)不訪問(wèn)它,那么,A對(duì)應(yīng)的物理頁(yè)是不會(huì)被分配的。
3進(jìn)程調(diào)用B=malloc(40K)以后,內(nèi)存空間如圖3.

4進(jìn)程調(diào)用C=malloc(200K)以后,內(nèi)存空間如圖4:默認(rèn)情況下,malloc函數(shù)分配內(nèi)存,如果請(qǐng)求內(nèi)存大于128K(可由 M_MMAP_THRESHOLD選項(xiàng)調(diào)節(jié)),那就不是去推_edata指針了,而是利用mmap系統(tǒng)調(diào)用,從堆和棧的中間分配一塊虛擬內(nèi)存。這樣子做主 要是因?yàn)閎rk分配的內(nèi)存需要等到高地址內(nèi)存釋放以后才能釋放(例如,在B釋放之前,A是不可能釋放的),而mmap分配的內(nèi)存可以單獨(dú)釋放。當(dāng)然,還有 其它的好處,也有壞處,再具體下去,有興趣的同學(xué)可以去看glibc里面malloc的代碼了。
5進(jìn)程調(diào)用D=malloc(100K)以后,內(nèi)存空間如圖5.
6進(jìn)程調(diào)用free(C)以后,C對(duì)應(yīng)的虛擬內(nèi)存和物理內(nèi)存一起釋放

4進(jìn)程調(diào)用C=malloc(200K)以后,內(nèi)存空間如圖4:默認(rèn)情況下,malloc函數(shù)分配內(nèi)存,如果請(qǐng)求內(nèi)存大于128K(可由 M_MMAP_THRESHOLD選項(xiàng)調(diào)節(jié)),那就不是去推_edata指針了,而是利用mmap系統(tǒng)調(diào)用,從堆和棧的中間分配一塊虛擬內(nèi)存。這樣子做主 要是因?yàn)閎rk分配的內(nèi)存需要等到高地址內(nèi)存釋放以后才能釋放(例如,在B釋放之前,A是不可能釋放的),而mmap分配的內(nèi)存可以單獨(dú)釋放。當(dāng)然,還有 其它的好處,也有壞處,再具體下去,有興趣的同學(xué)可以去看glibc里面malloc的代碼了。
5進(jìn)程調(diào)用D=malloc(100K)以后,內(nèi)存空間如圖5.
6進(jìn)程調(diào)用free(C)以后,C對(duì)應(yīng)的虛擬內(nèi)存和物理內(nèi)存一起釋放

7進(jìn)程調(diào)用free(B)以后,如圖7所示。B對(duì)應(yīng)的虛擬內(nèi)存和物理內(nèi)存都沒(méi)有釋放,因?yàn)橹挥幸粋€(gè)_edata指針,如果往回推,那么D這塊內(nèi)存怎 么辦呢?當(dāng)然,B這塊內(nèi)存,是可以重用的,如果這個(gè)時(shí)候再來(lái)一個(gè)40K的請(qǐng)求,那么malloc很可能就把B這塊內(nèi)存返回回去了。
8進(jìn)程調(diào)用free(D)以后,如圖8所示。B和D連接起來(lái),變成一塊140K的空閑內(nèi)存。
9默認(rèn)情況下:當(dāng)最高地址空間的空閑內(nèi)存超過(guò)128K(可由M_TRIM_THRESHOLD選項(xiàng)調(diào)節(jié))時(shí),執(zhí)行內(nèi)存緊縮操作(trim)。在上一個(gè)步驟free的時(shí)候,發(fā)現(xiàn)最高地址空閑內(nèi)存超過(guò)128K,于是內(nèi)存緊縮,變成圖9所示。

真相大白
說(shuō) 完內(nèi)存分配的原理,那么被測(cè)模塊在內(nèi)核態(tài)cpu消耗高的原因就很清楚了:每次請(qǐng)求來(lái)都malloc一塊2M的內(nèi)存,默認(rèn)情況下,malloc調(diào)用mmap 分配內(nèi)存,請(qǐng)求結(jié)束的時(shí)候,調(diào)用munmap釋放內(nèi)存。假設(shè)每個(gè)請(qǐng)求需要6個(gè)物理頁(yè),那么每個(gè)請(qǐng)求就會(huì)產(chǎn)生6個(gè)缺頁(yè)中斷,在2000的壓力下,每秒就產(chǎn)生 了10000多次缺頁(yè)中斷,這些缺頁(yè)中斷不需要讀取磁盤(pán)解決,所以叫做minflt;缺頁(yè)中斷在內(nèi)核態(tài)執(zhí)行,因此進(jìn)程的內(nèi)核態(tài)cpu消耗很大。缺頁(yè)中斷分 散在整個(gè)請(qǐng)求的處理過(guò)程中,所以表現(xiàn)為分配語(yǔ)句耗時(shí)(10us)相對(duì)于整條請(qǐng)求的處理時(shí)間(1000us)比重很小。

解決辦法
將動(dòng)態(tài)內(nèi)存改為靜態(tài)分配,或者啟動(dòng)的時(shí)候,用malloc為每個(gè)線程分配,然后保存在threaddata里面。但是,由于這個(gè)模塊的特殊性,靜態(tài)分配,或者啟動(dòng)時(shí)候分配都不可行。另外,Linux下默認(rèn)棧的大小限制是10M,如果在棧上分配幾M的內(nèi)存,有風(fēng)險(xiǎn)。
禁止malloc調(diào)用mmap分配內(nèi)存,禁止內(nèi)存緊縮。
在進(jìn)程啟動(dòng)時(shí)候,加入以下兩行代碼:
mallopt(M_MMAP_MAX, 0); // 禁止malloc調(diào)用mmap分配內(nèi)存
mallopt(M_TRIM_THRESHOLD, -1); // 禁止內(nèi)存緊縮
效果:加入這兩行代碼以后,用ps命令觀察,壓力穩(wěn)定以后,majlt和minflt都為0。進(jìn)程的系統(tǒng)態(tài)cpu從20降到10。

小結(jié)
可以用命令ps -o majflt minflt -C program來(lái)查看進(jìn)程的majflt, minflt的值,這兩個(gè)值都是累加值,從進(jìn)程啟動(dòng)開(kāi)始累加。在對(duì)高性能要求的程序做壓力測(cè)試的時(shí)候,我們可以多關(guān)注一下這兩個(gè)值。
如果一個(gè)進(jìn)程使用了mmap將很大的數(shù)據(jù)文件映射到進(jìn)程的虛擬地址空間,我們需要重點(diǎn)關(guān)注majflt的值,因?yàn)橄啾萴inflt,majflt對(duì)于性能的損害是致命的,隨機(jī)讀一次磁盤(pán)的耗時(shí)數(shù)量級(jí)在幾個(gè)毫秒,而minflt只有在大量的時(shí)候才會(huì)對(duì)性能產(chǎn)生影響。



]]>
系統(tǒng)管理中 bash shell 腳本常用方法總結(jié)http://www.tkk7.com/tinysun/archive/2010/09/30/333468.html何克勤何克勤Thu, 30 Sep 2010 02:54:00 GMThttp://www.tkk7.com/tinysun/archive/2010/09/30/333468.htmlhttp://www.tkk7.com/tinysun/comments/333468.htmlhttp://www.tkk7.com/tinysun/archive/2010/09/30/333468.html#Feedback0http://www.tkk7.com/tinysun/comments/commentRss/333468.htmlhttp://www.tkk7.com/tinysun/services/trackbacks/333468.html

在日常系統(tǒng)管理工作中,需要編寫(xiě)腳本來(lái)完成特定的功能,編寫(xiě)shell腳本是一個(gè)基本功了!
在編寫(xiě)的過(guò)程中,掌握一些常用的技巧和語(yǔ)法就可以完成大部分功能了,也就是2/8原則.

1. 單引號(hào)和雙引號(hào)的區(qū)別

單引號(hào)與雙引號(hào)的最大不同在于雙引號(hào)仍然可以引用變量的內(nèi)容,但單引號(hào)內(nèi)僅是 普通字符 ,不會(huì)作變量的引用,直接輸出字符竄。請(qǐng)看如下例子:

  [root@linux ~]# name=HaHa
[root@linux ~]# echo $name
HaHa
[root@linux ~]# myname="$name is wow"
[root@linux ~]# echo $myname
HaHa is wow
[root@linux ~]# myname='$name is wow'
[root@linux ~]# echo $myname
$name is wow

從上面例子可以看出,使用了單引號(hào)的時(shí)候,那么$name只是普通字符,直接輸出而已!

2. 逐行讀取文件

  • 使用for循環(huán)來(lái)讀取文件
      for line in `cat file.txt`
    do
    echo $line
    done

注意:由于使用for來(lái)讀入文件里的行時(shí),會(huì)自動(dòng)把空格和換行符作為一樣分隔符,如果行里有空格的時(shí)候,輸出的結(jié)果會(huì)很亂,所以只適用于行連續(xù)不能有空格或者換行符的文件

  • 使用while循環(huán)讀取文件
      cat file.txt |while read line
    do
    echo $line
    done

    或者:

    while read line
    do
    echo $line
    done < file.txt

注意:由于使用while來(lái)讀入文件里的行時(shí),會(huì)整行讀入,不會(huì)關(guān)注行的內(nèi)容(空格..),所以比f(wàn)or讀文件有更好的適用性,推薦使用while循環(huán)讀取文件

3. bash shell 腳本中常用隱含變量

$0 當(dāng)前執(zhí)行的腳本或者命令名稱
$1-$9 代表參數(shù)的位置. 舉例 $1 代表第一個(gè)參數(shù).
$# 腳本調(diào)用的參數(shù)的個(gè)數(shù)
$@ 所有參數(shù)的內(nèi)容
$* 所有參數(shù)的內(nèi)容
$$ 當(dāng)前運(yùn)行腳本的進(jìn)程號(hào)
$? 命令執(zhí)行后返回的狀態(tài)
$! 后臺(tái)運(yùn)行的最后一個(gè)進(jìn)程號(hào)

注意: $? 用于檢查上一個(gè)命令執(zhí)行是否正確(在Linux中,命令退出狀態(tài)為0表示該命令正確執(zhí)行,任何非0值表示命令出錯(cuò))
$$ 變量最常見(jiàn)的用途是用做暫存文件的名字以保證暫存文件不會(huì)重復(fù)。
$* 和 $@ 如果輸出是一樣的,但是在使用for循環(huán),在使用 雙引號(hào)(”")引用時(shí) “$*” 會(huì)輸出成一個(gè)元素 而 “$@” 會(huì)按照每個(gè)參數(shù)是一個(gè)元素方式輸出

請(qǐng)看測(cè)試?yán)?/p>

  #cat test.sh
#!/bin/sh
echo '"$@" output.....'
for i in "$@"
do
echo $i
done
echo '"$*" output ....'
for i in "$*"
do
echo $i
done

輸出結(jié)果

  #sh test.sh a b c d
"$@" output.....
a
b
c
d
"$*" output ....
a b c d

從輸出結(jié)果可以看出 “$*” 輸出是一行 而 “$@” 輸出則是四行

4. 變量?jī)?nèi)容的刪除與替換

我們?cè)谝恍┣闆r下,需要對(duì)變量中的字符竄進(jìn)行查找刪除或者替換,就需要使用下表列出的方法

變量設(shè)定方式 說(shuō)明
${變量#關(guān)鍵字} 若變量?jī)?nèi)容從頭開(kāi)始的資料符合‘關(guān)鍵字’,則將符合的最短資料刪除
${變量##關(guān)鍵字} 若變量?jī)?nèi)容從頭開(kāi)始的資料符合‘關(guān)鍵字’,則將符合的最長(zhǎng)資料刪除
${變量%關(guān)鍵字} 若變量?jī)?nèi)容從尾向前的資料符合‘關(guān)鍵字’,則將符合的最短資料刪除
${變量%%關(guān)鍵字} 若變量?jī)?nèi)容從尾向前的資料符合‘關(guān)鍵字’,則將符合的最長(zhǎng)資料刪除
${變量/舊字串/新字串} 若變量?jī)?nèi)容符合‘舊字串’則‘第一個(gè)舊字串會(huì)被新字串取代
${變量//舊字串/新字串} 若變量?jī)?nèi)容符合‘舊字串’則‘全部的舊字串會(huì)被新字串取代

舉例如下(刪除字符竄中的某個(gè)字符):

  [root@linux ~]# export test_str="/usr/kerberos/sbin:/usr/kerberos/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin"
[root@linux ~]# echo ${test_str#/*kerberos/bin:}
/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin

5. 變量條件測(cè)試賦值

在某些時(shí)刻我們需要‘判斷’某個(gè)變量是否存在,若變量存在則將此變量值賦值給新的變量,若變量不存在則將其他值賦值給新的變量.

變量設(shè)定方式 str 未定義 str 為空字串 str 已賦值為非空字串
var=${str-expr} var=expr var= var=$str
var=${str:-expr} var=expr var=expr var=$str
var=${str+expr} var= var=expr var=expr
var=${str:+expr} var= var= var=expr
var=${str?expr} expr 輸出至 stderr var= var=$str
var=${str:?expr} expr 輸出至 stderr expr 輸出至 stderr var=$str
var=${str=expr} var=expr var= var=$str
var=${str:=expr} var=expr var=expr var=$str

舉例如下:

  [root@linux ~]# test_name=""
[root@linux ~]# test_name=${test_name-root}
[root@linux ~]# echo $test_name
<== 因?yàn)?test_name 被設(shè)定為空字符竄!所以當(dāng)然還是保留為空字符竄!
[root@linux ~]# test_name=${test_name:-root}
[root@linux ~]# echo $test_name
root <== 加上‘:’后若變量?jī)?nèi)容為空或者是未設(shè)定,都能夠以后面的內(nèi)容替換!

基本上這種變量的測(cè)試也能夠透過(guò) shell script 內(nèi)的 if…then… 來(lái)處理,不過(guò)通過(guò)上述提及的簡(jiǎn)單的方法來(lái)測(cè)試變量,是程序看起來(lái)更精簡(jiǎn)一些!

6. shell 中分隔符 : 變量IFS 使用

shell腳本中,如果使用for循環(huán)一個(gè)字符竄的話,默認(rèn)使用空格來(lái)分割字符竄. 還有前面所提到的 使用for循環(huán)逐行讀取文件內(nèi)容時(shí)候,文件行中如果有空格的話輸出的結(jié)果也會(huì)變亂. 這個(gè)時(shí)候 使用 IFS 變量來(lái)設(shè)置特定的字符竄分割符來(lái),達(dá)到輸出正確的目的. 默認(rèn)情況下 IFS 是使用 <space><tab><newline>, 空格 "t "n 來(lái)作為默認(rèn)的分割符的.

我們將前面使用for逐行讀取文件的例子 改進(jìn)下就可以輸出正確了,請(qǐng)看下面

  #!/bin/bash
IFS_old=$IFS #將原IFS值保存,以便用完后恢復(fù)
IFS=$’"n’ #更改IFS值為$’"n’
for line in `cat file.txt`
do
echo $line
done

file.txt 文件內(nèi)容如下

  [root@linux]$ cat file.txt
sdfsdfsdfsdf
ssssss ssssss ssssss sssss
sdfsdfsdfsdfsdf

執(zhí)行測(cè)試程序 輸出結(jié)果如下(正確輸出)

  [root@linux]$ sh test.sh
sdfsdfsdfsdf
ssssss ssssss ssssss sssss
sdfsdfsdfsdfsdf

如果未設(shè)置IFS變量,使用默認(rèn)的IFS變量值 ,輸出結(jié)果如下

  [root@linux]$ sh test.sh
sdfsdfsdfsdf
ssssss
ssssss
ssssss
sssss
sdfsdfsdfsdfsdf

從以上測(cè)試程序輸出結(jié)果,可以根據(jù)自己的需求來(lái)設(shè)定 IFS變量,在舉一個(gè)例子如下:

  while IFS=: read userName passWord userID groupID geCos homeDir userShell
do
echo "$userName -> $homeDir"
done < /etc/passwd

7. shell 數(shù)組的使用

數(shù)組賦值方式:

  (1) array=(var1 var2 var3 ... varN)
(2) array=([0]=var1 [1]=var2 [2]=var3 ... [n]=varN)
(3) array[0]=var1
arrya[1]=var2
...
array[n]=varN

計(jì)算數(shù)組元素個(gè)數(shù)或者長(zhǎng)度:

  (1) ${#array[@]}
(2) ${#array[*]}

了解了數(shù)組基礎(chǔ)語(yǔ)法,舉例說(shuō)明,請(qǐng)看:

  #!/bin/bash
NAMESERVERS=("ns1.www.net." "ns2.www.net." "ns3.www.net.")
# 得到數(shù)組長(zhǎng)度
tLen=${#NAMESERVERS[@]}

# 循環(huán)數(shù)組
for (( i=0; i<${tLen}; i++ ));
do
echo ${NAMESERVERS[$i]}
done

在看一個(gè)復(fù)雜一點(diǎn)的例子,將文件內(nèi)容讀取到數(shù)組中:

  #!/bin/bash

# 設(shè)置IFS將分割符 設(shè)置為 換行符("n)
OLDIFS=$IFS
IFS=$'"n'

# 讀取文件內(nèi)容到數(shù)組
fileArray=($(cat file.txt))

# restore it
IFS=$OLDIFS
tLen=${#fileArray[@]}

# 循環(huán)顯示文件內(nèi)容
for (( i=0; i<${tLen}; i++ ));
do
echo "${fileArray[$i]}"
done

8. 邏輯判斷 條件測(cè)試

  • 文件屬性的判斷
操作符 測(cè)試結(jié)果
-e filename 文件存在返回1, 否則返回0
-r filename 文件可讀返回1,否則返回0
-w filename 文件可寫(xiě)返回1,否則返回0
-x filename 文件可執(zhí)行返回1,否則返回0
-o filename 文件屬于用戶本人返回1, 否則返回0
-z filename 文件長(zhǎng)度為0返回1, 否則返回0
-f filename 文件為普通文件返回1, 否則返回0
-d filename 文件為目錄文件時(shí)返回1, 否則返回0

舉例如下,測(cè)試文件是否存在:

  #!/bin/bash
echo "checks the existence of the messages file."
echo -n "Checking..."
if [ -f /var/log/messages ];then
echo "/var/log/messages exists."
fi
echo
echo "...done."
  • 字符串比較
操作符 比較結(jié)果
str1 = str2 當(dāng)兩個(gè)字串相等時(shí)為真
str1 != str2 當(dāng)兩個(gè)字串不等時(shí)為真
-n str1 當(dāng)字符串的長(zhǎng)度大于0時(shí)為真
-z str1 當(dāng)字符串的長(zhǎng)度為0時(shí)為真
str 當(dāng)字符串為非空時(shí)為真

舉例如下,比較字符串來(lái)測(cè)試用戶ID :

  if [ "$(whoami)" != 'root' ]; then
echo "You have no permission to run $0 as non-root user."
exit 1;
fi
  • 數(shù)值比較(整數(shù))
操作符 比較結(jié)果
num1 -eq num2 兩數(shù)相等為真
num1 -ne num2 兩數(shù)不等為真
num1 -gt num2 num1大于num2為真
num1 -ge num2 num1大于等于num2為真
num1 -lt num2 num1小于num2為真
num1 -le num2 num1小于等于num2為真

舉例如下:

  num=`wc -l work.txt`
if [ $num -gt 150 ];then
echo "you've worked hard enough for today."
echo
fi

如果要查看詳細(xì)的測(cè)試操作,可以查看man手冊(cè) man test



]]>
linux下which、whereis、locate、find 命令的區(qū)別http://www.tkk7.com/tinysun/archive/2010/09/30/333460.html何克勤何克勤Thu, 30 Sep 2010 02:17:00 GMThttp://www.tkk7.com/tinysun/archive/2010/09/30/333460.htmlhttp://www.tkk7.com/tinysun/comments/333460.htmlhttp://www.tkk7.com/tinysun/archive/2010/09/30/333460.html#Feedback1http://www.tkk7.com/tinysun/comments/commentRss/333460.htmlhttp://www.tkk7.com/tinysun/services/trackbacks/333460.html which       查看可執(zhí)行文件的位置
whereis    查看文件的位置
locate       配 合數(shù)據(jù)庫(kù)查看文件位置
find          實(shí)際搜尋硬盤(pán)查詢文件名稱

1、which
語(yǔ)法:
[root@redhat ~]# which 可執(zhí)行文件名稱
例如:
[root@redhat ~]# which passwd
/usr/bin/passwd
which是通過(guò) PATH環(huán)境變量到該路徑內(nèi)查找可執(zhí)行文件,所以基本的功能是尋找可執(zhí)行文件

2、whereis
語(yǔ)法:
[root@redhat ~]# whereis [-bmsu] 文件或者目錄名稱
參數(shù)說(shuō) 明:
-b : 只找二進(jìn)制文件
-m: 只找在說(shuō)明文件manual路徑下的文件
-s : 只找source源文件
-u : 沒(méi)有說(shuō)明文檔的文件
例如:
[root@redhat ~]# whereis passwd
passwd: /usr/bin/passwd /etc/passwd /usr/share/man/man1/passwd.1.gz /usr/share/man/man5/passwd.5.gz
將和passwd文件相關(guān)的文件都查找出來(lái)

[root@redhat ~]# whereis -b passwd
passwd: /usr/bin/passwd /etc/passwd
只將二進(jìn)制文件 查找出來(lái)

和find相比,whereis查找的速度非常快,這是因?yàn)閘inux系統(tǒng)會(huì)將 系統(tǒng)內(nèi)的所有文件都記錄在一個(gè)數(shù)據(jù)庫(kù)文件中,當(dāng)使用whereis和下面即將介紹的locate時(shí),會(huì)從數(shù)據(jù)庫(kù)中查找數(shù)據(jù),而不是像find命令那樣,通 過(guò)遍歷硬盤(pán)來(lái)查找,效率自然會(huì)很高。
但是該數(shù)據(jù)庫(kù)文件并不是實(shí)時(shí)更新,默認(rèn)情況下時(shí)一星期更新一次,因此,我們?cè)谟脀hereis和locate 查找文件時(shí),有時(shí)會(huì)找到已經(jīng)被刪除的數(shù)據(jù),或者剛剛建立文件,卻無(wú)法查找到,原因就是因?yàn)閿?shù)據(jù)庫(kù)文件沒(méi)有被更新。

3、 locate
語(yǔ)法:
[root@redhat ~]# locate 文件或者目錄名稱
例 如:
[root@redhat ~]# locate passwd
/home/weblogic/bea/user_projects/domains/zhanggongzhe112/myserver/stage/_appsdir_DB_war/DB.war/jsp/as/user/passwd.jsp
/home/weblogic/bea/user_projects/domains/zhanggongzhe112/myserver/stage/_appsdir_admin_war/admin.war/jsp/platform/passwd.jsp
/lib/security/pam_unix_passwd.so
/lib/security/pam_passwdqc.so
/usr/include/rpcsvc/yppasswd.x
/usr/include/rpcsvc/yppasswd.h
/usr/lib/perl5/5.8.5/i386-linux-thread-multi/rpcsvc/yppasswd.ph
/usr/lib/kde3/kded_kpasswdserver.la
/usr/lib/kde3/kded_kpasswdserver.so
/usr/lib/ruby/1.8/webrick/httpauth/htpasswd.rb
/usr/bin/vncpasswd
/usr/bin/userpasswd
/usr/bin/yppasswd
…………

4、 find
語(yǔ)法:
[root@redhat ~]# find 路徑 參數(shù)
參 數(shù)說(shuō)明:
時(shí)間查找參數(shù):
-atime n :將n*24小時(shí)內(nèi)存取過(guò)的的文件列出來(lái)
-ctime n :將n*24小時(shí)內(nèi)改變、新增的文件或者目錄列出來(lái)
-mtime n :將n*24小時(shí)內(nèi)修改過(guò)的文件或者目錄列出來(lái)
-newer file :把比f(wàn)ile還要新的文件列出來(lái)
名稱查找參數(shù):
-gid n       :尋找群組ID為n的文件
-group name  :尋找群組名稱為name的文件
-uid n       :尋找擁有者ID為n的文件
-user name   :尋找用戶者名稱為name的文件
-name file   :尋找文件名為file的文件(可以使用通配符)
例 如:
[root@redhat ~]# find / -name zgz
/home/zgz
/home/zgz/zgz
/home/weblogic/bea/user_projects/domains/zgz
/home/oracle/product/10g/cfgtoollogs/dbca/zgz
/home/oracle/product/10g/cfgtoollogs/emca/zgz
/home/oracle/oradata/zgz

[root@redhat ~]# find / -name '*zgz*'
/home/zgz
/home/zgz/zgz1
/home/zgz/zgzdirzgz
/home/zgz/zgz
/home/zgz/zgzdir
/home/weblogic/bea/user_projects/domains/zgz
/home/weblogic/bea/user_projects/domains/zgz/zgz.log00006
/home/weblogic/bea/user_projects/domains/zgz/zgz.log00002
/home/weblogic/bea/user_projects/domains/zgz/zgz.log00004
/home/weblogic/bea/user_projects/domains/zgz/zgz.log
/home/weblogic/bea/user_projects/domains/zgz/zgz.log00008
/home/weblogic/bea/user_projects/domains/zgz/zgz.log00005

當(dāng)我們用whereis和locate無(wú)法查找到我們需要的文件時(shí),可以使用find,但是find是在硬盤(pán)上遍歷查 找,因此非常消耗硬盤(pán)的資源,而且效率也非常低,因此建議大家優(yōu)先使用whereis和locate。
locate 是在數(shù)據(jù)庫(kù)里查找,數(shù)據(jù)庫(kù)大至每天更新一次。
whereis 可以找到可執(zhí)行命令和man page
find 就是根據(jù)條件查找文件。
which 可以找到可執(zhí)行文件和別名(alias)

]]>
bash快捷鍵http://www.tkk7.com/tinysun/archive/2010/09/28/333110.html何克勤何克勤Tue, 28 Sep 2010 01:57:00 GMThttp://www.tkk7.com/tinysun/archive/2010/09/28/333110.htmlhttp://www.tkk7.com/tinysun/comments/333110.htmlhttp://www.tkk7.com/tinysun/archive/2010/09/28/333110.html#Feedback0http://www.tkk7.com/tinysun/comments/commentRss/333110.htmlhttp://www.tkk7.com/tinysun/services/trackbacks/333110.htmlCtrl-A 相當(dāng)于HOME鍵,用于將光標(biāo)定位到本行最前面

Ctrl-E 相當(dāng)于End鍵,即將光標(biāo)移動(dòng)到本行末尾

Ctrl-B 相當(dāng)于左箭頭鍵,用于將光標(biāo)向左移動(dòng)一格

Ctrl-F 相當(dāng)于右箭頭鍵,用于將光標(biāo)向右移動(dòng)一格

Ctrl-D 相當(dāng)于Del鍵,即刪除光標(biāo)所在處的字符

Ctrl-K 用于刪除從光標(biāo)處開(kāi)始到結(jié)尾處的所有字符

Ctrl-L 清屏,相當(dāng)于clear命令

Ctrl-R 進(jìn)入歷史命令查找狀態(tài),然后你輸入幾個(gè)關(guān)鍵字符,就可以找到你使用過(guò)的命令

Ctrl-U 用于刪除從光標(biāo)開(kāi)始到行首的所有字符。一般在密碼或命令輸入錯(cuò)誤時(shí)常用

Ctrl-H 刪除光標(biāo)左側(cè)的一個(gè)字符

Ctrl-W 用于刪除當(dāng)前光標(biāo)左側(cè)的一個(gè)單詞

Ctrl-P 相當(dāng)于上箭頭鍵,即顯示上一個(gè)命令

Ctrl-N 相當(dāng)于下箭頭鍵,即顯示下一個(gè)命令

Ctrl-T 用于顛倒光標(biāo)所在處字符和前一個(gè)字符的位置。(目前不知道有什么作用,哪位朋友知道?)

Ctrl-J 相當(dāng)于回車鍵



]]>
內(nèi)存相關(guān)分享http://www.tkk7.com/tinysun/archive/2010/08/25/329890.html何克勤何克勤Wed, 25 Aug 2010 07:09:00 GMThttp://www.tkk7.com/tinysun/archive/2010/08/25/329890.htmlhttp://www.tkk7.com/tinysun/comments/329890.htmlhttp://www.tkk7.com/tinysun/archive/2010/08/25/329890.html#Feedback0http://www.tkk7.com/tinysun/comments/commentRss/329890.htmlhttp://www.tkk7.com/tinysun/services/trackbacks/329890.html一 linux內(nèi)存管理以及內(nèi)存碎片產(chǎn)生原因

                    
        最底層使用伙伴算法管理內(nèi)存頁(yè)面。系統(tǒng)將所有空閑內(nèi)存頁(yè)面分10個(gè)組,每個(gè)組中的內(nèi)存塊大小依次是1,2,4……512個(gè)內(nèi)存頁(yè)面,每組中的內(nèi)存塊大小相 同,并且以鏈表結(jié)構(gòu)保存。大小相同,并且內(nèi)存地址連續(xù)的兩個(gè)內(nèi)存塊稱為伙伴。伙伴算法的中心思想就是將成為伙伴的空閑內(nèi)存合并成一個(gè)更大的內(nèi)存塊。
        os中使用get_free_page獲取空閑頁(yè)面,如果找不到合適大小的空閑頁(yè)面,則從更大的組中找到空閑內(nèi)存塊,分配出去,并將剩余內(nèi)存分割,插入到 合適的組中。當(dāng)歸還內(nèi)存時(shí),啟動(dòng)伙伴算法合并空閑內(nèi)存。如果不停的申請(qǐng)內(nèi)存,并且部分歸還,但歸還的內(nèi)存不能成為伙伴,長(zhǎng)期運(yùn)行后,所有內(nèi)存將被分割成不 相鄰的小塊,當(dāng)再次申請(qǐng)大塊內(nèi)存時(shí),則可能由于找不到足夠大的連續(xù)內(nèi)存塊而失敗,這種零散的不相鄰的小塊內(nèi)存稱之為內(nèi)存碎片。當(dāng)然這只是理論上的說(shuō)明,伙 伴算法本身就是為了解決內(nèi)存碎片問(wèn)題。

二  malloc子系統(tǒng)內(nèi)存管理(dlmalloc)
        應(yīng)用層面的開(kāi)發(fā)并不是直接調(diào)用sbrk/mmap之類的函數(shù),而是調(diào)用malloc/free等malloc子系統(tǒng)提供的函數(shù),linux上安裝的大多為 DougLea的dlmalloc或者其變形ptmalloc。下面以dlmalloc為例說(shuō)明malloc工作的原理。
1 dlmalloc下名詞解釋:
   boundary tag: 邊界標(biāo)記,每個(gè)空閑內(nèi)存塊均有頭部表識(shí)和尾部標(biāo)識(shí),尾部表識(shí)的作為是合并空閑內(nèi)存塊時(shí)更快。這部分空間屬于無(wú)法被應(yīng)用層面使用浪費(fèi)的內(nèi)存空間。
   smallbins: 小內(nèi)存箱。dlmalloc將8,16,24……512大小的內(nèi)存分箱,相臨箱子中的內(nèi)存相差8字節(jié)。每個(gè)箱子中的內(nèi)存大小均相同,并且以雙向鏈表連接。
   treebins: 樹(shù)結(jié)構(gòu)箱。大于512字節(jié)的內(nèi)存不再是每8字節(jié)1箱,而是一個(gè)范圍段一箱。比如512~640, 640~896…..每個(gè)箱子的范圍段依次是128,256,512……。每箱中的結(jié)構(gòu)不再是雙向鏈表,而是樹(shù)形結(jié)構(gòu)。
   dv chunk:  當(dāng)申請(qǐng)內(nèi)存而在對(duì)應(yīng)大小的箱中找不到大小合適的內(nèi)存,則從更大的箱中找一塊內(nèi)存,劃分出需要的內(nèi)存,剩余的內(nèi)存稱之為dv chunk.
   top chunk: 當(dāng)dlmalloc中管理的內(nèi)存都找不到合適的內(nèi)存時(shí),則調(diào)用sbrk從系統(tǒng)申請(qǐng)內(nèi)存,可以增長(zhǎng)內(nèi)存方向的chunk稱為top chunk.
2 內(nèi)存分配算法
        從合適的箱子中尋找內(nèi)存塊–>從相臨的箱子中尋找內(nèi)存塊–>從dv chunk分配內(nèi)存–>從其他可行的箱子中分配內(nèi)存–>從top chunk中分配內(nèi)存–>調(diào)用sbrk/mmap申請(qǐng)內(nèi)存
3 內(nèi)存釋放算法
       臨近內(nèi)存合并–>如屬于top chunk,判斷top chunk>128k,是則歸還系統(tǒng)
                              –>不屬于chunk,則歸相應(yīng)的箱子

dlmalloc還有小內(nèi)存緩存等其他機(jī)制。可以看出經(jīng)過(guò)dlmalloc,頻繁調(diào)用malloc/free并不會(huì)產(chǎn)生內(nèi)存碎片,只要后續(xù)還有相同 的內(nèi)存大小的內(nèi)存被申請(qǐng),仍舊會(huì)使用以前的合適內(nèi)存,除非大量調(diào)用malloc之后少量釋放free,并且新的malloc又大于以前free的內(nèi)存大 小,造成dlmalloc不停的從系統(tǒng)申請(qǐng)內(nèi)存,而free掉的小內(nèi)存因被使用的內(nèi)存割斷,而使top chunk<128k,不能歸還給系統(tǒng)。即便如此,占用的總內(nèi)存量也小于的確被使用的內(nèi)存量的2倍(使用的內(nèi)存和空閑的內(nèi)存交叉分割,并且空閑的內(nèi) 存總是小于使用的內(nèi)存大小)。因此可以說(shuō),在沒(méi)有內(nèi)存泄露的情況,常規(guī)頻繁調(diào)用malloc/free并不會(huì)產(chǎn)生內(nèi)存碎片。

]]>
進(jìn)程組及會(huì)話的概念理解http://www.tkk7.com/tinysun/archive/2010/08/06/328127.html何克勤何克勤Fri, 06 Aug 2010 06:09:00 GMThttp://www.tkk7.com/tinysun/archive/2010/08/06/328127.htmlhttp://www.tkk7.com/tinysun/comments/328127.htmlhttp://www.tkk7.com/tinysun/archive/2010/08/06/328127.html#Feedback0http://www.tkk7.com/tinysun/comments/commentRss/328127.htmlhttp://www.tkk7.com/tinysun/services/trackbacks/328127.html 將闡述Linux內(nèi)核中的如下幾個(gè)概念
1) 進(jìn)程組
2) 會(huì)話
3) 控制終端
前面的概念來(lái)源于前人,我只是站在前人的肩膀上結(jié)合內(nèi)核中的實(shí)現(xiàn)加深概念理解。
1.概念:
a)進(jìn)程組
Shell 上的一條命令行形成一個(gè)進(jìn)程組
每個(gè)進(jìn)程屬于一個(gè)進(jìn)程組
每個(gè)進(jìn)程組有一個(gè)領(lǐng)頭進(jìn)程
進(jìn)程組的生命周期到組中最后一個(gè)進(jìn)程終止, 或加入其他進(jìn)程組為止
getpgrp: 獲得進(jìn)程組 id, 即領(lǐng)頭進(jìn)程的 pid
setpgid: 加入進(jìn)程組和建立新的進(jìn)程組
前臺(tái)進(jìn)程組和后臺(tái)進(jìn)程組
===============================================================================
       #include
        int setpgid (pid_t pid, pid_t pgid);
        pid_t getpgid (pid_t pid);
        int setpgrp (void);
        pid_t getpgrp (void);
-------------------------------------------------------------------------------
    進(jìn)程只能將自身和其子進(jìn)程設(shè)置為進(jìn)程組 id.
    某個(gè)子進(jìn)程調(diào)用 exec 函數(shù)之后, 就不能再將該子進(jìn)程的 id 作為進(jìn)程組 id.
===============================================================================
b)會(huì)話
一次登錄形成一個(gè)會(huì)話
一個(gè)會(huì)話可包含多個(gè)進(jìn)程組, 但只能有一個(gè)前臺(tái)進(jìn)程組.
setsid 可建立一個(gè)新的會(huì)話
===============================================================================
       #include
       pid_t setsid(void);
-------------------------------------------------------------------------------
    如果調(diào)用進(jìn)程不是進(jìn)程組的領(lǐng)頭進(jìn)程, 該函數(shù)才能建立新的會(huì)話.
    調(diào)用 setsid 之后, 進(jìn)程成為新會(huì)話的領(lǐng)頭進(jìn)程.
    進(jìn)程成為新進(jìn)程組的領(lǐng)頭進(jìn)程.
    進(jìn)程失去控制終端
===============================================================================
c)控制終端
會(huì)話的領(lǐng)頭進(jìn)程打開(kāi)一個(gè)終端之后, 該終端就成為該會(huì)話的控制終端 (SVR4/Linux)
與控制終端建立連接的會(huì)話領(lǐng)頭進(jìn)程稱為控制進(jìn)程 (session leader)
一個(gè)會(huì)話只能有一個(gè)控制終端
產(chǎn)生在控制終端上的輸入和信號(hào)將發(fā)送給會(huì)話的前臺(tái)進(jìn)程組中的所有進(jìn)程
終端上的連接斷開(kāi)時(shí) (比如網(wǎng)絡(luò)斷開(kāi)或 Modem 斷開(kāi)), 掛起信號(hào)將發(fā)送到控制進(jìn)程(session leader)

]]>
ulimit命令http://www.tkk7.com/tinysun/archive/2010/08/06/328122.html何克勤何克勤Fri, 06 Aug 2010 05:34:00 GMThttp://www.tkk7.com/tinysun/archive/2010/08/06/328122.htmlhttp://www.tkk7.com/tinysun/comments/328122.htmlhttp://www.tkk7.com/tinysun/archive/2010/08/06/328122.html#Feedback0http://www.tkk7.com/tinysun/comments/commentRss/328122.htmlhttp://www.tkk7.com/tinysun/services/trackbacks/328122.html
1,說(shuō)明:
ulimit用于shell啟動(dòng)進(jìn)程所占用的資源.
2,類別:
shell內(nèi)建命令
3,語(yǔ)法格式:
ulimit [-acdfHlmnpsStvw] [size]

[john@localhost ~]$ ulimit -a
core file size (blocks, -c) 0
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 0
file size (blocks, -f) unlimited
pending signals (-i) 4096
max locked memory (kbytes, -l) 32
max memory size (kbytes, -m) unlimited
open files (-n) 1024
pipe size (512 bytes, -p) 8
POSIX message queues (bytes, -q) 819200
real-time priority (-r) 0
stack size (kbytes, -s) 10240
cpu time (seconds, -t) unlimited
max user processes (-u) 4096
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited

4,參數(shù)介紹:
-H 設(shè)置硬件資源限制.
-S 設(shè)置軟件資源限制.
-a 顯示當(dāng)前所有的資源限制.
-c size:設(shè)置core文件的最大值.單位:blocks
-d size:設(shè)置數(shù)據(jù)段的最大值.單位:kbytes
-f size:設(shè)置創(chuàng)建文件的最大值.單位:blocks
-l size:設(shè)置在內(nèi)存中鎖定進(jìn)程的最大值.單位:kbytes
-m size:設(shè)置可以使用的常駐內(nèi)存的最大值.單位:kbytes
-n size:設(shè)置內(nèi)核可以同時(shí)打開(kāi)的文件描述符的最大值.單位:n
-p size:設(shè)置管道緩沖區(qū)的最大值.單位:kbytes
-s size:設(shè)置堆棧的最大值.單位:kbytes
-t size:設(shè)置CPU使用時(shí)間的最大上限.單位:seconds
-v size:設(shè)置虛擬內(nèi)存的最大值.單位:kbytes 5,簡(jiǎn)單實(shí)例:
5.舉例
在Linux下寫(xiě)程序的時(shí)候,如果程序比較大,經(jīng)常會(huì)遇到“段錯(cuò)誤”(segmentation fault)這樣的問(wèn)題,這主要就是由于Linux系統(tǒng)初始的堆棧大小(stack size)太小的緣故,一般為10M。我一般把stack size設(shè)置成256M,這樣就沒(méi)有段錯(cuò)誤了!命令為:
ulimit -s 262140
如果要系統(tǒng)自動(dòng)記住這個(gè)配置,就編輯/etc/profile文件,在 “ulimit -S -c 0 > /dev/null 2>&1”行下,添加“ulimit -s 262140”,保存重啟系統(tǒng)就可以了!
1]在RH8的環(huán)境文件/etc/profile中,我們可以看到系統(tǒng)是如何配置ulimit的:
#grep ulimit /etc/profile
ulimit -S -c 0 > /dev/null 2>&1
這條語(yǔ)句設(shè)置了對(duì)軟件資源和對(duì)core文件大小的設(shè)置
2]如果我們想要對(duì)由shell創(chuàng)建的文件大小作些限制,如:
#ll h
-rw-r--r-- 1 lee lee 150062 7月 22 02:39 h
#ulimit -f 100 #設(shè)置創(chuàng)建文件的最大塊(一塊=512字節(jié))
#cat h>newh
File size limit exceeded
#ll newh
-rw-r--r-- 1 lee lee 51200 11月 8 11:47 newh
文件h的大小是150062字節(jié),而我們?cè)O(shè)定的創(chuàng)建文件的大小是512字節(jié)x100塊=51200字節(jié)
當(dāng)然系統(tǒng)就會(huì)根據(jù)你的設(shè)置生成了51200字節(jié)的newh文件.
3]可以像實(shí)例1]一樣,把你要設(shè)置的ulimit放在/etc/profile這個(gè)環(huán)境文件中.
用途
設(shè)置或報(bào)告用戶資源極限。
語(yǔ)法
ulimit [ -H ] [ -S ] [ -a ] [ -c ] [ -d ] [ -f ] [ -m ] [ -n ] [ -s ] [ -t ] [ Limit ]
描述
ulimit 命令設(shè)置或報(bào)告用戶進(jìn)程資源極限,如 /etc/security/limits 文件所定義。文件包含以下缺省值極限:
fsize = 2097151
core = 2097151
cpu = -1
data = 262144
rss = 65536
stack = 65536
nofiles = 2000
當(dāng)新用戶添加到系統(tǒng)中時(shí),這些值被作為缺省值使用。當(dāng)向系統(tǒng)中添加用戶時(shí),以上值通過(guò) mkuser 命令設(shè)置,或通過(guò) chuser 命令更改。
極限分為軟性或硬性。通過(guò) ulimit 命令,用戶可將軟極限更改到硬極限的最大設(shè)置值。要更改資源硬極限,必須擁有 root 用戶權(quán)限。
很多系統(tǒng)不包括以上一種或數(shù)種極限。 特定資源的極限在指定 Limit 參數(shù)時(shí)設(shè)定。Limit 參數(shù)的值可以是每個(gè)資源中指定單元中的數(shù)字,或者為值 unlimited。要將特定的 ulimit 設(shè)置為 unlimited,可使用詞 unlimited。
注:在 /etc/security/limits 文件中設(shè)置缺省極限就是設(shè)置了系統(tǒng)寬度極限, 而不僅僅是創(chuàng)建用戶時(shí)用戶所需的極限。
省略 Limit 參數(shù)時(shí),將會(huì)打印出當(dāng)前資源極限。除非用戶指定 -H 標(biāo)志,否則打印出軟極限。當(dāng)用戶指定一個(gè)以上資源時(shí),極限名稱和單元在值之前打印。如果未給予選項(xiàng),則假定帶有了 -f 標(biāo)志。
由于 ulimit 命令影響當(dāng)前 shell 環(huán)境,所以它將作為 shell 常規(guī)內(nèi)置命令提供。如果在獨(dú)立的命令執(zhí)行環(huán)境中調(diào)用該命令,則不影響調(diào)用者環(huán)境的文件大小極限。以下示例中正是這種情況:
nohup ulimit -f 10000
env ulimit 10000
一旦通過(guò)進(jìn)程減少了硬極限,若無(wú) root 特權(quán)則無(wú)法增加,即使返回到原值也不可能。
關(guān)于用戶和系統(tǒng)資源極限的更多信息,請(qǐng)參見(jiàn) AIX 5L Version 5.3 Technical Reference: Base Operating System and Extensions Volume 1 中的 getrlimit、setrlimit 或 vlimit 子例程。
標(biāo)志
-a 列出所有當(dāng)前資源極限。
-c 以 512 字節(jié)塊為單位,指定核心轉(zhuǎn)儲(chǔ)的大小。
-d 以 K 字節(jié)為單位指定數(shù)據(jù)區(qū)域的大小。
-f 使用 Limit 參數(shù)時(shí)設(shè)定文件大小極限(以塊計(jì)),或者在未指定參數(shù)時(shí)報(bào)告文件大小極限。缺省值為 -f 標(biāo)志。
-H 指定設(shè)置某個(gè)給定資源的硬極限。如果用戶擁有 root 用戶權(quán)限,可以增大硬極限。任何用戶均可減少硬極限。
-m 以 K 字節(jié)為單位指定物理存儲(chǔ)器的大小。
-n 指定一個(gè)進(jìn)程可以擁有的文件描述符的數(shù)量的極限。
-s 以 K 字節(jié)為單位指定堆棧的大小。
-S 指定為給定的資源設(shè)置軟極限。軟極限可增大到硬極限的值。如果 -H 和 -S 標(biāo)志均未指定,極限適用于以上二者。
-t 指定每個(gè)進(jìn)程所使用的秒數(shù)。
退出狀態(tài)
返回以下退出值:
0 成功完成。
>0 拒絕對(duì)更高的極限的請(qǐng)求,或發(fā)生錯(cuò)誤。
示例
要將文件大小極限設(shè)置為 51,200 字節(jié),輸入:
ulimit -f 100


]]>
write的奧秘 轉(zhuǎn)http://www.tkk7.com/tinysun/archive/2010/08/06/328099.html何克勤何克勤Fri, 06 Aug 2010 02:00:00 GMThttp://www.tkk7.com/tinysun/archive/2010/08/06/328099.htmlhttp://www.tkk7.com/tinysun/comments/328099.htmlhttp://www.tkk7.com/tinysun/archive/2010/08/06/328099.html#Feedback0http://www.tkk7.com/tinysun/comments/commentRss/328099.htmlhttp://www.tkk7.com/tinysun/services/trackbacks/328099.html來(lái)源:http://www.linuxforum.net/doc/write-coly.html

摘要:介紹了一個(gè)簡(jiǎn)單的字符設(shè)備驅(qū)動(dòng)程序,深入剖析了write函數(shù)的工作原理

     在Linux下我們?cè)谑褂迷O(shè)備的時(shí)候,都會(huì)用到write這個(gè)函數(shù),通過(guò)這個(gè)函數(shù)我們可以象使
用文件那樣向設(shè)備傳送數(shù)據(jù)。可是為什么用戶使用write函數(shù)就可以把數(shù)據(jù)寫(xiě)到設(shè)備里面
去,這個(gè)過(guò)程到底是怎么實(shí)現(xiàn)的呢?

這個(gè)奧秘就在于設(shè)備驅(qū)動(dòng)程序的write實(shí)現(xiàn)中,這里我結(jié)合一些源代碼來(lái)解釋如何使得一
個(gè)簡(jiǎn)簡(jiǎn)單單的write函數(shù)能夠完成向設(shè)備里面寫(xiě)數(shù)據(jù)的復(fù)雜過(guò)程。

這里的源代碼主要來(lái)自兩個(gè)地方。第一是oreilly出版的《Linux device driver》中的
實(shí)例,第二是Linux Kernel 2.2.14核心源代碼。我只列出了其中相關(guān)部分的內(nèi)容,如果
讀者有興趣,也可以查閱其它源代碼。不過(guò)我不是在講解如何編寫(xiě)設(shè)備驅(qū)動(dòng)程序,所以不
會(huì)對(duì)每一個(gè)細(xì)節(jié)都進(jìn)行說(shuō)明,再說(shuō)有些地方我覺(jué)得自己還沒(méi)有吃透。

由于《Linux device driver》一書(shū)中的例子對(duì)于我們還是復(fù)雜了一些,我將其中的一個(gè)
例程簡(jiǎn)化了一下。這個(gè)驅(qū)動(dòng)程序支持這樣一個(gè)設(shè)備:核心空間中的一個(gè)長(zhǎng)度為10的數(shù)組
kbuf[10]。我們可以通過(guò)用戶程序open它,read它,write它,close它。這個(gè)設(shè)備的名
字我稱為short_t。

現(xiàn)在言歸正傳。
對(duì)于一個(gè)設(shè)備,它可以在/dev下面存在一個(gè)對(duì)應(yīng)的邏輯設(shè)備節(jié)點(diǎn),這個(gè)節(jié)點(diǎn)以文件的形式
存在,但它不是普通意義上的文件,它是設(shè)備文件,更確切的說(shuō),它是設(shè)備節(jié)點(diǎn)。這個(gè)節(jié)
點(diǎn)是通過(guò)mknod命令建立的,其中指定了主設(shè)備號(hào)和次設(shè)備號(hào)。主設(shè)備號(hào)表明了某一類設(shè)
備,一般對(duì)應(yīng)著確定的驅(qū)動(dòng)程序;次設(shè)備號(hào)一般是區(qū)分是標(biāo)明不同屬性,例如不同的使用
方法,不同的位置,不同的操作。這個(gè)設(shè)備號(hào)是從/proc/devices文件中獲得的,所以一
般是先有驅(qū)動(dòng)程序在內(nèi)核中,才有設(shè)備節(jié)點(diǎn)在目錄中。這個(gè)設(shè)備號(hào)(特指主設(shè)備號(hào))的主
要作用,就是聲明設(shè)備所使用的驅(qū)動(dòng)程序。驅(qū)動(dòng)程序和設(shè)備號(hào)是一一對(duì)應(yīng)的,當(dāng)你打開(kāi)一
個(gè)設(shè)備文件時(shí),操作系統(tǒng)就已經(jīng)知道這個(gè)設(shè)備所對(duì)應(yīng)的驅(qū)動(dòng)程序是哪一個(gè)了。這個(gè)"知道"
的過(guò)程后面就講。

我們?cè)僬f(shuō)說(shuō)驅(qū)動(dòng)程序的基本結(jié)構(gòu)吧。這里我只介紹動(dòng)態(tài)模塊型驅(qū)動(dòng)程序(就是我們使用
insmod加載到核心中并使用rmmod卸載的那種),因?yàn)槲抑皇煜み@種結(jié)構(gòu)。
模塊化的驅(qū)動(dòng)程序由兩個(gè)函數(shù)是固定的:int init_module(void) ;void
cleanup_module(void)。前者在insmod的時(shí)候執(zhí)行,后者在rmmod的時(shí)候執(zhí)行。
init_nodule在執(zhí)行的時(shí)候,進(jìn)行一些驅(qū)動(dòng)程序初始化的工作,其中最主要的工作有三
件:注冊(cè)設(shè)備;申請(qǐng)I/O端口地址范圍;申請(qǐng)中斷IRQ。這里和我們想知道的事情相關(guān)的只
有注冊(cè)設(shè)備。

下面是一個(gè)典型的init_module函數(shù):

int init_module(void){
int result = check_region(short_base,1);/* 察看端口地址*/
……
request_region(short_base,1,"short"); /* 申請(qǐng)端口地址*/
……
result = register_chrdev(short_major, "short", &short_fops); /* 注冊(cè)設(shè)備
*/
……
result = request_irq(short_irq, short_interrupt, SA_INTERRUPT, "short",
NULL); /* 申請(qǐng)IRQ */
……
return 0;
}/* init_module*/

上面這個(gè)函數(shù)我只保留了最重要的部分,其中最重要的函數(shù)是
result = register_chrdev(short_major, "short", &short_fops);
這是一個(gè)驅(qū)動(dòng)程序的精髓所在!!當(dāng)你執(zhí)行indmod命令時(shí),這個(gè)函數(shù)可以完成三件大事:
第一,申請(qǐng)主設(shè)備號(hào)(short_major),或者指定,或者動(dòng)態(tài)分配;第二,在內(nèi)核中注冊(cè)設(shè)
備的名字("short");第三,指定fops方法(&short_fops)。其中所指定的fops方法就是
我們對(duì)設(shè)備進(jìn)行操作的方法(例如read,write,seek,dir,open,release等),如何實(shí)現(xiàn)
這些方法,是編寫(xiě)設(shè)備驅(qū)動(dòng)程序大部分工作量所在。

現(xiàn)在我們就要接觸關(guān)鍵部分了--如何實(shí)現(xiàn)fops方法。
我們都知道,每一個(gè)文件都有一個(gè)file的結(jié)構(gòu),在這個(gè)結(jié)構(gòu)中有一個(gè)file_operations的
結(jié)構(gòu)體,這個(gè)結(jié)構(gòu)體指明了能夠?qū)υ撐募M(jìn)行的操作。

下面是一個(gè)典型的file_operations結(jié)構(gòu):
struct file_operations {
loff_t (*llseek) (struct file *, loff_t, int);
ssize_t (*read) (struct file *, char *, size_t, loff_t *);
ssize_t (*write) (struct file *, const char *, size_t, loff_t *);
int (*readdir) (struct file *, void *, filldir_t);
unsigned int (*poll) (struct file *, struct poll_table_struct *);
int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned
long);
int (*mmap) (struct file *, struct vm_area_struct *);
int (*open) (struct inode *, struct file *);
int (*flush) (struct file *);
int (*release) (struct inode *, struct file *);
int (*fsync) (struct file *, struct dentry *);
int (*fasync) (int, struct file *, int);
int (*check_media_change) (kdev_t dev);
int (*revalidate) (kdev_t dev);
int (*lock) (struct file *, int, struct file_lock *);
};

我們可以看到它實(shí)際上就是許多文件操作的函數(shù)指針,其中就有write,其它的我們就不
去管它了。這個(gè)write指針在實(shí)際的驅(qū)動(dòng)程序中會(huì)以程序員所實(shí)現(xiàn)的函數(shù)名字出現(xiàn),它指
向程序員實(shí)現(xiàn)的設(shè)備write操作函數(shù)。下面就是一個(gè)實(shí)際的例子,這個(gè)write函數(shù)可以向核
心內(nèi)存的一個(gè)數(shù)組里輸入一個(gè)字符串。

int short_write (struct inode *inode, struct file *filp, const char *buf,
int count){
int retval = count;
extern unsigned char kbuf[10];

if(count>10)
count=10;
copy_from_user(kbuf, buf, count);
return retval;
}/* short_write */
設(shè)備short_t對(duì)應(yīng)的fops方法是這樣聲明的:
struct file_operations short_fops = {
NULL, /* short_lseek */
short_read,
short_write,
NULL, /* short_readdir */
NULL, /* short_poll */
NULL, /* short_ioctl */
NULL, /* short_mmap */
short_open,
short_release,
NULL, /* short_fsync */
NULL, /* short_fasync */
/* nothing more, fill with NULLs */
};

其中NULL的項(xiàng)目就是不提供這個(gè)功能。所以我們可以看出short_t設(shè)備只提供了
read,write,open,release功能。其中write功能我們?cè)谏厦嬉呀?jīng)實(shí)現(xiàn)了,具體的實(shí)現(xiàn)函
數(shù)起名為short_write。這些函數(shù)就是真正對(duì)設(shè)備進(jìn)行操作的函數(shù),這就是驅(qū)動(dòng)程序的一
大好處:不管你實(shí)現(xiàn)的時(shí)候是多么的復(fù)雜,但對(duì)用戶來(lái)看,就是那些常用的文件操作函數(shù)。

但是我們可以看到,驅(qū)動(dòng)程序里的write函數(shù)有四個(gè)參數(shù),函數(shù)格式如下:
short_write (struct inode *inode, struct file *filp, const char *buf, int count)
而用戶程序中的write函數(shù)只有三個(gè)參數(shù),函數(shù)格式如下:
write(inf fd, char *buf, int count)
那他們兩個(gè)是怎么聯(lián)系在一起的呢?這就要靠操作系統(tǒng)核心中的函數(shù)sys_write了,下面
是Linux Kernel 2.2.14中sys_write中的源代碼:
asmlinkage ssize_t sys_write(unsigned int fd, const char * buf, size_t count)
{
ssize_t ret;
struct file * file;
struct inode * inode;
ssize_t (*write)(struct file *, const char *, size_t, loff_t *); /* 指向
驅(qū)動(dòng)程序中的wirte函數(shù)的指針*/

lock_kernel();
ret = -EBADF;
file = fget(fd); /* 通過(guò)文件描述符得到文件指針 */
if (!file)
goto bad_file;
if (!(file->f_mode & FMODE_WRITE))
goto out;
inode = file->f_dentry->d_inode; /* 得到inode信息 */
ret = locks_verify_area(FLOCK_VERIFY_WRITE, inode, file, file->f_pos,
count);
if (ret)
goto out;
ret = -EINVAL;
if (!file->f_op || !(write = file->f_op->write)) /* 將函數(shù)開(kāi)始時(shí)聲明的
write函數(shù)指針指向fops方法中對(duì)應(yīng)的write函數(shù) */
goto out;
down(&inode->i_sem);
ret = write(file, buf, count, &file->f_pos); /* 使用驅(qū)動(dòng)程序中的write函數(shù)
將數(shù)據(jù)輸入設(shè)備,注意看,這里就是四個(gè)參數(shù)了 */
up(&inode->i_sem);
out:
fput(file);
bad_file:
unlock_kernel();
return ret;
}

我寫(xiě)了一個(gè)簡(jiǎn)單的程序來(lái)測(cè)試這個(gè)驅(qū)動(dòng)程序,該程序源代碼節(jié)選如下(該省的我都省了):

main(){
int fd,count=0;
unsigned char buf[10];
fd=open("/dev/short_t",O_RDWR);
printf("input string:");
scanf("%s",buf);
count=strlen(buf);
if(count>10)
count=10;
count=write(fd,buf,count);
close(fd);
return 1;
}

現(xiàn)在我們就演示一下用戶使用write函數(shù)將數(shù)據(jù)寫(xiě)到設(shè)備里面這個(gè)過(guò)程到底是怎么實(shí)現(xiàn)的:
1,insmod驅(qū)動(dòng)程序。驅(qū)動(dòng)程序申請(qǐng)?jiān)O(shè)備名和主設(shè)備號(hào),這些可以在/proc/devieces中獲得。
2,從/proc/devices中獲得主設(shè)備號(hào),并使用mknod命令建立設(shè)備節(jié)點(diǎn)文件。這是通過(guò)主
設(shè)備號(hào)將設(shè)備節(jié)點(diǎn)文件和設(shè)備驅(qū)動(dòng)程序聯(lián)系在一起。設(shè)備節(jié)點(diǎn)文件中的file屬性中指明了
驅(qū)動(dòng)程序中fops方法實(shí)現(xiàn)的函數(shù)指針。
3,用戶程序使用open打開(kāi)設(shè)備節(jié)點(diǎn)文件,這時(shí)操作系統(tǒng)內(nèi)核知道該驅(qū)動(dòng)程序工作了,就
調(diào)用fops方法中的open函數(shù)進(jìn)行相應(yīng)的工作。open方法一般返回的是文件標(biāo)示符,實(shí)際
上并不是直接對(duì)它進(jìn)行操作的,而是有操作系統(tǒng)的系統(tǒng)調(diào)用在背后工作。
4,當(dāng)用戶使用write函數(shù)操作設(shè)備文件時(shí),操作系統(tǒng)調(diào)用sys_write函數(shù),該函數(shù)首先通
過(guò)文件標(biāo)示符得到設(shè)備節(jié)點(diǎn)文件對(duì)應(yīng)的inode指針和flip指針。inode指針中有設(shè)備號(hào)信
息,能夠告訴操作系統(tǒng)應(yīng)該使用哪一個(gè)設(shè)備驅(qū)動(dòng)程序,flip指針中有fops信息,可以告訴
操作系統(tǒng)相應(yīng)的fops方法函數(shù)在那里可以找到。
5,然后這時(shí)sys_write才會(huì)調(diào)用驅(qū)動(dòng)程序中的write方法來(lái)對(duì)設(shè)備進(jìn)行寫(xiě)的操作。
其中1-3都是在用戶空間進(jìn)行的,4-5是在核心空間進(jìn)行的。用戶的write函數(shù)和操作系統(tǒng)
的write函數(shù)通過(guò)系統(tǒng)調(diào)用sys_write聯(lián)系在了一起。
注意:
對(duì)于塊設(shè)備來(lái)說(shuō),還存在寫(xiě)的模式的問(wèn)題,這應(yīng)該是由GNU C庫(kù)來(lái)解決的,這里不予討
論,因?yàn)槲覜](méi)有看過(guò)GNU C庫(kù)的源代碼。
另外,這是一個(gè)測(cè)試版的文章,請(qǐng)各位朋友們多提意見(jiàn)和建議,非常感謝!
http://blog.csdn.net/lphpc/category/170686.aspx



]]>
UNIX環(huán)境高級(jí)編程(APUE) 總結(jié)http://www.tkk7.com/tinysun/archive/2010/07/29/327454.html何克勤何克勤Thu, 29 Jul 2010 08:26:00 GMThttp://www.tkk7.com/tinysun/archive/2010/07/29/327454.htmlhttp://www.tkk7.com/tinysun/comments/327454.htmlhttp://www.tkk7.com/tinysun/archive/2010/07/29/327454.html#Feedback0http://www.tkk7.com/tinysun/comments/commentRss/327454.htmlhttp://www.tkk7.com/tinysun/services/trackbacks/327454.html閱讀全文

]]>
學(xué)會(huì)用core dump調(diào)試程序錯(cuò)誤http://www.tkk7.com/tinysun/archive/2010/07/29/327453.html何克勤何克勤Thu, 29 Jul 2010 08:21:00 GMThttp://www.tkk7.com/tinysun/archive/2010/07/29/327453.htmlhttp://www.tkk7.com/tinysun/comments/327453.htmlhttp://www.tkk7.com/tinysun/archive/2010/07/29/327453.html#Feedback0http://www.tkk7.com/tinysun/comments/commentRss/327453.htmlhttp://www.tkk7.com/tinysun/services/trackbacks/327453.html      在使用半導(dǎo)體作為內(nèi)存的材料前,人類是利用線圈當(dāng)作內(nèi)存的材料(發(fā)明者為王安),線圈就叫作core ,用線圈做的內(nèi)存就叫作“core memory”。(線圈的單詞應(yīng)該是coil,呵呵)如今,半導(dǎo)體工業(yè)澎勃發(fā)展,已經(jīng)沒(méi)有人用線圈當(dāng)內(nèi)存了,不過(guò),在許多情況下,人們還是把內(nèi)存叫作“core”。 所以注意了:這里的core不是核心,而是內(nèi)存。不過(guò)結(jié)合實(shí)際來(lái)看,好像也有點(diǎn)“內(nèi)核所占內(nèi)存”的意思。
      core dump又是什么東東? 我 們?cè)陂_(kāi)發(fā)(或使用)一個(gè)程序時(shí),最怕的就是程序莫明其妙地掛掉。雖然系統(tǒng)沒(méi)事,但我們下次仍可能遇到相同的問(wèn)題。于是,這時(shí)操作系統(tǒng)就會(huì)把程序掛掉時(shí)的 內(nèi)存內(nèi)容寫(xiě)入一個(gè)叫做core的文件里(這個(gè)寫(xiě)入的動(dòng)作就叫dump,dump的英語(yǔ)意思是垃圾、傾倒。從這里來(lái)看,這些內(nèi)存的內(nèi)容是程序錯(cuò)誤運(yùn)行的結(jié) 果,所以算是垃圾,把他弄出來(lái)就好比從大的內(nèi)存池里“傾倒”。),以便于我們調(diào)試。這個(gè)過(guò)程,因此叫做core dump。

1. 在嵌入式系統(tǒng)中,有時(shí)core dump直接從串口打印出來(lái),結(jié)合objdump查找ra和epa地址,運(yùn)用棧回溯,可以找到程序出錯(cuò)的地方。

2. 在一般Linux系統(tǒng)中,默認(rèn)是不會(huì)產(chǎn)生core dump文件的,通過(guò)ulimit -c來(lái)查看core dump文件的大小,一般開(kāi)始是0,可以設(shè)置core文件大小,ulimit -c 1024(kbytes單位)或者ulimit -c unlimited。

3. core dump文件輸出設(shè)置,一般默認(rèn)是當(dāng)前目錄,可以在/proc/sys/kernel中找到core-user-pid,通過(guò)

echo "1" > /proc/sys/kernel/core-user-pid使core文件名加上pid號(hào),還可以用

mkdir -p /root/corefile

echo "/root/corefile/core-%e-%p-%t" > /proc/sys/kernel/core-pattern控制core文件保存位置和文件名格式。

以下是參數(shù)列表:
    %p - insert pid into filename 添加pid
    %u - insert current uid into filename 添加當(dāng)前uid
    %g - insert current gid into filename 添加當(dāng)前gid
    %s - insert signal that caused the coredump into the filename 添加導(dǎo)致產(chǎn)生core的信號(hào)
    %t - insert UNIX time that the coredump occurred into filename 添加core文件生成時(shí)的unix時(shí)間
    %h - insert hostname where the coredump happened into filename 添加主機(jī)名
    %e - insert coredumping executable name into filename 添加命令名

4. 用gdb查看core文件:
下面我們可以在發(fā)生運(yùn)行時(shí)信號(hào)引起的錯(cuò)誤時(shí)發(fā)生core dump了.編譯時(shí)加上-g
發(fā)生core dump之后, 用gdb進(jìn)行查看core文件的內(nèi)容, 以定位文件中引發(fā)core dump的行.
gdb [exec file] [core file]
如:
gdb ./test test.core
在進(jìn)入gdb后, 用bt命令查看backtrace以檢查發(fā)生程序運(yùn)行到哪里, 來(lái)定位core dump的文件行.

5. 給個(gè)例子

test.c

void a()

{

   char *p = NULL;

   printf("%d\n", *p);

}

int main()

{

    a();

    return 0;

}

編譯 gcc -g -o test test.c

運(yùn)行 ./test

報(bào)segmentation fault(core dump)

gdb ./test test.core如果生成的是test.core.



]]>
C語(yǔ)言的反匯編代碼(BP,SP的關(guān)系) 轉(zhuǎn)http://www.tkk7.com/tinysun/archive/2010/07/29/327413.html何克勤何克勤Thu, 29 Jul 2010 03:41:00 GMThttp://www.tkk7.com/tinysun/archive/2010/07/29/327413.htmlhttp://www.tkk7.com/tinysun/comments/327413.htmlhttp://www.tkk7.com/tinysun/archive/2010/07/29/327413.html#Feedback0http://www.tkk7.com/tinysun/comments/commentRss/327413.htmlhttp://www.tkk7.com/tinysun/services/trackbacks/327413.html閱讀全文

]]>
gcc編譯C++程序http://www.tkk7.com/tinysun/archive/2010/07/03/325117.html何克勤何克勤Sat, 03 Jul 2010 02:08:00 GMThttp://www.tkk7.com/tinysun/archive/2010/07/03/325117.htmlhttp://www.tkk7.com/tinysun/comments/325117.htmlhttp://www.tkk7.com/tinysun/archive/2010/07/03/325117.html#Feedback0http://www.tkk7.com/tinysun/comments/commentRss/325117.htmlhttp://www.tkk7.com/tinysun/services/trackbacks/325117.html單個(gè)源文件生成可執(zhí)行程序
下面是一個(gè)保存在文件 helloworld.cpp 中一個(gè)簡(jiǎn)單的 C++ 程序的代碼:
/* helloworld.cpp */
#include <iostream>
int main(int argc,char *argv[])
{
    std::cout << "hello, world" << std::endl;
    return(0);
}
程序使用定義在頭文件 iostream 中的 cout,向標(biāo)準(zhǔn)輸出寫(xiě)入一個(gè)簡(jiǎn)單的字符串。該代碼可用以下命令編譯為可執(zhí)行文件:
 
$  g++ helloworld.cpp
編譯器 g++ 通過(guò)檢查命令行中指定的文件的后綴名可識(shí)別其為 C++ 源代碼文件。編譯器默認(rèn)的動(dòng)作:編譯源代碼文件生成對(duì)象文件(object file),鏈接對(duì)象文件和 libstdc++ 庫(kù)中的函數(shù)得到可執(zhí)行程序。然后刪除對(duì)象文件。由于命令行中未指定可執(zhí)行程序的文件名,編譯器采用默認(rèn)的 a.out。程序可以這樣來(lái)運(yùn)行:
$ ./a.out
hello, world
更普遍的做法是通過(guò) -o 選項(xiàng)指定可執(zhí)行程序的文件名。下面的命令將產(chǎn)生名為 helloworld 的可執(zhí)行文件:
$ g++ helloworld.cpp -o helloworld
在命令行中輸入程序名可使之運(yùn)行:
$ ./helloworld
hello, world
程序 g++ 是將 gcc 默認(rèn)語(yǔ)言設(shè)為 C++ 的一個(gè)特殊的版本,鏈接時(shí)它自動(dòng)使用 C++ 標(biāo)準(zhǔn)庫(kù)而不用 C 標(biāo)準(zhǔn)庫(kù)。通過(guò)遵循源碼的命名規(guī)范并指定對(duì)應(yīng)庫(kù)的名字,用 gcc 來(lái)編譯鏈接 C++ 程序是可行的,如下例所示:
$ gcc helloworld.cpp -lstdc++ -o helloworld
選項(xiàng) -l (ell) 通過(guò)添加前綴 lib 和后綴 .a 將跟隨它的名字變換為庫(kù)的名字 libstdc++.a。而后它在標(biāo)準(zhǔn)庫(kù)路徑中查找該庫(kù)。gcc 的編譯過(guò)程和輸出文件與 g++ 是完全相同的。
 
在大多數(shù)系統(tǒng)中,GCC 安裝時(shí)會(huì)安裝一名為 c++ 的程序。如果被安裝,它和 g++ 是等同,如下例所示,用法也一致:
$ c++ helloworld.cpp -o helloworld
多個(gè)源文件生成可執(zhí)行程序
如果多于一個(gè)的源碼文件在 g++ 命令中指定,它們都將被編譯并被鏈接成一個(gè)單一的可執(zhí)行文件。下面是一個(gè)名為 speak.h 的頭文件;它包含一個(gè)僅含有一個(gè)函數(shù)的類的定義:
/* speak.h */
#include <iostream>
class Speak
{
    public:
        void sayHello(const char *);
};
下面列出的是文件 speak.cpp 的內(nèi)容:包含 sayHello() 函數(shù)的函數(shù)體:
/* speak.cpp */
#include "speak.h"
void Speak::sayHello(const char *str)
{
    std::cout << "Hello " << str << "\n";
}
文件 hellospeak.cpp 內(nèi)是一個(gè)使用 Speak 類的程序:
/* hellospeak.cpp */
#include "speak.h"
int main(int argc,char *argv[])
{
    Speak speak;
    speak.sayHello("world");
    return(0);
}
下面這條命令將上述兩個(gè)源碼文件編譯鏈接成一個(gè)單一的可執(zhí)行程序:
$ g++ hellospeak.cpp speak.cpp -o hellospeak
PS:這里說(shuō)一下為什么在命令中沒(méi)有提到“speak.h“該文件(原因是:在“speak.cpp“中包含有”#include"speak.h"“這句代碼,它的意思是搜索系統(tǒng)頭文件目錄之前將先在當(dāng)前目錄中搜索文件“speak.h“。而”speak.h“正在該目錄中,不用再在命令中指定了)。
源文件生成對(duì)象文件
選項(xiàng) -c 用來(lái)告訴編譯器編譯源代碼但不要執(zhí)行鏈接,輸出結(jié)果為對(duì)象文件。文件默認(rèn)名與源碼文件名相同,只是將其后綴變?yōu)?.o。例如,下面的命令將編譯源碼文件 hellospeak.cpp 并生成對(duì)象文件 hellospeak.o:
$ g++ -c hellospeak.cpp
命令 g++ 也能識(shí)別 .o 文件并將其作為輸入文件傳遞給鏈接器。下列命令將編譯源碼文件為對(duì)象文件并將其鏈接成單一的可執(zhí)行程序:
$ g++ -c hellospeak.cpp
$ g++ -c speak.cpp
$ g++ hellospeak.o speak.o -o hellospeak
選項(xiàng) -o 不僅僅能用來(lái)命名可執(zhí)行文件。它也用來(lái)命名編譯器輸出的其他文件。例如:除了中間的對(duì)象文件有不同的名字外,下列命令生將生成和上面完全相同的可執(zhí)行文件:
$ g++ -c hellospeak.cpp -o hspk1.o
$ g++ -c speak.cpp -o hspk2.o
$ g++ hspk1.o hspk2.o -o hellospeak
編譯預(yù)處理
選項(xiàng) -E 使 g++ 將源代碼用編譯預(yù)處理器處理后不再執(zhí)行其他動(dòng)作。下面的命令預(yù)處理源碼文件 helloworld.cpp 并將結(jié)果顯示在標(biāo)準(zhǔn)輸出中:
$ g++ -E helloworld.cpp
本文前面所列出的 helloworld.cpp 的源代碼,僅僅有六行,而且該程序除了顯示一行文字外什么都不做,但是,預(yù)處理后的版本將超過(guò) 1200 行。這主要是因?yàn)轭^文件 iostream 被包含進(jìn)來(lái),而且它又包含了其他的頭文件,除此之外,還有若干個(gè)處理輸入和輸出的類的定義。
預(yù)處理過(guò)的文件的 GCC 后綴為 .ii,它可以通過(guò) -o 選項(xiàng)來(lái)生成,例如:
$ gcc -E helloworld.cpp -o helloworld.ii
生成匯編代碼
選項(xiàng) -S 指示編譯器將程序編譯成匯編語(yǔ)言,輸出匯編語(yǔ)言代碼而后結(jié)束。下面的命令將由 C++ 源碼文件生成匯編語(yǔ)言文件 helloworld.s:
$ g++ -S helloworld.cpp
生成的匯編語(yǔ)言依賴于編譯器的目標(biāo)平臺(tái)。
創(chuàng)建靜態(tài)庫(kù)
靜態(tài)庫(kù)是編譯器生成的一系列對(duì)象文件的集合。鏈接一個(gè)程序時(shí)用庫(kù)中的對(duì)象文件還是目錄中的對(duì)象文件都是一樣的。庫(kù)中的成員包括普通函數(shù),類定義,類的對(duì)象實(shí)例等等。靜態(tài)庫(kù)的另一個(gè)名字叫歸檔文件(archive),管理這種歸檔文件的工具叫 ar 。
在下面的例子中,我們先創(chuàng)建兩個(gè)對(duì)象模塊,然后用其生成靜態(tài)庫(kù)。
頭文件 say.h 包含函數(shù) sayHello() 的原型和類 Say 的定義:
/* say.h */
#include <iostream>
void sayhello(void);
class Say {
    private:
        char *string;
    public:
        Say(char *str)
        {
            string = str;
        }
        void sayThis(const char *str)
        {
            std::cout << str << " from a static library\n";
        }
        void sayString(void);
};
下面是文件 say.cpp 是我們要加入到靜態(tài)庫(kù)中的兩個(gè)對(duì)象文件之一的源碼。它包含 Say 類中 sayString() 函數(shù)的定義體;類 Say 的一個(gè)實(shí)例 librarysay 的聲明也包含在內(nèi):
/* say.cpp */
#include "say.h"
void Say::sayString()
{
    std::cout << string << "\n";
}
Say librarysay("Library instance of Say");源碼文件 syshello.cpp 是我們要加入到靜態(tài)庫(kù)中的第二個(gè)對(duì)象文件的源碼。它包含函數(shù) sayhello() 的定義:
/* sayhello.cpp */
#include "say.h"
void sayhello()
{
    std::cout << "hello from a static library\n";
}
下面的命令序列將源碼文件編譯成對(duì)象文件,命令 ar 將其存進(jìn)庫(kù)中:
$ g++ -c sayhello.cpp
$ g++ -c say.cpp
$ ar -r libsay.a sayhello.o say.o
程序 ar 配合參數(shù) -r 創(chuàng)建一個(gè)新庫(kù) libsay.a 并將命令行中列出的對(duì)象文件插入。采用這種方法,如果庫(kù)不存在的話,參數(shù) -r 將創(chuàng)建一個(gè)新的庫(kù),而如果庫(kù)存在的話,將用新的模塊替換原來(lái)的模塊。
下面是主程序 saymain.cpp,它調(diào)用庫(kù) libsay.a 中的代碼:
/* saymain.cpp */
#include "say.h"
int main(int argc,char *argv[])
{
    extern Say librarysay;
    Say localsay = Say("Local instance of Say");
    sayhello();
    librarysay.sayThis("howdy");
    librarysay.sayString();
    localsay.sayString();
    return(0);
}
該程序可以下面的命令來(lái)編譯和鏈接:
$ g++ saymain.cpp libsay.a -o saymain
程序運(yùn)行時(shí),產(chǎn)生以下輸出:
hello from a static library
howdy from a static library
Library instance of SayLocal instance of Say


本文來(lái)自CSDN博客,轉(zhuǎn)載請(qǐng)標(biāo)明出處:http://blog.csdn.net/shanxiao528/archive/2010/05/11/5578743.aspx



]]>
kernel study noteshttp://www.tkk7.com/tinysun/archive/2010/06/28/324680.html何克勤何克勤Mon, 28 Jun 2010 08:06:00 GMThttp://www.tkk7.com/tinysun/archive/2010/06/28/324680.htmlhttp://www.tkk7.com/tinysun/comments/324680.htmlhttp://www.tkk7.com/tinysun/archive/2010/06/28/324680.html#Feedback0http://www.tkk7.com/tinysun/comments/commentRss/324680.htmlhttp://www.tkk7.com/tinysun/services/trackbacks/324680.html管道
FIFO
信號(hào) 承載信息量少, 可靠信號(hào) 不可靠信號(hào)


共享內(nèi)存 最快的IPC之一
消息隊(duì)列
信號(hào)量 不同進(jìn)程間 同一進(jìn)程不同線程間的同步

Socket 不同機(jī)器上的進(jìn)程間的通信


添加系統(tǒng)調(diào)用
添加系統(tǒng)模塊

內(nèi)存模型 分段 分頁(yè)
邏輯地址->線性地址->物理地址

所有的段寄存器 段內(nèi)偏移一樣

頁(yè)目錄 頁(yè)面 進(jìn)程私有的
虛擬地址描述符表 虛擬內(nèi)存管理 分配回收

物理內(nèi)存管理


-----
進(jìn)程


BSS
初始化的數(shù)據(jù)段
代碼段

內(nèi)核棧
控制塊

進(jìn)程Entry:
進(jìn)程的虛擬地址空間->分區(qū)->分頁(yè)
全局頁(yè)目錄pgd->pmd->page


線程同步
互斥鎖
pthread_mutex_t
pthread_mutex_initializer
pthread-mutex_int
pthread_mutex_lock
pthread_mutex_unlock

條件變量
pthread_cond_t
pthread_cond_init
ptread_con_wait
調(diào)用之前和調(diào)用之后都是上鎖的,一個(gè)條件變量關(guān)聯(lián)一個(gè)互斥鎖
函數(shù)內(nèi)部實(shí)現(xiàn)機(jī)制
解鎖
睡眠
上鎖
pthead_cond_destroy
pthread_cond_broadcast
pthead_cond_signalh



]]>
線程控制--私有數(shù)據(jù)http://www.tkk7.com/tinysun/archive/2010/05/29/322210.html何克勤何克勤Sat, 29 May 2010 07:16:00 GMThttp://www.tkk7.com/tinysun/archive/2010/05/29/322210.htmlhttp://www.tkk7.com/tinysun/comments/322210.htmlhttp://www.tkk7.com/tinysun/archive/2010/05/29/322210.html#Feedback0http://www.tkk7.com/tinysun/comments/commentRss/322210.htmlhttp://www.tkk7.com/tinysun/services/trackbacks/322210.html     線程私有數(shù)據(jù)采用了一種被稱為一鍵多值的技術(shù),即一個(gè)鍵對(duì)應(yīng)多個(gè)數(shù)值。訪問(wèn)數(shù)據(jù)時(shí)都是通過(guò)鍵值來(lái)訪問(wèn),好像是對(duì)一個(gè)變量進(jìn)行訪問(wèn),其實(shí)是在訪問(wèn)不同的數(shù)據(jù)。使用線程私有數(shù)據(jù)時(shí),首先要為每個(gè)線程數(shù)據(jù)創(chuàng)建一個(gè)相關(guān)聯(lián)的鍵。在各個(gè)線程內(nèi)部,都使用這個(gè)公用的鍵來(lái)指代線程數(shù)據(jù),但是在不同的線程中,這個(gè)鍵代表的數(shù)據(jù)是不同的。操作線程私有數(shù)據(jù)的函數(shù)主要有4個(gè):pthread_key_create(創(chuàng)建一個(gè)鍵),pthread_setspecific(為一個(gè)鍵設(shè)置線程私有數(shù)據(jù)),pthread_getspecific(從一個(gè)鍵讀取線程私有數(shù)據(jù)),pthread_key_delete(刪除一個(gè)鍵)。這幾個(gè)函數(shù)的聲明如下:
#include <pthread.h>
int pthread_key_create(pthread_key_t *key,void (*destr_function)(void *));
int pthread_setspecific(pthread_key_t key,const void *pointer));
void *pthread_getspecific(pthread_key_t key);
int pthread_key_delete(pthread_key_t key);
    pthread_key_create:從Linux的TSD池中分配一項(xiàng),將其值賦給key供以后訪問(wèn)使用,它的第一個(gè)參數(shù)key為指向鍵值的指針,第二個(gè)參數(shù)為一個(gè)函數(shù)指針,如果指針不為空,則在線程退出時(shí)將以key所關(guān)聯(lián)的數(shù)據(jù)為參數(shù)調(diào)用destr_function(),釋放分配的緩沖區(qū)。
    key一旦被創(chuàng)建,所有線程都可以訪問(wèn)它,但各線程可以根據(jù)自己的需要往key中填入不同的值,這就相當(dāng)于提供了一個(gè)同名而不同值的全局變量,一鍵多值。一鍵多值靠的是一個(gè)關(guān)鍵數(shù)據(jù)結(jié)構(gòu)數(shù)組,即TSD池其結(jié)構(gòu)如下:
static struct pthread_key_struct pthread_keys[PTHREAD_KEYS_MAX] ={{0,NULL}};
    創(chuàng)建一個(gè)TSD就相當(dāng)于將結(jié)構(gòu)數(shù)組中的某一項(xiàng)設(shè)置為“in_use”,并將其索引返回給*key,然后設(shè)置destructor函數(shù)destr_function。
    pthread_setspecific:該函數(shù)將pointer的值(不是內(nèi)容)與key相關(guān)聯(lián)。用pthread_setspecific為一個(gè)鍵指定新的線程數(shù)據(jù)時(shí),線程必須先釋放原有的線程數(shù)據(jù)用以回收空間。
    pthread_getspecific:通過(guò)該函數(shù)得到與key相關(guān)聯(lián)的數(shù)據(jù)。
    pthread_key_delete:該函數(shù)用來(lái)刪除一個(gè)鍵,鍵所占用的內(nèi)存將被釋放。需要注意的是,鍵占用的內(nèi)存被釋放,與該鍵關(guān)聯(lián)的線程數(shù)據(jù)所占用的內(nèi)存并不被釋放。因此,線程數(shù)據(jù)的釋放必須在釋放鍵之前完成。
    例8-4將實(shí)現(xiàn)如何創(chuàng)建和使用線程的私有數(shù)據(jù),具體代碼如下所示。
    例8-4
#include <stdio.h>
#include <string.h>
#include <pthread.h>

pthread_key_t key;

void * thread2(void *arg)
{
int tsd = 5;
printf("thread %d is running\n",pthread_self());
pthread_setspecific(key,(void *)tsd);
printf("thread %d returns %d\n",pthread_self(),pthread_getspecific(key));
}

void * thread1(void *arg)
{
int tsd = 0;
pthread_t thid2;

printf("thread %d is running\n",pthread_self());
pthread_setspecific(key,(void *)tsd);
pthread_create(&thid2,NULL,thread2,NULL);
sleep(2);
printf("thread %d return %d\n",pthread_self(),pthread_getspecific(key));
}

int main(void)
{
pthread_t thid1;
printf("main thread begins running\n");
pthread_key_create(&key,NULL);
pthread_create(&thid1,NULL,thread1,NULL);
sleep(5);
pthread_key_delete(key);
printf("main thread exit\n");
return 0;
}
    編譯并執(zhí)行,結(jié)果如下:
$ gcc -o 8-4 8-4.c -g -l pthread
$ ./8-4
main thread begins running
thread -1209746544 is running
thread -1218139248 is running
thread -1218139248 returns 5
thread -1209746544 return 0
main thread exit
    程序說(shuō)明:程序中,主線程創(chuàng)建了線程thread1,線程thread1創(chuàng)建了線程thread2。兩個(gè)線程分別將tsd作為線程私有數(shù)據(jù)。從程序運(yùn)行結(jié)果可以看出,兩個(gè)線程tsd的修改互不干擾,可以看出thread2先于thread1結(jié)束,線程在創(chuàng)建thread2后,睡眠3s等待thread2執(zhí)行完畢。主線程睡眠5s等待thread1結(jié)束。可以看出thread2對(duì)tsd的修改并沒(méi)影響到thread1的tsd的取值。


]]>
vi使用技巧(二):copy,paste,delete,塊編輯,redo/undohttp://www.tkk7.com/tinysun/archive/2010/05/12/320671.html何克勤何克勤Wed, 12 May 2010 02:34:00 GMThttp://www.tkk7.com/tinysun/archive/2010/05/12/320671.htmlhttp://www.tkk7.com/tinysun/comments/320671.htmlhttp://www.tkk7.com/tinysun/archive/2010/05/12/320671.html#Feedback0http://www.tkk7.com/tinysun/comments/commentRss/320671.htmlhttp://www.tkk7.com/tinysun/services/trackbacks/320671.html     yy : copy 光標(biāo)所在的行
  nyy: copy n line
    yw: copy 光標(biāo)所在的單詞
  nyw: copy 光標(biāo)所在位置到其后的n 個(gè)單詞(未必是同一行)
    y$:  copy 光標(biāo)所在位置到行尾($是行尾的標(biāo)示)
  ny$:  copy 光標(biāo)所在位置之后的n行(包括當(dāng)前行,當(dāng)前行=y$)
      p:  paste 在光標(biāo)所在位置之右
      P: --------------------------------左
2. delete, 和copy 類似
    dd : delete current line
  ndd:  delete n line
    dw: delete current word
  ndw: delete n word
    d$ : delete to the end of line.
  nd$ : delete n line. (current line = d$)
       x: delete one character(無(wú)論是ascii 還是unicode)
     nx: delete n characters.
3. block edit
    在命令模式下,輸入v 進(jìn)入塊編輯狀態(tài)
    a. 移動(dòng)光標(biāo)選定操作快
    b. c(cut), y(copy)
    c. p or P.
4. undo /redo
    u: undo
    U: 取消最近一行的改動(dòng)
    crtl +r: redo
    e!: 放棄所有改動(dòng),重新編輯。

]]>
VMware網(wǎng)絡(luò)設(shè)置(WindowsXP+虛擬Ret Hat Linux 9)http://www.tkk7.com/tinysun/archive/2010/05/04/320075.html何克勤何克勤Tue, 04 May 2010 14:14:00 GMThttp://www.tkk7.com/tinysun/archive/2010/05/04/320075.htmlhttp://www.tkk7.com/tinysun/comments/320075.htmlhttp://www.tkk7.com/tinysun/archive/2010/05/04/320075.html#Feedback0http://www.tkk7.com/tinysun/comments/commentRss/320075.htmlhttp://www.tkk7.com/tinysun/services/trackbacks/320075.html宿主機(jī):Windows XP Professinoal SP2
VMware:Red Hat Linux 9
網(wǎng)絡(luò):ADSL局域網(wǎng)512M

所用軟件:
VMware-workstation-6.0.0-45731.exe
Red Hat Linux 9

安裝過(guò)程:
1.安裝VMware-workstation-6.0.0-45731.exe
2.開(kāi)啟VMware.安裝Linux.
  安裝時(shí)可直接用硬盤(pán)iso文件.
  VMware=>VM=>Settings=>CD-RoM=>Use ISO image(選擇iso位置,安裝一張,在換另一個(gè)iso文件)
3.安裝VMware后,會(huì)在XP上自動(dòng)安裝2個(gè)虛擬網(wǎng)卡:
  VMware Virtual Ethernet Adapter for VMnet1
  VMware Virtual Ethernet Adapter for VMnet8
  XP運(yùn)行cmd=>ipconfig/all
  會(huì)查看到2個(gè)虛擬網(wǎng)卡及真實(shí)網(wǎng)卡的IP配置.
  因這2個(gè)虛擬網(wǎng)卡是裝在XP系統(tǒng)上的,所以XP上可以Ping通.
4.此外,虛擬機(jī)上的Linux也有1個(gè)虛擬網(wǎng)卡,它的配置文件為
  /etc/sysconfig/network-scrīpts/ifcfg-eth0
5.我們可以把Linux的網(wǎng)關(guān)設(shè)置為XP系統(tǒng)虛擬網(wǎng)卡的IP地址,
  這樣Linux就可以和XP通信了.
6.VMware=>VM=>Settings=>Ethemet=>Custom:Specific virtual network 選VMnet1(Host-only)
  將VMware Virtual Ethernet Adapter for VMnet1地址
  設(shè)置為: 192.168.0.1
          255.255.255.0
  修改ifcfg-eth0文件如下:
  DEVICE=eth0
  ōNBOOT=yes
  BOOTPROTO=none
  IPADDR=192.168.0.2
  NETMASK=255.255.255.0
  GATEWAY=192.168.0.1
  TYPE=Ethernet
  USERCTL=no
  PEERDNS=no
  NETWORK=192.168.0.0
  BROADCAST=192.168.0.255
  保存后更新下# service network restart

  也可以打開(kāi)Linux開(kāi)始=>系統(tǒng)設(shè)置=>網(wǎng)絡(luò),進(jìn)行設(shè)置.
7.這時(shí)XP和Linux可以通信了.
  XP運(yùn)行:ping 192.168.0.2  OK
8.此外SecureCRT是個(gè)不錯(cuò)的終端訪問(wèn)軟件.可以裝在XP上,訪問(wèn)Linux.
9.如果想讓虛擬機(jī)的Linux也上網(wǎng)的話.
  可以在XP=>本地連接屬性=>高級(jí)=>允許其他網(wǎng)絡(luò)用戶通過(guò)此計(jì)算機(jī)的Internet連接來(lái)連接
  (在家庭網(wǎng)絡(luò)連接中選擇VMware Network Adapter VMnet1)
  linux 主DNS 192.168.0.1

  這樣就可以在虛擬Linux上網(wǎng)了. 



]]>
使用異步 I/O 大大提高應(yīng)用程序的性能http://www.tkk7.com/tinysun/archive/2010/05/01/319860.html何克勤何克勤Sat, 01 May 2010 11:44:00 GMThttp://www.tkk7.com/tinysun/archive/2010/05/01/319860.htmlhttp://www.tkk7.com/tinysun/comments/319860.htmlhttp://www.tkk7.com/tinysun/archive/2010/05/01/319860.html#Feedback0http://www.tkk7.com/tinysun/comments/commentRss/319860.htmlhttp://www.tkk7.com/tinysun/services/trackbacks/319860.htmlLinux® 中最常用的輸入/輸出(I/O)模型是同步 I/O。在這個(gè)模型中,當(dāng)請(qǐng)求發(fā)出之后,應(yīng)用程序就會(huì)阻塞,直到請(qǐng)求滿足為止。這是很好的一種解決方案,因?yàn)檎{(diào)用應(yīng)用程序在等待 I/O 請(qǐng)求完成時(shí)不需要使用任何中央處理單元(CPU)。但是在某些情況中,I/O 請(qǐng)求可能需要與其他進(jìn)程產(chǎn)生交疊。可移植操作系統(tǒng)接口(POSIX)異步 I/O(AIO)應(yīng)用程序接口(API)就提供了這種功能。在本文中,我們將對(duì)這個(gè) API 概要進(jìn)行介紹,并來(lái)了解一下如何使用它。

AIO 簡(jiǎn)介

Linux 異步 I/O 是 Linux 內(nèi)核中提供的一個(gè)相當(dāng)新的增強(qiáng)。它是 2.6 版本內(nèi)核的一個(gè)標(biāo)準(zhǔn)特性,但是我們?cè)?2.4 版本內(nèi)核的補(bǔ)丁中也可以找到它。AIO 背后的基本思想是允許進(jìn)程發(fā)起很多 I/O 操作,而不用阻塞或等待任何操作完成。稍后或在接收到 I/O 操作完成的通知時(shí),進(jìn)程就可以檢索 I/O 操作的結(jié)果。

I/O 模型

在深入介紹 AIO API 之前,讓我們先來(lái)探索一下 Linux 上可以使用的不同 I/O 模型。這并不是一個(gè)詳盡的介紹,但是我們將試圖介紹最常用的一些模型來(lái)解釋它們與異步 I/O 之間的區(qū)別。圖 1 給出了同步和異步模型,以及阻塞和非阻塞的模型。


圖 1. 基本 Linux I/O 模型的簡(jiǎn)單矩陣
基本 Linux I/O 模型的簡(jiǎn)單矩陣

每個(gè) I/O 模型都有自己的使用模式,它們對(duì)于特定的應(yīng)用程序都有自己的優(yōu)點(diǎn)。本節(jié)將簡(jiǎn)要對(duì)其一一進(jìn)行介紹。

同步阻塞 I/O

I/O 密集型與 CPU 密集型進(jìn)程的比較

I/O 密集型進(jìn)程所執(zhí)行的 I/O 操作比執(zhí)行的處理操作更多。CPU 密集型的進(jìn)程所執(zhí)行的處理操作比 I/O 操作更多。Linux 2.6 的調(diào)度器實(shí)際上更加偏愛(ài) I/O 密集型的進(jìn)程,因?yàn)樗鼈兺ǔ?huì)發(fā)起一個(gè) I/O 操作,然后進(jìn)行阻塞,這就意味著其他工作都可以在兩者之間有效地交錯(cuò)進(jìn)行。

最常用的一個(gè)模型是同步阻塞 I/O 模型。在這個(gè)模型中,用戶空間的應(yīng)用程序執(zhí)行一個(gè)系統(tǒng)調(diào)用,這會(huì)導(dǎo)致應(yīng)用程序阻塞。這意味著應(yīng)用程序會(huì)一直阻塞,直到系統(tǒng)調(diào)用完成為止(數(shù)據(jù)傳輸完成或發(fā)生錯(cuò)誤)。調(diào)用應(yīng)用程序處于一種不再消費(fèi) CPU 而只是簡(jiǎn)單等待響應(yīng)的狀態(tài),因此從處理的角度來(lái)看,這是非常有效的。

圖 2 給出了傳統(tǒng)的阻塞 I/O 模型,這也是目前應(yīng)用程序中最為常用的一種模型。其行為非常容易理解,其用法對(duì)于典型的應(yīng)用程序來(lái)說(shuō)都非常有效。在調(diào)用 read 系統(tǒng)調(diào)用時(shí),應(yīng)用程序會(huì)阻塞并對(duì)內(nèi)核進(jìn)行上下文切換。然后會(huì)觸發(fā)讀操作,當(dāng)響應(yīng)返回時(shí)(從我們正在從中讀取的設(shè)備中返回),數(shù)據(jù)就被移動(dòng)到用戶空間的緩沖區(qū)中。然后應(yīng)用程序就會(huì)解除阻塞(read 調(diào)用返回)。


圖 2. 同步阻塞 I/O 模型的典型流程
同步阻塞 I/O 模型的典型流程

從應(yīng)用程序的角度來(lái)說(shuō),read 調(diào)用會(huì)延續(xù)很長(zhǎng)時(shí)間。實(shí)際上,在內(nèi)核執(zhí)行讀操作和其他工作時(shí),應(yīng)用程序的確會(huì)被阻塞。

同步非阻塞 I/O

同步阻塞 I/O 的一種效率稍低的變種是同步非阻塞 I/O。在這種模型中,設(shè)備是以非阻塞的形式打開(kāi)的。這意味著 I/O 操作不會(huì)立即完成,read 操作可能會(huì)返回一個(gè)錯(cuò)誤代碼,說(shuō)明這個(gè)命令不能立即滿足(EAGAINEWOULDBLOCK),如圖 3 所示。


圖 3. 同步非阻塞 I/O 模型的典型流程
同步非阻塞 I/O 模型的典型流程

非阻塞的實(shí)現(xiàn)是 I/O 命令可能并不會(huì)立即滿足,需要應(yīng)用程序調(diào)用許多次來(lái)等待操作完成。這可能效率不高,因?yàn)樵诤芏嗲闆r下,當(dāng)內(nèi)核執(zhí)行這個(gè)命令時(shí),應(yīng)用程序必須要進(jìn)行忙碌等待,直到數(shù)據(jù)可用為止,或者試圖執(zhí)行其他工作。正如圖 3 所示的一樣,這個(gè)方法可以引入 I/O 操作的延時(shí),因?yàn)閿?shù)據(jù)在內(nèi)核中變?yōu)榭捎玫接脩粽{(diào)用 read 返回?cái)?shù)據(jù)之間存在一定的間隔,這會(huì)導(dǎo)致整體數(shù)據(jù)吞吐量的降低。

異步阻塞 I/O

另外一個(gè)阻塞解決方案是帶有阻塞通知的非阻塞 I/O。在這種模型中,配置的是非阻塞 I/O,然后使用阻塞 select 系統(tǒng)調(diào)用來(lái)確定一個(gè) I/O 描述符何時(shí)有操作。使 select 調(diào)用非常有趣的是它可以用來(lái)為多個(gè)描述符提供通知,而不僅僅為一個(gè)描述符提供通知。對(duì)于每個(gè)提示符來(lái)說(shuō),我們可以請(qǐng)求這個(gè)描述符可以寫(xiě)數(shù)據(jù)、有讀數(shù)據(jù)可用以及是否發(fā)生錯(cuò)誤的通知。


圖 4. 異步阻塞 I/O 模型的典型流程 (select)
異步阻塞 I/O 模型的典型流程

select 調(diào)用的主要問(wèn)題是它的效率不是非常高。盡管這是異步通知使用的一種方便模型,但是對(duì)于高性能的 I/O 操作來(lái)說(shuō)不建議使用。

異步非阻塞 I/O(AIO)

最后,異步非阻塞 I/O 模型是一種處理與 I/O 重疊進(jìn)行的模型。讀請(qǐng)求會(huì)立即返回,說(shuō)明 read 請(qǐng)求已經(jīng)成功發(fā)起了。在后臺(tái)完成讀操作時(shí),應(yīng)用程序然后會(huì)執(zhí)行其他處理操作。當(dāng) read 的響應(yīng)到達(dá)時(shí),就會(huì)產(chǎn)生一個(gè)信號(hào)或執(zhí)行一個(gè)基于線程的回調(diào)函數(shù)來(lái)完成這次 I/O 處理過(guò)程。


圖 5. 異步非阻塞 I/O 模型的典型流程
異步非阻塞 I/O 模型的典型流程

在一個(gè)進(jìn)程中為了執(zhí)行多個(gè) I/O 請(qǐng)求而對(duì)計(jì)算操作和 I/O 處理進(jìn)行重疊處理的能力利用了處理速度與 I/O 速度之間的差異。當(dāng)一個(gè)或多個(gè) I/O 請(qǐng)求掛起時(shí),CPU 可以執(zhí)行其他任務(wù);或者更為常見(jiàn)的是,在發(fā)起其他 I/O 的同時(shí)對(duì)已經(jīng)完成的 I/O 進(jìn)行操作。

下一節(jié)將深入介紹這種模型,探索這種模型使用的 API,然后展示幾個(gè)命令。





回頁(yè)首


異步 I/O 的動(dòng)機(jī)

從前面 I/O 模型的分類中,我們可以看出 AIO 的動(dòng)機(jī)。這種阻塞模型需要在 I/O 操作開(kāi)始時(shí)阻塞應(yīng)用程序。這意味著不可能同時(shí)重疊進(jìn)行處理和 I/O 操作。同步非阻塞模型允許處理和 I/O 操作重疊進(jìn)行,但是這需要應(yīng)用程序根據(jù)重現(xiàn)的規(guī)則來(lái)檢查 I/O 操作的狀態(tài)。這樣就剩下異步非阻塞 I/O 了,它允許處理和 I/O 操作重疊進(jìn)行,包括 I/O 操作完成的通知。

除了需要阻塞之外,select 函數(shù)所提供的功能(異步阻塞 I/O)與 AIO 類似。不過(guò),它是對(duì)通知事件進(jìn)行阻塞,而不是對(duì) I/O 調(diào)用進(jìn)行阻塞。





回頁(yè)首


Linux 上的 AIO 簡(jiǎn)介

本節(jié)將探索 Linux 的異步 I/O 模型,從而幫助我們理解如何在應(yīng)用程序中使用這種技術(shù)。

在傳統(tǒng)的 I/O 模型中,有一個(gè)使用惟一句柄標(biāo)識(shí)的 I/O 通道。在 UNIX® 中,這些句柄是文件描述符(這對(duì)等同于文件、管道、套接字等等)。在阻塞 I/O 中,我們發(fā)起了一次傳輸操作,當(dāng)傳輸操作完成或發(fā)生錯(cuò)誤時(shí),系統(tǒng)調(diào)用就會(huì)返回。

Linux 上的 AIO

AIO 在 2.5 版本的內(nèi)核中首次出現(xiàn),現(xiàn)在已經(jīng)是 2.6 版本的產(chǎn)品內(nèi)核的一個(gè)標(biāo)準(zhǔn)特性了。

在異步非阻塞 I/O 中,我們可以同時(shí)發(fā)起多個(gè)傳輸操作。這需要每個(gè)傳輸操作都有惟一的上下文,這樣我們才能在它們完成時(shí)區(qū)分到底是哪個(gè)傳輸操作完成了。在 AIO 中,這是一個(gè) aiocb(AIO I/O Control Block)結(jié)構(gòu)。這個(gè)結(jié)構(gòu)包含了有關(guān)傳輸?shù)乃行畔ⅲ閿?shù)據(jù)準(zhǔn)備的用戶緩沖區(qū)。在產(chǎn)生 I/O (稱為完成)通知時(shí),aiocb 結(jié)構(gòu)就被用來(lái)惟一標(biāo)識(shí)所完成的 I/O 操作。這個(gè) API 的展示顯示了如何使用它。





回頁(yè)首


AIO API

AIO 接口的 API 非常簡(jiǎn)單,但是它為數(shù)據(jù)傳輸提供了必需的功能,并給出了兩個(gè)不同的通知模型。表 1 給出了 AIO 的接口函數(shù),本節(jié)稍后會(huì)更詳細(xì)進(jìn)行介紹。


表 1. AIO 接口 API
API 函數(shù) 說(shuō)明
aio_read 請(qǐng)求異步讀操作
aio_error 檢查異步請(qǐng)求的狀態(tài)
aio_return 獲得完成的異步請(qǐng)求的返回狀態(tài)
aio_write 請(qǐng)求異步寫(xiě)操作
aio_suspend 掛起調(diào)用進(jìn)程,直到一個(gè)或多個(gè)異步請(qǐng)求已經(jīng)完成(或失敗)
aio_cancel 取消異步 I/O 請(qǐng)求
lio_listio 發(fā)起一系列 I/O 操作

每個(gè) API 函數(shù)都使用 aiocb 結(jié)構(gòu)開(kāi)始或檢查。這個(gè)結(jié)構(gòu)有很多元素,但是清單 1 僅僅給出了需要(或可以)使用的元素。


清單 1. aiocb 結(jié)構(gòu)中相關(guān)的域
            struct aiocb {
            int aio_fildes;               // File Descriptor
            int aio_lio_opcode;           // Valid only for lio_listio (r/w/nop)
            volatile void *aio_buf;       // Data Buffer
            size_t aio_nbytes;            // Number of Bytes in Data Buffer
            struct sigevent aio_sigevent; // Notification Structure
            /* Internal fields */
            ...
            };
            

sigevent 結(jié)構(gòu)告訴 AIO 在 I/O 操作完成時(shí)應(yīng)該執(zhí)行什么操作。我們將在 AIO 的展示中對(duì)這個(gè)結(jié)構(gòu)進(jìn)行探索。現(xiàn)在我們將展示各個(gè) AIO 的 API 函數(shù)是如何工作的,以及我們應(yīng)該如何使用它們。

aio_read

aio_read 函數(shù)請(qǐng)求對(duì)一個(gè)有效的文件描述符進(jìn)行異步讀操作。這個(gè)文件描述符可以表示一個(gè)文件、套接字甚至管道。aio_read 函數(shù)的原型如下:

int aio_read( struct aiocb *aiocbp );
            

aio_read 函數(shù)在請(qǐng)求進(jìn)行排隊(duì)之后會(huì)立即返回。如果執(zhí)行成功,返回值就為 0;如果出現(xiàn)錯(cuò)誤,返回值就為 -1,并設(shè)置 errno 的值。

要執(zhí)行讀操作,應(yīng)用程序必須對(duì) aiocb 結(jié)構(gòu)進(jìn)行初始化。下面這個(gè)簡(jiǎn)短的例子就展示了如何填充 aiocb 請(qǐng)求結(jié)構(gòu),并使用 aio_read 來(lái)執(zhí)行異步讀請(qǐng)求(現(xiàn)在暫時(shí)忽略通知)操作。它還展示了 aio_error 的用法,不過(guò)我們將稍后再作解釋。


清單 2. 使用 aio_read 進(jìn)行異步讀操作的例子
            #include <aio.h>
            ...
            int fd, ret;
            struct aiocb my_aiocb;
            fd = open( "file.txt", O_RDONLY );
            if (fd < 0) perror("open");
            /* Zero out the aiocb structure (recommended) */
            bzero( (char *)&my_aiocb, sizeof(struct aiocb) );
            /* Allocate a data buffer for the aiocb request */
            my_aiocb.aio_buf = malloc(BUFSIZE+1);
            if (!my_aiocb.aio_buf) perror("malloc");
            /* Initialize the necessary fields in the aiocb */
            my_aiocb.aio_fildes = fd;
            my_aiocb.aio_nbytes = BUFSIZE;
            my_aiocb.aio_offset = 0;
            ret = aio_read( &my_aiocb );
            if (ret < 0) perror("aio_read");
            while ( aio_error( &my_aiocb ) == EINPROGRESS ) ;
            if ((ret = aio_return( &my_iocb )) > 0) {
            /* got ret bytes on the read */
            } else {
            /* read failed, consult errno */
            }
            

在清單 2 中,在打開(kāi)要從中讀取數(shù)據(jù)的文件之后,我們就清空了 aiocb 結(jié)構(gòu),然后分配一個(gè)數(shù)據(jù)緩沖區(qū)。并將對(duì)這個(gè)數(shù)據(jù)緩沖區(qū)的引用放到 aio_buf 中。然后,我們將 aio_nbytes 初始化成緩沖區(qū)的大小。并將 aio_offset 設(shè)置成 0(該文件中的第一個(gè)偏移量)。我們將 aio_fildes 設(shè)置為從中讀取數(shù)據(jù)的文件描述符。在設(shè)置這些域之后,就調(diào)用 aio_read 請(qǐng)求進(jìn)行讀操作。我們?nèi)缓罂梢哉{(diào)用 aio_error 來(lái)確定 aio_read 的狀態(tài)。只要狀態(tài)是 EINPROGRESS,就一直忙碌等待,直到狀態(tài)發(fā)生變化為止。現(xiàn)在,請(qǐng)求可能成功,也可能失敗。

使用 AIO 接口來(lái)編譯程序

我們可以在 aio.h 頭文件中找到函數(shù)原型和其他需要的符號(hào)。在編譯使用這種接口的程序時(shí),我們必須使用 POSIX 實(shí)時(shí)擴(kuò)展庫(kù)(librt)。

注意使用這個(gè) API 與標(biāo)準(zhǔn)的庫(kù)函數(shù)從文件中讀取內(nèi)容是非常相似的。除了 aio_read 的一些異步特性之外,另外一個(gè)區(qū)別是讀操作偏移量的設(shè)置。在傳統(tǒng)的 read 調(diào)用中,偏移量是在文件描述符上下文中進(jìn)行維護(hù)的。對(duì)于每個(gè)讀操作來(lái)說(shuō),偏移量都需要進(jìn)行更新,這樣后續(xù)的讀操作才能對(duì)下一塊數(shù)據(jù)進(jìn)行尋址。對(duì)于異步 I/O 操作來(lái)說(shuō)這是不可能的,因?yàn)槲覀兛梢酝瑫r(shí)執(zhí)行很多讀請(qǐng)求,因此必須為每個(gè)特定的讀請(qǐng)求都指定偏移量。

aio_error

aio_error 函數(shù)被用來(lái)確定請(qǐng)求的狀態(tài)。其原型如下:

int aio_error( struct aiocb *aiocbp );
            

這個(gè)函數(shù)可以返回以下內(nèi)容:

  • EINPROGRESS,說(shuō)明請(qǐng)求尚未完成
  • ECANCELLED,說(shuō)明請(qǐng)求被應(yīng)用程序取消了
  • -1,說(shuō)明發(fā)生了錯(cuò)誤,具體錯(cuò)誤原因可以查閱 errno

aio_return

異步 I/O 和標(biāo)準(zhǔn)塊 I/O 之間的另外一個(gè)區(qū)別是我們不能立即訪問(wèn)這個(gè)函數(shù)的返回狀態(tài),因?yàn)槲覀儾](méi)有阻塞在 read 調(diào)用上。在標(biāo)準(zhǔn)的 read 調(diào)用中,返回狀態(tài)是在該函數(shù)返回時(shí)提供的。但是在異步 I/O 中,我們要使用 aio_return 函數(shù)。這個(gè)函數(shù)的原型如下:

ssize_t aio_return( struct aiocb *aiocbp );
            

只有在 aio_error 調(diào)用確定請(qǐng)求已經(jīng)完成(可能成功,也可能發(fā)生了錯(cuò)誤)之后,才會(huì)調(diào)用這個(gè)函數(shù)。aio_return 的返回值就等價(jià)于同步情況中 readwrite 系統(tǒng)調(diào)用的返回值(所傳輸?shù)淖止?jié)數(shù),如果發(fā)生錯(cuò)誤,返回值就為 -1)。

aio_write

aio_write 函數(shù)用來(lái)請(qǐng)求一個(gè)異步寫(xiě)操作。其函數(shù)原型如下:

int aio_write( struct aiocb *aiocbp );
            

aio_write 函數(shù)會(huì)立即返回,說(shuō)明請(qǐng)求已經(jīng)進(jìn)行排隊(duì)(成功時(shí)返回值為 0,失敗時(shí)返回值為 -1,并相應(yīng)地設(shè)置 errno)。

這與 read 系統(tǒng)調(diào)用類似,但是有一點(diǎn)不一樣的行為需要注意。回想一下對(duì)于 read 調(diào)用來(lái)說(shuō),要使用的偏移量是非常重要的。然而,對(duì)于 write 來(lái)說(shuō),這個(gè)偏移量只有在沒(méi)有設(shè)置 O_APPEND 選項(xiàng)的文件上下文中才會(huì)非常重要。如果設(shè)置了 O_APPEND,那么這個(gè)偏移量就會(huì)被忽略,數(shù)據(jù)都會(huì)被附加到文件的末尾。否則,aio_offset 域就確定了數(shù)據(jù)在要寫(xiě)入的文件中的偏移量。

aio_suspend

我們可以使用 aio_suspend 函數(shù)來(lái)掛起(或阻塞)調(diào)用進(jìn)程,直到異步請(qǐng)求完成為止,此時(shí)會(huì)產(chǎn)生一個(gè)信號(hào),或者發(fā)生其他超時(shí)操作。調(diào)用者提供了一個(gè) aiocb 引用列表,其中任何一個(gè)完成都會(huì)導(dǎo)致 aio_suspend 返回。 aio_suspend 的函數(shù)原型如下:

int aio_suspend( const struct aiocb *const cblist[],
            int n, const struct timespec *timeout );
            

aio_suspend 的使用非常簡(jiǎn)單。我們要提供一個(gè) aiocb 引用列表。如果任何一個(gè)完成了,這個(gè)調(diào)用就會(huì)返回 0。否則就會(huì)返回 -1,說(shuō)明發(fā)生了錯(cuò)誤。請(qǐng)參看清單 3。


清單 3. 使用 aio_suspend 函數(shù)阻塞異步 I/O
            struct aioct *cblist[MAX_LIST]
            /* Clear the list. */
            bzero( (char *)cblist, sizeof(cblist) );
            /* Load one or more references into the list */
            cblist[0] = &my_aiocb;
            ret = aio_read( &my_aiocb );
            ret = aio_suspend( cblist, MAX_LIST, NULL );
            

注意,aio_suspend 的第二個(gè)參數(shù)是 cblist 中元素的個(gè)數(shù),而不是 aiocb 引用的個(gè)數(shù)。cblist 中任何 NULL 元素都會(huì)被 aio_suspend 忽略。

如果為 aio_suspend 提供了超時(shí),而超時(shí)情況的確發(fā)生了,那么它就會(huì)返回 -1errno 中會(huì)包含 EAGAIN

aio_cancel

aio_cancel 函數(shù)允許我們?nèi)∠麑?duì)某個(gè)文件描述符執(zhí)行的一個(gè)或所有 I/O 請(qǐng)求。其原型如下:

int aio_cancel( int fd, struct aiocb *aiocbp );
            

要取消一個(gè)請(qǐng)求,我們需要提供文件描述符和 aiocb 引用。如果這個(gè)請(qǐng)求被成功取消了,那么這個(gè)函數(shù)就會(huì)返回 AIO_CANCELED。如果請(qǐng)求完成了,這個(gè)函數(shù)就會(huì)返回 AIO_NOTCANCELED

要取消對(duì)某個(gè)給定文件描述符的所有請(qǐng)求,我們需要提供這個(gè)文件的描述符,以及一個(gè)對(duì) aiocbpNULL 引用。如果所有的請(qǐng)求都取消了,這個(gè)函數(shù)就會(huì)返回 AIO_CANCELED;如果至少有一個(gè)請(qǐng)求沒(méi)有被取消,那么這個(gè)函數(shù)就會(huì)返回 AIO_NOT_CANCELED;如果沒(méi)有一個(gè)請(qǐng)求可以被取消,那么這個(gè)函數(shù)就會(huì)返回 AIO_ALLDONE。我們?nèi)缓罂梢允褂?aio_error 來(lái)驗(yàn)證每個(gè) AIO 請(qǐng)求。如果這個(gè)請(qǐng)求已經(jīng)被取消了,那么 aio_error 就會(huì)返回 -1,并且 errno 會(huì)被設(shè)置為 ECANCELED

lio_listio

最后,AIO 提供了一種方法使用 lio_listio API 函數(shù)同時(shí)發(fā)起多個(gè)傳輸。這個(gè)函數(shù)非常重要,因?yàn)檫@意味著我們可以在一個(gè)系統(tǒng)調(diào)用(一次內(nèi)核上下文切換)中啟動(dòng)大量的 I/O 操作。從性能的角度來(lái)看,這非常重要,因此值得我們花點(diǎn)時(shí)間探索一下。lio_listio API 函數(shù)的原型如下:

int lio_listio( int mode, struct aiocb *list[], int nent,
            struct sigevent *sig );
            

mode 參數(shù)可以是 LIO_WAITLIO_NOWAITLIO_WAIT 會(huì)阻塞這個(gè)調(diào)用,直到所有的 I/O 都完成為止。在操作進(jìn)行排隊(duì)之后,LIO_NOWAIT 就會(huì)返回。list 是一個(gè) aiocb 引用的列表,最大元素的個(gè)數(shù)是由 nent 定義的。注意 list 的元素可以為 NULLlio_listio 會(huì)將其忽略。sigevent 引用定義了在所有 I/O 操作都完成時(shí)產(chǎn)生信號(hào)的方法。

對(duì)于 lio_listio 的請(qǐng)求與傳統(tǒng)的 readwrite 請(qǐng)求在必須指定的操作方面稍有不同,如清單 4 所示。


清單 4. 使用 lio_listio 函數(shù)發(fā)起一系列請(qǐng)求
            struct aiocb aiocb1, aiocb2;
            struct aiocb *list[MAX_LIST];
            ...
            /* Prepare the first aiocb */
            aiocb1.aio_fildes = fd;
            aiocb1.aio_buf = malloc( BUFSIZE+1 );
            aiocb1.aio_nbytes = BUFSIZE;
            aiocb1.aio_offset = next_offset;
            aiocb1.aio_lio_opcode = LIO_READ;
            ...
            bzero( (char *)list, sizeof(list) );
            list[0] = &aiocb1;
            list[1] = &aiocb2;
            ret = lio_listio( LIO_WAIT, list, MAX_LIST, NULL );
            

對(duì)于讀操作來(lái)說(shuō),aio_lio_opcode 域的值為 LIO_READ。對(duì)于寫(xiě)操作來(lái)說(shuō),我們要使用 LIO_WRITE,不過(guò) LIO_NOP 對(duì)于不執(zhí)行操作來(lái)說(shuō)也是有效的。





回頁(yè)首


AIO 通知

現(xiàn)在我們已經(jīng)看過(guò)了可用的 AIO 函數(shù),本節(jié)將深入介紹對(duì)異步通知可以使用的方法。我們將通過(guò)信號(hào)和函數(shù)回調(diào)來(lái)探索異步函數(shù)的通知機(jī)制。

使用信號(hào)進(jìn)行異步通知

使用信號(hào)進(jìn)行進(jìn)程間通信(IPC)是 UNIX 中的一種傳統(tǒng)機(jī)制,AIO 也可以支持這種機(jī)制。在這種范例中,應(yīng)用程序需要定義信號(hào)處理程序,在產(chǎn)生指定的信號(hào)時(shí)就會(huì)調(diào)用這個(gè)處理程序。應(yīng)用程序然后配置一個(gè)異步請(qǐng)求將在請(qǐng)求完成時(shí)產(chǎn)生一個(gè)信號(hào)。作為信號(hào)上下文的一部分,特定的 aiocb 請(qǐng)求被提供用來(lái)記錄多個(gè)可能會(huì)出現(xiàn)的請(qǐng)求。清單 5 展示了這種通知方法。


清單 5. 使用信號(hào)作為 AIO 請(qǐng)求的通知
            void setup_io( ... )
            {
            int fd;
            struct sigaction sig_act;
            struct aiocb my_aiocb;
            ...
            /* Set up the signal handler */
            sigemptyset(&sig_act.sa_mask);
            sig_act.sa_flags = SA_SIGINFO;
            sig_act.sa_sigaction = aio_completion_handler;
            /* Set up the AIO request */
            bzero( (char *)&my_aiocb, sizeof(struct aiocb) );
            my_aiocb.aio_fildes = fd;
            my_aiocb.aio_buf = malloc(BUF_SIZE+1);
            my_aiocb.aio_nbytes = BUF_SIZE;
            my_aiocb.aio_offset = next_offset;
            /* Link the AIO request with the Signal Handler */
            my_aiocb.aio_sigevent.sigev_notify = SIGEV_SIGNAL;
            my_aiocb.aio_sigevent.sigev_signo = SIGIO;
            my_aiocb.aio_sigevent.sigev_value.sival_ptr = &my_aiocb;
            /* Map the Signal to the Signal Handler */
            ret = sigaction( SIGIO, &sig_act, NULL );
            ...
            ret = aio_read( &my_aiocb );
            }
            void aio_completion_handler( int signo, siginfo_t *info, void *context )
            {
            struct aiocb *req;
            /* Ensure it's our signal */
            if (info->si_signo == SIGIO) {
            req = (struct aiocb *)info->si_value.sival_ptr;
            /* Did the request complete? */
            if (aio_error( req ) == 0) {
            /* Request completed successfully, get the return status */
            ret = aio_return( req );
            }
            }
            return;
            }
            

在清單 5 中,我們?cè)?aio_completion_handler 函數(shù)中設(shè)置信號(hào)處理程序來(lái)捕獲 SIGIO 信號(hào)。然后初始化 aio_sigevent 結(jié)構(gòu)產(chǎn)生 SIGIO 信號(hào)來(lái)進(jìn)行通知(這是通過(guò) sigev_notify 中的 SIGEV_SIGNAL 定義來(lái)指定的)。當(dāng)讀操作完成時(shí),信號(hào)處理程序就從該信號(hào)的 si_value 結(jié)構(gòu)中提取出 aiocb,并檢查錯(cuò)誤狀態(tài)和返回狀態(tài)來(lái)確定 I/O 操作是否完成。

對(duì)于性能來(lái)說(shuō),這個(gè)處理程序也是通過(guò)請(qǐng)求下一次異步傳輸而繼續(xù)進(jìn)行 I/O 操作的理想地方。采用這種方式,在一次數(shù)據(jù)傳輸完成時(shí),我們就可以立即開(kāi)始下一次數(shù)據(jù)傳輸操作。

使用回調(diào)函數(shù)進(jìn)行異步通知

另外一種通知方式是系統(tǒng)回調(diào)函數(shù)。這種機(jī)制不會(huì)為通知而產(chǎn)生一個(gè)信號(hào),而是會(huì)調(diào)用用戶空間的一個(gè)函數(shù)來(lái)實(shí)現(xiàn)通知功能。我們?cè)?sigevent 結(jié)構(gòu)中設(shè)置了對(duì) aiocb 的引用,從而可以惟一標(biāo)識(shí)正在完成的特定請(qǐng)求。請(qǐng)參看清單 6。


清單 6. 對(duì) AIO 請(qǐng)求使用線程回調(diào)通知
            void setup_io( ... )
            {
            int fd;
            struct aiocb my_aiocb;
            ...
            /* Set up the AIO request */
            bzero( (char *)&my_aiocb, sizeof(struct aiocb) );
            my_aiocb.aio_fildes = fd;
            my_aiocb.aio_buf = malloc(BUF_SIZE+1);
            my_aiocb.aio_nbytes = BUF_SIZE;
            my_aiocb.aio_offset = next_offset;
            /* Link the AIO request with a thread callback */
            my_aiocb.aio_sigevent.sigev_notify = SIGEV_THREAD;
            my_aiocb.aio_sigevent.notify_function = aio_completion_handler;
            my_aiocb.aio_sigevent.notify_attributes = NULL;
            my_aiocb.aio_sigevent.sigev_value.sival_ptr = &my_aiocb;
            ...
            ret = aio_read( &my_aiocb );
            }
            void aio_completion_handler( sigval_t sigval )
            {
            struct aiocb *req;
            req = (struct aiocb *)sigval.sival_ptr;
            /* Did the request complete? */
            if (aio_error( req ) == 0) {
            /* Request completed successfully, get the return status */
            ret = aio_return( req );
            }
            return;
            }
            

在清單 6 中,在創(chuàng)建自己的 aiocb 請(qǐng)求之后,我們使用 SIGEV_THREAD 請(qǐng)求了一個(gè)線程回調(diào)函數(shù)來(lái)作為通知方法。然后我們將指定特定的通知處理程序,并將要傳輸?shù)纳舷挛募虞d到處理程序中(在這種情況中,是個(gè)對(duì) aiocb 請(qǐng)求自己的引用)。在這個(gè)處理程序中,我們簡(jiǎn)單地引用到達(dá)的 sigval 指針并使用 AIO 函數(shù)來(lái)驗(yàn)證請(qǐng)求已經(jīng)完成。





回頁(yè)首


對(duì) AIO 進(jìn)行系統(tǒng)優(yōu)化

proc 文件系統(tǒng)包含了兩個(gè)虛擬文件,它們可以用來(lái)對(duì)異步 I/O 的性能進(jìn)行優(yōu)化:

  • /proc/sys/fs/aio-nr 文件提供了系統(tǒng)范圍異步 I/O 請(qǐng)求現(xiàn)在的數(shù)目。
  • /proc/sys/fs/aio-max-nr 文件是所允許的并發(fā)請(qǐng)求的最大個(gè)數(shù)。最大個(gè)數(shù)通常是 64KB,這對(duì)于大部分應(yīng)用程序來(lái)說(shuō)都已經(jīng)足夠了。




回頁(yè)首


結(jié)束語(yǔ)

使用異步 I/O 可以幫助我們構(gòu)建 I/O 速度更快、效率更高的應(yīng)用程序。如果我們的應(yīng)用程序可以對(duì)處理和 I/O 操作重疊進(jìn)行,那么 AIO 就可以幫助我們構(gòu)建可以更高效地使用可用 CPU 資源的應(yīng)用程序。盡管這種 I/O 模型與在大部分 Linux 應(yīng)用程序中使用的傳統(tǒng)阻塞模式都不同,但是異步通知模型在概念上來(lái)說(shuō)卻非常簡(jiǎn)單,可以簡(jiǎn)化我們的設(shè)計(jì)。




]]>
LINUX下的用戶和組管理http://www.tkk7.com/tinysun/archive/2010/05/01/319858.html何克勤何克勤Sat, 01 May 2010 11:20:00 GMThttp://www.tkk7.com/tinysun/archive/2010/05/01/319858.htmlhttp://www.tkk7.com/tinysun/comments/319858.htmlhttp://www.tkk7.com/tinysun/archive/2010/05/01/319858.html#Feedback0http://www.tkk7.com/tinysun/comments/commentRss/319858.htmlhttp://www.tkk7.com/tinysun/services/trackbacks/319858.html         1 超級(jí)用戶:用戶名為root,具有一切管理權(quán)限,他的UID為0,可以創(chuàng)建多個(gè)管理員的。
         2 普通用戶:在默認(rèn)情況下,普通用戶的UID是介于500-60000之間的。
         3 偽用戶:這些用戶的存在是為了方便系統(tǒng)管理,滿足相應(yīng)的系統(tǒng)進(jìn)程對(duì)文件屬主的要求。他不能夠登錄,它的ID值介于1--499之間。
 
LINUX的組:分為私有組和標(biāo)準(zhǔn)組
私有組:新創(chuàng)建一個(gè)用戶時(shí),如果沒(méi)有指定所屬組,則系統(tǒng)會(huì)為用戶創(chuàng)建一個(gè)同名的私有組。
標(biāo)準(zhǔn)組:事先創(chuàng)建好的,新創(chuàng)建一個(gè)用戶時(shí)就可以指定他所屬的組,它可以容納多個(gè)用戶。
 
創(chuàng)建用戶命令:useradd 用戶名   然后再加密碼:passwd 用戶名
 
LINUX系統(tǒng)是多用戶,多任務(wù)操作系統(tǒng),但是當(dāng)系統(tǒng)管理員需要備份時(shí),就不允許其它用戶登錄進(jìn)來(lái)。下面介紹一個(gè)方法來(lái)解決這個(gè)問(wèn)題。
    可以在/etc目錄下創(chuàng)建一個(gè)名為nologin的文件,當(dāng)其他用戶登錄時(shí),系統(tǒng)只要發(fā)現(xiàn)此文件存在,就會(huì)禁止其他用戶登錄。若要再次恢復(fù)用戶登錄,則只要把nologin文件刪除即可,或重新啟動(dòng)后,系統(tǒng)即會(huì)直接刪除這個(gè)文件,恢復(fù)用戶登錄。


]]>
linux 網(wǎng)絡(luò)相關(guān)命令http://www.tkk7.com/tinysun/archive/2010/04/16/318563.html何克勤何克勤Fri, 16 Apr 2010 12:56:00 GMThttp://www.tkk7.com/tinysun/archive/2010/04/16/318563.htmlhttp://www.tkk7.com/tinysun/comments/318563.htmlhttp://www.tkk7.com/tinysun/archive/2010/04/16/318563.html#Feedback0http://www.tkk7.com/tinysun/comments/commentRss/318563.htmlhttp://www.tkk7.com/tinysun/services/trackbacks/318563.html
第5章:網(wǎng)絡(luò)應(yīng)用
  
  5.1 常用網(wǎng)絡(luò)命令
  在Red Hat Linux 7.1系統(tǒng)提供了與網(wǎng)絡(luò)相關(guān)的工具,掌握好這些工具是十分必要的:
  第一類:設(shè)置工具
  1.netconf:
  netconf是Red Hat Linux提供的Linuxconf的一部分,主要用于設(shè)置與網(wǎng)絡(luò)相關(guān)的參數(shù)。它可以在consle下運(yùn)行(文本菜單),也可以在X-Window中運(yùn)行(圖形界面)。在前面,我們介紹過(guò)了netconf的一些應(yīng)用,它的使用比較簡(jiǎn)單,只要認(rèn)識(shí)上面的英文就可以了,所以在此就不再多說(shuō)。BTW,如果你設(shè)置好了X-Window的話,用用圖形界面的netconf,會(huì)更漂亮的喲。
  2.ifconfig
  ifconfig是Linux系統(tǒng)中最常用的一個(gè)用來(lái)顯示和設(shè)置網(wǎng)絡(luò)設(shè)備的工具。其中“if”是“interface”的縮寫(xiě)。它可以用來(lái)設(shè)備網(wǎng)卡的狀態(tài),或是顯示當(dāng)前的設(shè)置。
  下面我們簡(jiǎn)單地說(shuō)明常用的命令組合:
  1) 將第一塊網(wǎng)卡的IP地址設(shè)置為192.168.0.1:
bbs.bitsCN.com中國(guó)網(wǎng)管論壇

  ifconfig eth0 192.168.0.1 (格式:ifconfig 網(wǎng)絡(luò)設(shè)備名 IP地址)
  2) 暫時(shí)關(guān)閉或啟用網(wǎng)卡:
  關(guān)閉第一塊網(wǎng)卡:ifconfig eth0 down
  啟用第一塊網(wǎng)卡:ifconfig eth0 up
  3) 將第一塊網(wǎng)卡的子網(wǎng)掩碼設(shè)置為255.255.255.0:
  ifconfig eth0 netmask 255.255.255.0(格式:ifconfig 網(wǎng)絡(luò)設(shè)備名 netmask 子網(wǎng)掩碼)
  我們也可以同時(shí)設(shè)置IP地址和子網(wǎng)掩碼:
  ifconfig eth0 192.168.0.1 netmask 255.255.255.0
  4) 將第一塊網(wǎng)卡的廣播地址設(shè)置為192.168.0.255:
  ifconfig eth0 -broadcast 192.168.0.255
  5) 將第一塊網(wǎng)卡設(shè)置為不接收多播數(shù)據(jù)包:
  ifconifg eth0 allmulti
  如果要讓其接收,則使用命令:ifconfig eth0 -allmulti
  6) 查看第一塊網(wǎng)卡的狀態(tài):
  ifconfig eth0
  如果要查看所有的網(wǎng)卡狀態(tài),則直接使用不帶參數(shù)的ifconfig命令即可。
  ifconfig輸出的狀態(tài)信息是十分有用的,下面,我們就簡(jiǎn)單說(shuō)明一下:
  有幾個(gè)狀態(tài)比較重要:
  Ø UP/DOWN:網(wǎng)卡是否啟動(dòng)了,如果是DOWN的話,那肯定無(wú)法用的; [bitsCN.Com]
  Ø RX packets中的errors包的數(shù)量如果過(guò)大說(shuō)明網(wǎng)卡在接收時(shí)有問(wèn)題;
  Ø TX packets中的errors包的數(shù)量如果過(guò)大說(shuō)明網(wǎng)卡在發(fā)送時(shí)有問(wèn)題;
  3.route
  route命令是用來(lái)查看和設(shè)置Linux系統(tǒng)的路由信息,以實(shí)現(xiàn)與其它網(wǎng)絡(luò)的通訊。要實(shí)現(xiàn)兩個(gè)不同的子網(wǎng)之間的網(wǎng)絡(luò)通訊,需要一臺(tái)連接兩個(gè)網(wǎng)絡(luò)路由器或者同時(shí)位于兩個(gè)網(wǎng)絡(luò)的網(wǎng)關(guān)來(lái)實(shí)現(xiàn)。
  在Linux系統(tǒng)中,我們通常設(shè)置路由是為了解決以下問(wèn)題:該Linux機(jī)器在一個(gè)局域網(wǎng)中,局域網(wǎng)中有一個(gè)網(wǎng)關(guān),能夠讓你的機(jī)器訪問(wèn)Internet,那么我們就需要將這臺(tái)機(jī)器的IP地址設(shè)置為L(zhǎng)inux機(jī)器的默認(rèn)路由。
  1) 增加一個(gè)默認(rèn)路由:
  route add 0.0.0.0 gw 網(wǎng)關(guān)地址
  2) 刪除一個(gè)默認(rèn)路由:
  route del 0.0.0.0 gw 網(wǎng)關(guān)地址
  3) 顯示出當(dāng)前路由表
  route


  第二類:診斷工具
  1.ping
  ping是一個(gè)最常用的檢測(cè)是否能夠與遠(yuǎn)端機(jī)器建立網(wǎng)絡(luò)通訊連接。它是通過(guò)Internet控制報(bào)文協(xié)議ICMP來(lái)實(shí)現(xiàn)的。而現(xiàn)在有些主機(jī)對(duì)ICMP進(jìn)行過(guò)濾,在這種特殊的情況下,有可能使得一些主機(jī)Ping不通,但能夠建立網(wǎng)絡(luò)連接。這是一種特例,在此事先說(shuō)明。 bitsCN#com中國(guó)網(wǎng)管聯(lián)盟
  同樣的,在此不羅列ping命令的所有可選參數(shù),而是通過(guò)實(shí)例來(lái)說(shuō)明一些常用的組合,需要更詳細(xì)地了解的,可以通過(guò)www.linuxaid.com.cn網(wǎng)站在線培訓(xùn)的命令查詢工具獲得。
  1) 檢測(cè)與某機(jī)器的連接是否正常:
  ping 192.168.0.1
  ping www.linuxaid.com.cn
  也就是說(shuō),我們可以用IP地址或域名來(lái)指定機(jī)器。
  2) 指定ping回應(yīng)次數(shù)為4:
  在Linux下,如果你不指定回應(yīng)次數(shù),ping命令將一直不斷地向遠(yuǎn)方機(jī)器發(fā)送ICMP信息。我們可以通過(guò)-c參數(shù)來(lái)限定:ping -c 4 192.168.0.1
  3) 通過(guò)特定的網(wǎng)卡進(jìn)行ping:
  有時(shí),我們需要檢測(cè)某塊網(wǎng)卡(系統(tǒng)中有多塊)能否ping通遠(yuǎn)方機(jī)器。我們需要在執(zhí)行ping命令時(shí)指出:
  ping -I eth0 192.168.0.1
  2.traceroute
  如果你ping不通遠(yuǎn)方的機(jī)器,想知道是在什么地方出的問(wèn)題;或者你想知道你的信息到遠(yuǎn)方機(jī)器都經(jīng)過(guò)了哪些路由器,可以使用traceroute命令。顧名思義:trace是跟蹤,route是路由,也就是跟蹤路由。
  使用這個(gè)命令很簡(jiǎn)單:
  traceroute 遠(yuǎn)程主機(jī)IP地址或域名
  這個(gè)命令的輸出類似:
  1 路由器(網(wǎng)關(guān))的IP地址 訪問(wèn)所需時(shí)間1 訪問(wèn)所需時(shí)間2 訪問(wèn)所需時(shí)間3 www@bitscn@com
  2 路由器(網(wǎng)關(guān))的IP地址 訪問(wèn)所需時(shí)間1 訪問(wèn)所需時(shí)間2 訪問(wèn)所需時(shí)間3
  ………
  1) 最前面的數(shù)字代表“經(jīng)過(guò)第幾站”;
  2) 路由器(網(wǎng)關(guān))的IP地址就是“該站”的IP地址;
  3) 訪問(wèn)所需時(shí)間1、2、3是指訪問(wèn)到這個(gè)路由器(網(wǎng)關(guān))需要的時(shí)間。
  
  3.netstat
  在Linux系統(tǒng)中,提供了一個(gè)功能十分強(qiáng)大的查看網(wǎng)絡(luò)狀態(tài)的工具:netstat。它可以讓您得知整個(gè)Linux系統(tǒng)的網(wǎng)絡(luò)情況。
  1)統(tǒng)計(jì)出各網(wǎng)絡(luò)設(shè)備傳送、接收數(shù)據(jù)包的情況:
  使用命令:netstat -i
  這個(gè)命令將輸出一張表,其中包括:
  Iface:網(wǎng)絡(luò)接口名 MTU:最大傳輸單元
  RX-OK:共成功接收多少個(gè)包 RX-ERR:接收的包中共有多少個(gè)錯(cuò)誤包
  RX-DRP:接收時(shí)共丟失多少個(gè)包 RX-OVR:共接收了多少個(gè)碰撞包
  TX-OK:共成功發(fā)送多少個(gè)包 TX-ERR:發(fā)送的包中共有多少個(gè)錯(cuò)誤包
  TX-DRP:發(fā)磅時(shí)共丟失多少個(gè)包 TX-OVR:共接收了多少個(gè)碰撞包

bitsCN.nET*中國(guó)網(wǎng)管博客


  2)顯示網(wǎng)絡(luò)的統(tǒng)計(jì)信息
  使用命令:netstat -s
  使用這個(gè)命令,將會(huì)以摘要的形式統(tǒng)計(jì)出IP、ICMP、TCP、UDP、TCPEXT形式的通信信息。
  3)顯示出TCP傳輸協(xié)議的網(wǎng)絡(luò)連接情況:
  使用命令:netstat -t
  這個(gè)命令的輸出也是一張表,其中包括:
  Local Address:本地地址,格式是IP地址:端口號(hào)
  Foreign Address:遠(yuǎn)程地址,格式也是IP地址:端口號(hào)
  State:連接狀態(tài),包括LISTEN、ESTABLISHED、TIME_WAIT等。
  4)只顯示出使用UDP的網(wǎng)絡(luò)連接情況:
  使用命令:netstat -t
  輸出格式也是一樣的。
  5)顯示路由表:
  使用命令:netstat -r
  這個(gè)命令的輸出與route命令的輸出相同。
  
  5.2 網(wǎng)絡(luò)配置文件
  在Red Hat Linux 7.1中有一些用于存放網(wǎng)絡(luò)配置的文件:
  1./etc/hosts
  在該文件中存放的是一組IP地址與主機(jī)名的列表,如果在該列表中指出某臺(tái)主機(jī)的IP地址,那么訪問(wèn)該主機(jī)時(shí)將無(wú)需進(jìn)行DNS解析。 bbs.bitsCN.com中國(guó)網(wǎng)管論壇
  2./etc/host.conf
  該文件用來(lái)指定域名解析方法的順序,如:
  order hosts,bind
  它說(shuō)明,首先通過(guò)/etc/hosts文件解析,如果在該文件中沒(méi)有相應(yīng)的主機(jī)名與IP地址的對(duì)應(yīng)關(guān)系,再通過(guò)域名服務(wù)器bind進(jìn)行解析。
  3./etc/resolv.conf
  在該文件中存放域名服務(wù)器的IP地址。
  4./etc/protocols
  Red Hat Linux 7.1系統(tǒng)使用該文件辨別本主機(jī)使用的,并通過(guò)它完成協(xié)議和協(xié)議號(hào)之間的映射,用戶不應(yīng)修改該文件。
  5./etc/services
  該用戶用于定義現(xiàn)有的網(wǎng)絡(luò)服務(wù),用戶無(wú)需修改它,它通常由安裝網(wǎng)絡(luò)服務(wù)的程序來(lái)維護(hù)。該文件包括網(wǎng)絡(luò)服務(wù)名、網(wǎng)絡(luò)端口號(hào)和使用的協(xié)議類型,其中網(wǎng)絡(luò)端口號(hào)和使用的協(xié)議類型之間有一個(gè)斜杠分開(kāi),在設(shè)置行的最后還可以添加一些服務(wù)的別名。
  5./etc/xinetd.d目錄
  在Linux系統(tǒng)中有一個(gè)超級(jí)服務(wù)程序inetd,大部分的網(wǎng)絡(luò)服務(wù)都是由它啟動(dòng)的,如chargen、echo、finger、talk、telnet、wu-ftpd等…,在7.0之間的版本它的設(shè)置是在/etc/inetd.conf中配置的,在Red Hat 7.0后,它就改成了一個(gè)xinetd.d目錄。 bbs.bitsCN.com
  在xinetd.d目錄中,每一個(gè)服務(wù)都有一個(gè)相應(yīng)的配置文件,我們以telnet為例,說(shuō)明一下各個(gè)配置行的含義:
  service telnet
  {
  socket_type=stream
  wait=no
  user=root
  server=/usr/sbin/in.telnetd
  log_on_failure+=USERID
  disable=yes
  }
  第一行,說(shuō)明該配置用來(lái)設(shè)置telnet服務(wù)。
  第二行,說(shuō)明Socket連接類型是stream,也就是TCP
  第三行,是指不等待到啟動(dòng)完成
  第四行,是指以root用戶啟動(dòng)服務(wù)進(jìn)程
  第五行,是指服務(wù)進(jìn)程是/usr/sbin/in.telnetd
  第六行,是用于做一些出錯(cuò)日志
  第七行,是指禁止遠(yuǎn)方telnet,如果需要開(kāi)放則將該配置改為:disable=no
  修改了xinetd的配置,需要重啟xinetd才能夠生效,有兩種方法可以實(shí)現(xiàn):
  1) 執(zhí)行如下命令:
  /etc/rc.d/init.d/xinetd restart
  2) 執(zhí)行如下命令:
  killall -HUP xinetd
  
  5.3 網(wǎng)絡(luò)服務(wù)訪問(wèn)限制
  在Red Hat Linux 7.1中加強(qiáng)了網(wǎng)絡(luò)安全的防范,如果你安裝時(shí)安全等級(jí)不是在最低一級(jí)的話,那么本機(jī)之外的所有訪問(wèn)都可能被拒絕。這是因?yàn)樵赗ed Hat 7.1中做了一些默認(rèn)的ipchains設(shè)置,這是Linux內(nèi)置的防火墻機(jī)制,它可以使用一些規(guī)則來(lái)允許或禁止某種訪問(wèn)。

bbs.bitsCN.com


  它的規(guī)則存放在/etc/sysconfig/ipchains文件中,如果你想讓它暫時(shí)不生效,那你可以運(yùn)行/etc/rc.d/init.d/ipchains stop,那么所有的規(guī)則都被取消,所有的網(wǎng)絡(luò)訪問(wèn)都將被允許。
  你可以運(yùn)行/etc/rc.d/init.d/ipchains status來(lái)獲知現(xiàn)在對(duì)網(wǎng)絡(luò)訪問(wèn)的限制。關(guān)于這方面的知識(shí),本文限于篇幅無(wú)法詳細(xì)介紹,有興趣的讀者可參考《Linux防火墻》一書(shū)。
  
  5.4 WEB服務(wù)器
  在Linux系統(tǒng)中最適合于做服務(wù)器的當(dāng)數(shù)Apache,Red H


]]>
頻繁分配釋放內(nèi)存導(dǎo)致的性能問(wèn)題的分析[轉(zhuǎn)]http://www.tkk7.com/tinysun/archive/2010/03/28/316737.html何克勤何克勤Sun, 28 Mar 2010 02:48:00 GMThttp://www.tkk7.com/tinysun/archive/2010/03/28/316737.htmlhttp://www.tkk7.com/tinysun/comments/316737.htmlhttp://www.tkk7.com/tinysun/archive/2010/03/28/316737.html#Feedback0http://www.tkk7.com/tinysun/comments/commentRss/316737.htmlhttp://www.tkk7.com/tinysun/services/trackbacks/316737.html現(xiàn)象
1 壓力測(cè)試過(guò)程中,發(fā)現(xiàn)被測(cè)對(duì)象性能不夠理想,具體表現(xiàn)為:
進(jìn)程的系統(tǒng)態(tài)CPU消耗20,用戶態(tài)CPU消耗10,系統(tǒng)idle大約70
2 用ps -o majflt,minflt -C program命令查看,發(fā)現(xiàn)majflt每秒增量為0,而minflt每秒增量大于10000。

初步分析
majflt代表major fault,中文名叫大錯(cuò)誤,minflt代表minor fault,中文名叫小錯(cuò)誤。
這兩個(gè)數(shù)值表示一個(gè)進(jìn)程自啟動(dòng)以來(lái)所發(fā)生的缺頁(yè)中斷的次數(shù)。
當(dāng)一個(gè)進(jìn)程發(fā)生缺頁(yè)中斷的時(shí)候,進(jìn)程會(huì)陷入內(nèi)核態(tài),執(zhí)行以下操作:
檢查要訪問(wèn)的虛擬地址是否合法
查找/分配一個(gè)物理頁(yè)
填充物理頁(yè)內(nèi)容(讀取磁盤(pán),或者直接置0,或者啥也不干)
建立映射關(guān)系(虛擬地址到物理地址)
重新執(zhí)行發(fā)生缺頁(yè)中斷的那條指令
如果第3步,需要讀取磁盤(pán),那么這次缺頁(yè)中斷就是majflt,否則就是minflt。
此進(jìn)程minflt如此之高,一秒10000多次,不得不懷疑它跟進(jìn)程內(nèi)核態(tài)cpu消耗大有很大關(guān)系。

分析代碼
查看代碼,發(fā)現(xiàn)是這么寫(xiě)的:一個(gè)請(qǐng)求來(lái),用malloc分配2M內(nèi)存,請(qǐng)求結(jié)束后free這塊內(nèi)存。看日志,發(fā)現(xiàn)分配內(nèi)存語(yǔ)句耗時(shí)10us,平均一條請(qǐng)求處理耗時(shí)1000us 。 原因已找到!
雖然分配內(nèi)存語(yǔ)句的耗時(shí)在一條處理請(qǐng)求中耗時(shí)比重不大,但是這條語(yǔ)句嚴(yán)重影響了性能。要解釋清楚原因,需要先了解一下內(nèi)存分配的原理。

內(nèi)存分配的原理
從操作系統(tǒng)角度來(lái)看,進(jìn)程分配內(nèi)存有兩種方式,分別由兩個(gè)系統(tǒng)調(diào)用完成:brk和mmap(不考慮共享內(nèi)存)。brk是將數(shù)據(jù)段(.data)的最高地址指針_edata往高地址推,mmap是在進(jìn)程的虛擬地址空間中(一般是堆和棧中間)找一塊空閑的。這兩種方式分配的都是虛擬內(nèi)存,沒(méi)有分配物理內(nèi)存。在第一次訪問(wèn)已分配的虛擬地址空間的時(shí)候,發(fā)生缺頁(yè)中斷,操作系統(tǒng)負(fù)責(zé)分配物理內(nèi)存,然后建立虛擬內(nèi)存和物理內(nèi)存之間的映射關(guān)系。
在標(biāo)準(zhǔn)C庫(kù)中,提供了malloc/free函數(shù)分配釋放內(nèi)存,這兩個(gè)函數(shù)底層是由brk,mmap,munmap這些系統(tǒng)調(diào)用實(shí)現(xiàn)的。
下面以一個(gè)例子來(lái)說(shuō)明內(nèi)存分配的原理:

1進(jìn)程啟動(dòng)的時(shí)候,其(虛擬)內(nèi)存空間的初始布局如圖1所示。其中,mmap內(nèi)存映射文件是在堆和棧的中間(例如libc-2.2.93.so,其它數(shù)據(jù)文件等),為了簡(jiǎn)單起見(jiàn),省略了內(nèi)存映射文件。_edata指針(glibc里面定義)指向數(shù)據(jù)段的最高地址。
2進(jìn)程調(diào)用A=malloc(30K)以后,內(nèi)存空間如圖2:malloc函數(shù)會(huì)調(diào)用brk系統(tǒng)調(diào)用,將_edata指針往高地址推30K,就完成虛擬內(nèi)存分配。你可能會(huì)問(wèn):只要把_edata+30K就完成內(nèi)存分配了?事實(shí)是這樣的,_edata+30K只是完成虛擬地址的分配,A這塊內(nèi)存現(xiàn)在還是沒(méi)有物理頁(yè)與之對(duì)應(yīng)的,等到進(jìn)程第一次讀寫(xiě)A這塊內(nèi)存的時(shí)候,發(fā)生缺頁(yè)中斷,這個(gè)時(shí)候,內(nèi)核才分配A這塊內(nèi)存對(duì)應(yīng)的物理頁(yè)。也就是說(shuō),如果用malloc分配了A這塊內(nèi)容,然后從來(lái)不訪問(wèn)它,那么,A對(duì)應(yīng)的物理頁(yè)是不會(huì)被分配的。
3進(jìn)程調(diào)用B=malloc(40K)以后,內(nèi)存空間如圖3.


4進(jìn)程調(diào)用C=malloc(200K)以后,內(nèi)存空間如圖4:默認(rèn)情況下,malloc函數(shù)分配內(nèi)存,如果請(qǐng)求內(nèi)存大于128K(可由M_MMAP_THRESHOLD選項(xiàng)調(diào)節(jié)),那就不是去推_edata指針了,而是利用mmap系統(tǒng)調(diào)用,從堆和棧的中間分配一塊虛擬內(nèi)存。這樣子做主要是因?yàn)閎rk分配的內(nèi)存需要等到高地址內(nèi)存釋放以后才能釋放(例如,在B釋放之前,A是不可能釋放的),而mmap分配的內(nèi)存可以單獨(dú)釋放。當(dāng)然,還有其它的好處,也有壞處,再具體下去,有興趣的同學(xué)可以去看glibc里面malloc的代碼了。
5進(jìn)程調(diào)用D=malloc(100K)以后,內(nèi)存空間如圖5.
6進(jìn)程調(diào)用free(C)以后,C對(duì)應(yīng)的虛擬內(nèi)存和物理內(nèi)存一起釋放


7進(jìn)程調(diào)用free(B)以后,如圖7所示。B對(duì)應(yīng)的虛擬內(nèi)存和物理內(nèi)存都沒(méi)有釋放,因?yàn)橹挥幸粋€(gè)_edata指針,如果往回推,那么D這塊內(nèi)存怎么辦呢?當(dāng)然,B這塊內(nèi)存,是可以重用的,如果這個(gè)時(shí)候再來(lái)一個(gè)40K的請(qǐng)求,那么malloc很可能就把B這塊內(nèi)存返回回去了。
8進(jìn)程調(diào)用free(D)以后,如圖8所示。B和D連接起來(lái),變成一塊140K的空閑內(nèi)存。
9默認(rèn)情況下:當(dāng)最高地址空間的空閑內(nèi)存超過(guò)128K(可由M_TRIM_THRESHOLD選項(xiàng)調(diào)節(jié))時(shí),執(zhí)行內(nèi)存緊縮操作(trim)。在上一個(gè)步驟free的時(shí)候,發(fā)現(xiàn)最高地址空閑內(nèi)存超過(guò)128K,于是內(nèi)存緊縮,變成圖9所示。

真相大白
說(shuō)完內(nèi)存分配的原理,那么被測(cè)模塊在內(nèi)核態(tài)cpu消耗高的原因就很清楚了:每次請(qǐng)求來(lái)都malloc一塊2M的內(nèi)存,默認(rèn)情況下,malloc調(diào)用mmap分配內(nèi)存,請(qǐng)求結(jié)束的時(shí)候,調(diào)用munmap釋放內(nèi)存。假設(shè)每個(gè)請(qǐng)求需要6個(gè)物理頁(yè),那么每個(gè)請(qǐng)求就會(huì)產(chǎn)生6個(gè)缺頁(yè)中斷,在2000的壓力下,每秒就產(chǎn)生了10000多次缺頁(yè)中斷,這些缺頁(yè)中斷不需要讀取磁盤(pán)解決,所以叫做minflt;缺頁(yè)中斷在內(nèi)核態(tài)執(zhí)行,因此進(jìn)程的內(nèi)核態(tài)cpu消耗很大。缺頁(yè)中斷分散在整個(gè)請(qǐng)求的處理過(guò)程中,所以表現(xiàn)為分配語(yǔ)句耗時(shí)(10us)相對(duì)于整條請(qǐng)求的處理時(shí)間(1000us)比重很小。

解決辦法
將動(dòng)態(tài)內(nèi)存改為靜態(tài)分配,或者啟動(dòng)的時(shí)候,用malloc為每個(gè)線程分配,然后保存在threaddata里面。但是,由于這個(gè)模塊的特殊性,靜態(tài)分配,或者啟動(dòng)時(shí)候分配都不可行。另外,Linux下默認(rèn)棧的大小限制是10M,如果在棧上分配幾M的內(nèi)存,有風(fēng)險(xiǎn)。
禁止malloc調(diào)用mmap分配內(nèi)存,禁止內(nèi)存緊縮。
在進(jìn)程啟動(dòng)時(shí)候,加入以下兩行代碼:
mallopt(M_MMAP_MAX, 0); // 禁止malloc調(diào)用mmap分配內(nèi)存
mallopt(M_TRIM_THRESHOLD, -1); // 禁止內(nèi)存緊縮
效果:加入這兩行代碼以后,用ps命令觀察,壓力穩(wěn)定以后,majlt和minflt都為0。進(jìn)程的系統(tǒng)態(tài)cpu從20降到10。

小結(jié)
可以用命令ps -o majflt minflt -C program來(lái)查看進(jìn)程的majflt, minflt的值,這兩個(gè)值都是累加值,從進(jìn)程啟動(dòng)開(kāi)始累加。在對(duì)高性能要求的程序做壓力測(cè)試的時(shí)候,我們可以多關(guān)注一下這兩個(gè)值。
如果一個(gè)進(jìn)程使用了mmap將很大的數(shù)據(jù)文件映射到進(jìn)程的虛擬地址空間,我們需要重點(diǎn)關(guān)注majflt的值,因?yàn)橄啾萴inflt,majflt對(duì)于性能的損害是致命的,隨機(jī)讀一次磁盤(pán)的耗時(shí)數(shù)量級(jí)在幾個(gè)毫秒,而minflt只有在大量的時(shí)候才會(huì)對(duì)性能產(chǎn)生影響。

]]>
主站蜘蛛池模板: 羞羞视频在线观看免费| 亚洲一级特黄大片无码毛片| 亚洲婷婷第一狠人综合精品| 免费黄色福利视频| 亚洲国产成+人+综合| 18女人水真多免费高清毛片| 久久综合亚洲色HEZYO社区 | 久久乐国产精品亚洲综合| eeuss影院免费直达入口| 久久久久亚洲AV无码专区网站| 精品免费久久久久国产一区 | 亚洲ⅴ国产v天堂a无码二区| 久久这里只精品国产免费10| 亚洲视频小说图片| 91免费精品国自产拍在线不卡| 精品亚洲456在线播放| 日韩高清在线高清免费| 全黄A免费一级毛片| 中文字幕在线亚洲精品| 久久国产乱子精品免费女| 亚洲一区精品中文字幕| 免费三级毛片电影片| 亚洲精品日韩一区二区小说| 国产嫩草影院精品免费网址| 二级毛片免费观看全程| 国产精品亚洲精品日韩已满| 十九岁在线观看免费完整版电影| 亚洲视频在线免费播放| 最近中文字幕无吗高清免费视频| 亚洲国产区男人本色| 亚洲а∨天堂久久精品| 99精品视频免费| 久久亚洲精品成人无码网站| aa级一级天堂片免费观看| 亚洲AV日韩AV无码污污网站| 国产中文在线亚洲精品官网| 日韩免费视频一区二区| 自拍日韩亚洲一区在线| 国产99视频精品免费视频7| a级毛片免费观看在线| 1区1区3区4区产品亚洲|