当前位置: 首页 > article >正文

深入解析 OpenJDK 17 在 Linux 上的线程创建机制

在现代高性能 Java 应用中线程管理是 JVM 的核心功能之一。Java 线程的创建和调度最终依赖于底层操作系统的线程实现。在 Linux 系统上JVM 线程创建涉及 POSIX 线程pthread接口以及 Linux 内核的clone/clone3系统调用。本文将结合 OpenJDK 17 的源码和 Linux 6.8.12 内核的线程创建机制深入解析 Java 线程的创建过程。1. JVM 层的线程创建入口在 OpenJDK 中Java 线程最终对应一个Thread对象而 JVM 内部为每个线程维护一个OSThread对象它封装了操作系统层面的线程信息。os::create_thread是 JVM 在 Linux/Unix 系统上创建线程的主要入口bool os::create_thread(Thread* thread, ThreadType thr_type, size_t req_stack_size);核心流程可以总结为分配 OSThread 对象OSThread* osthread new OSThread(NULL, NULL);thread-set_osthread(osthread);osthread-set_state(ALLOCATED);JVM 先创建一个内部线程对象OSThread初始状态为ALLOCATED。初始化线程属性pthread_attr_tpthread_attr_init(attr);pthread_attr_setdetachstate(attr, PTHREAD_CREATE_DETACHED);使用 POSIXpthreadAPI 设置线程为“分离状态”避免线程结束后资源泄露。设置线程栈大小size_t stack_size os::Posix::get_initial_stack_size(thr_type, req_stack_size);if (stack_size 4096 * K) stack_size 64 * K;pthread_attr_setstacksize(attr, stack_size);JVM 对线程栈大小做了额外的调整避免小栈导致潜在的pthread库问题。创建线程pthread_create(tid, attr, (void* (*)(void*)) thread_native_entry, thread);JVM 最终调用pthread_create创建线程。线程启动函数为thread_native_entry它会完成 JVM 内部线程初始化并调用 Java 层线程执行函数。在 AIX 系统上JVM 会额外设置线程范围和调度继承pthread_attr_setscope(attr, PTHREAD_SCOPE_SYSTEM);pthread_attr_setinheritsched(attr, PTHREAD_EXPLICIT_SCHED);2. Linux 内核层线程创建clone3系统调用Linux 内核提供了低级线程创建接口clone和clone3允许用户态线程共享进程资源如地址空间、文件描述符、信号处理等。clone3是clone的增强版支持更多参数和更灵活的 TLS 设置。2.1clone3的包装函数在 GNU C Library (glibc) 中__clone3封装了系统调用extern int __clone3 (struct clone_args *__cl_args, size_t __size, int (*__func) (void *__arg), void *__arg);Linux 内核期望参数通过寄存器传递而用户态封装函数会把线程入口函数和参数传入内核movl $SYS_ify(clone3), %eax syscall内核会创建新线程执行指定函数func(arg)并在线程退出时清理 TID 等信息。2.2create_thread内部逻辑glibc 的create_thread会构建clone_args并设置线程标志const int clone_flags (CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SYSVSEM | CLONE_SIGHAND | CLONE_THREAD | CLONE_SETTLS | CLONE_PARENT_SETTID | CLONE_CHILD_CLEARTID);CLONE_VM共享内存空间CLONE_FILES共享文件描述符CLONE_THREAD线程属于同一线程组CLONE_SETTLS设置 TLS线程局部存储CLONE_PARENT_SETTID/CLONE_CHILD_CLEARTID父子线程 TID 维护最终调用int ret __clone_internal (args, start_thread, pd);新线程启动后会执行start_thread初始化线程控制块TCB、信号掩码、调度策略等。3. JVM 与 Linux 内核线程创建的联系结合 JVM 和 Linux 内核源码我们可以得到线程创建的完整流程JVM 分配 OSThread 对象设置线程类型、初始状态。JVM 设置 pthread 属性包括分离状态、栈大小、守护页等。JVM 调用pthread_create或clone3对应 glibc 内部函数__pthread_create_2_1→create_thread→__clone_internal。内核根据clone_args创建线程设置 TLS、TID、共享资源。新线程执行thread_native_entry/start_thread初始化 JVM 层线程对象设置调度策略、信号掩码调用 Java 层run()方法这一流程保证了 Java 线程与操作系统线程一一对应1:1 模型同时提供了 JVM 级别的线程管理功能如垃圾回收线程、守护线程等。4. JVM 特殊处理与优化线程栈大小调整为规避小栈导致的pthread_attr_setstacksize问题JVM 对小于 4MB 的线程栈增加了 64KB 额外空间。守护页优化对于 Java 线程JVM 禁用 OS 层守护页因为 JVM 已经提供了虚拟机层的保护页减少内存浪费。调度与 CPU 亲和性JVM 可以通过pthread_attr_t或内部属性设置线程调度策略、优先级以及 CPU 亲和性。5. 总结从 JVM 到 Linux 内核线程创建涉及多个层级JVM 层管理 OSThread 对象、线程栈、守护页调用 POSIX 接口glibc 层封装 pthread 接口处理 clone 参数和信号屏蔽Linux 内核低级线程创建与资源共享通过clone3实现轻量级线程理解这一流程对 JVM 性能调优、线程调度优化以及多线程调试至关重要。通过结合 OpenJDK 17 和 Linux 6.8.12 源码我们可以看到 Java 线程创建背后的精细设计和操作系统协作。##源码bool os::create_thread(Thread* thread, ThreadType thr_type, size_t req_stack_size) { assert(thread-osthread() NULL, caller responsible); // Allocate the OSThread object. OSThread* osthread new OSThread(NULL, NULL); if (osthread NULL) { return false; } // Set the correct thread state. osthread-set_thread_type(thr_type); // Initial state is ALLOCATED but not INITIALIZED osthread-set_state(ALLOCATED); thread-set_osthread(osthread); // Init thread attributes. pthread_attr_t attr; pthread_attr_init(attr); guarantee(pthread_attr_setdetachstate(attr, PTHREAD_CREATE_DETACHED) 0, ???); // Make sure we run in 1:1 kernel-user-thread mode. if (os::Aix::on_aix()) { guarantee(pthread_attr_setscope(attr, PTHREAD_SCOPE_SYSTEM) 0, ???); guarantee(pthread_attr_setinheritsched(attr, PTHREAD_EXPLICIT_SCHED) 0, ???); } // Start in suspended state, and in os::thread_start, wake the thread up. guarantee(pthread_attr_setsuspendstate_np(attr, PTHREAD_CREATE_SUSPENDED_NP) 0, ???); // Calculate stack size if its not specified by caller. size_t stack_size os::Posix::get_initial_stack_size(thr_type, req_stack_size); // JDK-8187028: It was observed that on some configurations (4K backed thread stacks) // the real thread stack size may be smaller than the requested stack size, by as much as 64K. // This very much looks like a pthread lib error. As a workaround, increase the stack size // by 64K for small thread stacks (arbitrarily choosen to be 4MB) if (stack_size 4096 * K) { stack_size 64 * K; } // On Aix, pthread_attr_setstacksize fails with huge values and leaves the // thread size in attr unchanged. If this is the minimal stack size as set // by pthread_attr_init this leads to crashes after thread creation. E.g. the // guard pages might not fit on the tiny stack created. int ret pthread_attr_setstacksize(attr, stack_size); if (ret ! 0) { log_warning(os, thread)(The %sthread stack size specified is invalid: SIZE_FORMAT k, (thr_type compiler_thread) ? compiler : ((thr_type java_thread) ? : VM ), stack_size / K); thread-set_osthread(NULL); delete osthread; return false; } // Save some cycles and a page by disabling OS guard pages where we have our own // VM guard pages (in java threads). For other threads, keep system default guard // pages in place. if (thr_type java_thread || thr_type compiler_thread) { ret pthread_attr_setguardsize(attr, 0); } pthread_t tid 0; if (ret 0) { ret pthread_create(tid, attr, (void* (*)(void*)) thread_native_entry, thread); } if (ret 0) { char buf[64]; log_info(os, thread)(Thread started (pthread id: UINTX_FORMAT , attributes: %s). , (uintx) tid, os::Posix::describe_pthread_attr(buf, sizeof(buf), attr)); } else { char buf[64]; log_warning(os, thread)(Failed to start thread - pthread_create failed (%d%s) for attributes: %s., ret, os::errno_name(ret), os::Posix::describe_pthread_attr(buf, sizeof(buf), attr)); // Log some OS information which might explain why creating the thread failed. log_info(os, thread)(Number of threads approx. running in the VM: %d, Threads::number_of_threads()); LogStream st(Log(os, thread)::info()); os::Posix::print_rlimit_info(st); os::print_memory_info(st); } pthread_attr_destroy(attr); if (ret ! 0) { // Need to clean up stuff weve allocated so far. thread-set_osthread(NULL); delete osthread; return false; } // OSThread::thread_id is the pthread id. osthread-set_thread_id(tid); return true; } #define __NR_clone3 435 /* The clone3 syscall wrapper. Linux/x86-64 version. Copyright (C) 2021-2024 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The GNU C Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with the GNU C Library; if not, see https://www.gnu.org/licenses/. */ /* clone3() is even more special than fork() as it mucks with stacks and invokes a function in the right context after its all over. */ #include sysdep.h /* The userland implementation is: int clone3 (struct clone_args *cl_args, size_t size, int (*func)(void *arg), void *arg); the kernel entry is: int clone3 (struct clone_args *cl_args, size_t size); The parameters are passed in registers from userland: rdi: cl_args rsi: size rdx: func rcx: arg The kernel expects: rax: system call number rdi: cl_args rsi: size */ .text ENTRY (__clone3) /* Sanity check arguments. */ movl $-EINVAL, %eax test %RDI_LP, %RDI_LP /* No NULL cl_args pointer. */ jz SYSCALL_ERROR_LABEL test %RDX_LP, %RDX_LP /* No NULL function pointer. */ jz SYSCALL_ERROR_LABEL /* Save the cl_args pointer in R8 which is preserved by the syscall. */ mov %RCX_LP, %R8_LP /* Do the system call. */ movl $SYS_ify(clone3), %eax /* End FDE now, because in the child the unwind info will be wrong. */ cfi_endproc syscall test %RAX_LP, %RAX_LP jl SYSCALL_ERROR_LABEL jz L(thread_start) ret L(thread_start): cfi_startproc /* Clearing frame pointer is insufficient, use CFI. */ cfi_undefined (rip) /* Clear the frame pointer. The ABI suggests this be done, to mark the outermost frame obviously. */ xorl %ebp, %ebp /* Set up arguments for the function call. */ mov %R8_LP, %RDI_LP /* Argument. */ call *%rdx /* Call function. */ /* Call exit with return value from function call. */ movq %rax, %rdi movl $SYS_ify(exit), %eax syscall cfi_endproc cfi_startproc PSEUDO_END (__clone3) libc_hidden_def (__clone3) weak_alias (__clone3, clone3) /* The clone3 syscall provides a superset of the functionality of the clone interface. The kernel might extend __CL_ARGS struct in the future, with each version with a different __SIZE. If the child is created, it will start __FUNC function with __ARG arguments. Different than kernel, the implementation also returns EINVAL for an invalid NULL __CL_ARGS or __FUNC (similar to __clone). All callers are responsible for correctly aligning the stack. The stack is not aligned prior to the syscall (this differs from the exported __clone). This function is only implemented if the ABI defines HAVE_CLONE3_WRAPPER. */ extern int __clone3 (struct clone_args *__cl_args, size_t __size, int (*__func) (void *__arg), void *__arg); static int create_thread (struct pthread *pd, const struct pthread_attr *attr, bool *stopped_start, void *stackaddr, size_t stacksize, bool *thread_ran) { /* Determine whether the newly created threads has to be started stopped since we have to set the scheduling parameters or set the affinity. */ bool need_setaffinity (attr ! NULL attr-extension ! NULL attr-extension-cpuset ! 0); if (attr ! NULL (__glibc_unlikely (need_setaffinity) || __glibc_unlikely ((attr-flags ATTR_FLAG_NOTINHERITSCHED) ! 0))) *stopped_start true; pd-stopped_start *stopped_start; if (__glibc_unlikely (*stopped_start)) lll_lock (pd-lock, LLL_PRIVATE); /* We rely heavily on various flags the CLONE function understands: CLONE_VM, CLONE_FS, CLONE_FILES These flags select semantics with shared address space and file descriptors according to what POSIX requires. CLONE_SIGHAND, CLONE_THREAD This flag selects the POSIX signal semantics and various other kinds of sharing (itimers, POSIX timers, etc.). CLONE_SETTLS The sixth parameter to CLONE determines the TLS area for the new thread. CLONE_PARENT_SETTID The kernels writes the thread ID of the newly created thread into the location pointed to by the fifth parameters to CLONE. Note that it would be semantically equivalent to use CLONE_CHILD_SETTID but it is be more expensive in the kernel. CLONE_CHILD_CLEARTID The kernels clears the thread ID of a thread that has called sys_exit() in the location pointed to by the seventh parameter to CLONE. The termination signal is chosen to be zero which means no signal is sent. */ const int clone_flags (CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SYSVSEM | CLONE_SIGHAND | CLONE_THREAD | CLONE_SETTLS | CLONE_PARENT_SETTID | CLONE_CHILD_CLEARTID | 0); TLS_DEFINE_INIT_TP (tp, pd); struct clone_args args { .flags clone_flags, .pidfd (uintptr_t) pd-tid, .parent_tid (uintptr_t) pd-tid, .child_tid (uintptr_t) pd-tid, .stack (uintptr_t) stackaddr, .stack_size stacksize, .tls (uintptr_t) tp, }; int ret __clone_internal (args, start_thread, pd); if (__glibc_unlikely (ret -1)) return errno; /* Its started now, so if we fail below, well have to let it clean itself up. */ *thread_ran true; /* Now we have the possibility to set scheduling parameters etc. */ if (attr ! NULL) { /* Set the affinity mask if necessary. */ if (need_setaffinity) { assert (*stopped_start); int res INTERNAL_SYSCALL_CALL (sched_setaffinity, pd-tid, attr-extension-cpusetsize, attr-extension-cpuset); if (__glibc_unlikely (INTERNAL_SYSCALL_ERROR_P (res))) return INTERNAL_SYSCALL_ERRNO (res); } /* Set the scheduling parameters. */ if ((attr-flags ATTR_FLAG_NOTINHERITSCHED) ! 0) { assert (*stopped_start); int res INTERNAL_SYSCALL_CALL (sched_setscheduler, pd-tid, pd-schedpolicy, pd-schedparam); if (__glibc_unlikely (INTERNAL_SYSCALL_ERROR_P (res))) return INTERNAL_SYSCALL_ERRNO (res); } } return 0; } int __clone3_internal (struct clone_args *cl_args, int (*func) (void *args), void *arg) { #ifdef HAVE_CLONE3_WRAPPER # if __ASSUME_CLONE3 return __clone3 (cl_args, sizeof (*cl_args), func, arg); # else static int clone3_supported 1; if (atomic_load_relaxed (clone3_supported) 1) { int ret __clone3 (cl_args, sizeof (*cl_args), func, arg); if (ret ! -1 || errno ! ENOSYS) return ret; atomic_store_relaxed (clone3_supported, 0); } # endif #endif __set_errno (ENOSYS); return -1; } int __clone_internal (struct clone_args *cl_args, int (*func) (void *arg), void *arg) { #ifdef HAVE_CLONE3_WRAPPER int saved_errno errno; int ret __clone3_internal (cl_args, func, arg); if (ret ! -1 || errno ! ENOSYS) return ret; /* NB: Restore errno since errno may be checked against non-zero return value. */ __set_errno (saved_errno); #endif return __clone_internal_fallback (cl_args, func, arg); } int __pthread_create_2_1 (pthread_t *newthread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg) { void *stackaddr NULL; size_t stacksize 0; /* Avoid a data race in the multi-threaded case, and call the deferred initialization only once. */ if (__libc_single_threaded_internal) { late_init (); __libc_single_threaded_internal 0; /* __libc_single_threaded can be accessed through copy relocations, so it requires to update the external copy. */ __libc_single_threaded 0; } const struct pthread_attr *iattr (struct pthread_attr *) attr; union pthread_attr_transparent default_attr; bool destroy_default_attr false; bool c11 (attr ATTR_C11_THREAD); if (iattr NULL || c11) { int ret __pthread_getattr_default_np (default_attr.external); if (ret ! 0) return ret; destroy_default_attr true; iattr default_attr.internal; } struct pthread *pd NULL; int err allocate_stack (iattr, pd, stackaddr, stacksize); int retval 0; if (__glibc_unlikely (err ! 0)) /* Something went wrong. Maybe a parameter of the attributes is invalid or we could not allocate memory. Note we have to translate error codes. */ { retval err ENOMEM ? EAGAIN : err; goto out; } /* Initialize the TCB. All initializations with zero should be performed in get_cached_stack. This way we avoid doing this if the stack freshly allocated with mmap. */ #if TLS_TCB_AT_TP /* Reference to the TCB itself. */ pd-header.self pd; /* Self-reference for TLS. */ pd-header.tcb pd; #endif /* Store the address of the start routine and the parameter. Since we do not start the function directly the stillborn thread will get the information from its thread descriptor. */ pd-start_routine start_routine; pd-arg arg; pd-c11 c11; /* Copy the thread attribute flags. */ struct pthread *self THREAD_SELF; pd-flags ((iattr-flags ~(ATTR_FLAG_SCHED_SET | ATTR_FLAG_POLICY_SET)) | (self-flags (ATTR_FLAG_SCHED_SET | ATTR_FLAG_POLICY_SET))); /* Inherit rseq registration state. Without seccomp filters, rseq registration will either always fail or always succeed. */ if ((int) THREAD_GETMEM_VOLATILE (self, rseq_area.cpu_id) 0) pd-flags | ATTR_FLAG_DO_RSEQ; /* Initialize the field for the ID of the thread which is waiting for us. This is a self-reference in case the thread is created detached. */ pd-joinid iattr-flags ATTR_FLAG_DETACHSTATE ? pd : NULL; /* The debug events are inherited from the parent. */ pd-eventbuf self-eventbuf; /* Copy the parents scheduling parameters. The flags will say what is valid and what is not. */ pd-schedpolicy self-schedpolicy; pd-schedparam self-schedparam; /* Copy the stack guard canary. */ #ifdef THREAD_COPY_STACK_GUARD THREAD_COPY_STACK_GUARD (pd); #endif /* Copy the pointer guard value. */ #ifdef THREAD_COPY_POINTER_GUARD THREAD_COPY_POINTER_GUARD (pd); #endif /* Setup tcbhead. */ tls_setup_tcbhead (pd); /* Verify the sysinfo bits were copied in allocate_stack if needed. */ #ifdef NEED_DL_SYSINFO CHECK_THREAD_SYSINFO (pd); #endif /* Determine scheduling parameters for the thread. */ if (__builtin_expect ((iattr-flags ATTR_FLAG_NOTINHERITSCHED) ! 0, 0) (iattr-flags (ATTR_FLAG_SCHED_SET | ATTR_FLAG_POLICY_SET)) ! 0) { /* Use the scheduling parameters the user provided. */ if (iattr-flags ATTR_FLAG_POLICY_SET) { pd-schedpolicy iattr-schedpolicy; pd-flags | ATTR_FLAG_POLICY_SET; } if (iattr-flags ATTR_FLAG_SCHED_SET) { /* The values were validated in pthread_attr_setschedparam. */ pd-schedparam iattr-schedparam; pd-flags | ATTR_FLAG_SCHED_SET; } if ((pd-flags (ATTR_FLAG_SCHED_SET | ATTR_FLAG_POLICY_SET)) ! (ATTR_FLAG_SCHED_SET | ATTR_FLAG_POLICY_SET)) collect_default_sched (pd); } if (__glibc_unlikely (__nptl_nthreads 1)) _IO_enable_locks (); /* Pass the descriptor to the caller. */ *newthread (pthread_t) pd; LIBC_PROBE (pthread_create, 4, newthread, attr, start_routine, arg); /* One more thread. We cannot have the thread do this itself, since it might exist but not have been scheduled yet by the time weve returned and need to check the value to behave correctly. We must do it before creating the thread, in case it does get scheduled first and then might mistakenly think it was the only thread. In the failure case, we momentarily store a false value; this doesnt matter because there is no kosher thing a signal handler interrupting us right here can do that cares whether the thread count is correct. */ atomic_fetch_add_relaxed (__nptl_nthreads, 1); /* Our local value of stopped_start and thread_ran can be accessed at any time. The PD-stopped_start may only be accessed if we have ownership of PD (see CONCURRENCY NOTES above). */ bool stopped_start false; bool thread_ran false; /* Block all signals, so that the new thread starts out with signals disabled. This avoids race conditions in the thread startup. */ internal_sigset_t original_sigmask; internal_signal_block_all (original_sigmask); if (iattr-extension ! NULL iattr-extension-sigmask_set) /* Use the signal mask in the attribute. The internal signals have already been filtered by the public pthread_attr_setsigmask_np interface. */ internal_sigset_from_sigset (pd-sigmask, iattr-extension-sigmask); else { /* Conceptually, the new thread needs to inherit the signal mask of this thread. Therefore, it needs to restore the saved signal mask of this thread, so save it in the startup information. */ pd-sigmask original_sigmask; /* Reset the cancellation signal mask in case this thread is running cancellation. */ internal_sigdelset (pd-sigmask, SIGCANCEL); } /* Start the thread. */ if (__glibc_unlikely (report_thread_creation (pd))) { stopped_start true; /* We always create the thread stopped at startup so we can notify the debugger. */ retval create_thread (pd, iattr, stopped_start, stackaddr, stacksize, thread_ran); if (retval 0) { /* We retain ownership of PD until (a) (see CONCURRENCY NOTES above). */ /* Assert stopped_start is true in both our local copy and the PD copy. */ assert (stopped_start); assert (pd-stopped_start); /* Now fill in the information about the new thread in the newly created threads data structure. We cannot let the new thread do this since we dont know whether it was already scheduled when we send the event. */ pd-eventbuf.eventnum TD_CREATE; pd-eventbuf.eventdata pd; /* Enqueue the descriptor. */ do pd-nextevent __nptl_last_event; while (atomic_compare_and_exchange_bool_acq (__nptl_last_event, pd, pd-nextevent) ! 0); /* Now call the function which signals the event. See CONCURRENCY NOTES for the nptl_db interface comments. */ __nptl_create_event (); } } else retval create_thread (pd, iattr, stopped_start, stackaddr, stacksize, thread_ran); /* Return to the previous signal mask, after creating the new thread. */ internal_signal_restore_set (original_sigmask); if (__glibc_unlikely (retval ! 0)) { if (thread_ran) /* State (c) and we not have PD ownership (see CONCURRENCY NOTES above). We can assert that STOPPED_START must have been true because thread creation didnt fail, but thread attribute setting did. */ { assert (stopped_start); /* Signal the created thread to release PD ownership and early exit so it could be joined. */ pd-setup_failed 1; lll_unlock (pd-lock, LLL_PRIVATE); /* Similar to pthread_join, but since thread creation has failed at startup there is no need to handle all the steps. */ pid_t tid; while ((tid atomic_load_acquire (pd-tid)) ! 0) __futex_abstimed_wait_cancelable64 ((unsigned int *) pd-tid, tid, 0, NULL, LLL_SHARED); } /* State (c) or (d) and we have ownership of PD (see CONCURRENCY NOTES above). */ /* Oops, we lied for a second. */ atomic_fetch_add_relaxed (__nptl_nthreads, -1); /* Free the resources. */ __nptl_deallocate_stack (pd); /* We have to translate error codes. */ if (retval ENOMEM) retval EAGAIN; } else { /* We dont know if we have PD ownership. Once we check the local stopped_start well know if were in state (a) or (b) (see CONCURRENCY NOTES above). */ if (stopped_start) /* State (a), we own PD. The thread blocked on this lock either because were doing TD_CREATE event reporting, or for some other reason that create_thread chose. Now let it run free. */ lll_unlock (pd-lock, LLL_PRIVATE); /* We now have for sure more than one thread. The main thread might not yet have the flag set. No need to set the global variable again if this is what we use. */ THREAD_SETMEM (THREAD_SELF, header.multiple_threads, 1); } out: if (destroy_default_attr) __pthread_attr_destroy (default_attr.external); return retval; } versioned_symbol (libc, __pthread_create_2_1, pthread_create, GLIBC_2_34); libc_hidden_ver (__pthread_create_2_1, __pthread_create) #ifndef SHARED strong_alias (__pthread_create_2_1, __pthread_create) #endif

