使当前正在执行的线程休眠(暂时停止执行)指定的毫秒数,取决于系统计时器和调度器的精度和准确性。线程不会失去任何监视器的所有权。

sleep(long millis) 仅仅调用 sleep 带两个参数版本的方法 sleep(long millis, int nanos),并将纳秒参数置为 0。

libcore/ojluni/src/main/java/java/lang/Thread.java

public
class Thread implements Runnable {......public static void sleep(long millis) throws InterruptedException {sleep(millis, 0);}......
}

sleep(long millis, int nanos) 内部首先判断 millis 和 nanos 这两个入参是否存在问题,最后调用 sleep(Object lock, long millis, int nanos) 这个 JNI 函数做实际的睡眠动作,之所以放到 while 循环中,是因为本地 Native 实现的 sleep(Object lock, long millis, int nanos) 方法实际上执行了一种特殊类型的等待,它可能会提前返回,因此循环直到睡眠时间结束。

libcore/ojluni/src/main/java/java/lang/Thread.java

public
class Thread implements Runnable {......@FastNativeprivate static native void sleep(Object lock, long millis, int nanos)throws InterruptedException;......public static void sleep(long millis, int nanos)throws InterruptedException {// BEGIN Android-changed: Improve exception messages./*if (millis < 0) {throw new IllegalArgumentException("timeout value is negative");}if (nanos < 0 || nanos > 999999) {throw new IllegalArgumentException("nanosecond timeout value out of range");}*/if (millis < 0) {throw new IllegalArgumentException("millis < 0: " + millis);}if (nanos < 0) {throw new IllegalArgumentException("nanos < 0: " + nanos);}if (nanos > 999999) {throw new IllegalArgumentException("nanos > 999999: " + nanos);}// END Android-changed: Improve exception messages.// BEGIN Android-changed: Implement sleep() methods using a shared native implementation.// Attempt nanosecond rather than millisecond accuracy for sleep();// RI code rounds to the nearest millisecond./*if (nanos >= 500000 || (nanos != 0 && millis == 0)) {millis++;}sleep(millis);*/// The JLS 3rd edition, section 17.9 says: "...sleep for zero// time...need not have observable effects."if (millis == 0 && nanos == 0) {// ...but we still have to handle being interrupted.if (Thread.interrupted()) {throw new InterruptedException();}return;}final int nanosPerMilli = 1000000;long start = System.nanoTime();long duration = (millis * nanosPerMilli) + nanos;Object lock = currentThread().lock;// The native sleep(...) method actually performs a special type of wait, which may return// early, so loop until sleep duration passes.synchronized (lock) {while (true) {sleep(lock, millis, nanos);long now = System.nanoTime();long elapsed = now - start;if (elapsed >= duration) {break;}duration -= elapsed;start = now;millis = duration / nanosPerMilli;nanos = (int) (duration % nanosPerMilli);}}// END Android-changed: Implement sleep() methods using a shared native implementation.}......
}

接下来进入 ART 虚拟机相关代码。sleep(Object lock, long millis, int nanos) 对应的 native 本地实现是 Thread_sleep(…)。

Thread_sleep(…) 内部重点调用 Monitor::Wait(…) 处理睡眠。

art/runtime/native/java_lang_Thread.cc

namespace art {......
static void Thread_sleep(JNIEnv* env, jclass, jobject java_lock, jlong ms, jint ns) {ScopedFastNativeObjectAccess soa(env);ObjPtr<mirror::Object> lock = soa.Decode<mirror::Object>(java_lock);Monitor::Wait(Thread::Current(), lock.Ptr(), ms, ns, true, kSleeping);
}
......
static JNINativeMethod gMethods[] = {......FAST_NATIVE_METHOD(Thread, sleep, "(Ljava/lang/Object;JI)V"),......
};void register_java_lang_Thread(JNIEnv* env) {REGISTER_NATIVE_METHODS("java/lang/Thread");
}}  // namespace art

跟着睡眠主线继续,这里的类型为 kSleeping,所以正常睡眠流程会进入 ConditionVariable TimedWait(…) 函数。

art/runtime/monitor.cc


void Monitor::Wait(Thread* self, int64_t ms, int32_t ns,bool interruptShouldThrow, ThreadState why) {......bool was_interrupted = false;bool timed_out = false;{......// 处理在调用 wait() 之前线程被中断的情况。if (self->IsInterrupted()) {was_interrupted = true;} else {// 等待通知或超时发生。if (why == kWaiting) {self->GetWaitConditionVariable()->Wait(self);} else {DCHECK(why == kTimedWaiting || why == kSleeping) << why;timed_out = self->GetWaitConditionVariable()->TimedWait(self, ms, ns);}was_interrupted = self->IsInterrupted();}}......
}
......
void Monitor::Wait(Thread* self,ObjPtr<mirror::Object> obj,int64_t ms,int32_t ns,bool interruptShouldThrow,ThreadState why) {......Monitor* mon = lock_word.FatLockMonitor();mon->Wait(self, ms, ns, interruptShouldThrow, why);
}

TimedWait(…) 内部实现根据 ART_USE_FUTEXES 去条件编译选择使用 futex 还是 pthread_cond_timedwait(…) 处理睡眠。这个宏定义在 art/runtime/base/mutex.h 中。它的具体值又是根据是否定义 __linux__ 宏去判断。在 Android OS 使用了 Linux 内核,这个宏是存在的。所以 Android OS 虚拟机内部睡眠是调用 futex 实现的。

futex (fast userspace mutex)快速用户空间互斥体,用来给上层应用构建更高级别的同步机制,是实现信号量和锁的基础。

bionic/libc/kernel/uapi/linux/futex.h 内定义了 FUTEX_WAIT_PRIVATE:#define FUTEX_WAIT_PRIVATE (FUTEX_WAIT | FUTEX_PRIVATE_FLAG)

art/runtime/base/mutex.cc

bool ConditionVariable::TimedWait(Thread* self, int64_t ms, int32_t ns) {......bool timed_out = false;......
#if ART_USE_FUTEXEStimespec rel_ts;InitTimeSpec(false, CLOCK_REALTIME, ms, ns, &rel_ts);......int32_t cur_sequence = sequence_.load(std::memory_order_relaxed);......if (futex(sequence_.Address(), FUTEX_WAIT_PRIVATE, cur_sequence, &rel_ts, nullptr, 0) != 0) {if (errno == ETIMEDOUT) {// Timed out we're done.timed_out = true;} else if ((errno == EAGAIN) || (errno == EINTR)) {// A signal or ConditionVariable::Signal/Broadcast has come in.} else {PLOG(FATAL) << "timed futex wait failed for " << name_;}}......
#else
#if !defined(__APPLE__)int clock = CLOCK_MONOTONIC;
#elseint clock = CLOCK_REALTIME;
#endif......timespec ts;InitTimeSpec(true, clock, ms, ns, &ts);int rc;while ((rc = pthread_cond_timedwait(&cond_, &guard_.mutex_, &ts)) == EINTR) {continue;}if (rc == ETIMEDOUT) {timed_out = true;} else if (rc != 0) {errno = rc;PLOG(FATAL) << "TimedWait failed for " << name_;}......
#endif......return timed_out;
}

由于 Android OS 定义了 __linux__ 宏,所以 ART_USE_FUTEXES 实际值为 1。

art/runtime/base/mutex.h

#if defined(__linux__)
#define ART_USE_FUTEXES 1
#else
#define ART_USE_FUTEXES 0
#endif

futex 在 ART 中是个内联函数,直接调用 syscall 实现。

art/runtime/base/mutex-inl.h

#if ART_USE_FUTEXES
static inline int futex(volatile int *uaddr, int op, int val, const struct timespec *timeout,volatile int *uaddr2, int val3) {return syscall(SYS_futex, uaddr, op, val, timeout, uaddr2, val3);
}
#endif  // ART_USE_FUTEXES

syscall 函数定义在 <unistd.h> 头文件中。

bionic/libc/include/unistd.h

......
long syscall(long __number, ...);
......

以 arm64 为例进行分析,这 syscall 函数实现在 arch-arm64/bionic/syscall.S 中。ENTRY(syscall) 和 END(syscall) 是两个宏定义。

  1. 从 x0 寄存器移动系统调用号到 x8 寄存器;
  2. 将系统调用参数从 x1 到 x6 移动到 x0 到 x5;
  3. 当用户空间通过系统调用陷入到内核空间的时候,通过 svc 指令生成一个指向异常级别1 (EL1)的异常,进入到内核空间;
  4. 检查 syscall 是否返回成功。

系统调用失败会在 x0 寄存器里面填入 -4095 ~ -1 这个区间内的出错值。MAX_ERRNO 被定义为 4095,MAX_ERRNO + 1 也就等于 4096,CMN <Xn|SP>, #{, } 指令用于比较负数,设置条件标志并丢弃结果:Rn + shift(imm),等价于:ADDS XZR, <Xn|SP>, # {, },Rd == ‘11111’。也就是 CMN 指令相加后只设置条件标志不保存结果,而 ADDS 会保存结果到结果寄存器中。CNEG , , 这条指令 为 hi(C == 1 && Z == 0)表示大于,Rd = if cond then -Rn else Rn,当系统调用出错的时候返回负值,执行 cmn 指令就会置位 C == 1 && Z == 0,所以当执行 cneg 指令时,满足 hi 条件就会将 x0 中的值取负数后写入,也就是变为了其绝对值。并且满足 b.hi (有条件地转移到 pc 相对偏移处的一个标签)分支跳转到 __set_errno_internal 标签出进一步处理。

如果系统调用成功则 x0 寄存器还是保存的之前的值,并且不会进入 __set_errno_internal 标签。

这里令人困惑的一点是 cmn 指令立即数的范围在手册上指出为无符号数 0~4095,然后超出 4095,比如 4096 运行并不报错而且是正常的。

Arm®v8 架构定义了四个异常级别,从 EL0 到 EL3,其中 EL3 是最高的异常级别,具有最多的执行特权。在接受异常时,异常级别可以增加或保持不变,而从异常返回时,异常级别可以减少或保持不变。

EL0  用户特权,用于运行普通用户程序。

EL1  系统特权,通常用于运行操作系统。

EL2  运行虚拟化扩展的虚拟监控程序(Hypervisor)。

EL3  运行安全世界中的安全监控器(Secure Monitor)。

bionic/libc/arch-arm64/bionic/syscall.S

#include <private/bionic_asm.h>ENTRY(syscall)/* Move syscall No. from x0 to x8 */mov     x8, x0/* Move syscall parameters from x1 thru x6 to x0 thru x5 */mov     x0, x1mov     x1, x2mov     x2, x3mov     x3, x4mov     x4, x5mov     x5, x6svc     #0/* check if syscall returned successfully */cmn     x0, #(MAX_ERRNO + 1)cneg    x0, x0, hib.hi    __set_errno_internalret
END(syscall)

ENTRY(f) 这个宏内部展开后又使用了 ENTRY_NO_DWARF(f) 宏,最后添加了 cfi_startproc 伪指令汇编指示符。

CFI 即 Call Frame Information,是 DWARF 2.0 定义的函数栈信息,DWARF 即 Debugging With Attributed Record Formats ,是一种调试信息格式。.cfi_ 开头的汇编指示符用来告诉汇编器生成相应的 DWARF 调试信息,主要是和函数有关。.cfi_startproc 定义函数开始,.cfi_endproc 定义函数结束。

ENTRY_NO_DWARF(f) 宏从名字看出来内部不包含 DWARF 相关信息。

.text; —— 表示代码段。

.globl f; —— 使得符号对链接器可见,变为对整个工程可用的全局变量,定义全局变量 f。

.balign __bionic_asm_align; —— 表征对齐,ARM64 里面对栈的操作是 16 字节对齐的,所以猜测 __bionic_asm_align 应该定义为 16。

.type f, __bionic_asm_function_type; —— 表示类型,__bionic_asm_function_type 展开后为 %function。

.size f, .-f; —— 该指令设置与符号名相关联的大小。以字节为单位的大小由表达式计算,表达式可以使用标签算法。此指令通常用于设置函数符号的大小。

__bionic_asm_align 和 __bionic_asm_function_type 定义在 bionic_asm_arm64.h 头文件,不同的 CPU 架构加载的头文件并不一样,具体由 __aarch64__、__arm__等这些宏是否定义确定。

__bionic_asm_custom_entry(f) 和 __bionic_asm_custom_end(f) 其实是个空定义。看样子是用来实现自定义的,但 bionic libc 其实并没有使用。

bionic/libc/private/bionic_asm.h

#define MAX_ERRNO 4095  /* For recognizing system call error returns. */#define __bionic_asm_custom_entry(f)
#define __bionic_asm_custom_end(f)
#define __bionic_asm_function_type @function#if defined(__aarch64__)
#include <private/bionic_asm_arm64.h>
#elif defined(__arm__)
#include <private/bionic_asm_arm.h>
#elif defined(__i386__)
#include <private/bionic_asm_x86.h>
#elif defined(__mips__)
#include <private/bionic_asm_mips.h>
#elif defined(__x86_64__)
#include <private/bionic_asm_x86_64.h>
#endif#define ENTRY_NO_DWARF(f) \.text; \.globl f; \.balign __bionic_asm_align; \.type f, __bionic_asm_function_type; \f: \__bionic_asm_custom_entry(f); \#define ENTRY(f) \ENTRY_NO_DWARF(f) \.cfi_startproc \#define END_NO_DWARF(f) \.size f, .-f; \__bionic_asm_custom_end(f) \#define END(f) \.cfi_endproc; \END_NO_DWARF(f) \

不难看出 __bionic_asm_align 定义为 16,如果 __bionic_asm_function_type 已经被定义了先取消,再宏定义为 %function。确实在 bionic_asm.h 先被定义为了 @function,这是不同的汇编语言使用的不同标记符。

bionic/libc/private/bionic_asm_arm64.h

#define __bionic_asm_align 16#undef __bionic_asm_function_type
#define __bionic_asm_function_type %function

内核代码基于 v4.19.111 分析。

系统调用切换到内核态后,首先会进入异常向量表处理。

el1_sync:当前处于内核态时,发生了指令执行异常、缺页中断(跳转地址或者取地址)。

el1_irq:当前处于内核态时,发生硬件中断。

el0_sync:当前处于用户态时,发生了指令执行异常、缺页中断(跳转地址或者取地址)、系统调用。

el0_iqr:当前处于用户态时,发生了硬件中断。

arch/arm64/kernel/entry.S

/** Exception vectors.*/.pushsection ".entry.text", "ax".align   11
ENTRY(vectors)kernel_ventry 1, sync_invalid         // Synchronous EL1tkernel_ventry    1, irq_invalid          // IRQ EL1tkernel_ventry    1, fiq_invalid          // FIQ EL1tkernel_ventry    1, error_invalid        // Error EL1tkernel_ventry  1, sync             // Synchronous EL1hkernel_ventry    1, irq              // IRQ EL1hkernel_ventry    1, fiq_invalid          // FIQ EL1hkernel_ventry    1, error            // Error EL1hkernel_ventry  0, sync             // Synchronous 64-bit EL0kernel_ventry  0, irq              // IRQ 64-bit EL0kernel_ventry  0, fiq_invalid          // FIQ 64-bit EL0kernel_ventry  0, error            // Error 64-bit EL0#ifdef CONFIG_COMPATkernel_ventry    0, sync_compat, 32      // Synchronous 32-bit EL0kernel_ventry  0, irq_compat, 32       // IRQ 32-bit EL0kernel_ventry  0, fiq_invalid_compat, 32   // FIQ 32-bit EL0kernel_ventry  0, error_compat, 32     // Error 32-bit EL0
#elsekernel_ventry  0, sync_invalid, 32     // Synchronous 32-bit EL0kernel_ventry  0, irq_invalid, 32      // IRQ 32-bit EL0kernel_ventry  0, fiq_invalid, 32      // FIQ 32-bit EL0kernel_ventry  0, error_invalid, 32        // Error 32-bit EL0
#endif
END(vectors)

用户态的系统调用会在 el0_sync 处理。

kernel_entry 0 用于保存用户态寄存器信息到内核栈。el0_sync 如果是发生了系统调用,调用 el0_svc。

arch/arm64/kernel/entry.S

/** EL0 mode handlers.*/.align   6
el0_sync:kernel_entry 0mrs  x25, esr_el1            // read the syndrome registerlsr    x24, x25, #ESR_ELx_EC_SHIFT // exception classcmp   x24, #ESR_ELx_EC_SVC64      // SVC in 64-bit stateb.eq  el0_svc......

el0_svc 转向 el0_svc_handler 进一步处理。

arch/arm64/kernel/entry.S

/** SVC handler.*/.align 6
el0_svc:mov x0, spbl    el0_svc_handlerb    ret_to_user
ENDPROC(el0_svc)

el0_svc_handler -> el0_svc_common -> invoke_syscall -> __invoke_syscall -> syscall_fn

核心流程为在系统调用表中查到对应的函数指针 syscall_fn_t,然后将 struct pt_regs * 指针作为入参调用它。

arch/arm64/kernel/syscall.c

static long __invoke_syscall(struct pt_regs *regs, syscall_fn_t syscall_fn)
{return syscall_fn(regs);
}static void invoke_syscall(struct pt_regs *regs, unsigned int scno,unsigned int sc_nr,const syscall_fn_t syscall_table[])
{long ret;if (scno < sc_nr) {syscall_fn_t syscall_fn;syscall_fn = syscall_table[array_index_nospec(scno, sc_nr)];ret = __invoke_syscall(regs, syscall_fn);} else {ret = do_ni_syscall(regs, scno);}regs->regs[0] = ret;
}
......
static void el0_svc_common(struct pt_regs *regs, int scno, int sc_nr,const syscall_fn_t syscall_table[])
{......invoke_syscall(regs, scno, sc_nr, syscall_table);......
}
......
asmlinkage void el0_svc_handler(struct pt_regs *regs)
{sve_user_discard();el0_svc_common(regs, regs->regs[8], __NR_syscalls, sys_call_table);
}

破解了系统调用表如何生成也就知道 futex 最终在内核中调用路径了。

[0 … __NR_syscalls - 1] 这种写法表示将数组中指定范围内的元素赋值,很明显 [0 … __NR_syscalls - 1] 中的所有元素赋值为了 __arm64_sys_ni_syscall,__arm64_sys_ni_syscall 内部仅仅调用了 sys_ni_syscall,它定义在 kernel/sys_ni.c 中。后面接着 include 了 asm/unistd.h 头文件,这里最终会调用到 __SYSCALL(nr, sym) [nr] = __arm64_##sym 宏,将单个 sys_call_table 中的元素进行了初始化。

arch/arm64/kernel/sys.c

asmlinkage long sys_ni_syscall(void);asmlinkage long __arm64_sys_ni_syscall(const struct pt_regs *__unused)
{return sys_ni_syscall();
}
......
#undef __SYSCALL
#define __SYSCALL(nr, sym)  asmlinkage long __arm64_##sym(const struct pt_regs *);
#include <asm/unistd.h>#undef __SYSCALL
#define __SYSCALL(nr, sym)  [nr] = __arm64_##sym,const syscall_fn_t sys_call_table[__NR_syscalls] = {[0 ... __NR_syscalls - 1] = __arm64_sys_ni_syscall,
#include <asm/unistd.h>
};

注释的含义很明确,未实现的系统调用在这里被重定向。ENOSYS 定义在 include/uapi/asm-generic/errno.h 中,其值为 38 表示无效的系统调用号。

#define ENOSYS 38 /* Invalid system call number */

kernel/sys_ni.c

/** Non-implemented system calls get redirected here.*/
asmlinkage long sys_ni_syscall(void)
{return -ENOSYS;
}

asm/unistd.h 头文件内仅仅根据是否定义了 __COMPAT_SYSCALL_NR 这个宏去判定 include uapi/asm/unistd.h 头文件与否。它和 arm64 兼容运行 arm32 程序有关。简单一点假设 __COMPAT_SYSCALL_NR 没定义。

arch/arm64/include/asm/unistd.h

#ifndef __COMPAT_SYSCALL_NR
#include <uapi/asm/unistd.h>
#endif

uapi/asm/unistd.h 头文件内又 include asm-generic/unistd.h 头文件。

arch/arm64/include/uapi/asm/unistd.h

......
#include <asm-generic/unistd.h>

asm-generic/unistd.h 头文件内部则进一步 include uapi/asm-generic/unistd.h 头文件。

include/asm-generic/unistd.h

#include <uapi/asm-generic/unistd.h>
......

同上,__SYSCALL_COMPAT 和兼容运行 arm32 程序有关,简单分析起见假设不兼容,那么 sys_call_table 中 futex 系统调用最终被初始化为指向 __arm64_sys_futex 的函数指针。

include/uapi/asm-generic/unistd.h

#ifdef __SYSCALL_COMPAT
#define __SC_COMP(_nr, _sys, _comp) __SYSCALL(_nr, _comp)
......
#else
#define __SC_COMP(_nr, _sys, _comp) __SYSCALL(_nr, _sys)
......
#endif
......
/* kernel/futex.c */
#define __NR_futex 98
__SC_COMP(__NR_futex, sys_futex, compat_sys_futex)
......

__arm64_sys_futex 定义隐藏的比较深,我们先来看 kernel/futex.c 代码找找线索。SYSCALL_DEFINE6 宏内部做了些必要的参数准备工作后,调用了 do_futex(…) 方法真正进入 futex 流程。SYSCALL_DEFINE6 宏定义在 syscalls.h 中。

kernel/futex.c

SYSCALL_DEFINE6(futex, u32 __user *, uaddr, int, op, u32, val,struct timespec __user *, utime, u32 __user *, uaddr2,u32, val3)
{struct timespec ts;ktime_t t, *tp = NULL;u32 val2 = 0;int cmd = op & FUTEX_CMD_MASK;if (utime && (cmd == FUTEX_WAIT || cmd == FUTEX_LOCK_PI ||cmd == FUTEX_WAIT_BITSET ||cmd == FUTEX_WAIT_REQUEUE_PI)) {if (unlikely(should_fail_futex(!(op & FUTEX_PRIVATE_FLAG))))return -EFAULT;if (copy_from_user(&ts, utime, sizeof(ts)) != 0)return -EFAULT;if (!timespec_valid(&ts))return -EINVAL;t = timespec_to_ktime(ts);if (cmd == FUTEX_WAIT)t = ktime_add_safe(ktime_get(), t);tp = &t;}/** requeue parameter in 'utime' if cmd == FUTEX_*_REQUEUE_*.* number of waiters to wake in 'utime' if cmd == FUTEX_WAKE_OP.*/if (cmd == FUTEX_REQUEUE || cmd == FUTEX_CMP_REQUEUE ||cmd == FUTEX_CMP_REQUEUE_PI || cmd == FUTEX_WAKE_OP)val2 = (u32) (unsigned long) utime;return do_futex(uaddr, op, val, tp, uaddr2, val2, val3);
}

下面的代码注释翻译一下:对于体系结构来说,重写 SYSCALL_DEFINE0() 和 __SYSCALL_DEFINEx() 宏的定义可能是有用的,特别是对系统调用使用不同的调用约定。为此,如果启用了 CONFIG_ARCH_HAS_SYSCALL_WRAPPER,下面的sys_*() 函数原型将不被包含。

include/linux/syscalls.h

#ifdef CONFIG_ARCH_HAS_SYSCALL_WRAPPER
/** It may be useful for an architecture to override the definitions of the* SYSCALL_DEFINE0() and __SYSCALL_DEFINEx() macros, in particular to use a* different calling convention for syscalls. To allow for that, the prototypes* for the sys_*() functions below will *not* be included if* CONFIG_ARCH_HAS_SYSCALL_WRAPPER is enabled.*/
#include <asm/syscall_wrapper.h>
#endif /* CONFIG_ARCH_HAS_SYSCALL_WRAPPER */

arm64 启用了 CONFIG_ARCH_HAS_SYSCALL_WRAPPER 宏。

arch/arm64/Kconfig

config ARM64def_bool y......select ARCH_HAS_SYSCALL_WRAPPER......helpARM 64-bit (AArch64) Linux support.

所以 syscalls.h 内部 include asm/syscall_wrapper.h 头文件。

也就是说 __SYSCALL_DEFINEx 宏展开后,我们找到了 __arm64_sys_futex 这个函数的定义,它内部调用了 __se_sys_futex,__se_sys_futex 内则调用了 __do_sys_futex。这里注意 __SYSCALL_DEFINEx 宏最后一行没有标点符号,意味着跟着的代码就是其实现,这段代码位于 kernel/futex.c SYSCALL_DEFINE6 宏展开后{......}内。

arch/arm64/include/asm/syscall_wrapper.h

#define __SYSCALL_DEFINEx(x, name, ...)                      \asmlinkage long __arm64_sys##name(const struct pt_regs *regs);     \ALLOW_ERROR_INJECTION(__arm64_sys##name, ERRNO);           \static long __se_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__));        \static inline long __do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)); \asmlinkage long __arm64_sys##name(const struct pt_regs *regs)      \{                                  \return __se_sys##name(SC_ARM64_REGS_TO_ARGS(x,__VA_ARGS__));   \}                                  \static long __se_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__))     \{                                  \long ret = __do_sys##name(__MAP(x,__SC_CAST,__VA_ARGS__));    \__MAP(x,__SC_TEST,__VA_ARGS__);                    \__PROTECT(x, ret,__MAP(x,__SC_ARGS,__VA_ARGS__));      \return ret;                            \}                                  \static inline long __do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__))

SYSCALL_DEFINE6 宏展开后使用了 SYSCALL_DEFINEx 这个宏,SYSCALL_DEFINEx 又使用了 __SYSCALL_DEFINEx 宏。这里的 6 代表系统调用有 6 个参数。

include/linux/syscalls.h

#define SYSCALL_DEFINE6(name, ...) SYSCALL_DEFINEx(6, _##name, __VA_ARGS__)
......
#define SYSCALL_DEFINEx(x, sname, ...)              \SYSCALL_METADATA(sname, x, __VA_ARGS__)            \__SYSCALL_DEFINEx(x, sname, __VA_ARGS__)

op 被设置为 FUTEX_WAIT | FUTEX_PRIVATE_FLAG,所以最终会调用 futex_wait 进一步处理,调用前将 val3 设置为了 FUTEX_BITSET_MATCH_ANY。

kernel/futex.c

long do_futex(u32 __user *uaddr, int op, u32 val, ktime_t *timeout,u32 __user *uaddr2, u32 val2, u32 val3)
{int cmd = op & FUTEX_CMD_MASK;unsigned int flags = 0;if (!(op & FUTEX_PRIVATE_FLAG))flags |= FLAGS_SHARED;......switch (cmd) {case FUTEX_WAIT:val3 = FUTEX_BITSET_MATCH_ANY;/* fall through */case FUTEX_WAIT_BITSET:return futex_wait(uaddr, flags, val, timeout, val3);......}return -ENOSYS;
}
  1. 设置高分辨率内核计时器;
  2. 准备等待 uaddr。如果成功,则持有 hb 锁并使 q.key refs 递增;
  3. queue_me 并等待唤醒、超时或信号;
  4. 如果被唤醒(而且没有排队)就成功了, unqueue_me 并删除 q.key ref;
  5. 销毁定时器。

kernel/futex.c

static int futex_wait(u32 __user *uaddr, unsigned int flags, u32 val,ktime_t *abs_time, u32 bitset)
{struct hrtimer_sleeper timeout, *to = NULL;struct restart_block *restart;struct futex_hash_bucket *hb;struct futex_q q = futex_q_init;int ret;if (!bitset)return -EINVAL;q.bitset = bitset;if (abs_time) {to = &timeout;hrtimer_init_on_stack(&to->timer, (flags & FLAGS_CLOCKRT) ?CLOCK_REALTIME : CLOCK_MONOTONIC,HRTIMER_MODE_ABS);hrtimer_init_sleeper(to, current);hrtimer_set_expires_range_ns(&to->timer, *abs_time,current->timer_slack_ns);}retry:/** Prepare to wait on uaddr. On success, holds hb lock and increments* q.key refs.*/ret = futex_wait_setup(uaddr, val, flags, &q, &hb);if (ret)goto out;/* queue_me and wait for wakeup, timeout, or a signal. */futex_wait_queue_me(hb, &q, to);/* If we were woken (and unqueued), we succeeded, whatever. */ret = 0;/* unqueue_me() drops q.key ref */if (!unqueue_me(&q))goto out;ret = -ETIMEDOUT;if (to && !to->task)goto out;/** We expect signal_pending(current), but we might be the* victim of a spurious wakeup as well.*/if (!signal_pending(current))goto retry;ret = -ERESTARTSYS;if (!abs_time)goto out;restart = &current->restart_block;restart->fn = futex_wait_restart;restart->futex.uaddr = uaddr;restart->futex.val = val;restart->futex.time = *abs_time;restart->futex.bitset = bitset;restart->futex.flags = flags | FLAGS_HAS_TIMEOUT;ret = -ERESTART_RESTARTBLOCK;out:if (to) {hrtimer_cancel(&to->timer);destroy_hrtimer_on_stack(&to->timer);}return ret;
}
  1. 当前进程状态置为 TASK_INTERRUPTIBLE,保证在另一个进程唤醒它之前设置。set_current_state() 是使用 smp_store_mb() 实现的,queue_me() 在完成时调用 spin_unlock(),两者都序列化了对哈希列表的访问,并强制设置另一个内存屏障。
  2. 调用 hrtimer_start_expires 开启定时器。
  3. 如果我们已从散列表中删除,那么另一个任务将试图唤醒我们,我们可以跳过对 schedule() 的调用。如果计时器已经过期,当前将被标记为重新调度。只有在没有超时或尚未过期的情况下才调用 schedule(freezable_schedule())。
  4. 设置当前进程状态为 TASK_RUNNING。

kernel/futex.c

static void futex_wait_queue_me(struct futex_hash_bucket *hb, struct futex_q *q,struct hrtimer_sleeper *timeout)
{/** The task state is guaranteed to be set before another task can* wake it. set_current_state() is implemented using smp_store_mb() and* queue_me() calls spin_unlock() upon completion, both serializing* access to the hash list and forcing another memory barrier.*/set_current_state(TASK_INTERRUPTIBLE);queue_me(q, hb);/* Arm the timer */if (timeout)hrtimer_start_expires(&timeout->timer, HRTIMER_MODE_ABS);/** If we have been removed from the hash list, then another task* has tried to wake us, and we can skip the call to schedule().*/if (likely(!plist_node_empty(&q->list))) {/** If the timer has already expired, current will already be* flagged for rescheduling. Only call schedule if there* is no timeout, or if it has yet to expire.*/if (!timeout || timeout->task)freezable_schedule();}__set_current_state(TASK_RUNNING);
}

freezable_schedule() 定义在 freezer.h 中。不管 CONFIG_FREEZER 宏定义与否最终都调用了 schedule() 进行进程调度,所以从 java 层下来的 sleep 最终会发生系统调度,切换到其他进程运行。

include/linux/freezer.h

#ifdef CONFIG_FREEZER
.......
/** These functions are intended to be used whenever you want allow a sleeping* task to be frozen. Note that neither return any clear indication of* whether a freeze event happened while in this function.*//* Like schedule(), but should not block the freezer. */
static inline void freezable_schedule(void)
{freezer_do_not_count();schedule();freezer_count();
}
......
#else /* !CONFIG_FREEZER */
......
#define freezable_schedule()  schedule()
......
#endif /* !CONFIG_FREEZER */

结论:java 层的睡眠并不是直接调用系统调用 nanosleep 实现,而是使用 futex 机制实现的!

从 Java sleep 来看 arm64 Linux 内核都干了些什么?相关推荐

