Linux进程冻结技术

作者:itrocker 发布于:2015-11-24 15:01 分类:电源管理子系统

1 什么是进程冻结

进程冻结技术(freezing of tasks)是指在系统hibernate或者suspend的时候,将用户进程和部分内核线程置于“可控”的暂停状态。

2 为什么需要冻结技术

假设没有冻结技术,进程可以在任意可调度的点暂停,而且直到cpu_down才会暂停并迁移。这会给系统带来很多问题:

(1)有可能破坏文件系统。在系统创建hibernate imagecpu down之间,如果有进程还在修改文件系统的内容,这将会导致系统恢复之后无法完全恢复文件系统;

(2)有可能导致创建hibernation image失败。创建hibernation image需要足够的内存空间,但是在这期间如果还有进程在申请内存,就可能导致创建失败;

(3)有可能干扰设备的suspendresume。在cpu down之前,device suspend期间,如果进程还在访问设备,尤其是访问竞争资源,就有可能引起设备suspend异常;

(4)有可能导致进程感知系统休眠。系统休眠的理想状态是所有任务对休眠过程无感知,睡醒之后全部自动恢复工作,但是有些进程,比如某个进程需要所有cpu online才能正常工作,如果进程不冻结,那么在休眠过程中将会工作异常。

3 代码实现框架

冻结的对象是内核中可以被调度执行的实体,包括用户进程、内核线程和work_queue。用户进程默认是可以被冻结的,借用信号处理机制实现;内核线程和work_queue默认是不能被冻结的,少数内核线程和work_queue在创建时指定了freezable标志,这些任务需要对freeze状态进行判断,当系统进入freezing时,主动暂停运行。

kernel threads可以通过调用kthread_freezable_should_stop来判断freezing状态,并主动调用__refrigerator进入冻结;work_queue通过判断max_active属性,如果max_active=0,则不能入队新的work,所有work延后执行。


标记系统freeze状态的有三个重要的全局变量:pm_freezingsystem_freezing_cntpm_nosig_freezing,如果全为0,表示系统未进入冻结;system_freezing_cnt>0表示系统进入冻结,pm_freezing=true表示冻结用户进程,pm_nosig_freezing=true表示冻结内核线程和workqueue。它们会在freeze_processesfreeze_kernel_threads中置位,在thaw_processesthaw_kernel_threads中清零。

fake_signal_wake_up函数巧妙的利用了信号处理机制,只设置任务的TIF_SIGPENDING位,但不传递任何信号,然后唤醒任务;这样任务在返回用户态时会进入信号处理流程,检查系统的freeze状态,并做相应处理。

任务主动调用try_to_freeze的代码如下:

static inline bool try_to_freeze_unsafe(void)
{
	if (likely(!freezing(current))) //检查系统是否处于freezing状态
		return false;
	return __refrigerator(false); //主动进入冻结
}

static inline bool freezing(struct task_struct *p)
{
	if (likely(!atomic_read(&system_freezing_cnt))) //系统总体进入freezing
		return false;
	return freezing_slow_path(p);
}

bool freezing_slow_path(struct task_struct *p)
{
	if (p->flags & PF_NOFREEZE)  //当前进程是否允许冻结
		return false;

	if (pm_nosig_freezing || cgroup_freezing(p))  //系统冻结kernel threads
		return true;

	if (pm_freezing && !(p->flags & PF_KTHREAD)) //系统冻结用户进程
		return true;

	return false;
}

进入冻结状态直到恢复的主要函数:

bool __refrigerator(bool check_kthr_stop)

{
...
	for (;;) {
		set_current_state(TASK_UNINTERRUPTIBLE);  //设置进程为UNINTERRUPTIBLE状态

		spin_lock_irq(&freezer_lock);
		current->flags |= PF_FROZEN;  //设置已冻结状态
		if (!freezing(current) ||
		    (check_kthr_stop && kthread_should_stop())) //判断系统是否还处于冻结
			current->flags &= ~PF_FROZEN;  //如果系统已解冻,则取消冻结状态
		spin_unlock_irq(&freezer_lock);

		if (!(current->flags & PF_FROZEN))  //如果已取消冻结,跳出循环,恢复执行
			break;
		was_frozen = true;
		schedule();
	}
......
}

4 参考文献

(1) http://www.wowotech.net/linux_kenrel/suspend_and_resume.html

(2) http://www.wowotech.net/linux_kenrel/std_str_func.html

(3) kenrel document: freezing-of-tasks.txt


标签: Linux freeze

评论:

rikeyone
2019-11-20 17:40
hibernate的流程是不是有点问题,我看到的代码是先执行freeze,然后在生成snapshot

error = freeze_processes();
if (error)
    goto Exit;

lock_device_hotplug();
/* Allocate memory management structures */
error = create_basic_memory_bitmaps();
if (error)
    goto Thaw;

