进程和线程有区别吗?
操作系统教材有标准答案,面试八股文也有全面的总结
但是 Linux 系统有线程吗?
fork() 创建的是进程,pthread_create() 创建的就是线程吗?
glibc 的源码告诉我们它们创建的都是进程,没有线程
fork() 函数是 userspace 函数,并不是系统调用,虽然 Linux 系统提供了 fork 系统调用,但是 glibc 并没有使用 fork 系统调用实现 fork() 函数
https://elixir.bootlin.com/glibc/glibc-2.43.9000/source/posix/fork.c#L40
pid_t
__libc_fork (void)
{
// ...
lastrun = __run_prefork_handlers (multiple_threads);
// ...
pid_t pid = _Fork ();
if (pid == 0)
{
// ...
__run_postfork_handlers (atfork_run_child, multiple_threads, lastrun);
}
else
{
// ...
__run_postfork_handlers (atfork_run_parent, multiple_threads, lastrun);
if (pid < 0)
__set_errno (save_errno);
}
return pid;
}
strong_alias (__libc_fork, __fork)
libc_hidden_def (__fork)
weak_alias (__libc_fork, fork)_Fork() 封装了平台相关的实现
https://elixir.bootlin.com/glibc/glibc-2.43.9000/source/sysdeps/nptl/_Fork.c#L25
pid_t
_Fork (void)
{
/* Block all signals to avoid revealing the inconsistent TCB state
to a signal handler after fork. The abort lock should AS-safe
to avoid deadlock if _Fork is called from a signal handler. */
internal_sigset_t original_sigmask;
__abort_lock_rdlock (&original_sigmask);
pid_t pid = arch_fork (&THREAD_SELF->tid);
if (pid == 0)
{
// ...
}
__abort_lock_unlock (&original_sigmask);
return pid;
}最后可以看到,fork() 函数调用 clone 系统调用
https://elixir.bootlin.com/glibc/glibc-2.43.9000/source/sysdeps/unix/sysv/linux/arch-fork.h#L35
static inline pid_t
arch_fork (void *ctid)
{
const int flags = CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID | SIGCHLD;
long int ret;
#ifdef __ASSUME_CLONE_BACKWARDS
# ifdef INLINE_CLONE_SYSCALL
ret = INLINE_CLONE_SYSCALL (flags, 0, NULL, 0, ctid);
# else
ret = INLINE_SYSCALL_CALL (clone, flags, 0, NULL, 0, ctid);
# endif
#elif defined(__ASSUME_CLONE_BACKWARDS2)
ret = INLINE_SYSCALL_CALL (clone, 0, flags, NULL, ctid, 0);
#elif defined(__ASSUME_CLONE_BACKWARDS3)
ret = INLINE_SYSCALL_CALL (clone, flags, 0, 0, NULL, ctid, 0);
#elif defined(__ASSUME_CLONE_DEFAULT)
ret = INLINE_SYSCALL_CALL (clone, flags, 0, NULL, ctid, 0);
#else
# error "Undefined clone variant"
#endif
return ret;
}pthread_create() 也同样调用 clone 系统调用
https://elixir.bootlin.com/glibc/glibc-2.43.9000/source/nptl/pthread_create.c#L784
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;
// ...
conststruct pthread_attr *iattr = (struct pthread_attr *) attr;
// ...
struct pthread *pd = NULL;
// 分别栈空间
int err = allocate_stack (iattr, &pd, &stackaddr, &stacksize);
int retval = 0;
// ...
pd->start_routine = start_routine;
pd->arg = arg;
pd->c11 = c11;
// ...
pd->schedpolicy = self->schedpolicy;
pd->schedparam = self->schedparam;
// ...
/* Start the thread. */
if (__glibc_unlikely (report_thread_creation (pd)))
{
// ...
}
else
retval = create_thread (pd, iattr, &stopped_start, stackaddr,
stacksize, &thread_ran);
// ...
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)
#endifhttps://elixir.bootlin.com/glibc/glibc-2.43.9000/source/nptl/pthread_create.c#L234
static int create_thread (struct pthread *pd, const struct pthread_attr *attr,
bool *stopped_start, void *stackaddr,
size_t stacksize, bool *thread_ran)
{
// ...
/* 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->joinstate,
.stack = (uintptr_t) stackaddr,
.stack_size = stacksize,
.tls = (uintptr_t) tp,
};
int ret = __clone_internal (&args, &start_thread, pd);
// ...
return 0;
}__clone_internal() 处理 clone 和 clone3 系统调用:如果有 clone3 系统调用,就尝试,否则使用 clone 系统调用
https://elixir.bootlin.com/glibc/glibc-2.43.9000/source/sysdeps/unix/sysv/linux/clone-internal.c#L95
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);
}https://www.man7.org/linux/man-pages/man2/clone.2.html
clone, __clone2, clone3 - create a child process
clone 系统调用创建的是进程,flag 控制了子进程和父进程共享的资源
fork() 和 pthread_create() 本质上创建的都是进程,pthread_create() 的 flag 中控制了子进程和父进程共享资源