1. 程式人生 > >AIX 5L 上的共享庫記憶體佔用

AIX 5L 上的共享庫記憶體佔用

http://blog.chinaunix.net/uid-10218589-id-348867.html

 

級別: 初級
George Cross ([email protected]), 高階軟體開發人員, Business Objects Americas
2008 年 7 月 03 日
瞭解 IBM? AIX? 上的共享庫機制和記憶體佔用情況。本文對於開發人員編寫伺服器程式碼或管理員管理生產 AIX 系統來說非常重要。本文為開發人員和管理員提供了分析 AIX 上的伺服器程序的記憶體需求所需的命令、技巧和知識。本文還有助於開發人員和管理員避免出現使用 ps 或 topas 等其他標準執行時分析工具時無法識別的資源短缺。本文是面向 AIX 系統管理員或本機應用程式開發人員。
引言
本文闡述 32 位 AIX 5L? (5.3) 上共享庫佔用記憶體的方式,並演示下列命令:
ps
svmon
slibclean
procldd
procmap
genkld
genld
本文討論程序的虛擬地址空間和核心共享庫段,以及如何檢視它們和如何解釋上述多種診斷實用工具的輸出。本文還討論如何診斷核心共享段完全佔用的情況,以及解決該問題的可能方法。
在貫穿全文的示例中,我們碰巧使用了來自軟體產品 Business Objects Enterprise Xir2? 的程序。這種選擇無關緊要,因為這些概念適用於在 AIX 5L 上執行的所有程序。

回頁首


回顧
為了保持思維同步,讓我們簡單回顧一下 32 位體系結構。為了達到目標,我將採用最有用的“bc”命令列計算器。
在 32 位處理器中,暫存器能夠儲存 2^32 大小的值,
    $ bc
    2^32
    4294967296
    obase=16
    2^32
    100000000


這是 4GB 的範圍。這表示在系統中執行的程式能夠訪問 0 到 2^32 – 1 範圍內的任何函式或資料地址。
    $ bc
     2^32 - 1 
    FFFFFFFF
    obase=10
    2^32 - 1 
    4294967295


現在,如您所知,任何作業系統都可能同時執行數百個程式。即使其中每個系統都能訪問 4GB 的記憶體範圍,這並不表示它們各自擁有 4GB 的實體記憶體分配。這是不切實際的。實際上,作業系統在適量實體記憶體和檔案系統中被指定為交換(或分頁)空間的區域之間實現了非常複雜的程式碼和資料交換方案。而且,雖然每個程序能夠訪問 4GB 的記憶體空間,但其中大部分空間都不會被使用。因此,作業系統僅為每個特定程序載入或交換所需數量的程式碼和資料。

圖 1. 虛擬記憶體的概念圖
 
該機制通常稱為虛擬記憶體和虛擬地址空間。
在可執行檔案執行時,作業系統的虛擬記憶體管理器檢視檔案包含的程式碼和資料,並決定將其中哪些部分載入到記憶體或交換空間中,或從檔案系統引用。同時,虛擬記憶體管理器建立一些結構以將物理位置對映到 4GB 範圍內的虛擬位置。其中 4GB 範圍表示程序的最大理論範圍(有時包括用於表示它的 VMM 結構),被稱為程序的虛擬地址空間。
在 AIX 上,將 4GB 虛擬地址空間劃分為 16 個 256MB 的段。這些段具有預定的功能,下面對其中一些段進行說明:
段 0 預留給核心相關資料。
段 1 預留給程式碼。
段 2 預留給堆疊和動態記憶體分配。
段 3 預留給記憶體對映檔案,即 mmap。
段 d 預留給共享庫程式碼。
段 f 預留給共享庫資料。
與之相比,HP-UX? 將地址空間劃分為 4 個象限(quadrants)。如果使用 chatr 命令並採用 +q3p enable 和 +q4p enable 選項進行指定,則象限 3 和象限 4 可用於共享庫對映。

回頁首


