QT技巧当日小结

  1. 在QT当中,可以通过把QString转换成QByteArray,把QByteArray对象写入到buffer中,再调用system()就可以在QT中使用linux下的命令.
  2. 可以在类当中再定义一个类,并定义一个该类的指针来指向其实例.
  3. 要用多线程时,有两种方法,一种是构造QThread的子类,一种是直接new一个QThread的实例,再把相应的对象移到线程中.
  4. 在用QT开发比较大的工程时,可以把关联性强的规为一种类型的.pri,在.pri文件中用SOURCES+=,HEADERS+=,INCLUDEPATH+=来包含这一类型的源代码文件,然后在.pro中用include(*.pri)来包含这些文件.
  5. 在使用一些静态库(.a),动态库(.s)之前必须先要确定这是哪一个平台的库,以免到后面出现库不兼容的报错.
  6. QThread的对象是从属于原线程,而QThread::run()是跑在新的线程中.
  7. 友元类B可以通过把其当作朋友的类A的对象去操作类A的对象里的数据成员及成员函数.
  8. 在QT中可以通过调用system()来执行shell命令.
  9. extern C{} 可以用来指定用C编译器来对源代码进行编译.
  10. #progma pack(push);//把当前内存对齐方式入栈
    #progma pack(1);//把当前内存对齐方式设为1字节
  11. 在实际开发中,一般喜欢将界面美化和功能代码分开,界面美化用QSS来完成,在resource.qrc文件中添加.qss文件,最后在qapp.setStyleSheet()中设置.
  12. 在QT中用QTextStream来对文件进行写入操作时,一定要设置它的编码格式,否则会出现乱码.比如:

    QTextStream out(&eventLogInfo); out.setCodec(QTextCodec::codecForName(“UTF-8”));

  13. 对文件进行操作时必须养成一个先对文件进行打开的好习惯,否则文件内容写入不了.
  14. 在linux平台下操作文件夹时,通过QDir::entryInfoList()也会返回 目录下的当前目录和父目录(即: ** . ** 和 ** .. **)
  15. 在QT下如果用system()来通过操作系统中的命令来操作文件(如复制)的话,由于启用的新的进程,再加上进程间的切换时间导致这种方式会比直接在当前进程下用代码通过文件句柄来操作慢了一万倍以上.
  16. 有时在PC平台下,用QT编写的界面在嵌入式平台上一些窗口不会在顶层,此时可以通过raise()升起.

前言:

在一些实际开发项目当中,一个类只允许一个实例存在,由此在程序员当中广范使用单例模式来实现.


正文:

单例模式就是在构造类时把构造函数进行私有化,通过静态数据成员来确定是否已经存在此类的实例.


实际例子:

实例一:

#### single.h:
class single { private: single(); ~single(); public: static single* instance(void); int get_num(); void set_num(int); private: // static single *p; static int num; };

single.cpp:

//single* single::p=0;
int single::num=8;
single* single::instance()
{
//	if(p==NULL)
//		p=new single();
//	return p;
  	static single* instance=0;
  if(instance==0)
	   instance=new single();
  return instance;
}
single::single()
{
    cout<<"constructor!"<<endl;
}
single::~single()
{
   	cout<<"析构"<<endl;
}
void single::set_num(int num)
{
	single::num=num;
}
int single::get_num()
{
	return single::num;
}

main.cpp:

#include "single.h"
int main()
{
    single* single_instance= single::instance();
    single_instance->set_num(88);
    cout<<"the single::num is: "<< single_instance->get_num()<<endl;

    single* single_p=single::instance();
    cout<<"the single::num is: "<< single_p->get_num()<<endl;
    return 0;
}

输出:

constructor!
the single::num is: 88
the single::num is: 88

结语:

static所修饰的静态变量定义只会执行一次.

一.前言:

python作为一门脚本语言,极具灵活性,拥有最大化精简代码的潜能,因此可以大大加快项目的开发效率.由于是脚本语言,代码的执行效率的确是其一大短板,但可以通过用C,C++来完成对运行效率要求高的部分,再进行库封装,给python调用.只要技术到位就可以集各家之所长来达到项目要求.鉴于python的简便性,使其在运维界也倍受重用.

二.python常用模块:

