經過 ulimit 改善系統性能

概述

系統性能一直是一個受關注的話題,如何經過最簡單的設置來實現最有效的性能調優, 如何在有限資源的條件下保證程序的運做,ulimit 是咱們在處理這些問題時,常用的一種簡單手段。ulimit 是一種 linux 系統的內鍵功能,它具備一套參數集,用於爲由它生成的 shell 進程及其子進程的資源使用設置限制。本文將在後面的章節中詳細說明 ulimit 的功能,使用以及它的影響,並以具體的例子來詳細地闡述它在限制資源使用方面的影響。 linux

回頁首 shell

ulimit 的功能和用法

ulimit 功能簡述

假設有這樣一種狀況,當一臺 Linux 主機上同時登錄了 10 我的,在系統資源無限制的狀況下,這 10 個用戶同時打開了 500 個文檔,而假設每一個文檔的大小有 10M,這時系統的內存資源就會受到巨大的挑戰。 性能優化

而 實際應用的環境要比這種假設複雜的多,例如在一個嵌入式開發環境中,各方面的資源都是很是緊缺的,對於開啓文件描述符的數量,分配堆棧的大小,CPU 時間,虛擬內存大小,等等,都有很是嚴格的要求。資源的合理限制和分配,不只僅是保證系統可用性的必要條件,也與系統上軟件運行的性能有着密不可分的聯 系。這時,ulimit 能夠起到很大的做用,它是一種簡單而且有效的實現資源限制的方式。 bash

ulimit 用於限制 shell 啓動進程所佔用的資源,支持如下各類類型的限制:所建立的內核文件的大小、進程數據塊的大小、Shell 進程建立文件的大小、內存鎖住的大小、常駐內存集的大小、打開文件描述符的數量、分配堆棧的最大大小、CPU 時間、單個用戶的最大線程數、Shell 進程所能使用的最大虛擬內存。同時,它支持硬資源和軟資源的限制。 多線程

做爲臨時限制,ulimit 能夠做用於經過使用其命令登陸的 shell 會話,在會話終止時便結束限制,並不影響於其餘 shell 會話。而對於長期的固定限制,ulimit 命令語句又能夠被添加到由登陸 shell 讀取的文件中,做用於特定的 shell 用戶。 app

圖 1. ulimit 的使用
ulimit 的使用

在下面的章節中,將詳細介紹如何使用 ulimit 作相應的資源限制。 dom

如何使用 ulimit

ulimit 經過一些參數選項來管理不一樣種類的系統資源。在本節,咱們將講解這些參數的使用。 socket

ulimit 命令的格式爲:ulimit [options] [limit] 性能

具體的 options 含義以及簡單示例能夠參考如下表格。 優化

表 1. ulimit 參數說明
選項 [options] 含義 例子
-H 設置硬資源限制,一旦設置不能增長。 ulimit – Hs 64;限制硬資源,線程棧大小爲 64K。
-S 設置軟資源限制,設置後能夠增長,可是不能超過硬資源設置。 ulimit – Sn 32;限制軟資源,32 個文件描述符。
-a 顯示當前全部的 limit 信息。 ulimit – a;顯示當前全部的 limit 信息。
-c 最大的 core 文件的大小, 以 blocks 爲單位。 ulimit – c unlimited; 對生成的 core 文件的大小不進行限制。
-d 進程最大的數據段的大小,以 Kbytes 爲單位。 ulimit -d unlimited;對進程的數據段大小不進行限制。
-f 進程能夠建立文件的最大值,以 blocks 爲單位。 ulimit – f 2048;限制進程能夠建立的最大文件大小爲 2048 blocks。
-l 最大可加鎖內存大小,以 Kbytes 爲單位。 ulimit – l 32;限制最大可加鎖內存大小爲 32 Kbytes。
-m 最大內存大小,以 Kbytes 爲單位。 ulimit – m unlimited;對最大內存不進行限制。
-n 能夠打開最大文件描述符的數量。 ulimit – n 128;限制最大可使用 128 個文件描述符。
-p 管道緩衝區的大小,以 Kbytes 爲單位。 ulimit – p 512;限制管道緩衝區的大小爲 512 Kbytes。
-s 線程棧大小,以 Kbytes 爲單位。 ulimit – s 512;限制線程棧的大小爲 512 Kbytes。
-t 最大的 CPU 佔用時間,以秒爲單位。 ulimit – t unlimited;對最大的 CPU 佔用時間不進行限制。
-u 用戶最大可用的進程數。 ulimit – u 64;限制用戶最多可使用 64 個進程。
-v 進程最大可用的虛擬內存,以 Kbytes 爲單位。 ulimit – v 200000;限制最大可用的虛擬內存爲 200000 Kbytes。