共享庫載入到何處
共享庫本身是用於共享的。具體而言,二進位制映像的只讀部分(即程式碼,也稱為“文字”)和只讀資料(常量資料,以及可以寫入時複製的資料)可以一次載入到實體記憶體中,然後將其多次對映到需要該資料的任何程序中。
為了演示此概念,請準備一臺執行 AIX 的計算機,然後檢視已載入的共享庫:
> su 
# genkld
Text address     Size File

    d1539fe0    1a011 /usr/lib/libcurses.a[shr.o]
    d122f100    36732 /usr/lib/libptools.a[shr.o]
    d1266080    297de /usr/lib/libtrace.a[shr.o]
    d020c000     5f43 /usr/lib/nls/loc/iconv/ISO8859-1_UCS-2
    d7545000    161ff /usr/java14/jre/bin/libnet.a
    d7531000    135e2 /usr/java14/jre/bin/libzip.a
.... [ lots more libs ] ....
d1297108 3a99 /opt/rational/clearcase/shlib/libatriastats_svr.a
[atriastats_svr-shr.o]
    d1bfa100    2bcdf /opt/rational/clearcase/shlib/libatriacm.a[atriacm-shr.o]
    d1bbf100    2cf3c /opt/rational/clearcase/shlib/libatriaadm.a[atriaadm-shr.o]
.... [ lots more libs ] ....
    d01ca0f8     17b6 /usr/lib/libpthreads_compat.a[shr.o]
    d10ff000    30b78 /usr/lib/libpthreads.a[shr.o]
    d00f0100    1fd2f /usr/lib/libC.a[shr.o]
    d01293e0    25570 /usr/lib/libC.a[shrcore.o]
    d01108a0    18448 /usr/lib/libC.a[ansicore_32.o]
.... [ lots more libs ] ....
    d04a2100    fdb4b /usr/lib/libX11.a[shr4.o]
    d0049000    365c4 /usr/lib/libpthreads.a[shr_xpg5.o]
    d0045000     3c52 /usr/lib/libpthreads.a[shr_comm.o]
    d05bb100     5058 /usr/lib/libIM.a[shr.o]
    d05a7100    139c1 /usr/lib/libiconv.a[shr4.o]
    d0094100    114a2 /usr/lib/libcfg.a[shr.o]
    d0081100    125ea /usr/lib/libodm.a[shr.o]
    d00800f8      846 /usr/lib/libcrypt.a[shr.o]
    d022d660   25152d /usr/lib/libc.a[shr.o]

結果很有趣,我們立刻可以看到這臺計算機正在執行 Clearcase 和 Java?。讓我們從中任選一個公共庫,假定選擇 libpthreads.a。瀏覽該庫並檢視它實現了哪些函式:
# dump -Tv /usr/lib/libpthreads.a | grep EXP
[278]   0x00002808    .data      EXP     RW SECdef        [noIMid] pthread_attr_default
[279] 0x00002a68 .data EXP RW SECdef [noIMid]
 pthread_mutexattr_default
[280]   0x00002fcc    .data      EXP     DS SECdef        [noIMid] pthread_create
[281]   0x0000308c    .data      EXP     DS SECdef        [noIMid] pthread_cond_init
[282]   0x000030a4    .data      EXP     DS SECdef        [noIMid] pthread_cond_destroy
[283]   0x000030b0    .data      EXP     DS SECdef        [noIMid] pthread_cond_wait
[284]   0x000030bc    .data      EXP     DS SECdef        [noIMid] pthread_cond_broadcast
[285]   0x000030c8    .data      EXP     DS SECdef        [noIMid] pthread_cond_signal
[286]   0x000030d4    .data      EXP     DS SECdef        [noIMid] pthread_setcancelstate
[287]   0x000030e0    .data      EXP     DS SECdef        [noIMid] pthread_join
.... [ lots more stuff ] ....

嗯,真棒!現在,我們來檢視目前系統中有哪些正在執行的程序載入了該庫:
# for i in $(ps -o pid -e | grep ^[0-9] ) ; do j=$(procldd $i | grep libpthreads.a); \
    if [ -n "$j" ] ; then ps -p $i -o comm | grep -v COMMAND; fi  ; done
