USE AT YOUR OWN RISK 内容总结自源码阅读,请自行斟酌,小心编程。

thread_terminate_internal

NextComputers.org > Kernel Functions

GitHub> apple/darwin-xnu

> https://opensource.apple.com/source/xnu/xnu-4570.41.2/osfmk/kern/thread_act.c.auto.html

thread_terminate_internal
终结线程执行的内部方法
> osfmk/kern/thread_act.c

1. 锁定对线程的访问,防止其他操作产生竞争条件 `thread_mtx_lock(thread);`
2. 设置线程活跃状态标记为不活跃 `thread->active = FALSE;`
3. 标记线程需要执行AST_APC回调来处理abort条件 `act_abort(thread);`
4. 直接将线程设置为唤醒状态并设置线程结果为THREAD_INTERRUPTED `clear_wait(thread, THREAD_INTERRUPTED);`
5. 清除线程和处理器之间的绑定设置 `thread_affinity_terminate(thread);`
6. 解锁对线程的独占访问 `thread_mtx_unlock(thread);`
7. 等待线程结束 `thread_wait(thread, FALSE);`
8. 返回结果。

关于thread affinity
> 来自下面的代码注释
> osfmk/kern/affinity.h
用于识别线程之间缓存类同关联的一个对象(C struct),里面有`namespace`字段,推测只在同样namespace内的线程间起效。还包含线程集和处理器集等数据。

/*
 * An affinity set object represents a set of threads identified by the user
 * to be sharing (cache) affinity. A task may have multiple affinity sets
 * defined. Each set has dis-affinity other sets. Tasks related by inheritance
 * may share the same affinity set namespace.
 * Affinity sets are used to advise (hint) thread placement.
 */
act_abort
标记线程需要执行AST_APC回调来处理abort条件。
> osfmk/kern/thread_act.c

关于内核抢占
在执行内核代码时,如果时间到了,调度器可以重新调度,内核程序(例如system call)未完成时也可以执行上下文切换,让其他线程有执行机会。这需要硬件级支持,可以看到`disable_preemption()`实际上使用了汇编代码来设置处理器寄存器来改变其行为。

0. 告诉处理器不再接收中断 `splsched()`
1. 阻止线程调度。 `thread_lock(thread);`
    - #define thread_lock(th)                 simple_lock(&(th)->sched_lock, &thread_lck_grp)
    - #define	simple_lock(l)		disable_preemption()
2. (if)增加abort标记位 `thread->sched_flags |= TH_SFLAG_ABORT;`
3. (if)增加APC_AST标记,表示需要在下次上下文切换时或获得执行时间时执行相应的处理程序。`thread_set_apc_ast_locked(thread);`
4. (if)取消线程优先级抑制,重新设置线程优先级。`thread_depress_abort_locked(thread);`
5. 允许线程调度。
6. 告诉处理器开始接收中断。
splsched
> https://github.com/apple/darwin-xnu/blob/main/osfmk/kern/spl.h
> osfmk/kern/spl.h

这个代码最终调用了汇编指令`cli(Clear Interrupt Flag)`
> IA32 Assembly Language Reference Manual
> https://docs.oracle.com/cd/E19455-01/806-3773/instructionset-15/index.html

(spl for `schedule priority level`)?
(ml for `machine level`)?

typedef unsigned spl_t;
#define splsched()      (spl_t) ml_set_interrupts_enabled(FALSE)
> https://en.wikipedia.org/wiki/Asynchronous_System_Trap
> https://www.ired.team/offensive-security/code-injection-process-injection/apc-queue-code-injection
> https://docs.microsoft.com/en-gb/windows/win32/sync/asynchronous-procedure-calls

关于AST_APC

Asynchronous System Trap
`kern/ast.h: Definitions for Asynchronous System Traps.`
typedef uint32_t ast_t;

/*
 * A processor detects an AST when it is about to return from an
 * interrupt context, and calls ast_taken_kernel or ast_taken_user
 * depending on whether it was returning from userspace or kernelspace.
 *
 * Machine-dependent code is responsible for maintaining
 * a set of reasons for an AST.
 */

Asynchronous Procedure Call
An asynchronous procedure call (APC) is a function that executes asynchronously in the context of a particular thread. When an APC is queued to a thread, the system issues a software interrupt. The next time the thread is scheduled, it will run the APC function. An APC generated by the system is called a kernel-mode APC. An APC generated by an application is called a user-mode APC. A thread must be in an alertable state to run a user-mode APC.