Linux Cgroup系列(03):限制cgroup的进程数(subsystem之pids)

算法蝉翼
• 阅读 18546

上一篇文章中介绍了如何管理cgroup,从这篇开始将介绍具体的subsystem。

本篇将介绍一个简单的subsystem,名字叫pids,功能是限制cgroup及其所有子孙cgroup里面能创建的总的task数量。

注意:这里的task指通过fork和clone函数创建的进程,由于clone函数也能创建线程(在Linux里面,线程是一种特殊的进程),所以这里的task也包含线程,本文统一以进程来代表task,即本文中的进程代表了进程和线程

本篇所有例子都在ubuntu-server-x86_64 16.04下执行通过

创建子cgroup

在ubuntu 16.04里面,systemd已经帮我们将各个subsystem和cgroup树绑定并挂载好了,我们直接用现成的就可以了。

#从这里的输出可以看到,pids已经被挂载在了/sys/fs/cgroup/pids,这是systemd做的
dev@dev:~$ mount|grep pids
cgroup on /sys/fs/cgroup/pids type cgroup (rw,nosuid,nodev,noexec,relatime,pids)

创建子cgroup,取名为test

#进入目录/sys/fs/cgroup/pids/并新建一个目录,即创建了一个子cgroup
dev@dev:~$ cd /sys/fs/cgroup/pids/
dev@dev:/sys/fs/cgroup/pids$ sudo mkdir test
#这里将test目录的owner设置成dev账号,这样后续操作就不用每次都敲sudo了,省去麻烦
dev@dev:/sys/fs/cgroup/pids$ sudo chown -R dev:dev ./test/

再来看看test目录下的文件

#除了上一篇中介绍的那些文件外,多了两个文件
dev@dev:/sys/fs/cgroup/pids$ cd test
dev@dev:/sys/fs/cgroup/pids/test$ ls
cgroup.clone_children  cgroup.procs  notify_on_release  pids.current  pids.max  tasks

下面是这两个文件的含义:

  • pids.current: 表示当前cgroup及其所有子孙cgroup中现有的总的进程数量

    #由于这是个新创建的cgroup,所以里面还没有任何进程
    dev@dev:/sys/fs/cgroup/pids/test$ cat pids.current 
    0
  • pids.max: 当前cgroup及其所有子孙cgroup中所允许创建的总的最大进程数量,在根cgroup下没有这个文件,原因显而易见,因为我们没有必要限制整个系统所能创建的进程数量。

    #max表示没做任何限制
    dev@dev:/sys/fs/cgroup/pids/test$ cat pids.max 
    max

限制进程数

这里我们演示一下如何让限制功能生效

#--------------------------第一个shell窗口----------------------
#将pids.max设置为1,即当前cgroup只允许有一个进程
dev@dev:/sys/fs/cgroup/pids/test$ echo 1 > pids.max
#将当前bash进程加入到该cgroup
dev@dev:/sys/fs/cgroup/pids/test$ echo $$ > cgroup.procs
#--------------------------第二个shell窗口----------------------
#重新打开一个bash窗口,在里面看看cgroup “test”里面的一些数据
#因为这是一个新开的bash,跟cgroup ”test“没有任何关系,所以在这里运行命令不会影响cgroup “test”
dev@dev:~$ cd /sys/fs/cgroup/pids/test
#设置的最大进程数是1
dev@dev:/sys/fs/cgroup/pids/test$ cat pids.max
1
#目前test里面已经有了一个进程,说明不能在fork或者clone进程了
dev@dev:/sys/fs/cgroup/pids/test$ cat pids.current
1
#这个进程就是第一个窗口的bash
dev@dev:/sys/fs/cgroup/pids/test$ cat cgroup.procs
3083
#--------------------------第一个shell窗口----------------------
#回到第一个窗口,随便运行一个命令,由于当前pids.current已经等于pids.max了,
#所以创建新进程失败,于是命令运行失败,说明限制生效
dev@dev:/sys/fs/cgroup/pids/test$ ls
-bash: fork: retry: No child processes
-bash: fork: retry: No child processes
-bash: fork: retry: No child processes
-bash: fork: retry: No child processes
-bash: fork: Resource temporarily unavailable

当前cgroup和子cgroup之间的关系