相关文章:

深入解析 OpenJDK 17 在 Linux 上的线程创建机制

在现代高性能 Java 应用中,线程管理是 JVM 的核心功能之一。Java 线程的创建和调度最终依赖于底层操作系统的线程实现。在 Linux 系统上,JVM 线程创建涉及 POSIX 线程(pthread)接口以及 Linux 内核的 clone/clone3 系统调用。本文…...

电-气-热综合能源系统优化调度模型详解

MATLAB代码:电-气-热综合能源系统耦合优化调度 关键词:综合能源系统 优化调度 电气热耦合 参考文档:自编文档,非常细致详细,可联系我查阅 仿真平台:MATLABCPLEX 主要内容:代码主要做的是一个考…...

拼多多电商数据采集实战:5分钟构建你的市场情报系统

拼多多电商数据采集实战:5分钟构建你的市场情报系统 【免费下载链接】scrapy-pinduoduo 拼多多爬虫,抓取拼多多热销商品信息和评论 项目地址: https://gitcode.com/gh_mirrors/sc/scrapy-pinduoduo 想要实时掌握拼多多平台的商品动态和用户反馈吗…...

机器学习算法评估:从指标选择到工程实践

1. 机器学习算法评估的核心逻辑在真实业务场景中,选择机器学习算法从来不是简单的"哪个准确率高就用哪个"。三年前我们团队在电商推荐系统升级时,曾因过度依赖单一评估指标导致上线后效果倒退。这个教训让我深刻认识到:算法评估是系…...

