ulimit限制系统资源


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进程,你会发现机器卡了,我的实验机器性能不怎么好,崩掉啦~

 

1> limits.conf:
--------------------------->
* soft nofile 1000000
* hard nofile 1000000
root soft nproc  20000
root hard nproc  20000
* soft nproc  20000
* hard nproc  20000
---------------------------->
实验结果:
[root@lin /]# ulimit -u   //20000
[root@lin /]# ulimit -n   //1000000
[root@lin /]# su - test
[test@lin ~]$ ulimit -u   //1024
[test@lin ~]$ ulimit -n   //1000000
####################################
2> limits.conf
--------------------------->
* soft nofile 1000000
* hard nofile 1000000
root soft nproc  20000
root hard nproc  20000
* soft nproc  20000
* hard nproc  20000
test soft nproc  20000
test hard nproc  20000
---------------------------->
实验结果:
[root@lin /]# ulimit -u   //20000
[root@lin /]# ulimit -n   //1000000
[root@lin /]# su - test
[test@lin ~]$ ulimit -u   //20000
[test@lin ~]$ ulimit -n   //1000000
######################################
3> limits.conf
---------------------------->
* soft nofile 1000000
* hard nofile 1000000
root soft nproc  30000
root hard nproc  30000
----------------------------->
实验结果:
[root@lin /]# su - root
[root@lin ~]# ulimit -u  //30000
[root@lin ~]# ulimit -n  //1000000
[root@lin /]# su - test
[test@lin ~]$ ulimit -u  //1024
[test@lin ~]$ ulimit -n  //1000000
#######################################
4> limits.conf
------------------------------>
* soft nofile 1000000
* hard nofile 1000000
* soft nproc  20000
* hard nproc  20000
------------------------------->
实验结果:
[root@lin /]# su - root
[root@lin ~]# ulimit -u  //1024
[root@lin ~]# ulimit -n  //1000000
[root@lin ~]# su - test
[test@lin ~]$ ulimit -u  //1024
[test@lin ~]$ ulimit -n  //1000000
########################################

Appendix:

ulimit是shell内建命令,贴出它的manual手册:

===================================================================================

ulimit [-HSTabcdefilmnpqrstuvx [limit]]
              Provides  control  over  the resources available to the
              shell and to processes started by it, on  systems  that
              allow such control.  The -H and -S options specify that
              the hard or soft limit is set for the  given  resource.
              A  hard  limit  cannot  be increased by a non-root user
              once it is set; a soft limit may be increased up to the
              value of the hard limit.  If neither -H nor -S is spec-
              ified, both the soft and  hard  limits  are  set.   The
              value  of  limit  can be a number in the unit specified
              for the resource or one of  the  special  values  hard,
              soft,  or  unlimited,  which stand for the current hard
              limit, the current soft limit, and  no  limit,  respec-
              tively.   If limit is omitted, the current value of the
              soft limit of the resource is printed,  unless  the  -H
              option is given.  When more than one resource is speci-
              fied, the limit name and unit are  printed  before  the 
              value.  Other options are interpreted as follows:

              -a     All current limits are reported
              -b     The maximum socket buffer size
              -c     The maximum size of core files created
              -d     The maximum size of a process’s data segment
              -e     The maximum scheduling priority ("nice")
              -f     The  maximum  size of files written by the shell
                     and its children
              -i     The maximum number of pending signals
              -l     The maximum size that may be locked into memory
              -m     The maximum resident set size (many  systems  do
                     not honor this limit)
              -n     The  maximum  number  of  open  file descriptors
                     (most systems do not allow this value to be set)
              -p     The  pipe  size in 512-byte blocks (this may not
                     be set)
              -q     The maximum number of  bytes  in  POSIX  message
                     queues
              -r     The maximum real-time scheduling priority
              -s     The maximum stack size
              -t     The maximum amount of cpu time in seconds
              -u     The  maximum  number of processes available to a
                     single user

              -v     The maximum amount of virtual  memory  available
                     to the shell
              -x     The maximum number of file locks
              -T     The maximum number of threads

              If limit is given, it is the new value of the specified
              resource (the -a option is display only).  If no option
              is  given, then -f is assumed.  Values are in 1024-byte
              increments, except for -t, which  is  in  seconds,  -p,
              which  is  in units of 512-byte blocks, and -T, -b, -n,
              and -u, which are unscaled values.  The  return  status
              is  0 unless an invalid option or argument is supplied,
              or an error occurs while setting a new limit.

=====================================================================================


转一个绝对牛掰的过来:(2013年6月加)

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 这个文件:

$ catt.stp
probe syscall.open.return{
  filename = user_string($filename)
  if(!isinstr(filename, "90-nproc.conf")) next;
  printf("%s %d\n", execname(), pid());
}
$ sudostap 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,
                     intargc, const char **argv)
{
[...]
   retval = init_limits(pamh, pl, ctrl);
    if(retval != PAM_SUCCESS) {
        pam_syslog(pamh, LOG_WARNING,"cannot initialize");
        returnPAM_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));
        returnPAM_SUCCESS;
    }
    if(retval != PAM_SUCCESS || pl->conf_file != NULL)
        /* skip reading limits.d if config file explicitely specified */
        gotoout;
 
    /* 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);
                returnPAM_SUCCESS;
            }
            if(retval != PAM_SUCCESS)
                gotoout;
        }
    }
 
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 是不是和实际的很接近?
因为物理页面会内存用作一些关键数据,所以实际的比计算出来的要小点。

小结: 源码说话

祝玩的开心!



上一篇:Apache Flink on K8s:四种运行模式,我该选择哪种?


下一篇:屏蔽Web_server版本信息!