进程被锁定到cpu固定核心

博客记录了一次使用 tmux 进程管理器时遇到的奇怪问题,所有进程被锁定在特定 CPU 核心,影响了服务器资源分配。通过排查,发现是 tmux 的全局配置问题导致,重启 tmux server 后恢复正常。同时,作者分享了从 tmux 切换到 slurm 集群管理系统的经历。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原因

没找到。。。反正不是tmux的问题。。
管理员怀疑某个有管理员权限的用户不小心改了什么全局参数。。
于是改用slurm来管理集群了,啊,这玩意儿真难用,远不如直接ssh来的爽,暴风哭泣

叙述一下心路历程:

端午节正在快乐炼丹,看了眼htop发现服务器发现两核有难62核围观,感觉挺有意思的,甚至发了条动态
在这里插入图片描述
因为之前也出现过类似现象,后来莫名其妙就好了(应该是碰巧服务器重启),也就没放在心上
直到第二天急着跑推理看结果,发现还是很慢,并且发现服务器变本加厉成了一核有难63核围观:
在这里插入图片描述
遂开始刨根问底,首先用htop查看本用户进程,发现本用户绝大部分进程被锁到了3号核心:
在这里插入图片描述
并且发现另一个用户也被锁:
在这里插入图片描述
而有的用户则未被锁(忘截图)
此时我开始怀疑难道是被管理员制裁了?
在这里插入图片描述
等程序跑完再看眼htop发现:恩???vscode远程连接进程似乎没被锁:
在这里插入图片描述
仔细查了一圈3号核心上的进程,似乎都是tmux中执行的!于是新建一个终端执行训练来测试,果然是tmux的问题!(并且另一个被锁在2号核心的用户进程也都是tmux的):
在这里插入图片描述
这可咋整,百度google无果后,无奈重启tmux server,结果非常啊妹zing:
在这里插入图片描述

