ulimit到底谁说了算?

最近在用nginx+PHP FastCGI(php-fpm)配置站点(php-fpm默认的文件打开数目如果不做规定,就是系统最大数目,也就是只要修改系统数目就好了),在最后阶段的优化过程中发现这ulimit的问题很奇怪,那就是/etc/security/limits.conf这个文件说了不算!
如下是我的配置文件

* soft nofile 65530
* hard nofile 65530

可以看见 软硬限制都是已经写到65530,但是重启后运行ulimit -n查看,还是默认1024
再Google上查阅资料,有人说看见ulimit -n变了才算是生效,有人说得看 cat /proc/{pid}/limits |grep 'Max open files'
经过多方求证,发现第二种方法才是最正确的方法,这里面真复杂。
根据IBM知识库

作为临时限制,ulimit 可以作用于通过使用其命令登录的 shell 会话,在会话终止时便结束限制,并不影响于其他 shell 会话。而对于长期的固定限制,ulimit 命令语句又可以被添加到由登录 shell 读取的文件中,作用于特定的 shell 用户。

这里没有提及/etc/security/limits.conf,因为这个配置文件起效必须基于pam验证,且PAM 的 limits 模块要启用。在我配置过程中,我已经都启用了,但是无论我怎么重启服务器,就是不生效。具体原因不详。
例外,文章所说的ulimit是作为一种“临时限制”来存在的。我来举个例子。假设我开了一个用户root登录shell A,设置ulimit -SHn 65535,这个用户启动了程序A(比如nginx),当用户退出shell A后A程序继续工作在65535个文件打开限制。这时候,当用户root登录到shell B,查看ulimit -n 还是1024个 查看(self是到当前程序 PID的软连接)

cat /proc/self/limits |grep 'Max open files'

看到的是一样的。

下面我们来获取程序A(nginx)的pid,查看他的limits

[bash]for pid in ps aux |grep nginx |grep -v grep|awk '{print $2}'
do
cat /proc/${pid}/limits |grep 'Max open files'
done[/bash]

显示

Max open files 65530 65530 files
Max open files 65530 65530 files
Max open files 65530 65530 files
Max open files 65530 65530 files

可见A程序(nginx)工作限制和当前用户所在地shell的限制无必然关系。推而广之,要想真正让nginx和php5-fpm工作在最大打开文件数目模式下,编辑他们的init.d内地文件,开头加上ulimit -SHn 65535即可
可是这样一两个程序还可以,要是多个程序呢?有人说写到rc.local/profile里面,我也测试了
首先我在profile里面设立了自己的ulimit,重启后进入shell查看,确实是65530,/proc/self/limits也是。那么nginx的运行状况如何呢?
还是用上面那个程序查看了一下,发现nginx还是工作在默认的1024限制下面,可见当前shell的ulimit和nginx工作的ulimit不相等(这种状况非常有迷惑性,误导了很多人,比如LNMP一键包就是加在profile里面的,这样的话,除非你手动登录上去重启nginx,否则他始终工作在1024的限制下面),那么写到rc.local结果如何呢?我测试了一下,除非你的nginx在rc.local里面启动,否则还是一样的结果(甚至更糟糕,所有不以是rc.local为父进程的程序都不能获得大的文件打开数目)。

说道这里问题的关键在于找到所有的程序的父进程,这个父进程有大文件数目打开权限,那么他的和他派生的子进程都会有大文件数目打开权限。但是我也不可能把所有的程序都赋予超大文件打开数目,会争夺资源。所以我的解决办法是rc.local调用一个shell脚本。这个脚本赋予大文件打开数目。然后再这个文件里面重新启动nginx/php-fpm/mysql等(不是reload,是restart.),同时为了自己在shell里面调试方便,profile里面也要设置。

当然这个只是折衷办法,要是能搞清/etc/security/limits.conf为何不生效就最好了。不知哪位可以解惑。


Update:
经过多次试验,好像终于有点明白了。

首先/etc/security/limits.conf里面的*默认是不匹配root用户的。你需要手动指定root才能在登录ssh后看见生效。
这个只在login这个动作发生的时候起作用。比如控制台登录等等。即使你修改了/etc/security/limits.conf使得通过控制台登录后看见ulimit -n生效了,但是nginx还是没有工作在那个大文件数目授权下面。因为启动nginx的时候是系统的服务管理器而不是你的会话。如果你一定要通过这种方式,你需要把nginx在你的会话里面重启一遍(不是reload 也不是reopen)nginx就能正常工作在你的大文件打开数目授权下了。

所以说,最保险的做法是什么?

我们把/etc/security/limits.conf恢复原状。把ulimit写在root的profile里面,这样是为了自己登录到会话后重新打开的nginx也能得到正确的授权。同时针对mysql/nginx/php-fpm/oracle等的launch程序做手脚,比如/etc/init.d/php5-fpm 的init程序做手脚。在里面加上ulimit语句,再比如自己编译的nginx 可以这样:新建一个/usr/local/bin/nginx脚本
[bash]#!/bin/bash
ulimit -SHn 65535
/usr/local/nginx/sbin/nginx $*[/bash]。这样可以确保万无一失。

总之,判断成功与否标准是什么?

这个程序检测下来的数目和你期望的相当,而不是你在任何会话里面执行ulimit -n看到的结果。

Author Info :
  • From:ulimit到底谁说了算?
  • URL:https://blog.ihipop.com/2011/01/2053.html
  • Please Reserve This Link,Thanks!
  • 《ulimit到底谁说了算?》上有7条评论

    1. 从你的现象上判断,你的系统应该是debian/ubuntu,在这两个系统里,如果你修改/etc/security/limits.conf,类似
      * hard nofile 65530
      * soft nofile 65530
      的话,默认是对root不起作用,这个*会对除root用户外的其他用户生效
      如果你需要对root用户生效的话需要修改为
      root - nofile 65530
      其中-就代表了hard和soft,不用分开写的
      如果你系统是rhel/centos的话,
      * hard nofile 65530
      * soft nofile 65530
      是对所有用户生效包括root

      1. 嗯。这个我倒是没有仔细去测试。不过我现在觉得所有都程序开放很大的文件打开数也不好。所以我接受了debian的做法 在/etc/default里面定义需要修改文件打开数的程序的ulimit。
        debian打包者里面的很多包编写的init script都会载入这里对每个程序的定义的变量 在这里面定义ulimit就可以了
        话说文章里面我也说了 登录后看到的ulimit不一定是自启动程序所运行在的ulimit 还是得看procfs里面的句柄显示的内容才能最终确定,另外如果你当前shell是最大ulimit了 那么登录后在当前shell里面启动停止程序自然也是最大的ulimit了

    回复 ihipop 取消回复

    您的电子邮箱地址不会被公开。 必填项已用*标注