当前cgroup中的pids.current和pids.max代表了当前cgroup及所有子孙cgroup的所有进程,所以子孙cgroup中的pids.max大小不能超过父cgroup中的大小,如果子cgroup中的pids.max设置的大于父cgroup里的大小,会怎么样?请看下面的演示

#继续使用上面的两个窗口
#--------------------------第二个shell窗口----------------------
#将pids.max设置成2
dev@dev:/sys/fs/cgroup/pids/test$ echo 2 > pids.max
#在test下面创建一个子cgroup
dev@dev:/sys/fs/cgroup/pids/test$ mkdir subtest
dev@dev:/sys/fs/cgroup/pids/test$ cd subtest/
#将subtest的pids.max设置为5
dev@dev:/sys/fs/cgroup/pids/test/subtest$ echo 5 > pids.max
#将当前bash进程加入到subtest中
dev@dev:/sys/fs/cgroup/pids/test/subtest$ echo $$ > cgroup.procs
#--------------------------第三个shell窗口----------------------
#重新打开一个bash窗口,看一下test和subtest里面的数据
#test里面的数据如下:
dev@dev:~$ cd /sys/fs/cgroup/pids/test
dev@dev:/sys/fs/cgroup/pids/test$ cat pids.max
2
#这里为2表示目前test和subtest里面总的进程数为2
dev@dev:/sys/fs/cgroup/pids/test$ cat pids.current
2
dev@dev:/sys/fs/cgroup/pids/test$ cat cgroup.procs
3083

#subtest里面的数据如下:
dev@dev:/sys/fs/cgroup/pids/test$ cat subtest/pids.max
5
dev@dev:/sys/fs/cgroup/pids/test$ cat subtest/pids.current
1
dev@dev:/sys/fs/cgroup/pids/test$ cat subtest/cgroup.procs
3185
#--------------------------第一个shell窗口----------------------
#回到第一个窗口,随便运行一个命令,由于test里面的pids.current已经等于pids.max了,
#所以创建新进程失败,于是命令运行失败,说明限制生效
dev@dev:/sys/fs/cgroup/pids/test$ ls
-bash: fork: retry: No child processes
-bash: fork: retry: No child processes
-bash: fork: retry: No child processes
-bash: fork: retry: No child processes
-bash: fork: Resource temporarily unavailable
#--------------------------第二个shell窗口----------------------
#回到第二个窗口,随便运行一个命令,虽然subtest里面的pids.max还大于pids.current,
#但由于其父cgroup “test”里面的pids.current已经等于pids.max了,
#所以创建新进程失败,于是命令运行失败,说明子cgroup中的进程数不仅受自己的pids.max的限制,
#还受祖先cgroup的限制
dev@dev:/sys/fs/cgroup/pids/test/subtest$ ls
-bash: fork: retry: No child processes
-bash: fork: retry: No child processes
-bash: fork: retry: No child processes
-bash: fork: retry: No child processes
-bash: fork: Resource temporarily unavailable

pids.current > pids.max的情况