portmap
rpc.statd
automountd
rpc.mountd
rpc.ttdbserver
dtexec
dtlogin
radiusd
radiusd
radiusd
dtexec
dtterm
procldd : no such process : 24622
dtterm
xmwlm
dtwm
dtterm
dtgreet
dtexec
ttsession
dtterm
dtexec
rdesktop
procldd : no such process : 34176
java
dtsession
dtterm
dtexec
dtexec

真棒!現在,我們以更簡潔的方式顯示相同的結果:
# cat prev.command.out.txt | sort | uniq 
       
automountd
dtexec
dtgreet
dtlogin
dtsession
dtterm
dtwm
java
portmap
radiusd
rdesktop
rpc.mountd
rpc.statd
rpc.ttdbserver
ttsession
xmwlm

於是,我們得到了目前正在執行並載入了 libpthreads.a 庫的二進位制檔案的排序列表。請注意,列出的程序只佔當時系統中正在執行的程序的一小部分:
# ps -e | wc -l     
      85

接下來,我們看一下每個程序載入 libpthreads.a 的位置:
# ps -e | grep java
 34648      -  4:13 java
#
# procmap 34648 | grep libpthreads.a
d0049000         217K  read/exec      /usr/lib/libpthreads.a[shr_xpg5.o]
f03e6000          16K  read/write     /usr/lib/libpthreads.a[shr_xpg5.o]
d0045000          15K  read/exec      /usr/lib/libpthreads.a[shr_comm.o]
f03a3000         265K  read/write     /usr/lib/libpthreads.a[shr_comm.o]
#
# ps -e | grep automountd
 15222      -  1:00 automountd
 25844      -  0:00 automountd
#
# procmap 15222 | grep libpthreads.a
d0049000         217K  read/exec      /usr/lib/libpthreads.a[shr_xpg5.o]
f03e6000          16K  read/write     /usr/lib/libpthreads.a[shr_xpg5.o]
d0045000          15K  read/exec      /usr/lib/libpthreads.a[shr_comm.o]
f03a3000         265K  read/write     /usr/lib/libpthreads.a[shr_comm.o]
d10ff000         194K  read/exec         /usr/lib/libpthreads.a[shr.o]
f0154000          20K  read/write        /usr/lib/libpthreads.a[shr.o]
#
# ps -e | grep portmap              
 12696      -  0:06 portmap
 34446      -  0:00 portmap
#
# procmap 12696 | grep libpthreads.a
d0045000          15K  read/exec      /usr/lib/libpthreads.a[shr_comm.o]
f03a3000         265K  read/write     /usr/lib/libpthreads.a[shr_comm.o]
d10ff000         194K  read/exec         /usr/lib/libpthreads.a[shr.o]
f0154000          20K  read/write        /usr/lib/libpthreads.a[shr.o]
#
# ps -e | grep dtlogin
  6208      -  0:00 dtlogin
  6478      -  2:07 dtlogin
 20428      -  0:00 dtlogin
#
# procmap 20428 | grep libpthreads.a
d0045000          15K  read/exec      /usr/lib/libpthreads.a[shr_comm.o]
f03a3000         265K  read/write     /usr/lib/libpthreads.a[shr_comm.o]
d0049000         217K  read/exec      /usr/lib/libpthreads.a[shr_xpg5.o]
f03e6000          16K  read/write     /usr/lib/libpthreads.a[shr_xpg5.o]

