深度睡眠/浅睡眠
在Linux系统中,进程有两种睡眠状态:深度睡眠和浅度睡眠。其中:深度睡眠只能被资源所唤醒,浅度睡眠可以被资源和signal
所唤醒。
深度睡眠有时不可避免,例如发生page fault
时:在执行某个程序时,若某些代码段还没有进内存,在我们调用到某个函数时,可能会发生缺页中断。此时Linux内核将把这个进程置为深度睡眠。
若上述情况不使用深度睡眠,也就是说若在发生page fault
后该进程可被signal
唤醒,那我们可以设想这样一种情况:在进程发生缺页中断后进入睡眠状态,此时该进程收到某个信号A,于是该进程需要执行信号A的信号处理函数,若该信号处理函数此时也不在内存内,则又将发生缺页中断并进入睡眠态,若此时该进程又收到信号A,那就进程将一直在唤醒->缺页->睡眠
这个循环中反复。因此在Linux内核中,深度睡眠不可避免。
以下是一段模拟进程在等待资源时进入深度睡眠的代码:
static ssize_t globalfifo_read(struct file *filp, char __user *buf,
size_t count, loff_t *ppos)
{
int ret;
struct globalfifo_dev *dev = container_of(filp->private_data,
struct globalfifo_dev, miscdev);
DECLARE_WAITQUEUE(wait, current);
mutex_lock(&dev->mutex);
add_wait_queue(&dev->r_wait, &wait);
while (dev->current_len == 0) {
//进入次循环表示暂时没有可读资源,需要等待
//若设置了非阻塞,则跳出循环
if (filp->f_flags & O_NONBLOCK) {
ret = -EAGAIN;
goto out;
}
//将进程的状态置为TASK_INTERUPTIBLE
//若设置为TASK_UNINTERRUPTIBLE,状态将变为深度睡眠
__set_current_state(TASK_INTERRUPTIBLE);
mutex_unlock(&dev->mutex);
//放弃cpu
schedule();
//若是被信号唤醒,直接goto跳出循环
if (signal_pending(current)) {
ret = -ERESTARTSYS;
goto out2;
}
//若不是被信号唤醒,则执行任务
mutex_lock(&dev->mutex);
}
if (count > dev->current_len)
count = dev->current_len;
if (copy_to_user(buf, dev->mem, count)) {
ret = -EFAULT;
goto out;
} else {
memcpy(dev->mem, dev->mem + count, dev->current_len - count);
dev->current_len -= count;
printk(KERN_INFO "read %d bytes(s),current_len:%d\n", count,
dev->current_len);
wake_up_interruptible(&dev->w_wait);
ret = count;
}
out:
mutex_unlock(&dev->mutex);
out2:
remove_wait_queue(&dev->r_wait, &wait);
set_current_state(TASK_RUNNING);
return ret;
}
进程0和进程1
在Linux,每一个task_struct
都是被父进程创建出来的。那么此处就存在一个问题:Linux系统中的进程数的根init
进程是如何被创建出来的呢?
init
进程也可以被称为Linux中的进程1,它是由Linux中的进程0所创建的。我们可以在/proc
中查看Linux中进程1的父进程:
可以看到进程1的PPid
字段显示的是0
。这说明进程1的父进程实际上是Linux系统中的进程0。而进程0是在Linux系统开机启动的过程中所创建的进程。
但是我们在pstree
中看不到Linux的进程0(注意,init
进程为进程1),因为进程0在创建进程1之后就退化成了Linux系统中的IDLE
进程。IDLE
进程是Linux系统中的一个特殊调度类:当所有的进程都睡眠或停止时,CPU才会调用IDLE
进程,同时CPU将被切换为低功耗模式。所以当进程0被调度时,CPU将会被置为Wait For Interrupter或者更深的睡眠状态。在Wait For Interrupter状态中时,CPU只有在接收到一个中断才会被唤醒,否则运行功耗十分低,并且一旦产生一个中断,就可能会唤醒另一个进程。
这种设计的好处之一在于有效避免了在进程睡眠或等待时的判断,使得每个进程在进入睡眠状态之前不需要考虑自己是否是最后一个可调度的进程而把CPU置为WFI状态。