error = hibernation_snapshot(hibernation_mode == HIBERNATION_PLATFORM);


否则,snapshot和 freeze之间是可能导致进程修改文件系统数据的
nowar
2019-08-27 14:52
有两个问题请帮忙解答,先行谢过
1)因为只有指明了freezable的内核线程才会进入freeze, 意思是在suspend后,其实系统中默认的内核线程原来处于running状态的依然在running状态吗?
2)那些本来就已经在uninterruptable 状态的用户线程是否也要响应freeze请求进入freeze状态呢?如果是的话,这些状态的线程没有机会来响应这个请求吧?
---------------------------------
static int try_to_freeze_tasks(bool user_only)
48                 do_each_thread(g, p) {   //这里应该遍历所有状态的线程,不管是否已经sleep了
49                         if (p == current || !freeze_task(p))
50                                 continue;
--------------------------
test
2018-06-01 15:52
从3.10的内核来看,内核线程的冻结这一块描述的不太准确,调查发现,对于内核线程的冻结,
一部分是忽略了绝大多数的内核线程,一部分是主动调用try_to_freeze()进行的冻结操作,
但是并没有调用kthread_freeable_should_stop.

请参考一下
https://www.kernel.org/doc/Documentation/power/freezing-of-tasks.txt
中的"III. Which kernel threads are freezable?"

相关的代码分析参考如下,

static int try_to_freeze_tasks(bool user_only)
48                 do_each_thread(g, p) {
49                         if (p == current || !freeze_task(p))
50                                 continue;
51
52                         if (!freezer_should_skip(p))
53                                 todo++;
54                 } while_each_thread(g, p);

在冻结用户空间的进程时,内核线程是直接忽略过去的
也就是freeze_task(p)返回的是false,

bool freeze_task(struct task_struct *p)
125         spin_lock_irqsave(&freezer_lock, flags);
126         if (!freezing(p) || frozen(p)) {
127                 spin_unlock_irqrestore(&freezer_lock, flags);
128                 return false;  
129         }      
因为查看freezing(),
34 static inline bool freezing(struct task_struct *p)
35 {
36         if (likely(!atomic_read(&system_freezing_cnt)))
37                 return false;
38         return freezing_slow_path(p);
39 }  
而freezing_slow_path的实现如下,
34 bool freezing_slow_path(struct task_struct *p)
35 {
36         if (p->flags & PF_NOFREEZE)
37                 return false;
38
39         if (pm_nosig_freezing || cgroup_freezing(p))
40                 return true;
41
42         if (pm_freezing && !(p->flags & PF_KTHREAD))
43                 return true;
44
45         return false;
46 }
对于绝大多数内核线程都是PF_NOFREEZE的.
而对于冻结用户空间进程的过程中,pm_freezing=true,所以对于内核线程,!(p->flags & PF_KTHREAD)=0,
所以freezing()返回false,进一步讲,freeze_task()返回false,所以在try_to_freeze_tasks()的循环中直接就continue了,没有让todo++,

而另一方面,对于冻结内核空间的进程而言,除去绝大多数的PF_NOFREEZE的内核线程以外,由于pm_nosig_freezing=true, freezing_slow_path()返回true,
freezing()返回true,
在freeze_task()中,
bool freeze_task(struct task_struct *p)
125         spin_lock_irqsave(&freezer_lock, flags);
126         if (!freezing(p) || frozen(p)) {
127                 spin_unlock_irqrestore(&freezer_lock, flags);
128                 return false;  
129         }      
需要等待 frozen(p)返回1,而进程的冻结就是通过进程自己主动调用try_to_freeze()来完成的.
典型的例子就是kswapd%d进程和file-storage进程.
Rafe
2018-04-09 18:28
@wowo : applicaton被freeze,然后系统resume回来,对于app来说,像是什么都没发生一样。
请问,application有方法知道,系统是resume回来的吗?
hzm
2018-01-11 22:17
@wowo:
你在下面回答的问题,我有个疑问
a.关键点在于:freeze task的过程是“请求freeze”,也就是说给要被freeze的thread发一个信号,接下来的freeze过程是thread自行处理的。
=>看了代码的流程,没看到有发信号的地方呀,能指出一下吗?
b.因此kthread_freezable_should_stop是被freeze thread自己调用的。
=>同样,没进程处理signal的时候有调用到这个函数,能一起把代码指出一下吗?

非常感谢~
hzm
2018-01-15 11:43
@hzm:看到了,应该是这里
2138int get_signal(struct ksignal *ksig)
2139{
2140    struct sighand_struct *sighand = current->sighand;
2141    struct signal_struct *signal = current->signal;
2142    int signr;
2143
2144    if (unlikely(current->task_works))
2145        task_work_run();
2146
2147    if (unlikely(uprobe_deny_signal()))
2148        return 0;
2149
2150    /*
2151     * Do this once, we can't return to user-mode if freezing() == T.
2152     * do_signal_stop() and ptrace_stop() do freezable_schedule() and
2153     * thus do not need another check after return.
2154     */
2155    try_to_freeze();
2156
wowo
2018-01-16 09:38
@hzm:找到就行了,最近太忙,没来得及回复,抱歉哈~~
wmzjzwlzs
2017-11-04 09:27
1.没有设置为可冻结状态的内核线程有可能在device suspend之后还在运行?
2.如果一个内核线程设置成可冻结的,是不是系统会等待这个线程主动冻结自己后才不调度它,如果这个线程就是不主动冻结自己,是不是系统就睡不下去了
wowo
2017-11-06 09:10
@wmzjzwlzs:是的。这个问题其实很好验证,了解了kernel的机制之后,你可以写一个不可冻结的应用程序,实际测试一下。
wmzjzwlzs
2017-11-06 15:23
@wowo:谢谢wowo大神,测试了下
1.没有设置为可冻结状态的内核线程确实在device suspend之后还在运行
2.我把一个内核线程设置成可冻结的,然后就是不主动冻结自己,系统确实就睡不下去了
wowo
2017-11-06 16:55
@wmzjzwlzs:实践是检验真理的唯一标准啊,赞!!!~~~
yinjian
2017-10-23 15:23
你好,对于内核线程freeze的过程,还有点疑问:执行完wake_up_state函数后,是如何跑到kthread_freezable_should_stop函数的? :)
wowo
2017-10-24 09:27
@yinjian:关键点在于:freeze task的过程是“请求freeze”,也就是说给要被freeze的thread发一个信号,接下来的freeze过程是thread自行处理的。
因此kthread_freezable_should_stop是被freeze thread自己调用的。
kernel中有对应的例子,你可以看看。
废柴
2017-04-01 15:15
@wo神,

有个疑惑,android6.0以后搞出来一个Doze模式,请问Doze和PM suspend有关系么,还是说Doze仅仅只是android层的一种低功耗模式,和PM suspend没毛线关系?

谢谢

废柴
wowo
2017-04-01 16:43
@废柴:Doze是应用层的节电行为,和kernel中的PM没有关系。
comlee
2017-02-18 14:41
@wowo
你好,请问一个弱弱的问题。
如果应用释放的wake_lock锁是系统中的最后一把wake_lock锁的话,那么系统会马上进入suspend吗?还是要系统中的所有应用不再跑或进入阻塞等状态才进suspend?
wowo
2017-02-18 16:11
@comlee:一般情况下,负责电源管理的进度或者线程,优先级应该是最低的。
另外,休眠的过程中,执行freeze的过程,也会给应用机会,所以休眠的时候,应该应该不跑了。
comlee
2017-02-19 12:33
@wowo:@wowo:我看到/proc/power/wake_unlock这个节点的内核实现会在unlock时check并进入suspend流程。另外,我自己写了两个应用进行测试:
1.    main
     {
         while(1)
           ;
     }
发现这个系统是可以进入suspend的。

但是
    main()
    {
         system("echo 'mylock'>/proc/power/wake_lock");
         while(1)
           ;
     }
跑这个的话,系统进不了suspend(两个测试程序分别都是在init rc里面启动)。

所以看上去系统不会等应用层的程序阻塞或休眠才suspend? 只要应用释放的是系统最后一把wake_lock就走suspend流程?
wowo
2017-02-19 21:18
@comlee:是的,wakeup lock的思路就是,假如你不想让系统睡,你就要持一个锁。既然你没有保持锁,系统就假设可以睡了。
heziq
2015-12-14 22:39
我比较关心那么没有freeze的kernel thread,在cpu down的时候,怎么处理的?resume的时候 ,它们又是从哪里开始的。
itrocker
2015-12-15 10:21
@heziq:这个点在文中第2节只是一笔带过。其实不只是kernel thread,如果没有freeze,所有的task都是在cpu down时从dying cpu的runqueue迁移到剩余active cpu的runqueue
resume时如果是STR,就直接从内存中的runqueue继续调度
C
2015-12-28 10:39
@itrocker:freeze跟cpu down没关系吧。有些涉及到磁盘操作的kernel thread是freezable的,因为freeze task最早提出是因为做磁盘的hibernation,比如kjournald,而workqueue则不再接收新的work item的queue请求。
C
2015-12-28 10:41
@C:sorry我没看到最初的问题,请无视上面的回答。
wmzjzwlzs
2017-11-04 09:26
@itrocker:1.没有设置为可冻结状态的内核线程有可能在device suspend之后还在运行?
2.如果一个内核线程设置成可冻结的,是不是系统会等待这个线程主动冻结自己后才不调度它,如果这个线程就是不主动冻结自己,是不是系统就睡不下去了

发表评论:

Copyright @ 2013-2015 蜗窝科技 All rights reserved. Powered by emlog