請注意,每個程序每次都在相同的地址載入該庫。不要為庫中的 .o 的構成列表感到困惑。在 AIX 上,您可以共享歸檔庫(通常是 .a 檔案)以及動態共享庫(通常是 .so 檔案)。這樣做的目的是能夠在連結時繫結符號,就像傳統的歸檔連結,但不需要將構成物件(歸檔中的 .o 檔案)複製到最終二進位制映像中。但是,不執行動態(或執行時)符號解析,動態共享庫(.so/.sl 檔案)也是如此。
還要注意,libpthreads.a 程式碼部分(那些標記為 read/exec 的部分)被載入到段 0xd 中。如上所述,該段在 AIX 中被指定為預留給共享庫程式碼。也就是說,核心將該共享庫的可共享段載入到在同一核心上執行的所有程序所共享的區域。
您可能注意到資料部分也載入到同一段中:共享庫段 0xf。但是,這並不表示每個程序也共享 libpthreads.a 的資料部分。這一點沒有明確定義(這樣的安排方式無法正常工作),因為不同的程序將需要在不同的時間維護不同的資料值。段 0xf 對於每個使用 libpthreads.a 的程序是獨立的,即使虛擬記憶體地址相同也是如此。
svmon 命令可以顯示程序在虛擬記憶體管理器中的段 ID (Vsid)。我們將看到共享庫的程式碼段都具有相同的 Vsid,而共享庫的資料段都具有不同的 Vsid。Esid 表示有效段 ID (Effective Segment ID),是位於程序地址空間範圍內的段 ID(僅僅是術語,不要為此感到困惑)。
# svmon -P 17314

-------------------------------------------------------------------------------
     Pid Command          Inuse      Pin     Pgsp  Virtual 64-bit Mthrd  16MB
   17314 dtexec           20245     9479       12    20292      N     N     N

    Vsid      Esid Type Description              PSize  Inuse   Pin Pgsp Virtual
       0         0 work kernel segment               s  14361  9477    0 14361 
   6c01b         d work shared library text          s   5739     0    9  5786 
   19be6         f work shared library data          s     83     0    1    87 
   21068         2 work process private              s     56     2    2    58 
   18726         1 pers code,/dev/hd2:65814          s      5     0    -     - 
    40c1         - pers /dev/hd4:2                   s      1     0    -     - 
#
# svmon -P 20428

-------------------------------------------------------------------------------
     Pid Command          Inuse      Pin     Pgsp  Virtual 64-bit Mthrd  16MB
   20428 dtlogin          20248     9479       23    20278      N     N     N

    Vsid      Esid Type Description              PSize  Inuse   Pin Pgsp Virtual
       0         0 work kernel segment               s  14361  9477    0 14361 
   6c01b         d work shared library text          s   5735     0    9  5782 
   7869e         2 work process private              s     84     2   10    94 
                   parent=786be
   590b6         f work shared library data          s     37     0    4    41 
                   parent=7531d
   6c19b         1 pers code,/dev/hd2:65670          s     29     0    -     - 
   381ae         - pers /dev/hd9var:4157             s      1     0    -     - 
    40c1         - pers /dev/hd4:2                   s      1     0    -     - 
   4c1b3         - pers /dev/hd9var:4158             s      0     0    -     - 


回頁首


執行計算
看看目前共享段 0xd 中有多少空間。我們將再次使用 bc 計算器工具。理所當然,我們將驗證段 0xd 的大小。
# bc    
ibase=16
E0000000-D0000000
268435456
ibase=A
268435456/(1024^2)
256

看起來不錯。如上所述,每個段為 256MB。接下來,看看目前使用了多少容量。
$ echo "ibase=16; $(genkld | egrep ^\ \{8\} | awk '{print $2}' | tr '[a-f]' '[A-F]' \
    |  tr '\n' '+' ) 0" | bc
39798104
$
$ bc < 39798104/(1024^2)
> EOF
37

也就是說,目前使用了 37MB。然後啟動 XIr2,並進行比較:
$ echo "ibase=16; $(genkld | egrep ^\ \{8\} | awk '{print $2}' | tr '[a-f]' '[A-F]' \
    |  tr '\n' '+' ) 0" | bc
266069692
$
$ bc < 266069692/(1024^2)
> EOF
253