  1. psutil模块:常用来获取系统信息,比如系统的内存使用细节,cpu时间占用细节,磁盘,网络使用情况,进程pid,用户log信息等.(这些信息用C语言来实现也简单,只要在linux的/proc/目录下读取各个进程pid目录下的动态记录文件,就可知晓这些系统信息.)
  2. IPy模块:可以快速规划Ip地址,其中包含了子网掩码,子网数等相关处理操作.
  3. dnspython模块:故名思义,此模块就是用来域名解析操作,它提供了一个DNS解析器类resolve,使用它的query方法来实现域名的查询功能.
  4. difflib模块:实现文件内容差异对比,是python的标准库,支持输出HTML文档格式.
  5. filecmp模块:文件目录差异对比方法,可以实现文件,目录,遍历子目录的差异对比功能,filecmp提供了三个操作方法,cmp(单文件比对),cmpfiles(多文件对比),dircmp(目录对比).
  6. os模块:操作系统相关方法.
  7. sys模块:包含了与python解释器和它的环境有关的函数.
  8. re模块:regular expression,正则表达式操作方法.
  9. shutil模块:高级文件操作工具.
  10. smtplib模块:smtp邮件发送协议相关操作对象,包含了smtplib.SMTP类.
  11. email模块:可以支持文本,图像,图片,表格等多种文件的邮件传送.
  12. xlsxwriter模块:excel文件操作工具,即表格操作.其中包含了workbook类(代表整个电子表格文件),workbook类有很多成员函数,可以对表格,图表进行操作.
  13. scapy模块:是一个交互式数据包处理程序,可以对数据包进行伪造,解包,包括发送数据包,包嗅探,应答和反馈匹配等功能.
  14. threading模块:多线程操作.
  15. nmap模块:类似于shell命令下的nmap,实现端口扫描.
  16. pexpect模块:可以实现对ssh,ftp,passwd,telnet等命令进行自动交互.
  17. pxssh模块:是pexpect的派生类,针对在SSH会话操作上再做一层封装,提供与基类更加直接的操作方法.
  18. getpass模块:get password.

