天天看點

ulimit限制之nproc問題

前兩天微網誌上的@王關勝同學問了個問題:

#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,而不是其他。

我們來簡單的做下實驗:

1

2

3

4

5

6

7

8

9

<code>$</code><code>cat</code>

<code>/etc/security/limits</code><code>.d</code><code>/90-nproc</code><code>.conf         </code>

<code>*      soft    nproc   8933</code>

<code>$</code><code>ulimit</code>

<code>-u</code>

<code>8933</code>

<code>/etc/security/limits</code><code>.d</code><code>/90-nproc</code><code>.conf     </code><code>#注釋掉</code>

<code>#*      soft    nproc   8933</code>

<code>385962</code>

我們可以看出就是說當注釋掉限制的話,不同的機器值是不同的。

我們先來回答第一個問題:為什麼limit配置檔案是 /etc/security/limits.d/90-nproc.conf 而不是其他

我們一步步來看這個問題,首先看下 誰在使用 90-nproc.conf 這個檔案:

<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來執行的。

10

11

12

<code>$</code><code>grep</code>

<code>-rin pam_limit</code><code>/etc/pam</code><code>.d</code>

<code>/etc/pam</code><code>.d</code><code>/sudo-i</code><code>:6:session    required     pam_limits.so</code>

<code>/etc/pam</code><code>.d</code><code>/smartcard-auth-ac</code><code>:16:session     required      pam_limits.so</code>

<code>/etc/pam</code><code>.d</code><code>/smartcard-auth</code><code>:16:session     required      pam_limits.so</code>

<code>/etc/pam</code><code>.d</code><code>/system-auth-ac</code><code>:20:session     required      pam_limits.so</code>

<code>/etc/pam</code><code>.d</code><code>/fingerprint-auth</code><code>:16:session     required      pam_limits.so</code>

<code>/etc/pam</code><code>.d</code><code>/sudo</code><code>:6:session    required     pam_limits.so</code>

<code>/etc/pam</code><code>.d</code><code>/runuser</code><code>:4:session            required        pam_limits.so</code>

<code>/etc/pam</code><code>.d</code><code>/password-auth-ac</code><code>:19:session     required      pam_limits.so</code>

<code>/etc/pam</code><code>.d</code><code>/password-auth</code><code>:19:session     required      pam_limits.so</code>

<code>/etc/pam</code><code>.d</code><code>/system-auth</code><code>:20:session     required      pam_limits.so</code>

<code>/etc/pam</code><code>.d</code><code>/fingerprint-auth-ac</code><code>:16:session     required      pam_limits.so</code>

瞄幾下modules/pam_limits/pam_limits.c就知道 限制如何執行的:

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

<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>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-&gt;pw_name, pwd-&gt;pw_uid, pwd-&gt;pw_gid, ctrl, pl);</code>

<code>(retval == PAM_IGNORE) {</code>

<code>        </code><code>D((</code><code>"the configuration file ('%s') has an applicable '&lt;domain&gt; -' entry"</code><code>, CONF_FILE));</code>

<code>PAM_SUCCESS;</code>

<code>(retval != PAM_SUCCESS || pl-&gt;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, &amp;globbuf);</code>

<code>(oldlocale != NULL)</code>

<code>        </code><code>setlocale</code>

<code>(LC_COLLATE, oldlocale);</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-&gt;conf_file = globbuf.gl_pathv[i];</code>

<code>            </code><code>retval = parse_config_file(pamh, pwd-&gt;pw_name, pwd-&gt;pw_uid, pwd-&gt;pw_gid, ctrl, pl);</code>

<code>          </code><code>if</code>

<code>                </code><code>D((</code><code>"the configuration file ('%s') has an applicable '&lt;domain&gt; -' entry"</code><code>, pl-&gt;conf_file));</code>

<code>                </code><code>globfree(&amp;globbuf);</code>

<code>                </code><code>return</code>

<code>            </code><code>}</code>

<code>            </code><code>if</code>

<code>(retval != PAM_SUCCESS)</code>

<code>                </code><code>goto</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, &amp;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</code><code>.6.18.x86_64</code><code>/kernel</code>

<code>-rin nproc .</code>

<code>.</code><code>/sys</code><code>.c:896:                            current-&gt;signal-&gt;rlim[RLIMIT_NPROC].rlim_cur &amp;&amp;</code>

<code>.</code><code>/fork</code><code>.c:176:   init_task.signal-&gt;rlim[RLIMIT_NPROC].rlim_cur = max_threads</code><code>/2</code><code>;</code>

<code>.</code><code>/fork</code><code>.c:177:   init_task.signal-&gt;rlim[RLIMIT_NPROC].rlim_max = max_threads</code><code>/2</code><code>;</code>

<code>.</code><code>/fork</code><code>.c:179:           init_task.signal-&gt;rlim[RLIMIT_NPROC];</code>

<code>.</code><code>/fork</code><code>.c:1130:                  p-&gt;signal-&gt;rlim[RLIMIT_NPROC].rlim_cur) {</code>

<code>.</code><code>/cpuset</code><code>.c:69:  int cnt;                /* unprocessed events count */</code>

<code>.</code><code>/cpuset</code><code>.c:1140: * Limit the count of unprocessed events to FM_MAXCNT, so as to avoid</code>

<code>.</code><code>/user</code><code>.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>/proc/meminfo</code> <code>|</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 是不是和實際的很接近?

因為實體頁面會記憶體用作一些關鍵資料,是以實際的比計算出來的要小點。

小結: 源碼說話!

祝玩的開心!

繼續閱讀