区块链DeFi实战

区块链DeFi实战:探索去中心化金融新机遇 近年来,区块链技术的快速发展催生了去中心化金融(DeFi)的崛起。DeFi通过智能合约和去中心化协议重构传统金融体系,为用户提供无需中介的借贷、交易和理财服务。本文将深入探讨…...

终极实战:5个高效微信自动化场景,用wxauto构建你的智能机器人

终极实战:5个高效微信自动化场景,用wxauto构建你的智能机器人 【免费下载链接】wxauto Windows版本微信客户端(非网页版)自动化,可实现简单的发送、接收微信消息,简单微信机器人 项目地址: https://gitco…...

WarcraftHelper:魔兽争霸3终极增强插件解决现代系统兼容性问题

WarcraftHelper:魔兽争霸3终极增强插件解决现代系统兼容性问题 【免费下载链接】WarcraftHelper Warcraft III Helper , support 1.20e, 1.24e, 1.26a, 1.27a, 1.27b 项目地址: https://gitcode.com/gh_mirrors/wa/WarcraftHelper WarcraftHelper是一款专为魔…...

精读双模态检测论文二十|北航 华东师大 腾讯优图官方跨模态Mamba封神!YOLOv8 原生适配,mAP 暴涨 5.9%,首个 Mamba 跨模态检测 SOTA!