現在使用了 253MB。這非常接近於限值 256MB。任意選擇一個程序,如 WIReportServer,然後檢視有多少共享庫已放入共享空間,以及有多少共享庫必須獨立對映。因為我們瞭解共享段是從地址 0xd000000 開始的,因此我們可以將其從 procmap 的輸出結果中篩選出來。請記住,只有程式碼段會對映到段 0xd 中,因此我們僅查詢帶有 read/exec 的行:
$ procmap 35620 | grep read/exec | grep -v ^d
10000000       10907K  read/exec         boe_fcprocd
31ad3000       14511K  read/exec
/crystal/sj1xir2a/xir2_r/bobje/enterprise115/aix_rs6000/libEnterpriseFramework.so
3167b000        3133K  read/exec
/crystal/sj1xir2a/xir2_r/bobje/enterprise115/aix_rs6000/libcpi18nloc.so
3146c000        1848K  read/exec
/crystal/sj1xir2a/xir2_r/bobje/enterprise115/aix_rs6000/libBOCP_1252.so
31345000         226K  read/exec
/crystal/sj1xir2a/xir2_r/bobje/enterprise115/aix_rs6000/btlat300.so

看起來上面四個庫無法對映到共享段。因此,它們對映到私有段 0x3 中,該私有段供通過呼叫 mmap() 例程分配的任何通用記憶體使用。
有幾種條件會強制共享庫獨立對映到 32 位 AIX 上:
共享段 0xd 中的空間不足(如上所述)。
共享庫沒有組或其他角色的執行許可權。您可以指定 rwxr-xr-x 許可權來更正此問題;但是,開發人員將希望使用私有許可權(例如 rwx------),這樣他們就不必在每次重新編譯和為測試而部署共享庫時執行 slibclean。
一些文件說明共享庫是通過 nfs 載入的。
如果同一個庫來自不同的位置,那麼 AIX 核心甚至會將該庫兩次載入到共享記憶體中:
sj2e652a-chloe:~/e652_r>genkld | grep libcplib.so
        d5180000    678c6 /space2/home/sj2e652a/e652_r/lib/libcplib.so
        d1cf5000    678c6 /home/sj1e652a/xir2_r/lib/libcplib.so


回頁首


處理錯誤
如果執行部署在不同目錄中的 XIr2 的另一個例項,我們將看到程序佔用記憶體有明顯區別:
$ ps -e -o pid,vsz,user,comm | grep WIReportServer
28166 58980   jbrown WIReportServer
46968 152408 sj1xir2a WIReportServer
48276 152716 sj1xir2a WIReportServer
49800 152788 sj1xir2a WIReportServer
50832 152708 sj1xir2a WIReportServer

帳戶“jbrown”的例項首先啟動,然後啟動帳戶“sj1xir2a”的例項。如果我們要執行一些微小、存在風險的操作,例如在我們的 bobje/setup/env.sh 檔案中的適當位置進行設定:
    LIBPATH=~jbrown/vanpgaix40/bobje/enterprise115/aix_rs6000:$LIBPATH

在啟動第二個例項之前,我們將看到記憶體佔用恢復正常了(我改用程序 boe_fcprocd,因為在這項 LIBPATH 測試中無法啟動 WIReportServer)。
$ ps -e -o pid,vsz,user,comm | grep boe_fcprocd   
29432 65036   jbrown boe_fcprocd
35910 67596   jbrown boe_fcprocd
39326 82488 sj1xir2a boe_fcprocd
53470 64964 sj1xir2a boe_fcprocd

我們看到 procmap 顯示檔案按預期從 ~jbrown 載入:
53470 : /crystal/sj1xir2a/xir2_r/bobje/enterprise115/aix_rs6000/boe_fcprocd
-name vanpg 
10000000       10907K  read/exec         boe_fcprocd
3000079c        1399K  read/write        boe_fcprocd
d42c9000        1098K  read/exec
/home7/jbrown/vanpgaix40/bobje/enterprise115/aix_rs6000/libcrypto.so
33e34160         167K  read/write
/home7/jbrown/vanpgaix40/bobje/enterprise115/aix_rs6000/libcrypto.so
33acc000        3133K  read/exec
/home7/jbrown/vanpgaix40/bobje/enterprise115/aix_rs6000/libcpi18nloc.so
33ddc697         349K  read/write
/home7/jbrown/vanpgaix40/bobje/enterprise115/aix_rs6000/libcpi18nloc.so


