limits.conf的工作原理:
limits.conf的後端是這樣工作的:limits.conf是 pam_limits.so的配置檔案,然後/etc/pam.d/下的應用程式
調用pam_***.so子產品。譬如說,當使用者通路伺服器,服務程式将請求發送到PAM子產品,PAM子產品根據服務名稱在/etc/pam.d目錄下選擇一個對應的服務檔案,然後根據服務檔案的内容選擇具體的PAM子產品進行處理。[摘抄]
ulimit是一種簡單并且有效的實作系統資源限制的方式!
下面分享一個小實驗!
實驗:limits實驗!關于能打開的最大檔案數和能并發的最大程序數:
[研究背景: nginx 與 php 的連接配接, 以及對其做壓力測試的時候! 由于php-cgi是單程序的,影響nginx的效率, 然後便使用産蛋程式 spawn.但是它依然受到限制, 怎麼辦? 系統最大并發程序數的控制, 也就是下面實驗的目的了! ]
[注意, 先将系統的預設全局參數調大 sysctl.conf -->kernel.threads-max = xxxxx .... ]
實驗結論:
a. nofile的配置,可以用 * 來通配對所有的使用者的設定!
b. noproc的配置,用 * 無法通配, 隻能指定特定的使用者!
溫馨提醒: 最大程序并發數的設定很危險的,如果你的機器性能不是很好的話,用spawn産出5000個cgi程序,然後用ab并發出上萬個查詢請求進行壓力測試!最後你發現,并發數小于5000的時候錯誤率為零,再高點兒,就有錯了! [當然,我使用的是台PC機,不是标配的伺服器! 實踐很重要!]
另外,當要killall全殺掉服務端開啟的cgi程序,你會發現機器卡了,我的實驗機器性能不怎麼好,崩掉啦~
Appendix:
ulimit是shell内建指令,貼出它的manual手冊:
===================================================================================
=====================================================================================
轉一個絕對牛掰的過來:(2013年6月加)
前兩天微網誌上的@王關勝同學問了個問題:
#ulimit問題# 關于nproc設定:centos6,核心版本是2.6.32. 預設情況下,ulimit -u的值為1024,是/etc/security/limits.d/90-nproc.conf的值限制;注釋掉這個限制後,值為95044;手工設定90-nproc.conf檔案,值為新設定的值。想請 問這個95044是怎麼來的?
這個問題挺有意思的,這裡面有二個資訊點:
1. 為什麼limit配置檔案是 /etc/security/limits.d/90-nproc.conf 而不是其他?
2. 為什麼是nproc的值95044,而不是其他。
我們來簡單的做下實驗:
<code>$</code><code>cat</code><code>/etc/security/limits.d/90-nproc.conf </code>
<code>* soft nproc 8933</code>
<code>$</code><code>ulimit</code><code>-u</code>
<code>8933</code>
<code>$</code><code>cat</code><code>/etc/security/limits.d/90-nproc.conf </code><code>#注釋掉</code>
<code>#* soft nproc 8933</code>
<code>385962</code>
我們可以看出就是說當注釋掉限制的話,不同的機器值是不同的。
我們先來回答第一個問題:為什麼limit配置檔案是 /etc/security/limits.d/90-nproc.conf 而不是其他
我們一步步來看這個問題,首先看下 誰在使用 90-nproc.conf 這個檔案:
<code>$</code><code>cat</code><code>t.stp</code>
<code>probe syscall.</code><code>open</code><code>.</code><code>return</code><code>{</code>
<code> </code><code>filename = user_string($filename)</code>
<code> </code><code>if</code><code>(!isinstr(filename,</code><code>"90-nproc.conf"</code><code>)) next;</code>
<code> </code><code>printf</code><code>(</code><code>"%s %d\n"</code><code>, execname(), pid());</code>
<code>}</code>
<code>$</code><code>sudo</code><code>stap t.stp</code>
<code>sshd 24844</code>
運作腳本後,開個ssh終端上去,就馬上知道sshd在使用這個檔案, 同時也驗證了配置是即刻生效的。
我們都知道linux下這個limit限制是由pam_limits來執行的。
<code>$</code><code>grep</code><code>-rin pam_limit /etc/pam.d</code>
<code>/etc/pam.d/</code><code>sudo</code><code>-i:6:session required pam_limits.so</code>
<code>/etc/pam.d/smartcard-auth-ac:16:session required pam_limits.so</code>
<code>/etc/pam.d/smartcard-auth:16:session required pam_limits.so</code>
<code>/etc/pam.d/system-auth-ac:20:session required pam_limits.so</code>
<code>/etc/pam.d/fingerprint-auth:16:session required pam_limits.so</code>
<code>/etc/pam.d/</code><code>sudo</code><code>:6:session required pam_limits.so</code>
<code>/etc/pam.d/runuser:4:session required pam_limits.so</code>
<code>/etc/pam.d/password-auth-ac:19:session required pam_limits.so</code>
<code>/etc/pam.d/password-auth:19:session required pam_limits.so</code>
<code>/etc/pam.d/system-auth:20:session required pam_limits.so</code>
<code>/etc/pam.d/fingerprint-auth-ac:16:session required pam_limits.so</code>
瞄幾下modules/pam_limits/pam_limits.c就知道 限制如何執行的:
<code>/* now the session stuff */</code>
<code>PAM_EXTERN</code><code>int</code>
<code>pam_sm_open_session (pam_handle_t *pamh,</code>
<code>int</code> <code>flags UNUSED,</code>
<code> </code><code>int</code><code>argc,</code><code>const</code> <code>char</code>
<code>**argv)</code>
<code>{</code>
<code>[...]</code>
<code> </code><code>retval = init_limits(pamh, pl, ctrl);</code>
<code> </code><code>if</code><code>(retval != PAM_SUCCESS) {</code>
<code> </code><code>pam_syslog(pamh, LOG_WARNING,</code><code>"cannot initialize"</code><code>);</code>
<code> </code><code>return</code><code>PAM_ABORT;</code>
<code> </code><code>}</code>
<code> </code><code>retval = parse_config_file(pamh, pwd->pw_name, pwd->pw_uid, pwd->pw_gid, ctrl, pl);</code>
<code> </code><code>if</code><code>(retval == PAM_IGNORE) {</code>
<code> </code><code>D((</code><code>"the configuration file ('%s') has an applicable '<domain> -' entry"</code><code>, CONF_FILE));</code>
<code> </code><code>return</code><code>PAM_SUCCESS;</code>
<code> </code><code>if</code><code>(retval != PAM_SUCCESS || pl->conf_file != NULL)</code>
<code> </code><code>/* skip reading limits.d if config file explicitely specified */</code>
<code> </code><code>goto</code><code>out;</code>
<code> </code><code>/* Read subsequent *.conf files, if they exist. */</code>
<code> </code><code>/* set the LC_COLLATE so the sorting order doesn't depend </code>
<code> </code><code>on system locale */</code>
<code> </code><code>oldlocale =</code><code>setlocale</code><code>(LC_COLLATE,</code><code>"C"</code><code>);</code>
<code> </code><code>glob_rc = glob(LIMITS_CONF_GLOB, GLOB_ERR, NULL, &globbuf);</code>
<code> </code><code>if</code><code>(oldlocale != NULL)</code>
<code> </code><code>setlocale</code><code>(LC_COLLATE, oldlocale);</code>
<code> </code><code>if</code><code>(!glob_rc) {</code>
<code> </code><code>/* Parse the *.conf files. */</code>
<code> </code><code>for</code><code>(i = 0; globbuf.gl_pathv[i] != NULL; i++) {</code>
<code> </code><code>pl->conf_file = globbuf.gl_pathv[i];</code>
<code> </code><code>retval = parse_config_file(pamh, pwd->pw_name, pwd->pw_uid, pwd->pw_gid, ctrl, pl);</code>
<code> </code><code>if</code><code>(retval == PAM_IGNORE) {</code>
<code> </code><code>D((</code><code>"the configuration file ('%s') has an applicable '<domain> -' entry"</code><code>, pl->conf_file));</code>
<code> </code><code>globfree(&globbuf);</code>
<code> </code><code>return</code><code>PAM_SUCCESS;</code>
<code> </code><code>}</code>
<code> </code><code>if</code><code>(retval != PAM_SUCCESS)</code>
<code> </code><code>goto</code><code>out;</code>
<code> </code><code>}</code>
<code>out:</code>
分析這段代碼可以知道先讀/etc/security/limits.conf,如果/etc/security/limits.d/目錄下還有配置檔案的話,也讀進來,一起分析。
這就意味/etc/security/limits.d/裡面的檔案裡面的配置會覆寫/etc/security/limits.conf的配置。
我們看下這行:glob_rc = glob(LIMITS_CONF_GLOB, GLOB_ERR, NULL, &globbuf);
讀取/etc/security/limits.d/目錄下檔案的函數,從名字就可以猜出,是周遊,檔案名的數字起到順序的作用。
到此就解釋了檔案名90-nproc.conf的作用。
接着看第二個問題: 為什麼是nproc的值95044, 而不是其他。
通過閱讀process_limit函數隻是看到 nproc的最大值限制,沒有看到其他的,那我們就很容易聯想,如果使用者不設定nproc的話,那麼這個值應該是由核心自己來決定。
我們看下核心代碼 2.6.18:
<code>$</code><code>pwd</code>
<code>/home/chuba/linux-2.6.18.x86_64/kernel</code>
<code>$</code><code>grep</code><code>-rin nproc .</code>
<code>./sys.c:896: current->signal->rlim[RLIMIT_NPROC].rlim_cur &&</code>
<code>./fork.c:176: init_task.signal->rlim[RLIMIT_NPROC].rlim_cur = max_threads/2;</code>
<code>./fork.c:177: init_task.signal->rlim[RLIMIT_NPROC].rlim_max = max_threads/2;</code>
<code>./fork.c:179: init_task.signal->rlim[RLIMIT_NPROC];</code>
<code>./fork.c:1130: p->signal->rlim[RLIMIT_NPROC].rlim_cur) {</code>
<code>./cpuset.c:69: int cnt; /* unprocessed events count */</code>
<code>./cpuset.c:1140: * Limit the count of unprocessed events to FM_MAXCNT, so as to avoid</code>
<code>./user.c:181: * new uid over his NPROC rlimit? We can check this now</code>
一下子就看出來了 預設值是 max_threads/2. 打開fork.c分析下:
//fork_init(num_physpages); //void __init fork_init(unsigned long mempages) /* * The default maximum number of threads is set to a safe * value: the thread structures can take up at most half * of memory. */ max_threads = mempages / (8 * THREAD_SIZE / PAGE_SIZE);
其中mempages是機器的實體頁面個數, THREAD_SIZE=8K, 也就是說:
default_nproc = max_threads / 2 = (mempages * PAGE_SIZE) / ( 2 * 8 *THREAD_SIZE ) = total_memory/128K;
我們來驗證下:
<code>$</code><code>cat</code><code>/proc/meminfo |</code><code>grep</code>
<code>MemTotal</code>
<code>MemTotal: 49421024 kB</code>
<code>$</code><code>echo</code><code>"49421024 / 128"</code><code>|</code><code>bc</code>
<code>386101</code>
算出來default_nproc =386101 是不是和實際的很接近?
因為實體頁面會記憶體用作一些關鍵資料,是以實際的比計算出來的要小點。
小結: 源碼說話!
祝玩的開心!