之前写过网关代理的项目, 会有大量的请求打到项目的端口上, 在本地测试的时候没有任何的问题, 但是上完线之后发现用户的请求经常会被拒绝, 经过日志排查, 主要的报错为: Too many open files. 该报错就和本文将要介绍的系统资源限制有关. 由于系统限制和选项会影响应用程序的功能,可移植的应用程序需要能够确定限制值以及选项是否被支持的方法。我个人的经验, 其实大部分的系统限制, 都可以通过命令行来操作使得系统限制修改生效, 并且各个语言也不太设置的函数不太一样, 因此只需要在使用的使用灵活变通进行选择即可. SUSv3针对其规定的每一项限制,要求所有实现都必须支持一个该限制的最小值。在大多数情况下,这个最小值在<limits.h>中定义为一个常量.运行时不变值
运行时不变值是这样一种限制:如果它在<limits.h>中定义,其值对于该具体实现是固定的。然而,这个值可能是不确定的(可能因为它取决于可用内存空间),因此可能未在<limits.h>中定义。在这种情况下(即使该限制也在<limits.h>中定义),应用程序可以使用 sysconf() 在运行时确定其值。 例如 MQ_PRIO_MAX 是一个用于定义 POSIX 消息队列中消息优先级上限的常量。它的作用是限制消息优先级的取值范围,确保所有消息的优先级都是一个有效的无符号整数,且小于该常量的值。它就是在运行时确定的值, 可以查看该值.getconf MQ_PRIO_MAX undefined
路径名变量值
路径名变量值是与路径名(文件、目录、终端等)相关的限制。每个限制对于某个具体实现可能是常数,也可能因文件系统而异。 NAME_MAX 限制是路径名变量值的一个例子。该限制定义了特定文件系统上文件名的最大长度。SUSv3定义了常量_POSIX_NAME_MAX,其值为14(这是旧System V文件系统的限制),作为所有实现必须支持的最小值。 在操作系统上, 可以使用命令行来查看, 如下表示, 在/目录下的文件名的最大长度为255, 作为正常的用户和开发者, 一般很少去关注文件名的长度, 但是特殊场景下, 例如名称有特殊含义的(时间+名称等), 为了安全编程(防止名称过长)还是需要根据系统的配置, 来设置文件的最大长度.getconf 命令行工具
在 shell 环境中,我们可以使用 getconf 命令 来获取特定 UNIX 实现所支持的限制值和选项信息。该命令的一般格式为:getconf variable-name [ pathname ] 它是一个查询命令,用于获取系统在编译时或内核中定义的配置常量值和文件系统路径的变量值。它是静态的、不可更改的、系统全局的。ulimit 命令行工具
上面的getconf可以查看相应的配置, 同时ulimit也可以查看对应的配置信息, 但是它是一个控制命令,用于设置或显示当前Shell及其启动进程的资源限制(软限制和硬限制)。它是动态的、可配置的、与用户和会话相关的。 因此这两个工具都是我们常用的, 回到文章开始的位置, 当发现日志里面有非常多的“Too many open files”的错误信息, 首先应该就是排查这个报错的原因是什么, 看报错最有可能是进程打开太多的文件了, 造成持有了太多的文件描述符. 查看下系统限制里面, 每个系统限制持有的文件描述符为多少, 一般情况下默认为256, 尝试修改一下该值就行了. 注意, 这里的ulimit设置是会话级的, 关闭了本次的会话进程之后, 下一个进程就不生效了. 因此后续想要持续使得它不受影响, 应该将它写到部署脚本中去. 那这里为什么部署的web服务, 也会有这个报错呢? 不是说上面的报错信息是打开了太多的文件导致的么? 在Linux系统中, 所有的东西也是文件, 套接字也是文件, 它也有对应的文件描述符, 因此上面这个报错信息, 是不是就显得很合理了?ulimit -n256ulimit -n 1024ulimit -n1024
工作中遇到的问题
之前在工作的时候, 我们开发过一个平台, 用户可以在编辑框里面编写它的python脚本, 我们的服务端可以执行它提交的代码, 服务端的实现为使用子进程来运行用户的代码文件, subprocess模块支持使用PIPE把标准输出和标准错误输出同步给父进程的能力. 但是遇到一个很有意思的问题, 子进程运行之后, 父进程拿到的数据格式是错误的, 因此我们去排查该问题, 发现是python里面PIPE传输数据的大小有限制, 也是去python里面查询修改方式之后, 修改完毕即可, 其实也就是下面图中的PIPE_BUF. 这样的问题, 也是大部分程序都不会遇到, 但是我们也需要有这样的意识, 就是系统为了安全, 会对各种资源的使用上有最大或者最小限制, 当遇到问题的时候, 就可以顺着该思路去排查即可.总结
操作系统中的配置定义在<limits.h>文件中, 可以使用对应的命令行来获取和设置, 正常程序都不会有影响, 但是进程有特殊用途超过了默认配置的时候, 可以定位并排查相关的问题, 下图为书中列出的对应配置和其含义.