🔥 本文定位:CSDN 原创硬核干货 | 顶刊级成果 | YOLOv5/v8/v11 全系列原生适配 | 端到端跨模态检测 SOTA🎯 核心收益:彻底解决跨模态检测三大行业痛点 ——模态差异大融合效果差、Transformer 融合计算量爆炸、CNN 融合全局建模能…...

AsrTools:3步完成音频转文字,本地免费语音识别工具

AsrTools:3步完成音频转文字,本地免费语音识别工具 【免费下载链接】AsrTools ✨ AsrTools: Smart Voice-to-Text Tool | Efficient Batch Processing | User-Friendly Interface | No GPU Required | Supports SRT/TXT Output | Turn your audio into a…...

机器学习实战:4个递进项目掌握Python数据科学全流程

1. 为什么选择这四个机器学习自学项目作为从业十年的数据科学家,我经常被问到"如何有效自学机器学习"。教科书式的理论学习往往让人陷入"学了很多却不会用"的困境。经过多年带新人的经验,我精选了这四个具有递进关系的实战项目&…...

拼多多数据洞察:如何用爬虫技术解锁电商市场真相

拼多多数据洞察:如何用爬虫技术解锁电商市场真相 【免费下载链接】scrapy-pinduoduo 拼多多爬虫,抓取拼多多热销商品信息和评论 项目地址: https://gitcode.com/gh_mirrors/sc/scrapy-pinduoduo 在电商竞争白热化的今天,数据已成为商业…...