  1. ARM64 Linux 内核页表的块映射

    作者 | 宋宝华  责编 | 张文 头图 | CSDN 下载自视觉中国 出品 | CSDN(ID:CSDNnews) 内核文档 Documentation/arm64/memory.rst 描述了 A ...

  2. QEMU启动ARM64 Linux内核

    目录 前言 前置知识 virt开发板 ARM处理器家族简介 安装qemu-system-aarch64 安装交叉编译工具 交叉编译ARM64 Linux内核 交叉编译ARM64 Busybox 使用b ...

  3. ARM DS-5单步调试ARM64 linux 内核

    目录 1 介绍 2 开发环境 3 准备工作 3.1 Ubuntu环境准备 3.2 源代码准备 3.3 DS-5准备 3.4 使用DS-5调试源码 3.4.1 建立源码工程 3.4.2 创建debug配 ...

  4. 读书:哲学家们都干了些什么

    哲学本质上是人理解人.人认识人的理性活动,是对世界基本和普遍之问题研究的学科,是关于世界观的理论体系.--百度百科 这本书应该是想用不那么严肃的方式串起整个哲学史,可能有一些地方不那么详实和严谨,但对 ...

  5. 进华为半年我都干了些啥

    楼主目前在华为云搞Java开发,22年8月份入职的.和大家分享下入职半年我都干了些什么事情. 主要分为两个方面. 第一个是业务,华为云是微服务架构,楼主是Java开发和Python开发,进入了其中一个 ...

