php进程睡眠和唤醒,Linux进程的睡眠和唤醒简析

本文详细介绍了Linux操作系统中进程的睡眠和唤醒过程,包括就绪进程、可中断和不可中断睡眠状态。通过示例代码展示了进程如何进入睡眠并被唤醒。同时,文章讨论了无效唤醒问题,这是一个由于竞争条件导致的潜在问题,举例说明了在多进程操作共享数据时可能遇到的困境。
摘要由CSDN通过智能技术生成

1、Linux进程的睡眠和唤醒

在Linux中,仅等待CPU时间的进程称为就绪进程,它们被放置在一个运行队列中,一个就绪进程的状 态标志位为TASK_RUNNING。一旦一个运行中的进程时间片用完, Linux 内核的调度器会剥夺这个进程对CPU的控制权,并且从运行队列中选择一个合适的进程投入运行。

当然,一个进程也可以主动释放CPU的控制权。函数 schedule()是一个调度函数,它可以被一个进程主动调用,从而调度其它进程占用CPU。一旦这个主动放弃CPU的进程被重新调度占用 CPU,那么它将从上次停止执行的位置开始执行,也就是说它将从调用schedule()的下一行代码处开始执行。

有时候,进程需要等待直到某个特定的事件发生,例如设备初始化完成、I/O 操作完成或定时器到时等。在这种情况下,进程则必须从运行队列移出,加入到一个等待队列中,这个时候进程就进入了睡眠状态。

Linux 中的进程睡眠状态有两种:

一种是可中断的睡眠状态,其状态标志位TASK_INTERRUPTIBLE;

另一种是不可中断的睡眠状态,其状态标志位为TASK_UNINTERRUPTIBLE。可中断的睡眠状态的进程会睡眠直到某个条件变为真,比如说产生一个硬件中断、释放 进程正在等待的系统资源或是传递一个信号都可以是唤醒进程的条件。不可中断睡眠状态与可中断睡眠状态类似,但是它有一个例外,那就是把信号传递到这种睡眠 状态的进程不能改变它的状态,也就是说它不响应信号的唤醒。不可中断睡眠状态一般较少用到,但在一些特定情况下这种状态还是很有用的,比如说:进程必须等 待,不能被中断,直到某个特定的事件发生。

在现代的Linux操作系统中,进程一般都是用调用schedule()的方法进入睡眠状态的,下面的代码演示了如何让正在运行的进程进入睡眠状态。

sleeping_task = current;

set_current_state(TASK_INTERRUPTIBLE);

schedule();

func1();

/* Rest of the code ... */

在第一个语句中,程序存储了一份进程结构指针sleeping_task,current 是一个宏,它指向正在执行的进程结构。set_current_state()将该进程的状态从执行状态TASK_RUNNING 变成睡眠状态TASK_INTERRUPTIBLE。 如果schedule()是被一个状态为TASK_RUNNING 的进程调度,那么schedule()将调度另外一个进程占用CPU;如果schedule()是被一个状态为TASK_INTERRUPTIBLE 或TASK_UNINTERRUPTIBLE 的进程调度,那么还有一个附加的步骤将被执行:当前执行的进程在另外一个进程被调度之前会被从运行队列中移出,这将导致正在运行的那个进程进入睡眠,因为它已经不在运行队列中了。

我们可以使用下面的这个函数将刚才那个进入睡眠的进程唤醒。

wake_up_process(sleeping_task);

在调用了wake_up_process()以后,这个睡眠进程的状态会被设置为TASK_RUNNING,而且调度器会把它加入到运行队列中去。当然,这个进程只有在下次被调度器调度到的时候才能真正地投入运行。

2、无效唤醒

几乎在所有的情况下,进程都会在检查了某些条件之后,发现条件不满足才进入睡眠。可是有的时候进程却会在 判定条件为真后开始睡眠,如果这样的话进程就会无限期地休眠下去,这就是所谓的无效唤醒问题。在操作系统中,当多个进程都企图对共享数据进行某种处理,而 最后的结果又取决于进程运行的顺序时,就会发生竞争条件,这是操作系统中一个典型的问题,无效唤醒恰恰就是由于竞争条件导致的。

设想有两个进程A 和B,A 进程正在处理一个链表,它需要检查这个链表是否为空,如果不空就对链表里面的数据进行一些操作,同时B进程也在往这个链表添加节点。当这个链表是空的时候,由于无数据可操作,这时A进程就进入睡眠,当B进程向链表里面添加了节点之后它就唤醒A 进程,其代码如下:

A进程:

1 spin_lock(&list_lock);

2 if(list_empty(&list_head)) {

3 spin_unlock(&list_lock);

4 set_current_state(TASK_INTERRUPTIBLE);

5 schedule();

6 spin_lock(&list_lock);

7 }

8

9 /* Rest of the code ... */

10 spin_unlock(&list_lock);

21/212>

CANopen协议是一种应用于控制系统中的开放式网络通讯协议,CANopen协议栈是一种实现CANopen协议的软件库,而CanFestival是其中的一种开源实现。CanFestival协议栈提供了一套用于实现CANopen从站设备的软件工具,用户可以利用CanFestival来开发符合CANopen协议的从站设备。 CanFestival协议栈的实现主要由以下几个步骤构成:首先,用户需要配置CanFestival协议栈,包括设置从站设备的节点ID、通信速率等参数;然后,用户需要定义从站设备的对象字典,包括输入对象、输出对象、PDO对象等;接着,用户需要编写应用程序,根据需求实现从站设备的控制逻辑;最后,用户需要编译链接代码,并将生成的可执行文件下载到从站设备中运行。 CanFestival协议栈的源码主要包括以下几个模块:CO_Data模块负责管理CANopen通信过程中的数据;对于CAN总线的操作交由CO_CAN模块处理;NMT模块实现CANopen网络管理功能,负责从站设备的初始化和启动过程;SDO模块用于实现从站设备上的服务数据对象传输;PDO模块处理实时数据的传输;EMCY模块处理紧急事件报告;SYNC模块管理同步传输数据;TIME模块提供时间基准。 值得注意的是,CanFestival协议栈的实现需要根据具体的硬件平台和应用场景进行调整和修改,用户在使用时需要充分理解CANopen协议的原理和特点,结合具体需求进行定制和优化。 CanFestival协议栈的开源性质意味着用户可以根据需要对其进行二次开发和定制。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值