ControlFlow框架:用Python构建可控的智能体工作流

1. 项目概述:从代码到智能的“指挥家”如果你和我一样,在过去几年里尝试过用大语言模型(LLM)构建自动化应用,那你一定经历过这种场景:写一段提示词,调用API,然后祈祷返回的结果格式正…...

终极内存清理指南:3分钟释放Windows内存,告别卡顿烦恼!

终极内存清理指南:3分钟释放Windows内存,告别卡顿烦恼! 【免费下载链接】memreduct Lightweight real-time memory management application to monitor and clean system memory on your computer. 项目地址: https://gitcode.com/gh_mirro…...

MCP 2026动态权限分配失效事故复盘(某央企数据泄露溯源报告·内部首曝)

更多请点击: https://intelliparadigm.com 第一章:MCP 2026动态权限分配失效事故全景概览 2026年3月17日,某金融级多云控制平台(MCP)在执行跨租户策略同步时突发权限分配失效事件,导致约12%的生产工作负载…...

MCP 2026证书链校验绕过漏洞(CVE-2026-0947):如何用3行OpenSSL命令快速定位受影响节点?

更多请点击: https://intelliparadigm.com 第一章:MCP 2026证书链校验绕过漏洞(CVE-2026-0947)概述 CVE-2026-0947 是一个高危逻辑缺陷,影响主流 MCP(Multi-Channel Protocol)2026 实现中 TLS…...