咱們能夠經過如下幾種方式來使用 ulimit:

  • 在用戶的啓動腳本中

    若是用戶使用的是 bash,就能夠在用戶的目錄下的 .bashrc 文件中,加入 ulimit – u 64,來限制用戶最多可使用 64 個進程。此外,能夠在與 .bashrc 功能至關的啓動腳本中加入 ulimt。

  • 在應用程序的啓動腳本中

    若是用戶要對某個應用程序 myapp 進行限制,能夠寫一個簡單的腳本 startmyapp。

    ulimit – s 512 
     myapp

    之後只要經過腳本 startmyapp 來啓動應用程序,就能夠限制應用程序 myapp 的線程棧大小爲 512K。

  • 直接在控制檯輸入
    user@tc511-ui:~>ulimit – p 256

    限制管道的緩衝區爲 256K。

回頁首

用戶進程的有效範圍

ulimit 做爲對資源使用限制的一種工做,是有其做用範圍的。那麼,它限制的對象是單個用戶,單個進程,仍是整個系統呢?事實上,ulimit 限制的是當前 shell 進程以及其派生的子進程。舉例來講,若是用戶同時運行了兩個 shell 終端進程,只在其中一個環境中執行了 ulimit – s 100,則該 shell 進程裏建立文件的大小收到相應的限制,而同時另外一個 shell 終端包括其上運行的子程序都不會受其影響:

Shell 進程 1
ulimit – s 100 
 cat testFile > newFile 
 File size limit exceeded
Shell 進程 2
cat testFile > newFile 
 ls – s newFile 
 323669 newFile

那麼,是否有針對某個具體用戶的資源加以限制的方法呢?答案是有的,方法是經過修改系統的 /etc/security/limits 配置文件。該文件不只能限制指定用戶的資源使用,還能限制指定組的資源使用。該文件的每一行都是對限定的一個描述,格式以下:

<domain> <type> <item> <value>

domain 表示用戶或者組的名字,還可使用 * 做爲通配符。Type 能夠有兩個值,soft 和 hard。Item 則表示須要限定的資源,能夠有不少候選值,如 stack,cpu,nofile 等等,分別表示最大的堆棧大小,佔用的 cpu 時間,以及打開的文件數。經過添加對應的一行描述,則能夠產生相應的限制。例如:

* hard noflle 100

該行配置語句限定了任意用戶所能建立的最大文件數是 100。

現 在已經能夠對進程和用戶分別作資源限制了,看似已經足夠了,其實否則。不少應用須要對整個系統的資源使用作一個總的限制,這時候咱們須要修改 /proc 下的配置文件。/proc 目錄下包含了不少系統當前狀態的參數,例如 /proc/sys/kernel/pid_max,/proc/sys/net/ipv4/ip_local_port_range 等等,從文件的名字大體能夠猜出所限制的資源種類。因爲該目錄下涉及的文件衆多,在此不一一介紹。有興趣的讀者可打開其中的相關文件查閱說明。

回頁首

ulimit 管理系統資源的例子

