Linux是一个多用户&任务系统。如何防止某些用户占用服务器大量资源,避免影响其他用户的正常使用,是一个永不过时的话题。
很多人采用了国外的Cloudlinux,不过几十元一个月的授权费对于我这样一个学生来说,压力有点大。
前几个星期,我的某个服务器上某个用户被CC了,该用户的HTTPD进程占用了服务器大量资源,最后只能硬重启解决。
从那天开始,如何免费实现限制单用户的资源成为了我的一个迫不及待要解决的问题。
一:获取用户进程以及资源使用信息
Linux下能查看各用户的进程以及资源占用情况的命令有ps和top。其中top是交互式的,不太适合文本处理,因此要选用ps。
ps常用的参数有以下:
1 2 3 4 5 6 7 |
-A :显示所有进程; -a :不显示与终端有关的进程; -u :显示进程的用户名; x :通常和参数a一起用,可输出详细的信息,其中格式如下: l :较长、较详细地将该 PID 的信息列出; j :工作的格式 (jobs format) -f :做一个更为完整的输出。 |
要获取各各用户的进程数,所占用的CPU与内存,就要用到参数a,u,x了。
执行
1 |
ps aux |
可以看到输出了类似下面的信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
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仍然能提供正常服务。
例子:
1 |
killall -9 -u username |
三:步骤一与二结合
最后的问题就是,把上面的一与二结合起来。可以考虑使用for循环:
1 2 3 4 |
for variable2 in $variable1 do ... done |
把步骤二所获取到的用户信息赋值到一个变量,一个用户一行,使用for循环就能实现逐个用户处理。
四:循环
可能有人会想到cron,这东西频率太低了。循环执行该Shell Script,可以制造一个“死循环”,while命令即可:
1 2 3 4 5 |
while [ "$loop" = "" ] do sleep 1 ... done |
上面使用了sleep进行延时,作用就是降低一个循环的间隔,如果不延时,可能会使该Shell Script所占用的CPU异常高。
可能有人会担心,第一步,也就是获取信息的那一步,用户量大时,数据的处理要花太多时间,不过经过我的测试,只要内存使用率(量)求和的方法正确,所花的时间基本能忽略。
由于被某论坛的人恶心了,因此我就不打算把成品发出来了,各位根据此文章的思路,自行开发吧。
Comments are closed.