服务器, 运营维护

Linux下简单实现单用户资源限制思路

Linux是一个多用户&任务系统。如何防止某些用户占用服务器大量资源,避免影响其他用户的正常使用,是一个永不过时的话题。

很多人采用了国外的Cloudlinux,不过几十元一个月的授权费对于我这样一个学生来说,压力有点大。

前几个星期,我的某个服务器上某个用户被CC了,该用户的HTTPD进程占用了服务器大量资源,最后只能硬重启解决。

从那天开始,如何免费实现限制单用户的资源成为了我的一个迫不及待要解决的问题。

一:获取用户进程以及资源使用信息

Linux下能查看各用户的进程以及资源占用情况的命令有ps和top。其中top是交互式的,不太适合文本处理,因此要选用ps。

ps常用的参数有以下:

-A  :显示所有进程;
-a  :不显示与终端有关的进程;
-u  :显示进程的用户名;
x   :通常和参数a一起用,可输出详细的信息,其中格式如下:
l   :较长、较详细地将该 PID 的信息列出;
j   :工作的格式 (jobs format)
-f  :做一个更为完整的输出。

要获取各各用户的进程数,所占用的CPU与内存,就要用到参数a,u,x了。

执行

ps aux

可以看到输出了类似下面的信息:

USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.0   3356  1740 ?        Ss   Dec08   0:00 init
root         2  0.0  0.0      0     0 ?        S    Dec08   0:00 [kthreadd/2000]
root         3  0.0  0.0      0     0 ?        S    Dec08   0:00 [khelper/2000]
root        83  0.0  0.0   2788   708 ?        S    Dec08   0:00 upstart-udev-bridge --daemon
root        90  0.0  0.0   2796  1012 ?        Ss   Dec08   0:00 /sbin/udevd --daemon
root       129  0.0  0.0   2792   648 ?        S    Dec08   0:00 /sbin/udevd --daemon
root       131  0.0  0.0   2792   644 ?        S    Dec08   0:00 /sbin/udevd --daemon
root       192  0.0  0.0   2800   600 ?        S    Dec08   0:00 upstart-socket-bridge --daemon
root       278  0.0  0.0   2572   912 ?        Ss   Dec08   0:00 cron
syslog     311  0.0  0.0   2356   740 ?        Ss   Dec08   0:02 /sbin/syslogd -u syslog
root       332  0.0  0.1   6636  2328 ?        Ss   Dec08   0:01 /usr/sbin/sshd -D
root       366  0.0  0.0  13836   892 ?        Ss   Dec08   0:00 /usr/sbin/saslauthd -a pam -c -m /var/run/saslauthd -n 2
root       367  0.0  0.0  13836   572 ?        S    Dec08   0:00 /usr/sbin/saslauthd -a pam -c -m /var/run/saslauthd -n 2
root       448  0.0  0.0   2516   992 ?        Ss   Dec08   0:00 /usr/sbin/xinetd -dontfork -pidfile /var/run/xinetd.pid -stayalive -inetd_compat -inetd_ipv6
root       473  0.0  0.0  14900  1804 ?        Ss   Dec08   0:10 sendmail: MTA: accepting connections          
mysql     2432  0.0  1.6 348192 35252 ?        Ssl  Dec09   1:10 /usr/sbin/mysqld
php-fpm   7827  0.0  0.2  77788  5428 ?        S    Dec13   0:00 php-fpm: pool www                                         
php-fpm   7828  0.0  0.2  77788  5428 ?        S    Dec13   0:00 php-fpm: pool www                                         
php-fpm   7829  0.0  0.2  77788  5428 ?        S    Dec13   0:00 php-fpm: pool www                                         
php-fpm   7830  0.0  0.2  77788  5428 ?        S    Dec13   0:00 php-fpm: pool www                                         
php-fpm   7849  0.0  0.2  77788  5560 ?        S    Dec13   0:00 php-fpm: pool www                                         
root     18827  0.5  0.1   9596  3020 ?        Ss   20:10   0:00 sshd: root@pts/1    
root     18839  0.0  0.0   3428  1748 pts/1    Ss   20:10   0:00 -bash
root     18850  0.0  0.0   2816  1032 pts/1    R+   20:10   0:00 ps aux
root     25727  0.0  0.0  43612  1224 ?        Ss   Dec09   0:00 nginx: master process /usr/sbin/nginx
www-data 25728  0.0  0.1  44148  2220 ?        S    Dec09   0:18 nginx: worker process
www-data 25730  0.0  0.1  44160  2408 ?        S    Dec09   0:00 nginx: worker process
www-data 25731  0.0  0.0  43780  1620 ?        S    Dec09   0:18 nginx: worker process
www-data 25732  0.0  0.0  43780  1620 ?        S    Dec09   0:18 nginx: worker process
www-data 25734  0.0  0.0  43756  1528 ?        S    Dec09   0:01 nginx: cache manager process
root     25751  0.0  0.0  87644  1332 ?        Ss   Dec09   0:09 /usr/sbin/varnishd -P /var/run/varnishd.pid -a :80 -T localhost:6082 -f /etc/varnish/default.vcl -S /etc/varnish/s
nobody   25753  0.0  0.5 174400 11064 ?        Sl   Dec09   4:25 /usr/sbin/varnishd -P /var/run/varnishd.pid -a :80 -T localhost:6082 -f /etc/varnish/default.vcl -S /etc/varnish/s
root     25782  0.0  0.1  77184  4096 ?        Ss   Dec09   0:10 php-fpm: master process (/etc/php5/fpm/php-fpm.conf)      
memcache 25794  0.0  0.0  58596  1256 ?        Sl   Dec09   0:12 /usr/bin/memcached -m 64 -p 11211 -u memcache -l 127.0.0.1