  6. 【一周读书】哲学家,你们都干了些什么?

    书籍:<哲学家们都干了些什么> 在读这本书之前,我对哲学的印象是这样的: 哲学似乎和宗教有点关系?似乎在解决人的精神痛苦方面的问题?哲学就是一大堆难懂并且无用的理论!我要是和同学谈哲学肯定 ...

  7. 什么是哲学?《哲学家们都干了些什么?》读后感

    <哲学家们都干了些什么?> 前言 自从听到哲学这两个字开始,其实多年来心中有个问题,对,什么是哲学,哲学有什么用,直到我读了,<哲学家们都干了些什么?>林欣浩,林欣浩 并不是什 ...

  8. 【哲学问题】-《哲学家们都干了些什么?》

    引言 本文来源于<哲学家们都干了些什么?>这本书以及本人阅读此书之后的一些想法.这本书虽通熟易懂,但贯穿了哲学所涉及的大部分内容,我将挑出书中涉及的比较重要的内容并融入我的部分思考分几次来 ...

  9. 西西弗的石头----读《哲学家都干了些什么》有感

    <西西弗的神话>里讲述了一个希腊神话,说西西弗被众神惩罚,把一个巨石推向山顶.但石头一到山顶又会自己滚下来,西西弗必须重复这样的苦役,直到永远.加缪用这个例子来说明我们生活的荒谬. 西西弗 ...

最新文章

