CPUSET - Linux手册页
Linux程序员手册 第7部分
更新日期: 2020-06-09
名称
cpuset-将进程限制为处理器和内存节点子集
说明
cpuset文件系统是内核cpuset机制的伪文件系统接口,用于控制处理器的处理器位置和进程的内存位置。它通常安装在/ dev / cpuset上。
在内核编译为内置支持cpuset的系统上,所有进程都附加到cpuset,并且cpusets始终存在。如果系统支持cpuset,则它将在文件/ proc / filesystems中具有条目nodev cpuset。通过安装cpuset文件系统(请参阅下面的示例部分),管理员可以在系统上配置cpuset,以控制该系统上处理器和进程的内存位置。默认情况下,如果未修改系统上的cpuset配置或未安装cpuset文件系统,则cpuset机制(尽管存在)对系统的行为没有影响。
cpuset定义CPU和内存节点的列表。
系统的CPU包括可以在其上执行进程的所有逻辑处理单元,包括(如果存在)程序包中的多个处理器内核和处理器内核中的超线程。内存节点包括所有不同的主内存;小型系统和SMP系统通常只有一个内存节点,其中包含系统的所有主内存,而NUMA(非统一内存访问)系统具有多个内存节点。
cpuset在分层的伪文件系统中表示为目录,其中层次结构中的顶层目录(/ dev / cpuset)表示整个系统(所有联机CPU和内存节点),并且它是另一个父级的子级(后代)的任何cpuset cpuset包含该父级的CPU和内存节点的子集。代表cpuset的目录和文件具有正常的文件系统权限。
系统中的每个进程都完全属于一个cpuset。进程被限制为只能在其所属的cpuset中的CPU上运行,并且只能在该cpuset中的内存节点上分配内存。当进程fork(2)时,子进程将与其父进程放置在同一cpuset中。如果有足够的特权,则可以将进程从一个cpuset移动到另一个cpuset,并且可以更改现有cpuset的允许的CPU和内存节点。
当系统开始引导时,将定义一个单独的cpuset,其中包括系统上的所有CPU和内存节点,并且所有进程都在该cpuset中。在引导过程中,或稍后在正常系统运行期间,可以在系统管理员的控制下创建其他cpuset,作为此顶级cpuset的子目录,并将进程放置在这些其他cpuset中。
Cpuset与sched_setaffinity(2)调度亲和力机制以及mbind(2)和set_mempolicy(2)内存放置机制集成在内核中。这两种机制均不允许进程使用该进程的cpuset不允许的CPU或内存节点。如果对进程的cpuset放置的更改与其他机制冲突,则将强制执行cpuset放置,即使这意味着要覆盖这些其他机制。内核通过将其他机制所请求的CPU和内存节点默默地限制为调用进程的cpuset所允许的CPU和内存节点来实现此覆盖。这可能会导致这些其他调用返回错误,例如,在该请求被限制为调用进程的cpuset之后,如果该调用最终请求一个空的CPU或内存节点集。
通常,cpuset用于管理一组协作进程(例如批处理调度程序作业)的CPU和内存节点限制,而这些其他机制则用于管理该组或作业中各个进程或内存区域的放置。
文件
/ dev / cpuset下的每个目录都代表一个cpuset,并包含一组描述该cpuset状态的固定伪文件。
使用mkdir(2)系统调用或mkdir(1)命令创建新的cpuset。可以通过读取或写入该cpuset目录中的相应文件来查询和修改cpuset的属性(例如其标志,允许的CPU和内存节点以及附加的进程),如下所示。
由于mkdir(2)的调用,在创建cpuset时将自动创建每个cpuset目录中的伪文件。无法直接添加或删除这些伪文件。
可以使用rmdir(2)或rmdir(1)删除不包含子cpuset目录且没有附加进程的cpuset目录。在删除目录之前,没有必要或不可能删除目录中的伪文件。
每个cpuset目录中的伪文件都是小型文本文件,可以使用传统的shell实用程序(例如cat(1)和echo(1))或通过使用文件I / O库函数或系统调用从程序中读取和写入这些文本文件。 ,例如open(2),read(2),write(2)和close(2)。
cpuset目录中的伪文件表示内部内核状态,并且磁盘上没有任何持久映像。这些每个cpuset文件中的每一个都在下面列出并描述。
- tasks
- 该cpuset中的进程的进程ID(PID)的列表。该列表的格式为一系列ASCII十进制数字,每个数字后跟一个换行符。通过将进程的PID写入该cpuset的任务文件(带有或不带有尾随换行符),可以将进程添加到cpuset(自动将其从先前包含该进程的cpuset中删除)。
- 警告:一次只能将一个PID写入任务文件。如果写入的字符串包含多个PID,则仅使用第一个。
- notify_on_release
- 标志(0或1)。如果设置为(1),则该cpuset在释放后将接受特殊处理,即在所有进程停止使用它之后(即终止或移动到另一个cpuset)并且所有子cpuset目录已被删除。请参阅下面的"发布时通知"部分。
- cpuset.cpus
- 允许在该cpuset中执行进程的CPU的物理编号列表。有关cpus格式的说明,请参见下面的列表格式。
- 可以通过将新列表写入其cpus文件来更改允许使用cpuset的CPU。
- cpuset.cpu_exclusive
- 标志(0或1)。如果设置为(1),则cpuset专用于其CPU(兄弟姐妹或堂兄cpuset不能与CPU重叠)。默认情况下,它是关闭的(0)。新创建的cpusets最初也默认将其设置为off(0)。
- 如果两个cpuset在/ dev / cpuset层次结构中共享相同的父cpuset,则它们是同级cpuset。如果两个cpuset是彼此的祖先,则它们是表兄弟cpuset。无论cpu_exclusive设置如何,如果一个cpuset是另一个cpuset的祖先,并且如果这两个cpuset都具有非空cpus,则它们的cpus必须重叠,因为任何cpuset的cpus始终都是其父cpuset的cpus的子集。
- cpuset.mems
- 允许此cpuset中的进程在其上分配内存的内存节点列表。有关内存格式的说明,请参见下面的列表格式。
- cpuset.mem_exclusive
- 标志(0或1)。如果设置为(1),则cpuset专用于其内存节点(兄弟姐妹或表兄弟不能重叠)。同样,如果设置为(1),则cpuset为Hardwall cpuset(请参见下文)。默认情况下,它是关闭的(0)。新创建的cpusets最初也默认将其设置为off(0)。
- 不管mem_exclusive设置如何,如果一个cpuset是另一个cpuset的祖先,则它们的内存节点必须重叠,因为任何cpuset的内存节点始终是该cpuset的父cpuset的内存节点的子集。
- cpuset.mem_hardwall(since Linux 2.6.26)
- 标志(0或1)。如果设置为(1),则该cpuset为Hardwall cpuset(请参见下文)。与mem_exclusive不同,对标记为mem_hardwall的cpuset是否可以具有具有同级或堂兄cpuset的重叠内存节点没有任何限制。默认情况下,它是关闭的(0)。新创建的cpusets最初也默认将其设置为off(0)。
- cpuset.memory_migrate(since Linux 2.6.16)
- 标志(0或1)。如果设置为(1),则启用内存迁移。默认情况下,它是关闭的(0)。请参阅下面的"内存迁移"部分。
- cpuset.memory_pressure(since Linux 2.6.16)
- 衡量此cpuset中的进程造成多少内存压力的度量。请参阅下面的"内存压力"部分。除非启用了memory_pressure_enabled,否则始终值为零(0)。该文件是只读的。请参阅下面的"警告"部分。
- cpuset.memory_pressure_enabled(since Linux 2.6.16)
- 标志(0或1)。该文件仅存在于根cpuset中,通常为/ dev / cpuset。如果设置为(1),则为系统中的所有CPU启用memory_pressure计算。默认情况下,它是关闭的(0)。请参阅下面的"内存压力"部分。
- cpuset.memory_spread_page(since Linux 2.6.17)
- 标志(0或1)。如果设置为(1),则内核页面缓存(文件系统缓冲区)中的页面在cpuset中均匀分布。默认情况下,顶部cpuset中的此选项为关闭(0),并且从新创建的cpuset中的父cpuset继承。请参阅下面的"内存传播"部分。
- cpuset.memory_spread_slab(since Linux 2.6.17)
- 标志(0或1)。如果设置为(1),则文件I / O(目录和索引节点结构)的内核slab缓存在cpuset中均匀分布。默认情况下,默认情况下,顶部cpuset中的设置为off(0),并从新创建的cpusets中的父cpuset继承。请参阅下面的"内存传播"部分。
- cpuset.sched_load_balance(since Linux 2.6.24)
- 标志(0或1)。如果设置(默认值1),内核将自动对该CPU集中该CPU上允许的CPU上的进程进行负载平衡。如果清除(0),内核将避免在此cpuset中进行负载平衡过程,除非其他一些CPU重叠的cpuset设置了sched_load_balance标志。有关更多详细信息,请参见下面的调度程序负载平衡。
- cpuset.sched_relax_domain_level(since Linux 2.6.26)
- 整数,介于-1和较小的正值之间。 sched_relax_domain_level控制CPU范围的宽度,内核调度程序在该范围内执行CPU之间可运行任务的立即重新平衡。如果禁用sched_load_balance,则sched_relax_domain_level的设置无关紧要,因为不会进行此类负载平衡。如果启用了sched_load_balance,则sched_relax_domain_level的值越高,尝试立即进行负载平衡的CPU范围越广。有关更多详细信息,请参见下面的Scheduler Relax Domain Level。
除了/ dev / cpuset下每个目录中的上述伪文件之外,每个进程还有一个伪文件/ proc // cpuset,该伪文件显示该进程的cpuset目录相对于cpuset文件系统根目录的路径。
此外,每个进程的/ proc // status文件还增加了四行,分别以掩码格式和列表这两种格式显示了该进程的Cpus_allowed(可以在其上调度其CPU)和Mems_allowed(它可以在哪些内存节点上获得内存)。格式(如下所示),如以下示例所示:
Cpus_allowed: ffffffff,ffffffff,ffffffff,ffffffff Cpus_allowed_list: 0-127 Mems_allowed: ffffffff,ffffffff Mems_allowed_list: 0-63
Linux 2.6.24中添加了" allowed"字段;在Linux 2.6.26中添加了" allowed_list"字段。
EXTENDED CAPABILITIES
除了控制允许使用某个进程的cpus和mems之外,cpuset还提供以下扩展功能。
Exclusive cpusets
如果将cpuset标记为cpu_exclusive或mem_exclusive,则除了直接祖先或后代之外,其他任何cpuset都不能共享任何相同的CPU或内存节点。
一个mem_exclusive的cpuset限制了内核为多个用户通常共享的缓冲区高速缓存页面和其他内部内核数据页面的内核分配。所有cpuset(无论是否为mem_exclusive)都限制为用户空间分配内存。这样就可以配置系统,以便几个独立的作业可以共享公用内核数据,同时将每个作业的用户分配隔离在其自己的cpuset中。为此,请构造一个大型mem_exclusive cpuset来容纳所有作业,并为每个单独的作业构造子级非mem_exclusive cpuset。即使是mem_exclusive cpuset,也只能将少量的内核内存(例如来自中断处理程序的请求)放置在内存节点上。
Hardwall
设置了mem_exclusive或mem_hardwall的cpuset是硬墙cpuset。硬墙cpuset限制内核为多个用户通常共享的页面,缓冲区和其他数据分配内核。所有cpuset(无论是否为硬墙)都会限制用户空间的内存分配。
这样可以配置系统,以便几个独立的作业可以共享公用内核数据,例如文件系统页面,同时将每个作业的用户分配隔离在自己的cpuset中。为此,请构造一个大的硬墙cpuset来容纳所有作业,并为每个单独的作业构造非硬墙cpuset的子cpuset。
即使是硬墙cpuset,也只能将少量内核内存(例如来自中断处理程序的请求)带到外部。
Notify on release
如果在cpuset中启用了notify_on_release标志(1),则每当cpuset中的最后一个进程离开(退出或附加到其他cpuset)并且该cpuset的最后一个子cpuset被删除时,内核将运行命令/ sbin / cpuset_release_agent,提供放弃的cpuset的路径名(相对于cpuset文件系统的安装点)。这样可以自动删除废弃的cpuset。
禁用系统启动时根cpuset中的notify_on_release的默认值(0)。创建时其他cpuset的默认值是其父级的notify_on_release设置的当前值。
调用命令/ sbin / cpuset_release_agent,并在argv [1]中使用要发布的cpuset的名称(/ dev / cpuset相对路径)。
命令/ sbin / cpuset_release_agent的通常内容只是shell脚本:
#!/bin/sh rmdir /dev/cpuset/
与下面的其他标志值一样,可以通过将ASCII数字0或1(带有可选的尾随换行符)写入文件来分别清除或设置该标志,从而更改此标志。
Memory pressure
cpuset的memory_pressure提供了每个cpuset的简单运行平均值,该平均值是cpuset中的进程尝试释放cpuset节点上的使用中内存以满足其他内存请求的速率。
这使正在监视在专用cpuset中运行的作业的批处理管理器可以有效地检测到该作业造成的内存压力级别。
这在运行大量提交作业的紧密管理的系统上很有用,这些系统可以选择终止或优先处理那些试图使用比分配给它们的节点更多的内存的作业,以及紧密耦合,长时间运行,大规模并行的作业如果科学计算工作开始使用超出其允许范围的内存,将大大无法达到所需的性能目标。
此机制为批处理管理器提供了一种非常经济的方式来监视cpuset的内存压力迹象。如果检测到内存压力迹象,则由批次管理器或其他用户代码决定要采取的措施。
除非通过设置伪文件/dev/cpuset/cpuset.memory_pressure_enabled启用了内存压力计算,否则不会为任何cpuset计算该压力,并且从任何memory_pressure读取的值始终返回零,如ASCII字符串" 0 \ n"所示。请参阅下面的"警告"部分。
出于以下原因,采用了每个计算机的运行平均值:
- *
- 由于此仪表是按CPU而不是按进程或按虚拟内存区域的,因此在大型系统上,监视此指标的批处理调度程序所施加的系统负载将大大减少,因为可以避免在每组查询上都扫描任务列表。
- *
- 由于此仪表是运行平均值,而不是累加计数器,因此批处理调度程序可以通过一次读取来检测内存压力,而不必在一段时间内读取和累加结果。
- *
- 由于此仪表是按CPU而不是按进程的,因此批处理计划程序可以通过一次读取来获取关键信息-CPU中的内存压力-而不用查询和累积所有结果(动态cpuset中的一组进程)。
cpuset的memory_pressure使用保留在内核中的每个cpuset简单数字滤波器来计算。对于每个cpuset,此过滤器将跟踪连接到该cpuset的进程输入内核直接回收代码的最新速率。
每当进程必须通过首先找到其他一些要重用的页面来满足内存页面请求时,都会输入内核直接回收代码,这是因为缺少任何易于使用的已经空闲的页面。通过首先将脏文件系统页面写入磁盘来重新利用它们。只需删除未修改的文件系统缓冲区页面即可重新利用它们的用途,但是如果再次需要该页面,则必须从磁盘中重新读取该页面。
cpuset.memory_pressure文件提供一个整数,表示由cpuset中的任何进程引起的直接回收代码的近期(半衰期为10秒)条目的速率,以每秒尝试回收的次数乘以1000为单位。
Memory spread
每个cpuset有两个布尔标志文件,它们控制内核在哪里为文件系统缓冲区和相关的内核数据结构分配页面。它们分别称为cpuset.memory_spread_page和cpuset.memory_spread_slab。
如果设置了每个cpuset布尔标志文件cpuset.memory_spread_page,则内核将在允许故障处理的所有节点上平均分配文件系统缓冲区(页面缓存),而不是倾向于将这些页面放在节点上进程在哪里运行。
如果设置了每个cpuset布尔标志文件cpuset.memory_spread_slab,则内核将在允许故障处理的所有节点上平均分配一些与文件系统相关的slab缓存,例如用于inode和目录条目的slab缓存。倾向于将这些页面放在进程正在运行的节点上。
这些标志的设置不会影响数据段(请参阅brk(2))或进程的堆栈段页。
默认情况下,两种内存分配都处于关闭状态,并且内核更喜欢在运行请求进程的本地节点上分配内存页面。如果该进程的NUMA内存策略或cpuset配置不允许该节点,或者该节点上的可用内存页不足,则内核会寻找允许的最近节点并具有足够的可用内存。
创建新的cpuset时,它们会继承其父级的内存扩展设置。
设置内存扩展会导致对受影响的页面或slab缓存的分配忽略进程的NUMA内存策略,而是进行扩展。但是,由mbind(2)或set_mempolicy(2)调用隐藏了由cpuset指定的内存扩展导致的这些内存位置更改的影响。这两个NUMA内存策略调用始终看起来好像没有cpuset指定的内存扩展生效一样,即使实际上也是如此。如果随后关闭了cpuset内存扩展,则会自动重新应用这些调用最近指定的NUMA内存策略。
cpuset.memory_spread_page和cpuset.memory_spread_slab都是布尔标志文件。默认情况下,它们包含" 0",表示该cpuset的功能已关闭。如果将" 1"写入该文件,则将打开命名功能。
Cpuset指定的内存扩展的行为类似于(在其他上下文中)轮询或交错内存放置。
特定于Cpuset的内存扩展可以为以下任务提供显着的性能改进:
- a)
- 需要将线程本地数据放置在靠近运行最常访问该数据的线程的CPU的内存节点上;但是也
- b)
- 需要访问大型文件系统数据集,这些数据集必须分布在作业cpuset中的多个节点上才能适合。
如果没有此策略,则作业cpuset中各节点之间的内存分配可能会变得非常不均匀,尤其是对于可能只有单个线程初始化或读取数据集的作业。
Memory migration
通常,在cpuset.memory_migrate的默认设置(禁用)下,一旦分配了一个页面(给定主内存的物理页面),则该页面将停留在分配的节点上,只要该页面保持分配状态,即使cpuset的内存放置策略内存随后更改。
在cpuset中启用内存迁移后,如果更改了cpuset的mems设置,则cpuset中任何不再允许在内存节点上的进程使用的任何内存页面将被迁移到被允许。
此外,如果在启用了memory_migrate的情况下将进程移入cpuset,则其使用的,位于其先前cpuset中允许的内存节点上但其新cpuset中不允许的内存页面将被迁移到新的cpuset。
如果可能的话,在这些迁移操作期间会保留cpuset中已迁移页面的相对位置。例如,如果页面位于先前cpuset的第二个有效节点上,则该页面将放置在新cpuset的第二个有效节点上(如果可能)。
Scheduler load balancing
内核调度程序会自动对进程进行负载平衡。如果一个CPU未被充分利用,内核将在诸如cpusets和sched_setaffinity(2)之类的放置机制的约束下,在其他更过载的CPU上寻找进程,并将这些进程移至未充分利用的CPU。
负载均衡的算法成本及其对关键共享内核数据结构(如进程列表)的影响,随着所均衡的CPU数量的增加,线性增加的幅度更大。例如,在一组大型CPU上进行负载平衡要比在两组较小的CPU上进行平衡(分别为较大组的一半)花费的成本高。 (要平衡的CPU数量与负载平衡成本之间的精确关系取决于内核进程调度程序的实现细节,随着改进的内核调度程序算法的实施,该细节可能会随着时间而变化。)
per-cpuset标志sched_load_balance提供了一种机制,可以在不需要自动调度程序负载平衡的情况下抑制该自动调度程序的负载平衡,并通过抑制这种平衡会带来有价值的性能收益。
默认情况下,负载均衡在所有CPU上完成,但使用内核启动时间" isolcpus ="参数标记为隔离的CPU除外。 (请参阅下面的Scheduler Relax Domain Level,以更改此默认值。)
所有CPU上的默认负载平衡均不适用于以下两种情况:
- *
- 在大型系统上,跨多个CPU的负载平衡非常昂贵。如果使用cpusets管理系统以将独立的作业放置在不同的CPU组上,则不需要完全的负载平衡。
- *
- 在某些CPU上支持实时的系统需要使这些CPU的系统开销最小化,包括避免不需要的进程负载平衡。
启用每个cpuset标志sched_load_balance(默认设置)后,它将请求该cpuset允许的CPU中所有CPU的负载平衡,从而确保负载平衡可以从该cpuset中的任何CPU到其他任何CPU。
当禁用了每个cpuset标志sched_load_balance时,调度程序将避免在该cpuset中的CPU之间进行负载平衡,除非有必要,因为某些重叠的cpuset启用了sched_load_balance。
因此,例如,如果顶部cpuset启用了sched_load_balance标志,则调度程序将在所有CPU上进行负载平衡,而其他cpuset中的sched_load_balance标志的设置无效,因为我们已经完全实现了负载平衡。
因此,在以上两种情况下,应该在顶部cpuset中禁用标志sched_load_balance,并且只有某些较小的子cpuset可以启用此标志。
这样做时,您通常不希望在顶部cpuset中保留任何可能占用大量CPU的未固定进程,因为此类进程可能被人为地限制在某些CPU子集中,具体取决于后代中此标志设置的具体情况。 cpusets。即使这样的进程可以在其他一些CPU中使用空闲的CPU周期,内核调度程序也可能不会考虑将该进程负载平衡到未充分利用的CPU的可能性。
当然,可以将固定到特定CPU的进程留在cpuset中,以禁用sched_load_balance,因为这些进程无论如何都不会进行其他操作。
Scheduler relax domain level
每当CPU空闲或其他任务变得可运行时,内核调度程序都会立即执行负载平衡。这种负载平衡的作用是确保有用的运行任务尽可能多地使用CPU。内核还根据time(7)中描述的软件时钟执行定期负载平衡。 sched_relax_domain_level的设置仅适用于立即负载平衡。无论sched_relax_domain_level设置如何,都将尝试在所有CPU上进行定期负载平衡(除非通过关闭sched_load_balance来禁用。)当然,在任何情况下,任务都将安排为仅在其cpuset允许的CPU上运行,这由sched_setaffinity(2修改) )系统调用。
在小型系统(例如,只有几个CPU的系统)上,即时负载平衡对于改善系统交互性并最大程度地减少浪费的空闲CPU周期很有用。但是,在大型系统上,根据作业组合和硬件的特定性能特征,尝试在大量CPU上立即进行负载平衡可能比其价值更高。
sched_relax_domain_level小整数值的确切含义将取决于内核调度程序代码的内部实现细节以及硬件的非统一体系结构。两者都将随着时间的推移而发展,并且会因系统架构和内核版本而异。
在撰写本文时,在Linux 2.6.26中引入此功能时,在某些流行的体系结构上,sched_relax_domain_level的正值具有以下含义。
- (1)
- 在同一内核上的超线程同级中立即执行负载平衡。
- (2)
- 在同一程序包中的其他内核之间执行即时负载平衡。
- (3)
- 在同一节点或刀片服务器上的其他CPU上立即执行负载平衡。
- (4)
- 在多个(实现细节)节点上执行即时负载平衡[在NUMA系统上]。
- (5)
- 在系统中的所有CPU上执行即时负载平衡[在NUMA系统上]。
sched_relax_domain_level的值为零(0)始终表示不立即执行负载平衡,因此,负载平衡仅定期执行,而不是在CPU可用或其他任务可运行时立即执行。
sched_relax_domain_level值减一(-1)始终表示使用系统默认值。系统默认值可能会因体系结构和内核版本而异。可以通过内核引导时间" relax_domain_level ="参数更改该系统默认值。
如果多个重叠的cpuset具有不同的sched_relax_domain_level值,则此类值中的最大值适用于任何重叠的cpuset中的所有CPU。在这种情况下,值减去一(-1)是最小值,被其他任何值覆盖,而值零(0)是下一个最小值。
FORMATS
以下格式用于表示CPU和内存节点的集合。
Mask format
掩码格式用于表示/ proc // status文件中的CPU和内存节点位掩码。
此格式以十六进制显示每个32位字(使用ASCII字符" 0"-" 9"和" a"-" f");如有必要,单词用前导零填充。对于长度超过一个单词的掩码,在单词之间使用逗号分隔符。单词以高位在先的顺序显示,其顺序是最高位。单词中的十六进制数字也按大端顺序排列。
根据位掩码的大小,显示的32位字的数目是显示位掩码的所有位所需的最小数目。
掩码格式的示例:
00000001 # just bit 0 set 40000000,00000000,00000000 # just bit 94 set 00000001,00000000,00000000 # just bit 64 set 000000ff,00000000 # bits 32-39 set 00000000,000e3862 # 1,5,6,11-13,17-19 set
设置了0、1、2、4、8、16、32和64位的掩码显示为:
00000001,00000001,00010117
第一个" 1"用于位64,第二个用于位32,第三个用于位16,第四个用于位8,第五个用于位4," 7"用于位2、1和0。
List format
cpus和mems的列表格式是用逗号分隔的CPU或内存节点编号以及编号范围的列表,以ASCII十进制表示。
列表格式的示例:
0-4,9 # bits 0, 1, 2, 3, 4, and 9 set 0-2,7,12-14 # bits 0, 1, 2, 7, 12, 13, and 14 set
RULES
以下规则适用于每个cpuset:
- *
- 它的CPU和内存节点必须是其父节点的(可能相等)子集。
- *
- 仅当其父级为时才可以将其标记为cpu_exclusive。
- *
- 仅当其父项为mem_exclusive时,才能将其标记为mem_exclusive。
- *
- 如果它是cpu_exclusive,则其CPU可能不会与任何同级重叠。
- *
- 如果为memory_exclusive,则其内存节点可能不会与任何同级重叠。
PERMISSIONS
cpuset的权限由cpuset文件系统中目录和伪文件的权限确定,该目录和伪文件通常安装在/ dev / cpuset中。
例如,如果进程可以为该cpuset编写任务文件,则可以将其放入其他cpuset中(当前进程除外)。这需要包含目录的执行权限和任务文件的写入权限。
附加约束适用于将某些其他进程放入cpuset的请求。一个进程可能不会将另一个进程附加到cpuset,除非它有权发送该进程信号(请参阅kill(2))。
如果进程可以访问和写入父cpuset目录,则该进程可以创建子cpuset。如果它可以访问该cpuset的目录(在每个父目录上具有执行权限)并写入相应的cpus或mems文件,则它可以修改cpuset中的CPU或内存节点。
评估这些权限的方式与评估正常文件系统操作权限的方式之间只有一个细微的差别。内核解释从进程的当前工作目录开始的相对路径名。即使正在对cpuset文件进行操作,相对路径名也会相对于进程的当前工作目录而不是相对于进程的当前cpuset进行解释。可以使用相对于进程当前cpuset的cpuset路径的唯一方法是:如果该进程的当前工作目录是其cpuset(它首先在其/ dev / cpuset下的cpuset目录中执行了cd或chdir(2),异常)或某些用户代码将相对cpuset路径转换为完整的文件系统路径。
从理论上讲,这意味着用户代码应使用绝对路径名指定cpuset,这需要知道cpuset文件系统的安装点(通常但不一定是/ dev / cpuset)。实际上,该作者知道的所有用户级别代码都仅假设如果已安装cpuset文件系统,则将其安装在/ dev / cpuset。此外,通常的做法是精心编写用户代码,以验证伪文件/ dev / cpuset / tasks的存在,以验证当前是否已安装cpuset伪文件系统。
WARNINGS
Enabling memory_pressure
默认情况下,每个cpuset文件cpuset.memory_pressure始终包含零(0)。除非通过在伪文件/dev/cpuset/cpuset.memory_pressure_enabled中写入" 1"来启用此功能,否则内核不会计算每个CPU的memory_pressure。
Using the echo command
在shell提示符下使用echo命令更改cpuset文件的值时,请注意,如果write(2)系统调用失败,则某些shell中的内置echo命令不会显示错误消息。例如,如果命令:
echo 19 > cpuset.mems
失败,因为不允许使用内存节点19(也许当前系统没有内存节点19),那么echo命令可能不会显示任何错误。最好使用/ bin / echo external命令来更改cpuset文件设置,因为此命令将显示write(2)错误,如示例中所示:
/bin/echo 19 > cpuset.mems /bin/echo: write error: Invalid argument
EXCEPTIONS
Memory placement
由于以下原因,并非所有系统内存分配都受cpusets约束。
如果使用热插拔功能删除当前分配给cpuset的所有CPU,则内核将自动更新该cpuset中与CPU相连的所有进程的cpus_allowed,以允许所有CPU。当可用的用于删除内存节点的内存热插拔功能可用时,类似的例外也将适用于此。通常,内核更喜欢违反cpuset放置,而不是使所有允许的CPU或内存节点都脱机的进程死机。使用热插拔添加或删除此类资源时,用户代码应将cpusets重新配置为仅引用联机CPU和内存节点。
必须立即满足一些标记为GFP_ATOMIC的关键内核,内部内存分配请求。如果这些分配之一失败,内核可能会丢弃某些请求或出现故障。如果在当前进程的cpuset中不能满足这样的请求,则我们放松cpuset,并在可以找到它的任何地方寻找内存。最好是违反cpuset而不是强调内核。
内核驱动程序在处理中断时请求的内存分配缺少任何相关的进程上下文,并且不受cpuset限制。
Renaming cpusets
您可以使用rename(2)系统调用来重命名cpuset。仅支持简单的重命名;也就是说,允许更改cpuset目录的名称,但不允许将目录移动到其他目录。
错误说明
cpusets的Linux内核实现将errno设置为指定失败的系统调用影响cpusets的原因。
下表列出了在失败的cpuset调用上设置时可能的errno设置及其含义。
- E2BIG
- 尝试在特殊cpuset文件上执行write(2),该文件的长度大于内核确定的此类写入长度的上限。
- EACCES
- 尝试在某人没有移动该进程的权限时将其进程ID(PID)写入(2)到cpuset任务文件中。
- EACCES
- 尝试使用write(2)将CPU或内存节点添加到cpuset中时,该CPU或内存节点尚未位于其父节点中。
- EACCES
- 尝试使用write(2)在父级缺少相同设置的cpuset上设置cpuset.cpu_exclusive或cpuset.mem_exclusive。
- EACCES
- 尝试写入(2)一个cpuset.memory_pressure文件。
- EACCES
- 尝试在cpuset目录中创建文件。
- EBUSY
- 尝试使用rmdir(2)删除具有附加进程的cpuset。
- EBUSY
- 尝试使用rmdir(2)删除带有子cpuset的cpuset。
- EBUSY
- 尝试从也位于该cpuset子级中的cpuset中删除CPU或内存节点。
- EEXIST
- 尝试使用mkdir(2)创建一个已经存在的cpuset。
- EEXIST
- 尝试将cpuset重命名(2)为已经存在的名称。
- EFAULT
- 尝试使用写过程外部可访问地址空间之外的缓冲区读取(2)或写入(2)cpuset文件。
- EINVAL
- 尝试使用write(2)更改cpuset,其方式会违反该cpuset或其任何同级的cpu_exclusive或mem_exclusive属性。
- EINVAL
- 尝试将一个空的cpuset.cpus或cpuset.mems列表写入(2)到已附加进程或子cpusets的cpuset。
- EINVAL
- 尝试写入(2)一个cpuset.cpus或cpuset.mems列表,该列表包含第二个数字小于第一个数字的范围。
- EINVAL
- 尝试写入(2)一个cpuset.cpus或cpuset.mems列表,该列表在字符串中包含无效字符。
- EINVAL
- 尝试将列表写入(2)到不包含任何在线CPU的cpuset.cpus文件。
- EINVAL
- 尝试将列表写入(2)到不包含任何联机内存节点的cpuset.mems文件。
- EINVAL
- 尝试将列表写入(2)到cpuset.mems文件,该文件包含不包含任何内存的节点。
- EIO
- 尝试将字符串写入(2)到不以ASCII十进制整数开头的cpuset任务文件中。
- EIO
- 尝试将cpuset重命名(2)到另一个目录中。
- ENAMETOOLONG
- 尝试读取(2)/ proc // cpuset文件以获取比内核页面大小长的cpuset路径。
- ENAMETOOLONG
- 尝试使用mkdir(2)创建基本目录名称超过255个字符的cpuset。
- ENAMETOOLONG
- 尝试使用mkdir(2)创建一个cpuset,其完整路径名(包括安装点(通常为" / dev / cpuset /")前缀)长于4095个字符。
- ENODEV
- 在cpuset目录中的一个伪文件上尝试执行write(2)的同时,另一个进程删除了cpuset。
- ENOENT
- 尝试使用mkdir(2)在不存在的父cpuset中创建cpuset。
- ENOENT
- 尝试访问(2)或打开(2)cpuset目录中不存在的文件。
- ENOMEM
- 内核内存不足。可能会在影响cpuset的各种系统调用上发生,但前提是系统内存极度不足。
- ENOSPC
- 当cpuset具有空的cpuset.cpus或空的cpuset.mems设置时,尝试将(2)进程的进程ID(PID)写入cpuset任务文件。
- ENOSPC
- 尝试将空的cpuset.cpus或cpuset.mems设置写入(2)到已附加任务的cpuset。
- ENOTDIR
- 试图重命名(2)不存在的cpuset。
- EPERM
- 尝试从cpuset目录中删除文件。
- ERANGE
- 为内核指定一个cpuset.cpus或cpuset.mems列表,其中包含一个太大的数字,内核无法在其位掩码中进行设置。
- ESRCH
- 尝试将不存在的进程的进程ID(PID)写入(2)到cpuset任务文件中。
版本
Cpusets出现在Linux内核的2.6.12版本中。
备注
尽管有其名称,pid参数实际上是线程ID,并且线程组中的每个线程都可以连接到不同的cpuset。调用gettid(2)返回的值可以在pid参数中传递。
BUGS
可以打开cpuset.memory_pressure cpuset文件以进行写入,创建或截断,但是随后,由于将errno设置为EACCES,write(2)失败,并且open(2)上的创建和截断选项无效。
示例
以下示例演示了使用Shell命令查询和设置cpuset选项。
Creating and attaching to a cpuset.
要创建一个新的cpuset并将当前命令外壳附加到它,步骤如下:
- 1)
- mkdir / dev / cpuset(如果尚未完成)
- 2)
- mount -t cpuset none / dev / cpuset(如果尚未完成)
- 3)
- 使用mkdir(1)创建新的cpuset。
- 4)
- 将CPU和内存节点分配给新的cpuset。
- 5)
- 将外壳连接到新的cpuset。
例如,以下命令序列将设置一个名为" Charlie"的cpuset,仅包含CPU 2和3,以及内存节点1,然后将当前shell附加到该cpuset。
$ mkdir /dev/cpuset $ mount -t cpuset cpuset /dev/cpuset $ cd /dev/cpuset $ mkdir Charlie $ cd Charlie $ /bin/echo 2-3 > cpuset.cpus $ /bin/echo 1 > cpuset.mems $ /bin/echo $$ > tasks # The current shell is now running in cpuset Charlie # The next line should display '/Charlie' $ cat /proc/self/cpuset
Migrating a job to different memory nodes.
要将作业(连接到cpuset的一组进程)迁移到系统中的不同CPU和内存节点,包括移动当前分配给该作业的内存页,请执行以下步骤。
- 1)
- 假设我们要将作业在cpuset alpha(CPU 4-7和内存节点2-3)中移动到新的cpuset beta(CPU 16-19和内存节点8-9)。
- 2)
- 首先创建新的cpuset beta。
- 3)
- 然后在beta中允许CPU 16-19和内存节点8-9。
- 4)
- 然后在beta中启用memory_migration。
- 5)
- 然后将每个过程从alpha移到beta。
以下命令序列可以完成此任务。
$ cd /dev/cpuset $ mkdir beta $ cd beta $ /bin/echo 16-19 > cpuset.cpus $ /bin/echo 8-9 > cpuset.mems $ /bin/echo 1 > cpuset.memory_migrate $ while read i; do /bin/echo $i; done < ../alpha/tasks > tasks
上面应该将任何进程从alpha移到beta,并将这些进程在内存节点2-3上持有的任何内存分别移到内存节点8-9。
请注意,以上序列的最后一步没有执行:
$ cp ../alpha/tasks tasks
使用while循环,而不是使用cp(1)命令看似更容易,是必要的,因为一次只能将一个进程PID写入任务文件。
通过使用sed(-un)选项,可以更有效地完成与while循环相同的效果(一次写入一个PID),并且可以减少击键次数和在任何shell上使用的语法,但是更晦涩难懂。 1):
$ sed -un p < ../alpha/tasks > tasks
另外参见
任务集(1),get_mempolicy(2),getcpu(2),mbind(2),sched_getaffinity(2),sched_setaffinity(2),sched_setscheduler(2),set_mempolicy(2),CPU_SET(3),proc(5), cgroups(7),numa(7),sched(7),migratepages(8),numactl(8)
Linux内核源代码树中的Documentation / admin-guide / cgroup-v1 / cpusets.rst(或Linux 4.18之前的Documentation / cgroup-v1 / cpusets.txt和Linux 2.6.29之前的Documentation / cpusets.txt)
出版信息
这个页面是Linux手册页项目5.08版的一部分。有关项目的说明、有关报告错误的信息以及此页面的最新版本,请访问https://www.kernel.org/doc/man-pages/。