让我们来分析一下上面的数据。

第一列:此进程的用户

第二列:此进程的PID

第三列:此进程的CPU使用率

第四列:此进程的物理内存使用率

第五列:虚拟内存使用量

其余的没多大关系,我也不介绍了。

可以利用该命令与参数所输出的内容进行处理,格式化输出。例如内存使用率(量)总和,该用户的进程数目,其中可能使用到的有awk,print,cut,uniq和sort,从而实现获取某个用户的进程数,CPU使用率,内存使用率(量),再与自己所设置的值进行对比。

 

二:结束超资源用户的进程

当用户的资源超过设定值怎么办呢?由于我不是像CloudLinux那样的改动内核,因此也无法实现禁止某个用户继续fork进程。唯一的办法就是结束掉该用户的所有进程。那么就出现非常关键的一步:排除root,还有其他相关的守护进程的master进程。可以在前面获取用户进程的步骤使用grep或者egrep的v参数进行排除。

结束掉一个用户的所有进程,最佳的命令是killall了,其中的-u参数可以指定用户名。另外,killall结束进程时也可以指定对进程发出的信号,常用的有以下数个:

号码 名称 内容
1 SIGHUP 让该进程重新读取配置文件。
2 SIGINT 退出,相当于按Ctrl+[c]
9 SIGKILL 强制结束进程。
15 SIGTERM 以正常方式结束该进程。
19 SIGSTOP 暂停该进程,相当于Ctrl+[z]

与结束相关的有15和9,不指定信号的情况下,killall默认对进程发出信号15,这信号是正常退出进程,也就是让该进程完成任务再退出。明显不允许这样,因此我建议使用信号9。

FAQ:如果该用户的apache进程超了,被killall结束掉,会不会影响其余用户的正常使用?

答案是否定的,只要master进程还在,该用户和其他用户的apache进程还会被创建。也就是apache仍然能提供正常服务。

例子:

killall -9 -u username

三:步骤一与二结合

最后的问题就是,把上面的一与二结合起来。可以考虑使用for循环:

for variable2 in $variable1
do
...
done

把步骤二所获取到的用户信息赋值到一个变量,一个用户一行,使用for循环就能实现逐个用户处理。

四:循环

可能有人会想到cron,这东西频率太低了。循环执行该Shell Script,可以制造一个“死循环”,while命令即可:

while [ "$loop" = "" ]
do
sleep 1
...
done

上面使用了sleep进行延时,作用就是降低一个循环的间隔,如果不延时,可能会使该Shell Script所占用的CPU异常高。

可能有人会担心,第一步,也就是获取信息的那一步,用户量大时,数据的处理要花太多时间,不过经过我的测试,只要内存使用率(量)求和的方法正确,所花的时间基本能忽略。

由于被某论坛的人恶心了,因此我就不打算把成品发出来了,各位根据此文章的思路,自行开发吧。

101 Posts

自信、努力、活出精彩;以前未所见的颜色,绘大千世界!
View all posts

2 thoughts on “Linux下简单实现单用户资源限制思路”

Leave a reply

Your email address will not be published. Required fields are marked *