  1. 埃森哲、亚马逊和万事达卡抱团推出的区块链项目有何神通?
  2. 七十七、React中的propTypes,defaultProps和生命周期函数
  3. php内存映射,如何用ZwMapViewOfSection将Driver分配的内存映射到App空间?
  4. oracle快速插入大量数据
  5. 自定义有多个按钮节点的SliderView
  6. @程序员,这 TOP 11 物联网云平台速码!
  7. 多多客DOODOOKE 1.x升级2.x指南
  8. java io流不关闭_Java IO流关闭问题的深入研究
  9. 教你快速填充Excel中不同的数据,别再一个个向下拉动啦
  10. 老男孩python课程_老男孩python课程
  11. Photoshop水平线快捷键怎么使用的?
  12. WPS word解决公式上浮的问题
  13. Windows 7下 IE升级到,IE 11的F12控制台不能使用的解决
  14. 一台电脑寿命一般几年?
  15. 记录win10安装多个版本cuda与cudnn+切换使用+发现的一些有趣现象
  16. 最新的全球78707个主要城市数据库,包含经纬度坐标值国家省份
  17. b站直播html5黑屏,用bilibili直播姬的抓屏为什么是黑屏而不是界面呢
  18. Android RxJava 基本用法
  19. 单片机ch2o程序_基于单片机的甲醛浓度检测仪的设计_李娟娟
  20. oracle no privileges on tablespace 'USERS

热门文章

  1. AI语音定制化,将给2020带来三个可能
  2. 流量卡之家:运营商7月份运营数据 中国电信移动用户首超中国联通
  3. 【C语言】密码验证的详细解读
  4. 计算机图形学笔记五:光栅化(消隐算法)和 前几节内容总结
  5. 西部数据(WD)固件级数据恢复案例
  6. hgame-2022-week4
  7. Centos修改静态IP
  8. IEEE ICME 2023论文|基于预训练和图网络的语音主题分类
  9. 【养生保健:脂肪肝发生前的七种征兆】
  10. 【敬伟ps教程】色彩基础