android睡眠过程中对进程的处理
来源:互联网 发布:wex5 php服务器端 编辑:程序博客网 时间:2024/05/30 04:10
最近调试的时候碰到一个问题,每次设备进入睡眠的时候,有个进程总是会打印出-512的错误,经查这个是因为daemon被wake_up后,condition不满足但是daemon收到了signal导致的。
具体问题如下:
daemon通过系统调用ioctl进入内核空间,然后调用wait_event_interruptible函数让自身休眠,注意这个wait_event_interruptible是可以被signal打断的。
wait_event_interruptible的具体代码如下,其只是对__wait_event_interruptible的封装,所以直接看封装前的代码,如下:
#define __wait_event_interruptible(wq, condition, ret)\do {\DEFINE_WAIT(__wait);\\for (;;) {\prepare_to_wait(&wq, &__wait, TASK_INTERRUPTIBLE);\if (condition)\break;\if (!signal_pending(current)) {\schedule();\continue;\}\ret = -ERESTARTSYS;\break;\}\finish_wait(&wq, &__wait);\} while (0)
从上面就可以清楚的看到,
1.醒来后会去先检查condition,如果满足直接break去执行后续代码。
2.如果condition不满足则判断下当前进程获取的signal状态,如果没有signal处于pending状态,则通过schedule把自身调度出去进入睡眠。
3.如果进程此时有signa则后返回ERESTARTSYS,即512,也会break,进程醒来执行后续代码。
我当时碰到的问题就是,进程被wake_up,condition不满足,但是进程的标志位被置为TIF_SIGPENDING,导致这里的signal_pending条件为真返回的是-512,同时把进程唤醒了。
经过研究发现这里是和内核中电源管理中的freeze有关,下面分析。
睡眠的函数调用大致如下,网上有很多文章,这里只列出主要的函数调用。
state_store->pm_suspend->enter_state->suspend_prepare->suspend_freeze_processes。
当调用到suspend_freeze_processes时就是我们需要关心的了,这里面会做很多进程相关的操作。
函数源码如下:
[baseline\kernel\power\power.h]
static inline int suspend_freeze_processes(void){int error;error = freeze_processes();/* * freeze_processes() automatically thaws every task if freezing * fails. So we need not do anything extra upon error. */if (error)return error;error = freeze_kernel_threads();/* * freeze_kernel_threads() thaws only kernel threads upon freezing * failure. So we have to thaw the userspace tasks ourselves. */if (error)thaw_processes();return error;}
可以看见它针对普通进程和内核进程分别调用了对应函数来进行freeze操作。我们先看freeze_processes。
[baseline\kernel\power\process.c]
int freeze_processes(void){int error;error = __usermodehelper_disable(UMH_FREEZING);if (error)return error;if (!pm_freezing)atomic_inc(&system_freezing_cnt);printk("Freezing user space processes ... ");pm_freezing = true;error = try_to_freeze_tasks(true);if (!error) {printk("done.");__usermodehelper_set_disable_depth(UMH_DISABLED);oom_killer_disable();}printk("\n");BUG_ON(in_atomic());if (error)thaw_processes();return error;}
我们这边主要关心try_to_freeze_tasks函数,其它暂时不关心。
[baseline\kernel\power\power.c]
static int try_to_freeze_tasks(bool user_only){... ... while (true) { todo = 0; read_lock(&tasklist_lock); do_each_thread(g, p) { if (p == current || !freeze_task(p)) -->freeze_task continue; /* * Now that we've done set_freeze_flag, don't * perturb a task in TASK_STOPPED or TASK_TRACED. * It is "frozen enough". If the task does wake * up, it will immediately call try_to_freeze. * * Because freeze_task() goes through p's scheduler lock, it's * guaranteed that TASK_STOPPED/TRACED -> TASK_RUNNING * transition can't race with task state testing here. */ if (!task_is_stopped_or_traced(p) && !freezer_should_skip(p)) todo++; } while_each_thread(g, p); read_unlock(&tasklist_lock); if (!user_only) { wq_busy = freeze_workqueues_busy(); todo += wq_busy; } if (!todo || time_after(jiffies, end_time)) break; if (pm_wakeup_pending()) { wakeup = true; break; } /* * We need to retry, but first give the freezing tasks some * time to enter the refrigerator. Start with an initial * 1 ms sleep followed by exponential backoff until 8 ms. */ usleep_range(sleep_usecs / 2, sleep_usecs); if (sleep_usecs < 8 * USEC_PER_MSEC) sleep_usecs *= 2; } do_gettimeofday(&end); elapsed_msecs64 = timeval_to_ns(&end) - timeval_to_ns(&start); do_div(elapsed_msecs64, NSEC_PER_MSEC); elapsed_msecs = elapsed_msecs64; if (todo) { printk("\n"); printk(KERN_ERR "Freezing of tasks %s after %d.%03d seconds " "(%d tasks refusing to freeze, wq_busy=%d):\n", wakeup ? "aborted" : "failed", elapsed_msecs / 1000, elapsed_msecs % 1000, todo - wq_busy, wq_busy); if (!wakeup) { read_lock(&tasklist_lock); do_each_thread(g, p) { if (p != current && !freezer_should_skip(p) && freezing(p) && !frozen(p)) sched_show_task(p); } while_each_thread(g, p); read_unlock(&tasklist_lock); } } else { printk("(elapsed %d.%03d seconds) ", elapsed_msecs / 1000, elapsed_msecs % 1000); } return todo ? -EBUSY : 0;}函数首先是一个while循环,这个循环只能通过内部的break来跳出。我们先分析freeze_task。
[baseline\kernel\freezer.c]
bool freeze_task(struct task_struct *p){unsigned long flags;/* * This check can race with freezer_do_not_count, but worst case that * will result in an extra wakeup being sent to the task. It does not * race with freezer_count(), the barriers in freezer_count() and * freezer_should_skip() ensure that either freezer_count() sees * freezing == true in try_to_freeze() and freezes, or * freezer_should_skip() sees !PF_FREEZE_SKIP and freezes the task * normally. */if (freezer_should_skip(p))return false;spin_lock_irqsave(&freezer_lock, flags);if (!freezing(p) || frozen(p)) {spin_unlock_irqrestore(&freezer_lock, flags);return false;}if (!(p->flags & PF_KTHREAD)) {fake_signal_wake_up(p);/* * fake_signal_wake_up() goes through p's scheduler * lock and guarantees that TASK_STOPPED/TRACED -> * TASK_RUNNING transition can't race with task state * testing in try_to_freeze_tasks(). */} else {wake_up_state(p, TASK_INTERRUPTIBLE);}spin_unlock_irqrestore(&freezer_lock, flags);return true;}可以看见这个函数首先判断下当前进程处于什么状态,是应该skip还是frozen等,并且给相应flag置位。
然后判断p->flags,检测进程是普通进程还是内核进程。这里普通进程要比内核进程多一些处理,就是发送fake的signal
这里所谓的fake signal就是将进程中的标志位置为TIF_SIGPENDING,这样进程在返回用户空间时候检查标志位,如果有signal处于pending状态便调用do_notify_resume来处理以达到欺骗的目的,所以其实这里没有发送真实的signal。最终普通进程或者内核进程都会调用到try_to_wake_up来唤醒相应的进程。
freeze_task的目的就是先把进行设置为TIF_SIGPENDING,然后将其设置为TASK_RUNNING将其唤醒,然后我们再回到try_to_freeze_tasks接着往下看。
如果当前的进程不是TASK_STOP和TASK_TRACED,且没有被设置为PF_FREEZER_SKIP。也就是说当前的进程free
if (!task_is_stopped_or_traced(p) && !freezer_should_skip(p))todo++;
当前进程通过freeze机制进一步处理的,则todo会增加。
当所有进程都没扫描处理过后,函数退出do...while循环执行后续代码,我们这里的user_only为true,所以这里函数一次调用如下代码:
if (!todo || time_after(jiffies, end_time)) break;
在我碰到的情况中,这个todo是有数值的,所以这里time_after还是会执行计算下是否超时。
接着执行pm_wakeup_pending,用来判断当前的的power transition是否应该终止。正常情况下这里不应该终止,这个函数以后有空再分析。
if (pm_wakeup_pending()) {wakeup = true;break;}
接着执行如下代码:
usleep_range(sleep_usecs / 2, sleep_usecs);if (sleep_usecs < 8 * USEC_PER_MSEC)sleep_usecs *= 2;
到这里需要让出CPU,让本进程睡眠一下,以便刚才唤醒的进程有机会执行。当调度回来后还是在此while(true)循环中,这是个死循环,所以继续从头开始执行。
那么执行到什么时候结束呢?肯定是把所有该freeze的进程都freeze后才回退出,比如:最后一次进入while(true)后,如下这个if应该已经不会进入了。所以todo=false
if (!task_is_stopped_or_traced(p) && !freezer_should_skip(p))todo++;
todo是false之后,那么如下这个判断的break就会执行,这个while(true)就退出了。
if (!todo || time_after(jiffies, end_time))break;
退出while(true)后,后续代码:
if (todo) {printk("\n");printk(KERN_ERR "Freezing of tasks %s after %d.%03d seconds " "(%d tasks refusing to freeze, wq_busy=%d):\n", wakeup ? "aborted" : "failed", elapsed_msecs / 1000, elapsed_msecs % 1000, todo - wq_busy, wq_busy);if (!wakeup) {read_lock(&tasklist_lock);do_each_thread(g, p) {if (p != current && !freezer_should_skip(p) && freezing(p) && !frozen(p))sched_show_task(p);} while_each_thread(g, p);read_unlock(&tasklist_lock);}} else {printk("(elapsed %d.%03d seconds) ", elapsed_msecs / 1000,elapsed_msecs % 1000);}
此时正常情况下todo应该是0,所以执行else的语句。
到此我们freeze用户进程的流程分析完了,内核空间的过程也是类似的。
所以,freeze的过程就是1.将用户进程的flag设置为TIF_SIGPENDING,然后将其wake_up。2.wake_up内核空间workqueue和进程。
这里将用户空间进程的flag设置为TIF_SIGPENDING后,会将进程设置为TASK_UNINTERRUPTIABLE进入睡眠,我们在其他文章中再分析。
- android睡眠过程中对进程的处理
- Linux中进程的睡眠和唤醒
- 中断处理中不能睡眠的原因
- 中断处理中不能睡眠的原因
- android开发过程中,测试apk进程对设备内存占用的一般方法
- sql中睡眠的进程定时删除的方法
- android 睡眠和唤醒过程
- android 内核对S5PC110睡眠模式的支持
- Android -- Init进程对信号的处理流程
- Android -- Init进程对属性系统的处理流程分析
- 进程不能睡眠的情况
- Linux进程睡眠的原则
- 中断处理程序中不能出现睡眠代码的原因
- MFC对进程的处理
- 进程对信号的处理
- Android JNI中对String的处理
- Android中对ListView的优化处理
- android中对小数精度的处理
- String,StringBuffer与StringBuilder的区别
- Java程序员集合框架面试题(一)
- error: property's synthesized getter follows Cocoa naming convention for returning 'owned' objects
- PHP中$_SERVER的详细参数与说明
- 写给年轻程序员的一封信
- android睡眠过程中对进程的处理
- Android借助Application重写App的Crash(完整版)
- Android 如何隐藏应用程序的图标
- mfc 对话框应用程序 如何利用按钮弹出另一对话框
- Fragment生命周期
- 边缘检测算法
- 内存管理
- 无法打开SQL Server的连接
- 数据结构学习心得(二)-----线性表