29、Linux 系统的电源管理与进程线程机制解析

Linux 系统的电源管理与进程线程机制解析

1. CPUIdle 驱动

CPUIdle 与 CPUFreq 子系统类似,由属于板级支持包(BSP)的驱动和决定策略的调节器组成。不过,与 CPUFreq 不同的是,CPUIdle 的调节器在运行时不能更改,且没有用户空间调节器的接口。

CPUIdle 在 /sys/devices/system/cpu/cpu0/cpuidle 目录中公开每个空闲状态的信息,每个睡眠状态都有一个子目录,命名为 state0 stateN state0 是最浅的睡眠状态, stateN 是最深的。需要注意的是,编号与 C 状态的编号不匹配,并且 CPUIdle 没有与 C0(运行)等效的状态。每个状态下有以下文件:
- desc :状态的简短描述
- disable :通过写入 1 来禁用此状态的选项
- latency :CPU 核心退出此状态恢复正常操作所需的时间,以微秒为单位
- name :此状态的名称
- power :处于此空闲状态时消耗的功率,以毫瓦为单位
- time :在此空闲状态下花费的总时间,以微秒为单位
- usage :进入此状态的次数

以 BeagleBone Black 上的 AM335x SoC 为例,有两个空闲状态:

# cd /sys/devices/system/cpu/cpu0/cpuidle
# grep "" state0/*
state0/desc:ARM WFI 
state0/disable:0 
state0/latency:1 
state0/name:WFI 
state0/power:4294967295 
state0/residency:1 
state0/time:1780015 
state0/usage:2159 

这个状态名为 WFI,指的是 ARM 暂停指令“Wait For Interrupt”。延迟为 1 微秒,因为它只是一个暂停指令,消耗的功率显示为 -1,意味着(至少 CPUIdle)不知道功率预算。

# cd /sys/devices/system/cpu/cpu0/cpuidle
# grep "" state1/*
state1/desc:Bypass MPU PLL 
state1/disable:0 
state1/latency:100 
state1/name:C1 
state1/power:497 
state1/residency:200 
state1/time:8763012731 
state1/usage:345285 

这个状态名为 C1,延迟为 100 微秒,功率为 497 毫瓦。空闲状态可以硬编码到 CPUIdle 驱动中,也可以在设备树中呈现。AM335x 采用前者,以下是另一个 SoC 的示例:

cpus {
  cpu: cpu0 {
    compatible = "arm,cortex-a9";
    enable-method = "ti,am4372";
    device-type = "cpu";
    reg = <0>;
    cpu-idle-states = <&mpu_gate>;
  };
  idle-states {
    compatible = "arm,idle-state";
    entry-latency-us = <40>;
    exit-latency-us = <100>;
    min-residency-us = <300>;
    local-timer-stop;
  };
};

CPUIdle 有两个调节器:
- ladder :根据上一个空闲周期所花费的时间,一次向上或向下调整一个空闲状态。它在使用规则定时器滴答时效果良好,但在动态滴答时效果不佳。
- menu :根据预期的空闲时间选择空闲状态。它在动态滴答系统中效果良好。

用户可以根据 NOHZ 的配置选择其中一个调节器。在 /sys/devices/system/cpu/cpuidle 目录中有两个文件:
- current_driver :cpuidle 驱动的名称
- current_governor_ro :调节器的名称

这些文件显示正在使用的驱动和调节器。空闲状态可以在 PowerTOP 的“Idle stats”选项卡中查看。

2. 无滴答操作

无滴答(NOHZ)选项是一个相关主题。如果系统真正空闲,最可能的中断源将是系统定时器,它被编程为以每秒 HZ 次的速率生成规则的时间滴答,通常 HZ 为 100。历史上,Linux 使用定时器滴答作为测量超时的主要时间基准。

然而,如果在特定时刻没有注册定时器事件,唤醒 CPU 来处理定时器中断显然是浪费的。动态滴答内核配置选项 CONFIG_NO_HZ 会在定时器处理例程结束时查看定时器队列,并在下次事件发生时安排下一次中断,避免不必要的唤醒,使 CPU 能够更长时间地空闲。在任何对功率敏感的应用中,都应该启用此内核配置选项。

3. 外设断电

前面的讨论主要围绕 CPU 以及如何在其运行或空闲时降低功耗。现在关注系统的其他部分——外设,看看是否能在这里实现节能。

在 Linux 内核中,这由运行时电源管理系统(runtime pm)管理。它与支持运行时 pm 的驱动程序配合工作,关闭未使用的设备,并在下次需要时唤醒它们。这是动态的,并且对用户空间应该是透明的。设备驱动程序负责实现硬件管理,通常包括关闭子系统的时钟(时钟门控),并在可能的情况下关闭核心电路。

