limit-as限制子进程资源,导致Mysqld服务无法启动

问题发现

现象

在nginx+uwsgi+python环境中,启动mysqld服务,发生服务无法正常启动现象(闪退)。而在shell终端中可以正常启动mysqld服务。说明“nginx+uwsgi+python”这个环境限制了某些资源,导致mysqld服务启动失败。

uwsgi错误日志(/var/log/uwsgi.log)

1
Starting MySQL. ERROR! The server quit without updating PID file (/store/xxx.pid)

mysql错误日志(/{mysqldatadir}/{hostname}.err)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
2016-09-22 16:21:41 28084 [Note] InnoDB: Using atomics to ref count buffer pool pages
2016-09-22 16:21:41 28084 [Note] InnoDB: The InnoDB memory heap is disabled
2016-09-22 16:21:41 28084 [Note] InnoDB: Mutexes and rw_locks use GCC atomic builtins
2016-09-22 16:21:41 28084 [Note] InnoDB: Memory barrier is not used
2016-09-22 16:21:41 28084 [Note] InnoDB: Compressed tables use zlib 1.2.3
2016-09-22 16:21:41 28084 [Note] InnoDB: Using CPU crc32 instructions
2016-09-22 16:21:41 28084 [Note] InnoDB: Initializing buffer pool, size = 1.0G
InnoDB: mmap(137363456 bytes) failed; errno 12
2016-09-22 16:21:41 28084 [ERROR] InnoDB: Cannot allocate memory for the buffer pool
2016-09-22 16:21:41 28084 [ERROR] Plugin 'InnoDB' init function returned error.
2016-09-22 16:21:41 28084 [ERROR] Plugin 'InnoDB' registration as a STORAGE ENGINE failed.
2016-09-22 16:21:41 28084 [ERROR] Unknown/unsupported storage engine: InnoDB
2016-09-22 16:21:41 28084 [ERROR] Aborting
2016-09-22 16:21:41 28084 [Note] Binlog end

分析

根据mysql错误日志提示,mysql在初始化InnoDB引擎时,申请“Buffer Pool”内存失败,此处的mmap是用来申请内存的(allocate申请内存时,小于128KB的在栈上进行分配内存,大于128K的,从堆上分配内存)。
使用shell终端正常启动mysqld服务,查看mysqld占用内存大小发现需要2GB左右的虚拟内存,如下所示:

1
2
3
[root@host]# ps aux | grep mysqld | grep -v grep
root 31635 0.0 0.1 11300 1520 ? S Sep22 0:00 /bin/sh /cs/mysql/bin/mysqld_safe --datadir=/store --pid-file=/store/zhoubo.pid
root 32165 0.0 25.5 2041620 259716 ? Sl Sep22 0:15 /cs/mysql/bin/mysqld --basedir=/cs/mysql --datadir=/store --plugin-dir=/cs/mysql/lib/plugin --user=root --log-error=/store/zhoubo.err --open-files-limit=65535 --pid-file=/store/zhoubo.pid --socket=/cs/mysql/data/mysql.sock --port=3306

由此可以看出,“nginx+uwsgi+python”这个组合在其中某个环节对内存做了内存限制导致内存不能正常分配;nginx父进程为init,uwsgi父进程也为init,二者之间使用管道进行通信,所以不可能由nginx限制uwsgi进程资源。而uwsgi以fork形式调用python,fork过程中可以限制资源,python进程的资源被限制后再启动mysqld服务,就会导致mysqld分配内存失败,从而出现上述的错误现象。

解决

根据上诉分析,找到了问题的原因出在uwsgi对进程资源进行了限制,所以将uwsgi.ini中关于资源限制的配置去掉,即可解决。
在uwsgi.ini中去掉limit-as 256选项。

原理

uwsgi.ini中资源限制项包括limit-aslimit-nproclimit-port等,他们对进程资源的限制的访问获取都是通过系统接口getrlimt和setrlimit来实现的(POSIX API)。

1
2
3
4
5
#include <sys/time.h>
#include <sys/resource.h>
int getrlimit(int resource, struct rlimit *rlim);
int setrlimit(int resource, const struct rlimit *rlim);
int prlimit(pid_t pid, int resource, const struct rlimit *new_limit,struct rlimit *old_limit);

核心结构体

1
2
3
4
struct rlimit{
rlim_t rlim_cur; // soft limit
rlim_t rlim_max; // hard limit
}

rlim_cur

为soft limit是指内核所能支持的资源上限。比如对于RLIMIT_NOFILE(一个进程能打开的最大文件数,内核默认是1024),soft limit最大也只能达到1024。对于RLIMIT_CORE(core文件的大小,内核不做限制),soft limit最大能是unlimited。

rlim_max

为hard limit在资源中只是作为soft limit的上限。当你设置hard limit后,你以后设置的soft limit只能小于hard limit。要说明的是,hard limit只针对非特权进程,也就是进程的有效用户ID(effective user ID)不是0的进程。具有特权级别的进程(具有属性CAP_SYS_RESOURCE),soft limit则只有内核上限。

可限制资源列表

资源 描述
RLIMIT_AS 进程的最大虚拟内存空间,字节为单位
RLIMIT_CORE 内核转存文件的最大长度
RLIMIT_CPU 最大允许的CPU使用时间,秒为单位
RLIMIT_DATA 进程数据段的最大
RLIMIT_FSIZE 进程可建立的文件的最大长度
RLIMIT_LOCKS 进程可建立的锁和租赁的最大值
RLIMIT_MEMLOCK 进程可锁定在内存中的最大数据量,字节为单位
RLIMIT_MSGQUEUE 进程可为POSIX消息队列分配的最大字节数
RLIMIT_NICE 进程可通过setpriority() 或 nice()调用设置的最大完美值
RLIMIT_NOFILE 指定比进程可打开的最大文件描述词大一的值,超出此值,将会产生EMFILE错误
RLIMIT_NPROC 用户可拥有的最大进程数
RLIMIT_RTPRIO 进程可通过sched_setscheduler 和 sched_setparam设置的最大实时优先级
RLIMIT_SIGPENDING 用户可拥有的最大挂起信号数
RLIMIT_STACK 最大的进程堆栈,以字节为单位

参考及鸣谢