最后30天!Docker Hub官方宣布2026.0版本将停用旧版AI插件API:迁移 checklist、兼容性矩阵与回滚熔断方案(含CLI一键检测脚本)

更多请点击: https://intelliparadigm.com 第一章:Docker Hub AI插件API停用公告与影响全景分析 Docker 官方于 2024 年 7 月 15 日正式宣布,自 2024 年 10 月 1 日起全面停用 Docker Hub 的 AI 插件 API(/v2/plugins/ai/ 端点&a…...

Sunshine游戏串流服务器:三步搭建你的跨平台游戏乐园

Sunshine游戏串流服务器:三步搭建你的跨平台游戏乐园 【免费下载链接】Sunshine Self-hosted game stream host for Moonlight. 项目地址: https://gitcode.com/GitHub_Trending/su/Sunshine 想要在任何设备上畅玩PC游戏吗?Sunshine作为一款免费开…...

为什么你的MCP 2026边缘服务始终达不到SLA 99.99%?——基于17个真实客户集群的优化归因分析

更多请点击: https://intelliparadigm.com 第一章:MCP 2026边缘服务SLA达标性诊断框架 MCP 2026边缘服务SLA达标性诊断框架是一套面向低时延、高可用边缘计算场景的轻量化可观测性验证体系,聚焦于响应延迟、服务连续性与资源隔离三类核心SLA…...