运行时电源管理通过 sysfs 接口公开。每个设备都有一个名为 power 的子目录,其中包含以下文件:
- control :允许用户空间确定是否在此设备上使用运行时 pm。如果设置为 auto ,则启用运行时 pm;如果设置为 on ,则设备始终开启,不使用运行时 pm。
- runtime_enabled :报告运行时 pm 是否启用、禁用,如果 control 设置为 on ,则报告 forbidden
- runtime_status :报告设备的当前状态,可能是 active suspended unsupported
- autosuspend_delay_ms :设备暂停前的时间,-1 表示永远等待。如果暂停设备硬件的成本很高,一些驱动程序会实现此功能,以防止快速的暂停/恢复循环。

以 BeagleBone Black 上的 MMC 驱动为例:

# cd /sys/devices/platform/ocp/481d8000.mmc/
mmc_host/mmc1/mmc1:0001/power
# grep "" *
async:disabled
autosuspend_delay_ms:3000
control:auto
runtime_active_kids:0
runtime_active_time:5170
runtime_enabled:enabled
runtime_status:suspended
runtime_suspended_time:137560
runtime_usage:0

运行时 pm 已启用,设备当前处于暂停状态,最后一次使用后 3000 毫秒会再次暂停。读取设备的一个块后:

# dd if=/dev/mmcblk1p3 of=/dev/null count=1
1+0 records in
1+0 records out
# grep "" *
async:disabled
autosuspend_delay_ms:3000
control:auto
runtime_active_kids:0
runtime_active_time:7630
runtime_enabled:enabled
runtime_status:active
runtime_suspended_time:200680
runtime_usage:0

MMC 驱动变为活动状态,板卡的功率从 320 mW 增加到 500 mW。3 秒后再次暂停,功率恢复到 320 mW。

4. 系统睡眠

另一种电源管理技术是将整个系统置于睡眠模式,预计一段时间内不会再使用。在 Linux 内核中,这称为系统睡眠,通常由用户发起,例如用户决定让设备关闭一段时间。

在笔记本电脑领域,通常有两个选项:挂起(suspend)或休眠(hibernate)。
- 挂起(也称为挂起到 RAM):关闭除系统内存之外的所有设备,因此机器仍会消耗少量功率。系统唤醒时,内存保留所有先前的状态,笔记本电脑可以在几秒钟内恢复运行。
- 休眠:将内存内容保存到硬盘。系统完全不消耗功率,可以无限期保持此状态,但唤醒时需要一些时间从磁盘恢复内存。在嵌入式系统中很少使用休眠,主要是因为闪存存储的读写速度较慢,而且会干扰工作流程。

Linux 支持四种睡眠状态,与 ACPI S 状态的对应关系如下表所示:
| Linux 系统睡眠状态 | ACPI S 状态 | 描述 |
| — | — | — |
| freeze | [S0] | 停止(冻结)用户空间的所有活动,但 CPU 和内存正常运行。由于不运行用户空间代码而实现节能。ACPI 没有等效状态,S0 最接近,S0 是运行系统的状态。 |
| standby | S1 | 与 freeze 类似,但除了引导 CPU 外,将所有 CPU 离线。 |
| mem | S3 | 关闭系统电源,将内存置于自刷新模式,也称为挂起到 RAM。 |
| disk | S4 | 将内存保存到硬盘并关闭电源,也称为挂起到磁盘。 |

并非所有系统都支持所有状态。可以通过读取 /sys/power/state 文件来查看可用状态,例如:

# cat /sys/power/state 
freeze standby mem disk

要进入系统睡眠状态,只需将所需状态写入 /sys/power/state 。对于嵌入式设备,最常见的需求是使用 mem 选项挂起到 RAM,例如:

# echo mem > /sys/power/state 
[ 1646.158274] PM: Syncing filesystems ...done.
[ 1646.178387] Freezing user space processes ...(elapsed 0.001 seconds) done.
[ 1646.188098] Freezing remaining freezable tasks ... 
(elapsed 0.001 seconds) done.
[ 1646.197017] Suspending console(s) (use
no_console_suspend to debug)
[ 1646.338657] PM: suspend of devices complete
after 134.322 msecs
[ 1646.343428] PM: late suspend of devices
complete after 4.716 msecs
[ 1646.348234] PM: noirq suspend of devices
complete after 4.755 msecs
[ 1646.348251] Disabling non-boot CPUs ...
[ 1646.348264] PM: Successfully put all
powerdomains to target state