ulimit 提供了在 shell 進程中限制系統資源的功能。本章列舉了一些使用 ulimit 對用戶進程進行限制的例子,詳述了這些限制行爲以及對應的影響,以此來講明 ulimit 如何對系統資源進行限制,從而達到調節系統性能的功能。

使用 ulimit 限制 shell 的內存使用

在這一小節裏向讀者展現如何使用 – d,– m 和 – v 選項來對 shell 所使用的內存進行限制。

首先咱們來看一下不設置 ulimit 限制時調用 ls 命令的狀況:

圖 2. 未設置 ulimit 時 ls 命令使用狀況
未設置 ulimit 時 ls 命令使用狀況

你們能夠看到此時的 ls 命令運行正常。下面設置 ulimit:

>ulimit -d 1000 -m 1000 -v 1000

這裏再溫習一下前面章節裏介紹過的這三個選項的含義:

-d:設置數據段的最大值。單位:KB。

-m:設置可使用的常駐內存的最大值。單位:KB。

-v:設置虛擬內存的最大值。單位:KB。

經過上面的 ulimit 設置咱們已經把當前 shell 所能使用的最大內存限制在 1000KB 如下。接下來咱們看看這時運行 ls 命令會獲得什麼樣的結果:

haohe@sles10-hehao:~/code/ulimit> ls test -l 
 /bin/ls: error while loading shared libraries: libc.so.6: failed to map segment 
 from shared object: Cannot allocate memory

從上面的結果能夠看到,此時 ls 運行失敗。根據系統給出的錯誤信息咱們能夠看出是因爲調用 libc 庫時內存分配失敗而致使的 ls 出錯。那麼咱們來看一下這個 libc 庫文件到底有多大:

圖 3. 查看 libc 文件大小
查看 libc 文件大小

從上面的信息能夠看出,這個 libc 庫文件的大小是 1.5MB。而咱們用 ulimit 所設置的內存使用上限是 1000KB,小於 1.5MB,這也就充分證實了 ulimit 所起到的限制 shell 內存使用的功能。

使用 ulimit 限制 shell 建立的文件的大小

接下來向讀者展現如何使用 -f 選項來對 shell 所能建立的文件大小進行限制。

首先咱們來看一下,沒有設置 ulimit -f 時的狀況:

圖 4. 查看文件
查看文件

現有一個文件 testFile 大小爲 323669 bytes,如今使用 cat 命令來建立一個 testFile 的 copy:

圖 5. 未設置 ulimit 時建立複本
未設置 ulimit 時建立複本

從上面的輸出能夠看出,咱們成功的建立了 testFile 的拷貝 newFile。

下面咱們設置 ulimt – f 100:

> ulimit -f 100

-f 選項的含義是:用來設置 shell 能夠建立的文件的最大值。單位是 blocks。

如今咱們再來執行一次相同的拷貝命令看看會是什麼結果:

圖 6. 設置 ulimit 時建立複本
設置 ulimit 時建立複本

這 次建立 testFile 的拷貝失敗了,系統給出的出錯信息時文件大小超出了限制。在 Linux 系統下一個 block 的默認大小是 512 bytes。因此上面的 ulimit 的含義就是限制 shell 所能建立的文件最大值爲 512 x 100 = 51200 bytes,小於 323669 bytes,因此建立文件失敗,符合咱們的指望。這個例子說明了如何使用 ulimit 來控制 shell 所能建立的最大文件。

使用 ulimit 限制程序所能建立的 socket 數量