Copilot Next 工作流配置不踩坑,深度解析YAML Schema校验机制、Context Token 限制与上下文注入失效根因,2024最新版避坑手册

更多请点击: https://intelliparadigm.com 第一章:Copilot Next 工作流配置全景概览 Copilot Next 是 GitHub 官方推出的下一代智能协作引擎,深度集成于 VS Code、JetBrains IDEs 及 GitHub Actions 运行时中。其工作流配置以 YAML 驱动&…...

Dream-Creator:基于Stable Diffusion的本地AI图像生成工作站部署与实战

1. 项目概述:一个面向未来的AI图像生成工具最近在GitHub上闲逛,发现了一个名为“Dream-Creator”的项目,作者是Xianyu33666。这个项目名本身就挺有意思的,“梦想创造者”,听起来就充满了想象力。点进去一看&#xff0c…...

PyVision:让视觉大模型动态生成代码工具,突破传统视觉智能体局限

1. 项目概述:让视觉大模型学会“造轮子” 最近在跟进多模态大模型(MLLM)的智能体(Agent)应用时,我发现了一个挺有意思的“瓶颈”:大多数视觉推理任务,模型还是被框在一个预设好的工…...

基于Git提交历史的本地AI代码助手:Machtiani深度解析与实践指南

1. 项目概述:Machtiani,一个能与你的代码库深度对话的本地AI助手 如果你和我一样,每天都要面对一个拥有数千次提交、数万行代码的庞大项目,那么你一定理解那种在代码海洋中寻找特定逻辑或修复一个陈年Bug时的无力感。传统的全局搜…...

