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

之前我也寫了些ulimit的問題的解決,參見 這裏

我們來簡單的做下實驗:

cat /etc/security/limits.d/90-nproc.conf         
*      soft    nproc   8933
ulimit -u
8933
 
cat /etc/security/limits.d/90-nproc.conf      #註釋掉
#*      soft    nproc   8933
ulimit -u
385962

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

我們先來回答第一個問題:爲什麼limit配置文件是 /etc/security/limits.d/90-nproc.conf 而不是其他
這個問題早些時候 楊德華 同學碰到了,也寫了篇 博文 來解釋redhat6下面如何破解nproc的限制,但是文章沒提到這個問題。

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

cat t.stp
probe syscall.open.return {
  filename = user_string($filename)
  if (!isinstr(filename, "90-nproc.conf")) next;
  printf("%s %d\n", execname(), pid());
}
sudo stap t.stp
sshd 24844


運行腳本後,開個ssh終端上去,就馬上知道sshd在使用這個文件, 同時也驗證了配置是即刻生效的。

我們都知道linux下這個limit限制是由pam_limits來執行的。
那麼什麼是PAM以及它的架構,參考 這裏

grep -rin pam_limit /etc/pam.d
/etc/pam.d/sudo-i:6:session    required     pam_limits.so
/etc/pam.d/smartcard-auth-ac:16:session     required      pam_limits.so
/etc/pam.d/smartcard-auth:16:session     required      pam_limits.so
/etc/pam.d/system-auth-ac:20:session     required      pam_limits.so
/etc/pam.d/fingerprint-auth:16:session     required      pam_limits.so
/etc/pam.d/sudo:6:session    required     pam_limits.so
/etc/pam.d/runuser:4:session            required        pam_limits.so
/etc/pam.d/password-auth-ac:19:session     required      pam_limits.so
/etc/pam.d/password-auth:19:session     required      pam_limits.so
/etc/pam.d/system-auth:20:session     required      pam_limits.so
/etc/pam.d/fingerprint-auth-ac:16:session     required      pam_limits.so

那很自然,我們就會去找pam_limits的代碼來看, 代碼在 這裏 可以下載,目前的版本是 Linux-PAM-1.1.6。

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

/* now the session stuff */
PAM_EXTERN int
pam_sm_open_session (pam_handle_t *pamh, int flags UNUSED,
                     int argc, const char **argv)
{
[...]
   retval = init_limits(pamh, pl, ctrl);
    if (retval != PAM_SUCCESS) {
        pam_syslog(pamh, LOG_WARNING, "cannot initialize");
        return PAM_ABORT;
    }
 
    retval = parse_config_file(pamh, pwd->pw_name, pwd->pw_uid, pwd->pw_gid, ctrl, pl);
    if (retval == PAM_IGNORE) {
        D(("the configuration file ('%s') has an applicable '<domain> -' entry", CONF_FILE));
        return PAM_SUCCESS;
    }
    if (retval != PAM_SUCCESS || pl->conf_file != NULL)
        /* skip reading limits.d if config file explicitely specified */
        goto out;
 
    /* Read subsequent *.conf files, if they exist. */
 
    /* set the LC_COLLATE so the sorting order doesn't depend                       
        on system locale */
 
    oldlocale = setlocale(LC_COLLATE, "C");
    glob_rc = glob(LIMITS_CONF_GLOB, GLOB_ERR, NULL, &globbuf);
 
    if (oldlocale != NULL)
        setlocale (LC_COLLATE, oldlocale);
 
    if (!glob_rc) {
        /* Parse the *.conf files. */
        for (i = 0; globbuf.gl_pathv[i] != NULL; i++) {
            pl->conf_file = globbuf.gl_pathv[i];
            retval = parse_config_file(pamh, pwd->pw_name, pwd->pw_uid, pwd->pw_gid, ctrl, pl);
          if (retval == PAM_IGNORE) {
                D(("the configuration file ('%s') has an applicable '<domain> -' entry", pl->conf_file));
                globfree(&globbuf);
                return PAM_SUCCESS;
            }
            if (retval != PAM_SUCCESS)
                goto out;
        }
    }
 
out:
[...]
}

分析這段代碼可以知道先讀/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:

pwd
/home/chuba/linux-2.6.18.x86_64/kernel
grep -rin nproc .
./sys.c:896:                            current->signal->rlim[RLIMIT_NPROC].rlim_cur &&
./fork.c:176:   init_task.signal->rlim[RLIMIT_NPROC].rlim_cur = max_threads/2;
./fork.c:177:   init_task.signal->rlim[RLIMIT_NPROC].rlim_max = max_threads/2;
./fork.c:179:           init_task.signal->rlim[RLIMIT_NPROC];
./fork.c:1130:                  p->signal->rlim[RLIMIT_NPROC].rlim_cur) {
./cpuset.c:69:  int cnt;                /* unprocessed events count */
./cpuset.c:1140: * Limit the count of unprocessed events to FM_MAXCNT, so as to avoid
./user.c:181:    * new uid over his NPROC rlimit?  We can check this now

一下子就看出來了 默認值是 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;

我們來驗證下:

cat /proc/meminfo |grep MemTotal
MemTotal:       49421024 kB
echo "49421024 / 128"bc 
386101
ulimit -u
385962

算出來default_nproc =386101 是不是和實際的很接近?
因爲物理頁面會內存用作一些關鍵數據,所以實際的比計算出來的要小點。

小結: 源碼說話!



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章