考 慮一個現實中的實際需求。對於一個 C/S 模型中的 server 程序來講,它會爲多個 client 程序請求建立多個 socket 端口給與響應。若是剛好有大量的 client 同時向 server 發出請求,那麼此時 server 就會須要建立大量的 socket 鏈接。但在一個系統當中,每每須要限制單個 server 程序所能使用的最大 socket 數,以供其餘的 server 程序所使用。那麼咱們如何來作到這一點呢?答案是咱們能夠經過 ulimit 來實現!細心的讀者可能會發現,經過前面章節的介紹彷佛沒有限制 socket 使用的 ulimit 選項。是的,ulimit 並無哪一個選項直接說是用來限制 socket 的數量的。可是,咱們有 -n 這個選項,它是用於限制一個進程所能打開的文件描述符的最大值。在 Linux 下一切資源皆文件,普通文件是文件,磁盤打印機是文件,socket 固然也是文件。在 Linux 下建立一個新的 socket 鏈接,實際上就是建立一個新的文件描述符。以下圖所示(查看某個進程當前打開的文件描述符信息):

圖 7. 查看進程打開文件描述符
查看進程打開文件描述符

所以,咱們能夠經過使用 ulimit – n 來限制程序所能打開的最大文件描述符數量,從而達到限制 socket 建立的數量。

使用 ulimit 限制 shell 多線程程序堆棧的大小(增長可用線程數量)

在最後一個例子中,向你們介紹如何使用 -s(單位 KB)來對線程的堆棧大小進行限制,從而減小整個多線程程序的內存使用,增長可用線程的數量。這個例子取自於一個真實的案例。咱們所遇到的問題是系統對咱們的多線程程序有以下的限制:

ulimit -v 200000

根據本文前面的介紹,這意味着咱們的程序最多隻能使用不到 200MB 的虛擬內存。因爲咱們的程序是一個多線程程序,程序在運行時會根據須要建立新的線程,這勢必會增長總的內存需求量。一開始咱們對堆棧大小的限制是 1024 (本例子中使用 1232 來講明):

# ulimit – s 1232

當咱們的程序啓動後,經過 pmap 來查看其內存使用狀況,能夠看到多個佔用 1232KB 的數據段,這些就是程序所建立的線程所使用的堆棧:

圖 8. 程序線程所使用的堆棧
程序線程所使用的堆棧

每當一個新的線程被建立時都須要新分配一段大小爲 1232KB 的內存空間,而咱們總的虛擬內存限制是 200MB,因此若是咱們須要建立更多的線程,那麼一個能夠改進的方法就是減小每一個線程的固定堆棧大小,這能夠經過 ulimit – s 來實現:

# ulimit -s 512

咱們將堆棧大小設置爲 512KB,這時再經過 pmap 查看一下咱們的設置是否起做用:

圖 9. 設置 ulimit 後堆棧大小
設置 ulimit 後堆棧大小

從上面的信息能夠看出,咱們已經成功的將線程的堆棧大小改成 512KB 了,這樣在總內存使用限制不變的狀況下,咱們能夠經過本小節介紹的方法來增長能夠建立的線程數,從而達到改善程序的多線程性能。

回頁首

總結

綜 上所述,linux 系統中的 ulimit 指令,對資源限制和系統性能優化提供了一條便捷的途徑。從用戶的 shell 啓動腳本,應用程序啓動腳本,以及直接在控制檯,均可以經過該指令限制系統資源的使用,包括所建立的內核文件的大小、進程數據塊的大小、Shell 進程建立文件的大小、內存鎖住的大小、常駐內存集的大小、打開文件描述符的數量、分配堆棧的最大大小、CPU 時間、單個用戶的最大線程數、Shell 進程所能使用的最大虛擬內存,等等方面。本文中的示例很是直觀的說明了 ulimit 的使用及其產生的效果,顯而易見,ulimit 對咱們在 Linux 平臺的應用和開發工做是很是實用的。

回頁首

特別感謝

賀皓爲本篇文章的撰寫提出了不少建議,作了大量實驗,提供了文章中的實驗數據和結論。

賀皓是 IBM CSTL(中國系統與科技開發中心)的軟件工程師。他在復旦大學得到了計算機科學專業的學士與碩士學位。目前他在 IBM SVC Agent 開發小組從事研發工做。

相關文章
相關標籤/搜索