LTTng/Perf:用于退出 (sched_process_exit) 和释放 (sched_process_free) 进程的事件之间的差异
LTTng/Perf: Difference between events used for exiting (sched_process_exit) and freeing (sched_process_free) a process
目前,我正在讨论使用 LTTng 和 Perf 进行内核跟踪的主题。我对跟踪进程所处的不同状态特别感兴趣。
我偶然发现了事件 sched_process_free
和 sched_process_exit
。我想知道我现在的理解是否正确:
如果退出进程,sched_process_exit
将写入跟踪。但是,进程描述符可能仍在内存中,这会导致僵尸进程。当连接到进程的整个内存被释放时,调用 sched_process_free
。这意味着,如果我真的想确保进程完全“终止”并从内存中删除,我必须在跟踪中收听 sched_process_free
而不是 sched_process_exit
。这是正确的吗?
我找时间编辑我的答案以使其更清楚。如果还有问题,请告诉我,我们可以讨论清楚。让我们深入到任务的结尾:
有两个系统调用:exit_group()
和exit()
,它们都会转到do_exit()
,它会做以下事情。
- 设置
PF_EXTING
表示任务正在删除
- 通过
del_timer_sync()
从计时器中删除任务描述符
- 调用
exit_mm(), exit_sem(), __exit_fs()
和其他人来释放该任务的结构
- 呼叫perf_event_exit_task(tsk);
- 减少引用计数
- 将
exit_code
设置为_exit()/exit_group()
或错误
- 致电
exit_notify()
- 更新与 parent 和 child
的关系
- 检查
exit_signal
,发送SIGCHLD
- 如果任务未被跟踪或return值为-1,将exit_state设置为
EXIT_DEAD
,调用release_task()
回收其他内存并减少引用计数。
- 如果任务被跟踪,将exit_state设置为
EXIT_ZOMBIE
- 将任务标志设置为
PF_DEAD
- 致电
schedule()
我们需要僵尸状态因为parent可能需要使用那些文件描述符所以我们不能在第一时间删除所有的东西。 parent 任务将需要使用类似 wait()
的东西来检查 child 是否已死。在wait()
之后,release_task()
是僵尸完全释放的时间
- 减少所有者的任务数
- 如果任务被跟踪,从
ptrace_children
列表中删除
- 调用
__exit_signal()
删除所有未决信号并释放signal_struct描述符并exit_itimers()
删除所有定时器
- 调用
__exit_sighand()
删除信号处理程序
- 致电
__unhash_process()
nr_threads
--
- 调用
detach_pid()
从 PIDTYPE_PID
和 PIDTYPE_TGID
中删除任务描述符
- 调用
REMOVE_LINKS
从列表中删除任务
- 致电
sched_exit()
安排parent的时间片
- 调用
put_task-struct()
减少计数器,释放内存和任务描述符
- 呼叫delayed_put_task_struct()
所以,我们知道sched_process_exit
状态会在do_exit()中进行,但是我们不能确定进程是否被释放(可能调用release_task () 与否,这将触发 sched_process_free
)。这就是为什么我们需要两个性能事件点。
目前,我正在讨论使用 LTTng 和 Perf 进行内核跟踪的主题。我对跟踪进程所处的不同状态特别感兴趣。
我偶然发现了事件 sched_process_free
和 sched_process_exit
。我想知道我现在的理解是否正确:
如果退出进程,sched_process_exit
将写入跟踪。但是,进程描述符可能仍在内存中,这会导致僵尸进程。当连接到进程的整个内存被释放时,调用 sched_process_free
。这意味着,如果我真的想确保进程完全“终止”并从内存中删除,我必须在跟踪中收听 sched_process_free
而不是 sched_process_exit
。这是正确的吗?
我找时间编辑我的答案以使其更清楚。如果还有问题,请告诉我,我们可以讨论清楚。让我们深入到任务的结尾:
有两个系统调用:exit_group()
和exit()
,它们都会转到do_exit()
,它会做以下事情。
- 设置
PF_EXTING
表示任务正在删除 - 通过
del_timer_sync()
从计时器中删除任务描述符
- 调用
exit_mm(), exit_sem(), __exit_fs()
和其他人来释放该任务的结构 - 呼叫perf_event_exit_task(tsk);
- 减少引用计数
- 将
exit_code
设置为_exit()/exit_group()
或错误 - 致电
exit_notify()
- 更新与 parent 和 child 的关系
- 检查
exit_signal
,发送SIGCHLD
- 如果任务未被跟踪或return值为-1,将exit_state设置为
EXIT_DEAD
,调用release_task()
回收其他内存并减少引用计数。 - 如果任务被跟踪,将exit_state设置为
EXIT_ZOMBIE
- 将任务标志设置为
PF_DEAD
- 致电
schedule()
我们需要僵尸状态因为parent可能需要使用那些文件描述符所以我们不能在第一时间删除所有的东西。 parent 任务将需要使用类似 wait()
的东西来检查 child 是否已死。在wait()
之后,release_task()
- 减少所有者的任务数
- 如果任务被跟踪,从
ptrace_children
列表中删除 - 调用
__exit_signal()
删除所有未决信号并释放signal_struct描述符并exit_itimers()
删除所有定时器 - 调用
__exit_sighand()
删除信号处理程序 - 致电
__unhash_process()
nr_threads
--- 调用
detach_pid()
从PIDTYPE_PID
和PIDTYPE_TGID
中删除任务描述符
- 调用
REMOVE_LINKS
从列表中删除任务
- 致电
sched_exit()
安排parent的时间片 - 调用
put_task-struct()
减少计数器,释放内存和任务描述符 - 呼叫delayed_put_task_struct()
所以,我们知道sched_process_exit
状态会在do_exit()中进行,但是我们不能确定进程是否被释放(可能调用release_task () 与否,这将触发 sched_process_free
)。这就是为什么我们需要两个性能事件点。