并不是所有情况下都是pids.max >= pids.current,在下面两种情况下,会出现pids.max < pids.current 的情况:

  • 设置pids.max时,将其值设置的比pids.current小

    #继续使用上面的三个窗口
    #--------------------------第三个shell窗口----------------------
    #将test的pids.max设置为1
    dev@dev:/sys/fs/cgroup/pids/test$ echo 1 > pids.max
    dev@dev:/sys/fs/cgroup/pids/test$ cat pids.max
    1
    #这个时候就会出现pids.current > pids.max的情况
    dev@dev:/sys/fs/cgroup/pids/test$ cat pids.current
    2
    
    #--------------------------第一个shell窗口----------------------
    #回到第一个shell
    #还是运行失败,说明虽然pids.current > pids.max,但限制创建新进程的功能还是会生效
    dev@dev:/sys/fs/cgroup/pids/test$ ls
    -bash: fork: retry: No child processes
    -bash: fork: retry: No child processes
    -bash: fork: retry: No child processes
    -bash: fork: retry: No child processes
    -bash: fork: Resource temporarily unavailable
  • pids.max只会在当前cgroup中的进程fork、clone的时候生效,将其他进程加入到当前cgroup时,不会检测pids.max,所以将其他进程加入到当前cgroup有可能会导致pids.current > pids.max

    #继续使用上面的三个窗口
    #--------------------------第三个shell窗口----------------------
    #将subtest中的进程移动到根cgroup下,然后删除subtest
    dev@dev:/sys/fs/cgroup/pids/test$ sudo sh -c 'echo 3185 > /sys/fs/cgroup/pids/cgroup.procs'
    #里面没有进程了,说明移动成功
    dev@dev:/sys/fs/cgroup/pids/test$ cat subtest/cgroup.procs
    #移除成功
    dev@dev:/sys/fs/cgroup/pids/test$ rmdir subtest/
    
    #这时候test下的pids.max等于pids.current了
    dev@dev:/sys/fs/cgroup/pids/test$ cat pids.max
    1
    dev@dev:/sys/fs/cgroup/pids/test$ cat pids.current
    1
    
    #--------------------------第二个shell窗口----------------------
    #将当前bash加入到test中
    dev@dev:/sys/fs/cgroup/pids/test/subtest$ cd ..
    dev@dev:/sys/fs/cgroup/pids/test$ echo $$ > cgroup.procs
    
    #--------------------------第三个shell窗口----------------------
    #回到第三个窗口,查看相关信息
    #第一个和第二个窗口的bash都属于test
    dev@dev:/sys/fs/cgroup/pids/test$ cat cgroup.procs
    3083
    3185
    dev@dev:/sys/fs/cgroup/pids/test$ cat pids.max
    1
    #出现了pids.current > pids.max的情况,这是因为我们将第二个窗口的shell加入了test
    dev@dev:/sys/fs/cgroup/pids/test$ cat pids.current
    2
    #--------------------------第二个shell窗口----------------------
    #对fork调用的限制仍然生效
    dev@dev:/sys/fs/cgroup/pids/test$ ls
    -bash: fork: retry: No child processes
    -bash: fork: retry: No child processes
    -bash: fork: retry: No child processes
    -bash: fork: retry: No child processes
    -bash: fork: Resource temporarily unavailable

清理

#--------------------------第三个shell窗口----------------------
dev@dev:/sys/fs/cgroup/pids/test$ sudo sh -c 'echo 3185 > /sys/fs/cgroup/pids/cgroup.procs'
dev@dev:/sys/fs/cgroup/pids/test$ sudo sh -c 'echo 3083 > /sys/fs/cgroup/pids/cgroup.procs'
dev@dev:/sys/fs/cgroup/pids/test$ cd ..
dev@dev:/sys/fs/cgroup/pids$ sudo rmdir test/

结束语

本文介绍了如何利用pids这个subsystem来限制cgroup中的进程数,以及一些要注意的地方,总的来说pids比较简单。下一篇将介绍稍微复杂点的内存控制。

参考

点赞
收藏
评论区
推荐文章
blmius blmius
4年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
Wesley13 Wesley13
4年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Wesley13 Wesley13
4年前
FLV文件格式
1.        FLV文件对齐方式FLV文件以大端对齐方式存放多字节整型。如存放数字无符号16位的数字300(0x012C),那么在FLV文件中存放的顺序是:|0x01|0x2C|。如果是无符号32位数字300(0x0000012C),那么在FLV文件中的存放顺序是:|0x00|0x00|0x00|0x01|0x2C。2.  
Wesley13 Wesley13
4年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
4年前
PHP创建多级树型结构
<!lang:php<?php$areaarray(array('id'1,'pid'0,'name''中国'),array('id'5,'pid'0,'name''美国'),array('id'2,'pid'1,'name''吉林'),array('id'4,'pid'2,'n
Wesley13 Wesley13
4年前
Java日期时间API系列36
  十二时辰,古代劳动人民把一昼夜划分成十二个时段,每一个时段叫一个时辰。二十四小时和十二时辰对照表:时辰时间24时制子时深夜11:00凌晨01:0023:0001:00丑时上午01:00上午03:0001:0003:00寅时上午03:00上午0
Wesley13 Wesley13
4年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
4年前
Linux日志安全分析技巧
0x00前言我正在整理一个项目,收集和汇总了一些应急响应案例(不断更新中)。GitHub地址:https://github.com/Bypass007/EmergencyResponseNotes本文主要介绍Linux日志分析的技巧,更多详细信息请访问Github地址,欢迎Star。0x01日志简介Lin
Python进阶者 Python进阶者
2年前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这