简单三步:用MyTV-Android让老旧电视焕发新生的终极解决方案

简单三步:用MyTV-Android让老旧电视焕发新生的终极解决方案 【免费下载链接】mytv-android 使用Android原生开发的视频播放软件 项目地址: https://gitcode.com/gh_mirrors/my/mytv-android 还在为家中老旧Android电视无法安装现代直播应用而烦恼吗&#xff…...

协议转换失败率骤降91.7%的关键动作,深度拆解MCP 2026与LoRaWAN/Modbus双栈协同架构

更多请点击: https://intelliparadigm.com 第一章:协议转换失败率骤降91.7%的关键动作,深度拆解MCP 2026与LoRaWAN/Modbus双栈协同架构 在工业边缘网关部署中,协议转换失败长期制约设备接入一致性。MCP 2026协议引擎通过重构数据…...

终极性能解锁:如何用OmenSuperHub彻底释放惠普OMEN游戏本潜力

终极性能解锁:如何用OmenSuperHub彻底释放惠普OMEN游戏本潜力 【免费下载链接】OmenSuperHub 使用 WMI BIOS控制性能和风扇速度,自动解除DB功耗限制。 项目地址: https://gitcode.com/gh_mirrors/om/OmenSuperHub 你是否曾为惠普OMEN游戏本的性能…...

FanControl终极指南:轻松掌握Windows风扇控制艺术

FanControl终极指南:轻松掌握Windows风扇控制艺术 【免费下载链接】FanControl.Releases This is the release repository for Fan Control, a highly customizable fan controlling software for Windows. 项目地址: https://gitcode.com/GitHub_Trending/fa/Fan…...

MCP 2026组件集成失效率骤升47%?揭秘3个被92%开发团队忽略的上下文绑定陷阱

更多请点击: https://intelliparadigm.com 第一章:MCP 2026组件集成失效率骤升的行业警讯 近期,多家头部云原生平台在升级至 MCP(Model-Centric Platform)2026 版本后,报告其核心组件(如 mcp-r…...

【MCP 2026医疗脱敏权威指南】:覆盖12类敏感字段、7大合规基线与3种动态策略配置实操手册

更多请点击: https://intelliparadigm.com 第一章:MCP 2026医疗脱敏规范演进与核心定位 MCP(Medical Confidentiality Protocol)2026 是中国信通院联合国家卫健委信息标准委员会于2024年Q4正式立项、2026年1月起强制实施的医疗数…...

苹果触控板在Windows系统的完美重生:mac-precision-touchpad驱动深度解析

苹果触控板在Windows系统的完美重生:mac-precision-touchpad驱动深度解析 【免费下载链接】mac-precision-touchpad Windows Precision Touchpad Driver Implementation for Apple MacBook / Magic Trackpad 项目地址: https://gitcode.com/gh_mirrors/ma/mac-pre…...

如何用开源项目Ryujinx在PC上免费畅玩Switch游戏?终极探索指南

如何用开源项目Ryujinx在PC上免费畅玩Switch游戏?终极探索指南 【免费下载链接】Ryujinx 用 C# 编写的实验性 Nintendo Switch 模拟器 项目地址: https://gitcode.com/GitHub_Trending/ry/Ryujinx 想象一下,你正坐在电脑前,想要体验《…...