设备在不到一秒内断电,功率消耗降至 10 毫瓦以下。

5. 唤醒事件

在暂停设备之前,必须有唤醒它的方法。如果没有至少一个唤醒源,系统将拒绝暂停,并显示消息:“No sources enabled to wake-up! Sleep abort.”。这意味着即使在最深的睡眠状态下,系统的某些部分也必须保持通电,通常包括电源管理集成电路(PMIC)、实时时钟(RTC),可能还包括 GPIO、UART 和以太网等接口。

唤醒事件通过 sysfs 控制。 /sys/device 中的每个设备都有一个 power 子目录,其中包含一个 wakeup 文件,该文件包含以下字符串之一:
- enabled :此设备将生成唤醒事件
- disabled :此设备不会生成唤醒事件
- (空):此设备无法生成唤醒事件

要获取可以生成唤醒事件的设备列表,可以搜索 wakeup 文件中包含 enabled disabled 的所有设备:

$ find /sys/devices -name wakeup | xargs grep “abled”

在 BeagleBone Black 中,UART 是唤醒源,因此在控制台按下按键可以唤醒设备。

6. 实时时钟定时唤醒

大多数系统都有一个实时时钟(RTC),可以在未来 24 小时内生成闹钟中断。如果存在, /sys/class/rtc/rtc0 目录将存在,其中应包含 wakealarm 文件。向 wakealarm 写入一个数字将使其在该数字表示的秒数后生成闹钟。如果还启用了 RTC 的唤醒事件,它将唤醒暂停的设备。例如,在 30 秒后唤醒系统:

# cd /sys/devices/platform/pmic_rtc.1/rtc/rtc0
# echo “+30” > wakealarm
# echo “enabled” > power/wakeup
7. 进程与线程的选择

许多熟悉实时操作系统(RTOS)的嵌入式开发人员认为 Unix 进程模型很繁琐。另一方面,他们看到 RTOS 任务和 Linux 线程之间的相似性,并倾向于将现有的 RTOS 设计一对一映射到线程。

一个进程是一个内存地址空间和一个执行线程。地址空间是进程私有的,不同进程中的线程无法访问。进程运行时会分配资源,如栈空间、堆内存、文件引用等,进程终止时,这些资源会被系统回收。进程之间可以使用进程间通信(IPC),如本地套接字进行通信。

一个线程是进程内的执行线程。所有进程都从一个运行 main() 函数的主线程开始,可以使用 POSIX 函数 pthread_create(3) 创建额外的线程,多个线程在同一地址空间中执行。同一进程中的线程共享资源,可以读写相同的内存并使用相同的文件描述符,线程间通信相对容易,但需要注意同步和锁定问题。

假设有一个包含 40 个 RTOS 任务的系统要移植到 Linux,有两种极端设计:
- 将任务映射到进程,有 40 个独立的程序通过 IPC 通信,如通过套接字发送消息。这样可以大大减少内存损坏问题,因为每个进程中的主线程相互保护,并且每个进程退出后会清理资源。但进程间的消息接口很复杂,在一组进程紧密协作的情况下,消息数量可能很大,成为系统性能的限制因素。而且任何一个进程可能因错误而终止,其他 39 个进程需要处理邻居进程不再运行的情况并优雅恢复。
- 将任务映射到线程,将系统实现为一个包含 40 个线程的单进程。这样协作变得容易,因为它们共享相同的地址空间和文件描述符,发送消息的开销减少或消除,线程间的上下文切换比进程间更快。但引入了一个任务损坏另一个任务的堆或栈的可能性,如果任何一个线程遇到致命错误,整个进程将终止,调试复杂的多线程进程可能是一场噩梦。

结论是这两种设计都不理想,需要更深入地研究 API 以及进程和线程的行为来找到更好的方法。

8. 进程与线程的详细分析
8.1 进程的特点
  • 资源独立性 :进程拥有独立的内存地址空间,这意味着一个进程的内存操作不会影响到其他进程。例如,一个进程崩溃时,不会导致其他进程的数据丢失或运行异常。
  • 资源管理 :进程在运行过程中会分配各种资源,如内存、文件描述符等。当进程终止时,系统会自动回收这些资源,保证系统资源的有效利用。
  • 进程间通信 :进程之间可以通过多种方式进行通信,如管道、消息队列、共享内存、套接字等。这些通信方式各有优缺点,适用于不同的场景。例如,管道适用于父子进程之间的简单数据传输;消息队列适用于不同进程之间的异步通信;共享内存适用于需要大量数据共享的场景;套接字则适用于网络通信。