<think>我们正在讨论Linux内核中的per_cpu_ptr宏的用法和实现细节。per_cpu_ptr用于访问每个CPU的变量,这些变量在每个CPU上都有独立的副本,以避免锁竞争并提高性能。 根据引用[1]和引用[3],我们知道:-内核是可抢占的,访问per-cpu变量时需要知道当前运行在哪个CPU上。-在访问per-cpu变量时,需要禁用抢占,以防止进程被移动到另一个CPU上,导致访问错误的per-cpu数据。 per_cpu_ptr的用法:```c#include <linux/percpu.h>//声明一个per-cpu变量DEFINE_PER_CPU(int,my_percpu_var); //在代码中访问当前CPU的per-cpu变量int*ptr= this_cpu_ptr(&my_percpu_var);*ptr=10;//设置当前CPU上的值为10//如果要访问指定CPU的per-cpu变量,使用per_cpu_ptrintcpu =1;//指定CPU1int*ptr=per_cpu_ptr(&my_percpu_var,cpu);*ptr=20;//设置CPU1上的值为20```但是,使用`per_cpu_ptr`访问非当前CPU的per-cpu变量时,需要特别注意: 1.确保在访问期间目标CPU上的变量不会被并发修改(通常需要额外的同步机制,如锁或者禁用中断等,具体取决于使用场景)。2.访问当前CPU的per-cpu变量时,应使用`this_cpu_ptr`,并且需要禁用抢占(因为禁用抢占可以保证在访问过程中不会被调度到其他CPU上)。 实现细节:- `per_cpu_ptr`宏定义在`include/linux/percpu.h`中,其实现大致如下:```c#defineper_cpu_ptr(ptr, cpu)({(void)(cpu); __percpu_ptr((ptr));})```实际上,它会将指针转换为相对于当前CPU的per-cpu变量基址的偏移。具体实现依赖于体系结构。-在访问per-cpu变量时,为了安全,通常需要禁用抢占。例如:```cint*ptr;preempt_disable(); //禁用抢占ptr= this_cpu_ptr(&my_percpu_var);//对ptr进行操作*ptr =*ptr +1;preempt_enable(); //重新启用抢占``` 为什么需要禁用抢占?因为如果不禁用抢占,在访问per-cpu变量时,如果被抢占并且重新调度到另一个CPU上,那么继续访问的per-cpu变量就是新CPU上的副本,导致不一致。另外,在SMP系统中,per-cpu变量是每个CPU都有一个独立的副本,所以它们不会相互竞争(除非访问其他CPU的副本)。因此,在仅访问当前CPU的per-cpu变量时,不需要锁,但需要禁用抢占。 示例:统计网络接收数据包在网络驱动中,通常使用per-cpu变量来统计接收和发送的数据包数量,以避免多个CPU同时更新同一个计数器造成的缓存行竞争。```cstruct pcpu_sw_netstats{u64rx_packets;u64rx_bytes;u64 tx_packets;u64 tx_bytes;} ____cacheline_aligned; DEFINE_PER_CPU(struct pcpu_sw_netstats, dev_stats); //在接收数据包时更新统计voiddev_update_stats(struct net_device *dev){struct pcpu_sw_netstats*tstats =this_cpu_ptr(&dev_stats);u64 rx_packets,rx_bytes;//更新当前CPU的统计tstats->rx_packets++;tstats->rx_bytes +=skb->len;//如果需要获取全局统计,需要遍历所有CPU的副本并求和}``` 注意:当需要获取全局统计信息(如通过`dev_get_stats`)时,需要遍历所有CPU的per-cpu变量副本,并将它们相加。在这个过程中,需要确保在读取每个CPU的副本时,该副本不会被同时修改(通常通过禁用抢占或使用锁来实现,但内核提供了专门的API来安全地读取per-cpu变量,如`percpu_counter_sum`等)。安全访问其他CPU的per-cpu变量:如果要访问其他CPU的per-cpu变量,需要确保该变量不会被并发修改。一种方法是停止目标CPU(这通常代价很高,不常用),另一种方法是使用原子操作或者设计数据结构避免并发问题。在大多数情况下,我们不会直接修改其他CPU的per-cpu变量。总结:-`per_cpu_ptr(ptr,cpu)`:返回指定CPU的per-cpu变量的指针。- `this_cpu_ptr(ptr)`:返回当前CPU的per-cpu变量的指针(需要禁用抢占)。 -访问当前CPU的per-cpu变量时,必须使用`preempt_disable`和`preempt_enable`来保护。引用[1]提到,访问per-cpu变量时,内核代码不能被抢占并移动到另一个CPU,因此需要使用`preempt_disable`。此外,`this_cpu_ptr`宏内部可能使用了当前CPU的ID来获取正确的地址。最后,注意在模块中使用per-cpu变量时,需要在模块卸载时释放这些变量,使用`free_percpu`函数。 相关问题: 1.如何在Linux内核中定义和使用per-cpu变量? 2.为什么在访问per-cpu变量时需要禁用抢占? 3.如何安全地汇总所有CPU的per-cpu变量?4.per-cpu变量与原子操作相比有什么优势?</think>### Linux 内核中 `per_cpu_ptr` 的用法与实现细节 `per_cpu_ptr` 是 Linux 内核中用于访问 **每 CPU 变量** 的关键宏,它通过避免锁竞争来提升性能。以下是详细解析: --- #### 一、核心作用与原理 1. **解决并发问题** 在 SMP 系统中,全局变量会导致 CPU 缓存行竞争(Cache Line Bouncing)。`per_cpu_ptr` 为每个 CPU 创建变量副本,消除锁需求[^1]。 2. **内存布局** 每 CPU 变量在内存中按 CPU 数量分段存储: ```c CPU0副本 | CPU1副本 | ... | CPU_N副本 ``` 访问时通过 `per_cpu_ptr(var, cpu_id)` 定位到指定 CPU 的副本地址。 --- #### 二、基本用法 ```c #include <linux/percpu.h> // 1. 声明每 CPU 变量 DEFINE_PER_CPU(struct mystat, my_percpu_stats); // 2. 获取当前 CPU 的指针(需禁用抢占) preempt_disable(); // 防止被迁移到其他 CPU struct mystat *stat = this_cpu_ptr(&my_percpu_stats); stat->counter++; // 安全修改当前 CPU 的数据 preempt_enable(); // 3. 获取指定 CPU 的指针 int target_cpu = 2; struct mystat *target_stat = per_cpu_ptr(&my_percpu_stats, target_cpu); ``` --- #### 三、关键实现机制 1. **地址计算** 宏展开后实质是内存偏移计算: ```c #define per_cpu_ptr(ptr, cpu) \ ((typeof(ptr))((char*)(ptr) + per_cpu_offset(cpu))) ``` 其中 `per_cpu_offset(cpu)` 返回该 CPU 副本区的基址偏移量[^1]。 2. **禁用抢占必要性** 如引用[1]所述,访问时必须锁定当前 CPU: ```c preempt_disable(); // 此时保证代码在固定 CPU 执行 per_cpu_ptr(...); preempt_enable(); ``` 若被抢占后调度到其他 CPU,会访问错误副本[^1]。 3. **数据结构关联** 每 CPU 变量常与 `task_struct` 关联(引用[2]),例如调度器为每个 CPU 维护独立的运行队列。 --- #### 四、性能优化场景 1. **网络统计** 网卡驱动使用每 CPU 计数器避免锁竞争: ```c struct pcpu_sw_netstats __percpu *stats; stats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats); // 更新数据 struct pcpu_sw_netstats *tstats = this_cpu_ptr(stats); tstats->rx_packets++; ``` 2. **虚拟化中断统计** 如引用[4]所示,KVM 记录 vCPU 的每 CPU 中断退出次数: ```c // vcpu->stat.irq_exits 是每 CPU 变量 ++vcpu->stat.irq_exits; ``` --- #### 五、使用注意事项 1. **初始化** 使用 `alloc_percpu()` 动态分配或 `DEFINE_PER_CPU()` 静态声明。 2. **跨 CPU 访问** 访问其他 CPU 的副本时需额外同步(如 IPI 中断),直接修改可能引发竞态。 3. **调试工具** 通过 `/sys/kernel/debug/percpu` 查看每 CPU 变量内存布局。 --- #### 六、与 `this_cpu_ptr` 对比 | 宏 | 作用 | 是否需要禁用抢占 | |--------------------|--------------------------|------------------| | `per_cpu_ptr()` | 访问任意 CPU 的副本 | 是 | | `this_cpu_ptr()` | 仅访问当前 CPU 的副本 | 是 | > 优先使用 `this_cpu_ptr` 可避免传递 CPU ID 参数。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值