  1. 在.py文件首部加上#!/usr/bin/python3 可以像.bin,shell script文件一样执行.
  2. Python使用缩进来组织代码块,坚持使用4个空格的缩进;当语句以冒号:结尾时,缩进的语句视为代码块
  3. Python允许用’'’…’'’的格式表示多行内容 >print(‘'’line1 … line2 … line3’’’)
  4. 在Python中,通常用全部大写的变量名表示常量.
  5. //除法只取结果的整数部分,/做精确的除法.
  6. python支持的数据类型:整数,浮点数,字符串,布尔,None;
  7. 在计算机内存中,统一使用Unicode编码,当需要保存到硬盘或者需要传输的时候,就转换为UTF-8编码。
    用记事本编辑的时候,从文件读取的UTF-8字符被转换为Unicode字符到内存里,编辑完成后,保存的时候再把Unicode转换为UTF-8保存到文件
  8. 浏览网页的时候,服务器会把动态生成的Unicode内容转换为UTF-8再传输到浏览器
  9. 在最新的Python 3版本中,字符串是以Unicode编码的,也就是说,Python的字符串支持多语言.
    对于单个字符的编码,Python提供了ord()函数获取字符的整数表示,chr()函数把编码转换为对应的字符
  10. Python的字符串类型是str,在内存中以Unicode表示,一个字符对应若干个字节。如果要在网络上传输,或者保存到磁盘上,就需要把str变为以字节为单位的bytes.如: >x = b’ABC’
  11. 以Unicode表示的str通过encode()方法可以编码为指定的bytes,例如: >’中文’.encode(‘utf-8’)
    b’\xe4\xb8\xad\xe6\x96\x87’
  12. 要把bytes变为str,就需要用decode()方法:
    >b’\xe4\xb8\xad\xe6\x96\x87’.decode(‘utf-8’)
    ‘中文’
  13. len()函数计算的是str的字符数.
  14. ython中,采用的格式化方式和C语言是一致的,用%实现,例如:
    >’Hi, %s, you have $%d.’ % (‘Michael’, 1000000) ‘Hi, Michael, you have $1000000.’
  15. 字符串里面的%是一个普通字符怎么办?这个时候就需要转义,用%%来表示一个%:
    >’growth rate: %d %%’ % 7
    ‘growth rate: 7 %’
  16. Python内置的一种数据类型是列表:list。list是一种有序的集合,可以随时添加和删除其中的元素,list里面的元素的数据类型也可以不同,list元素也可以是另一个list。
    list是一个可变的有序表,所以,可以往list中追加元素到末尾:
    > classmates.append(‘Adam’)
    也可以把元素插入到指定的位置,比如索引号为1的位置:
    classmates.insert(1, ‘Jack’)
    要删除list末尾的元素,用pop()方法:
    classmates.pop()
    要删除指定位置的元素,用pop(i)方法,其中i是索引位置:
    classmates.pop(1)
    要把某个元素替换成别的元素,可以直接赋值给对应的索引位置:
    classmates[1] = ‘Sarah’
  17. 另一种有序列表叫元组:tuple。tuple和list非常类似,但是tuple一旦初始化就不能修改.tuple的数据成员可以是list.只有1个元素的tuple定义时必须加一个逗号,否则当作数学中的().
  18. if…elif…else;if…else.
  19. Python的循环有两种,一种是for…in循环,依次把list或tuple中的每个元素迭代出来:
    >names = [‘Michael’, ‘Bob’, ‘Tracy’]
    for name in names:
    print(name)
  20. Python提供一个range()函数,可以生成一个整数序列,再通过list()函数可以转换为list:
    >list(range(100,200))
    [100,…,199]
  21. 第二种循环是while循环,只要条件满足,就不断循环,条件不满足时退出循环.
    > a=15
    while a>=10:
    print(a)
    a–
  22. Python内置了字典:dict的支持,dict全称dictionary,在其他语言中也称为map,使用键-值(key-value)存储,具有极快的查找速度.
    > d = {‘Michael’: 95, ‘Bob’: 75, ‘Tracy’: 85}
    d[‘Michael’]
    95
  23. set和dict类似,也是一组key的集合,但不存储value。由于key不能重复,所以,在set中,没有重复的key,要创建一个set,需要提供一个list作为输入集合:
    > s = set([1, 2, 3])
    s
    {1, 2, 3}
    有两个成员函数:add(),remove().
  24. 两个set可以做数学意义上的交集、并集等操作:
    > s1 = set([1, 2, 3])
    s2 = set([2, 3, 4])
    s1 & s2
    {2, 3}
    s1 | s2
    {1, 2, 3, 4}

  1. 在Python中,定义一个函数要使用def语句,依次写出函数名、括号、括号中的参数和冒号:,然后,在缩进块中编写函数体,函数的返回值用return语句返回。
  2. 在语法上,返回一个tuple可以省略括号,而多个变量可以同时接收一个tuple,按位置赋给对应的值,所以,Python的函数返回多值其实就是返回一个tuple
  3. 默认参数可以简化函数的调用。设置默认参数时,有几点要注意: 一是必选参数在前,默认参数在后,否则Python的解释器会报错;默认参数必须指向不变对象
  4. 定义可变参数和定义一个list或tuple参数相比,仅仅在参数前面加了一个号。在函数内部,参数numbers接收到的是一个tuple,因此,函数代码完全不变。但是,调用该函数时,可以传入任意个参数,包括0个参数:
    >def calc(
    numbers):
    sum = 0
    for n in numbers: sum = sum + n * n
    return sum
  5. 命名关键字参数需要一个特殊分隔符后面的参数被视为命名关键字参数,调用方式如下:
    >person(‘Jack’, 24, city=’Beijing’, job=’Engineer’)
    Jack 24 Beijing Engineer
  6. 在Python中定义函数,可以用必选参数、默认参数、可变参数、关键字参数和命名关键字参数,这5种参数都可以组合使用。但是,参数定义的顺序必须是:必选参数、默认参数、可变参数、命名关键字参数和关键字参数。
  7. *args是可变参数,args接收的是一个tuple;
    **kw是关键字参数,kw接收的是一个dict.
  8. 使用递归函数需要注意防止栈溢出。在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出。解决递归调用栈溢出的方法是通过尾递归优化,事实上尾递归和循环的效果是一样的,所以,把循环看成是一种特殊的尾递归函数也是可以的。
    尾递归是指,在函数返回的时候,调用自身本身,并且,return语句不能包含表达式。这样,编译器或者解释器就可以把尾递归做优化,使递归本身无论调用多少次,都只占用一个栈帧,不会出现栈溢出的情况。

  1. Python切片(Slice)操作符:lists[2:9],取lists[2]…lists[8]
    字符串’xxx’也可以看成是一种list,每个元素就是一个字符。因此,字符串也可以用切片操作,只是操作结果仍是字符串:
    > ‘ABCDEFG’[:3]
    ‘ABC’
    ‘ABCDEFG’[::2]
    ‘ACEG’
  2. Python内置的enumerate函数可以把一个list变成索引-元素对,这样就可以在for循环中同时迭代索引和元素本身:
    >for i, value in enumerate([‘A’, ‘B’, ‘C’]):
    print(i, value)
    0 A
    1 B
    2 C
  3. 生成器都是Iterator对象,但list、dict、str虽然是Iterable,却不是Iterator。把list、dict、str等Iterable变成Iterator可以使用iter()函数:
    > from collections import Iterator
    isinstance(iter([]),Iterator)
  4. Python的Iterator对象表示的是一个数据流,Iterator对象可以被next()函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误。可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算。
  5. 凡是可作用于for循环的对象都是Iterable类型;
    Python的for循环本质上就是通过不断调用next()函数实现的.
    凡是可作用于next()函数的对象都是Iterator类型,它们表示一个惰性计算的序列;
  6. 函数式编程允许把函数本身作为参数传入另一个函数,还允许返回一个函数!
  7. 高阶函数:可以接收一个为函数的参数.高阶函数除了可以接受函数作为参数外,还可以把函数作为结果值返回:
    > def lazy_sum(*args):
    def sum():
    ax = 0
    for n in args:
    ax = ax + n
    return ax
    return sum
  8. map()函数接收两个参数,一个是函数,一个是Iterable,map将传入的函数依次作用到序列的每个元素,并把结果作为新的Iterator返回。
  9. Python内建的filter()函数用于过滤序列。 和map()类似,filter()也接收一个函数和一个序列。和map()不同的是,filter()把传入的函数依次作用于每个元素,然后根据返回值是True还是False决定保留还是丢弃该元素。
  10. sorted()也是一个高阶函数。用sorted()排序的关键在于实现一个映射函数。
  11. 关键字lambda表示匿名函数,匿名函数有个限制,就是只能有一个表达式,不用写return,返回值就是该表达式的结果。 用匿名函数有个好处,因为函数没有名字,不必担心函数名冲突。
  12. 在面向对象(OOP)的设计模式中,decorator被称为装饰模式。OOP的装饰模式需要通过继承和组合来实现,而Python除了能支持OOP的decorator外,直接从语法层次支持decorator。Python的decorator可以用函数实现,也可以用类实现。
  13. 简单总结functools.partial的作用就是,把一个函数的某些参数给固定住(也就是设置默认值),返回一个新的函数,调用这个新函数会更简单。

  1. python中通过以_开头的变量命名来使模块中的成员私有化.
  2. __init__方法的第一个参数永远是self,表示创建的实例本身,因此,在__init__方法内部,就可以把各种属性绑定到self,因为self就指向创建的实例本身。
  3. 在Python中,实例的变量名如果以__开头,就变成了一个私有变量(private),只有内部可以访问,外部不能访问.而变量名类似__xxx__的,也就是以双下划线开头,并且以双下划线结尾的,是特殊变量,特殊变量是可以直接访问的,不是private变量
  4. 如果要获得一个对象的所有属性和方法,可以使用dir()函数,它返回一个包含字符串的list.
  5. 由于Python是动态语言,根据类创建的实例可以任意绑定属性。
  6. 特殊的__slots__变量,来限制该class实例能添加的属性:
    > class Student(object):
    slots = (‘name’, ‘age’)
  7. __slots__定义的属性仅对当前类实例起作用,对继承的子类是不起作用的.
  8. str()返回用户看到的字符串,而__repr__()返回程序开发者看到的字符串,也就是说,repr()是为调试服务的。
  9. 如果一个类想被用于for … in循环,类似list或tuple那样,就必须实现一个__iter__()方法,该方法返回一个迭代对象,然后,Python的for循环就会不断调用该迭代对象的__next__()方法拿到循环的下一个值,直到遇到StopIteration错误时退出循环。
  10. 按照下标取出元素,需要实现__getitem__()方法 .
  11. getattr()方法,动态返回一个属性。
  12. 只有在没有找到属性的情况下,才调用__getattr__,已有的属性,不会在__getattr__中查找。__getattr__默认返回是None
  13. call()方法,可以直接在类中对本类中的方法进行调用.
  14. 通过callable()函数,可以判断一个对象是否是“可调用”对象。
  15. type()函数可以查看一个类型或变量的类型,又可以创建出新的类型.type()函数依次传入3个参数:
    class的名称;
    继承的父类集合,注意Python支持多重继承,如果只有一个父类,别忘了tuple的单元素写法;
    class的方法名称与函数绑定.
  16. metaclass,直译为元类,简单的解释就是: 当我们定义了类以后,就可以根据这个类创建出实例,所以:先定义类,然后创建实例。
    但是如果我们想创建出类呢?那就必须根据metaclass创建出类,所以:先定义metaclass,然后创建类。
    连接起来就是:先定义metaclass,就可以创建类,最后创建实例。
    所以,metaclass允许你创建类或者修改类。换句话说,你可以把类看成是metaclass创建出来的“实例”.
    metaclass是类的模板,所以必须从type类型派生:
    >class ListMetaclass(type):
    def new(cls, name, bases, attrs):
    attrs[‘add’] = lambda self, value: self.append(value)
    return type.new(cls, name, bases, attrs).

  1. open()函数还接收一个errors参数,表示如果遇到编码错误后如何处理。最简单的方式是直接忽略:
    >f = open(‘/Users/michael/gbk.txt’, ‘r’, encoding=’gbk’, errors=’ignore’)
  2. 在Python中,文件读写是通过open()函数打开的文件对象完成的。使用with语句操作文件IO可以免去手动对文件进行close().
  3. StringIO是在内存中读写str.要把str写入StringIO,需要先创建一个StringIO,然后,像文件一样写入即可.
    >from io import StringIO
    f = StringIO()
    f.write(‘hello’)
    print(f.getvalue())
  4. 要读取StringIO,可以用一个str初始化StringIO,然后,像读文件一样读取.
  5. StringIO操作的只能是str,如果要操作二进制数据,就需要使用BytesIO. BytesIO实现了在内存中读写bytes.
  6. shutil模块提供了copyfile()的函数,hutil模块很多实用函数,它们可以看做是os模块的补充。
  7. 把变量从内存中变成可存储或传输的过程称之为序列化,在Python中叫pickling,序列化之后,就可以把序列化后的内容写入磁盘,或者通过网络传输到别的机器上. 反过来,把变量内容从序列化的对象重新读到内存里称之为反序列化,即unpickling.Python提供了pickle模块来实现序列化.
  8. 当我们要把对象从磁盘读到内存时,可以先把内容读到一个bytes,然后用pickle.loads()方法反序列化出对象,也可以直接用pickle.load()方法从一个file-like Object中直接反序列化出对象.
  9. 如果我们要在不同的编程语言之间传递对象,就必须把对象序列化为标准格式,比如XML,但更好的方法是序列化为JSON,因为JSON表示出来就是一个字符串,可以被所有语言读取,也可以方便地存储到磁盘或者通过网络传输。JSON不仅是标准格式,并且比XML更快,而且可以直接在Web页面中读取,非常方便。
  10. Python内置的json模块提供了非常完善的Python对象到JSON格式的转换.JSON标准规定JSON编码是UTF-8.

  1. multiprocessing模块就是跨平台版本的多进程模块.multiprocessing模块提供了一个Process类来代表一个进程对象.
  2. 如果要启动大量的子进程,可以用进程池的方式批量创建子进程:
    > from multiprocessing import Pool
    import os, time, random
    def long_time_task(name):
    print(‘Run task %s (%s)…’ % (name, os.getpid()))
    start = time.time()
    time.sleep(random.random() * 3)
    end = time.time()
    print(‘Task %s runs %0.2f seconds.’ % (name, (end - start)))
    if name==’main’:
    print(‘Parent process %s.’ % os.getpid())
    p = Pool(4)
    for i in range(5):
    p.apply_async(long_time_task, args=(i,))
    print(‘Waiting for all subprocesses done…’)
    p.close()
    p.join()#进程间同步
    print(‘All subprocesses done.’)
  3. subprocess模块可以让我们非常方便地启动一个子进程,然后控制其输入和输出.
  4. Python的multiprocessing模块包装了底层的机制,提供了Queue、Pipes等多种方式来交换数据.

  1. asm(操作符 :输出部:输入部:数据被修改部)中的”r”表示使用寄存器输入value,即将value的值复制给某一寄存器;而“=r”表示使用寄存器输出到value,即将某一寄存器的值赋给变量value。
  2. MCR 指令
    MCR 指令的格式为:
    MCR{条件} 协处理器编码,协处理器操作码1,源寄存器,目的寄存器1,目的寄存器2,协处理器操作码2。
    MCR 指令用于将ARM 处理器寄存器中的数据传送到协处理器寄存器中,若协处理器不能成功完成操作,则产生未定义指令异常。其中协处理器操作码1 和协处理器操作码2 为协处理器将要执行的操作,源寄存器为ARM 处理器的寄存器,目的寄存器1 和目的寄存器2 均为协处理器的寄存器。
    指令示例: MCR P3 , 3 , R0 , C4 , C5 , 6 ;该指令将 ARM 处理器寄存器 R0 中的数据传送到协处理器 P3 的寄存器 C4 和 C5 中。
  3. MRC 指令
    MRC 指令的格式为:
    MRC{条件} 协处理器编码,协处理器操作码1,目的寄存器,源寄存器1,源寄存器2,协处理器操作码2。
    MRC 指令用于将协处理器寄存器中的数据传送到ARM 处理器寄存器中,若协处理器不能成功完成操作,则产生未定义指令异常。其中协处理器操作码1 和协处理器操作码2 为协处理器将要执行的操作,目的寄存器为ARM 处理器的寄存器,源寄存器1 和源寄存器2 均为协处理器的寄存器。
    指令示例: MRC P3 , 3 , R0 , C4 , C5 , 6 ;该指令将协处理器 P3 的寄存器中的数据传送到 ARM 处理器寄存器中.
  4. CONFIG_ARM_VIRT_EXT:Enable the kernel to make use of the ARM Virtualization Extensions to install hypervisors without run-time firmware assistance.
    A compliant bootloader is required in order to make maximum use of this feature.
  5. CONFIG_ARM_LPAE:Support for the Large Physical Address Extension
    Say Y if you have an ARMv7 processor supporting the LPAE page table format and you would like to access memory beyond the 4GB limit. The resulting kernel image will not run on processors without the LPA extension.
  6. CONFIG_XIP_KERNEL:Kernel Execute-In-Place from ROM.
  7. CONFIG_ARM_PATCH_PHYS_VIRT:Patch phys-to-virt and virt-to-phys translation functions at boot and module load time according to the position of the kernel in system memory.
    This can only be used with non-XIP MMU kernels where the base of physical memory is at a 16MB boundary, or theoretically 64K for the MSM machine class.
  8. Cgroups是control groups的缩写,是Linux内核提供的一种可以限制、记录、隔离进程组(process groups)所使用的物理资源(如:cpu,memory,IO等等)的机制。
  9. 任务(task)。在cgroups中,任务就是系统的一个进程。
  10. 控制族群(control group)。控制族群就是一组按照某种标准划分的进程。Cgroups中的资源控制都是以控制族群为单位实现。一个进程可以加入到某个控制族群,也从一个进程组迁移到另一个控制族群。一个进程组的进程可以使用cgroups以控制族群为单位分配的资源,同时受到cgroups以控制族群为单位设定的限制。
  11. 层级(hierarchy)。控制族群可以组织成hierarchical的形式,既一颗控制族群树。控制族群树上的子节点控制族群是父节点控制族群的孩子,继承父控制族群的特定的属性。
  12. 子系统(subsystem)。一个子系统就是一个资源控制器,比如cpu子系统就是控制cpu时间分配的一个控制器。子系统必须附加(attach)到一个层级上才能起作用,一个子系统附加到某个层级以后,这个层级上的所有控制族群都受到这个子系统的控制。