8.2 线程的特点
  • 资源共享性 :线程是进程内的执行单元,同一进程内的多个线程共享进程的资源,如内存、文件描述符等。这使得线程之间的通信更加高效,因为它们可以直接访问相同的内存区域。
  • 轻量级 :线程的创建和销毁开销相对较小,上下文切换也比进程快。这使得多线程程序在处理并发任务时具有更高的性能。
  • 同步问题 :由于线程共享资源,多个线程同时访问共享资源时可能会产生竞争条件,导致数据不一致。因此,在多线程编程中,需要使用同步机制,如互斥锁、信号量、条件变量等,来保证线程安全。
9. 调度策略

调度策略决定了操作系统如何分配 CPU 时间给不同的进程或线程。在 Linux 中,主要有两种调度策略:分时调度和实时调度。

9.1 分时调度
  • 原理 :分时调度是一种基于时间片的调度策略,每个进程或线程被分配一个固定的时间片,在该时间片内可以执行任务。当时间片用完后,操作系统会将 CPU 分配给下一个进程或线程。
  • 适用场景 :分时调度适用于大多数普通应用程序,如桌面应用、Web 服务器等。它可以保证每个进程或线程都有机会执行,避免某个进程或线程长时间占用 CPU。
9.2 实时调度
  • 原理 :实时调度是为了满足对时间要求严格的应用程序而设计的。实时任务具有较高的优先级,操作系统会优先分配 CPU 时间给实时任务,以保证其能够及时响应。
  • 适用场景 :实时调度适用于对时间敏感的应用程序,如工业控制、航空航天、医疗设备等。在这些应用中,任务的执行时间必须严格控制,否则可能会导致严重的后果。

以下是一个简单的 mermaid 流程图,展示了 Linux 系统中进程和线程的调度过程:

graph TD;
    A[系统启动] --> B[创建进程];
    B --> C{进程是否创建线程};
    C -- 是 --> D[创建线程];
    C -- 否 --> E[进程运行];
    D --> F[线程运行];
    E --> G{是否时间片用完};
    F --> H{是否时间片用完};
    G -- 是 --> I[调度到其他进程];
    H -- 是 --> J[调度到其他线程];
    G -- 否 --> E;
    H -- 否 --> F;
    I --> E;
    J --> F;
10. 总结

Linux 系统提供了丰富的电源管理和进程线程机制,以满足不同应用场景的需求。在电源管理方面,通过 CPUIdle 驱动、无滴答操作、外设断电、系统睡眠等技术,可以有效降低系统功耗。在进程线程方面,进程和线程各有优缺点,需要根据具体的应用场景选择合适的设计方案。同时,合理的调度策略可以提高系统的性能和响应速度。

在实际开发中,需要根据系统的需求和特点,综合考虑电源管理、进程线程设计和调度策略等因素,以实现高效、稳定的嵌入式系统。例如,在对功耗要求较高的应用中,可以启用无滴答操作和运行时电源管理;在对实时性要求较高的应用中,可以选择实时调度策略。

总之,深入理解 Linux 系统的电源管理和进程线程机制,对于开发高质量的嵌入式系统具有重要意义。

内容概要:本文围绕六自由度机械臂的人工神经网络(ANN)设计展开,重点研究了正向逆向运动学求解、正向动力学控制以及基于拉格朗日-欧拉法推导逆向动力学方程,并通过Matlab代码实现相关算法。文章结合理论推导仿真实践,利用人工神经网络对复杂的非线性关系进行建模逼近,提升机械臂运动控制的精度效率。同时涵盖了路径规划中的RRT算法B样条优化方法,形成从运动学到动力学再到轨迹优化的完整技术链条。; 适合人群:具备一定机器人学、自动控制理论基础,熟悉Matlab编程,从事智能控制、机器人控制、运动学六自由度机械臂ANN人工神经网络设计:正向逆向运动学求解、正向动力学控制、拉格朗日-欧拉法推导逆向动力学方程(Matlab代码实现)建模等相关方向的研究生、科研人员及工程技术人员。; 使用场景及目标:①掌握机械臂正/逆运动学的数学建模ANN求解方法;②理解拉格朗日-欧拉法在动力学建模中的应用;③实现基于神经网络的动力学补偿高精度轨迹跟踪控制;④结合RRTB样条完成平滑路径规划优化。; 阅读建议:建议读者结合Matlab代码动手实践,先从运动学建模入手,逐步深入动力学分析神经网络训练,注重理论推导仿真实验的结合,以充分理解机械臂控制系统的设计流程优化策略。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值