回頁首


清理工作
關閉應用程式後,共享庫可能仍然駐留在共享段 0xd 中。在這種情況下,您可以使用實用工具“slibclean”解除安裝不再引用的任何共享庫。該實用工具不需要引數:
slibclean

還可以使用實用工具 genld,在傳遞 -l 選項時,該工具可以顯示類似於 procmap 的結果,但它會顯示系統中的所有現有程序:
genld -l

有時,在執行 slibclean 後,您可能仍然無法複製共享庫。例如:
$ cp /build/dev/bin/release/libc3_calc.so   /runtime/app/lib/
cp: /runtime/app/lib/libc3_calc.so: Text file busy

您可能已經運行了 slibclean,並且執行“genld –l”時未顯示任何程序載入了該庫。但是系統仍然在保護該檔案。您可以通過以下方法解除此限制:首先刪除目標位置的共享庫,然後複製新的共享庫:
$ rm /runtime/app/lib/libc3_calc.so
$ cp /build/dev/bin/release/libc3_calc.so   /runtime/app/lib/

在開發共享庫時,如果您要執行重複的編譯、連結、執行和測試練習,您可以通過將共享庫設定為只有所有者才能執行(例如,r_xr__r__)來避免在每個週期中執行 slibclean 的麻煩。這將導致您用於測試的程序單獨地載入和對映您的共享庫。但是,請確保讓所有人都可以執行它(例如,產品釋出時設為 r_xr_xr_x)。

回頁首


總結
我希望您能夠更詳細地瞭解共享庫佔用記憶體的方式以及可用於檢測它們的實用工具。這樣您就能更好地評估應用程式的規模需求,並分析在 AIX 系統中執行的程序的記憶體佔用構成情況。


參考資料
學習
您可以參閱本文在 developerWorks 全球站點上的 英文原文 。

Performance Management Guide:Using Shared Memory 提供了對 32 位 AIX 中的段以及使用 EXTSHM、shmat() 和 mmap() 子例程的一般性說明。

When Segments Collide提供對 AIX 中的虛擬地址空間和體系結構的深入分析,以及通過 Java 虛擬機器演示的執行時使用情況。

BIN DIRECTORY FILLING UP AFTER COMPILES OF SHARED LIBRARY 討論 slibclean 和 genkld 命令的簡短故障排除提示。

General Programming Concepts:Writing and Debugging Programs 第 19 章“Shared Libraries and Shared Memory”提供有關為共享庫和記憶體分配提供的作業系統工具的資訊。

Solaris Internals:Solaris 10 and OpenSolaris Kernel Architecture,Richard McDougall,Jim Mauro,2006 年 7 月,第 21 章的第 4 部分提供了有關比較 Unix 實現的虛擬記憶體體系結構的大量資訊。第 14 章提供了分析活動 VM 段中的檔案對映的優秀示例。

AIX 5L Porting Guide 詳細說明了將應用程式從其他基於 Unix 的平臺遷移到 AIX 5L 作業系統時最容易出現的問題型別。 

AIX and UNIX 專區:developerWorks 的“AIX and UNIX 專區”提供了大量與 AIX 系統管理的所有方面相關的資訊,您可以利用它們來擴充套件自己的 UNIX 技能。

AIX and UNIX 新手入門:訪問“AIX and UNIX 新手入門”頁面可瞭解更多關於 AIX 和 UNIX 的內容。

AIX and UNIX 專題彙總:AIX and UNIX 專區已經為您推出了很多的技術專題,為您總結了很多熱門的知識點。我們在後面還會繼續推出很多相關的熱門專題給您,為了方便您的訪問,我們在這裡為你把本專區的所有專題進行彙總,讓您更方便的找到你需要的內容。