I read it to get a feel for the difference between multi-process / thread, so I summarized it. See version read 5.4 torvalds/linux
Threads scheduled by virtual machines rather than the OS. Emulate a multithreaded environment regardless of OS functionality.
The OS scheduler schedules thread execution (Can be displayed with -L in the ps command)
M: N thread is an asymmetric thread model in which the kernel thread is M and the user thread is N. Operates by mapping multiple kernel threads to multiple user threads It seems that golang uses this and the switch cost is very low.
Basically, both fork (2) and pthred_create () internally call do_fork (). clone calls do_fork after it is called. It seems that the difference between process and thread creation is distinguished by the difference in the flag when calling this do_fork ().
long sys_clone(unsigned long clone_flags, unsigned long newsp,
void __user *parent_tid, void __user *child_tid)
{
long ret;
if (!newsp)
newsp = UPT_SP(¤t->thread.regs.regs);
current->thread.forking = 1;
ret = do_fork(clone_flags, newsp, ¤t->thread.regs, 0, parent_tid,
child_tid);
current->thread.forking = 0;
return ret;
}
Outline of processing flow
① do_fork() ② _do_fork() ③ copy_process() ④ copy_mm() ⑤ dup_mmap () (If it is not LWP, process cow below) ⑥ copy_page_range() ⑦ copy_one_pte () Copy vm area from one task to another
static int copy_mm(unsigned long clone_flags, struct task_struct *tsk)
{
struct mm_struct *mm, *oldmm;
int retval;
tsk->min_flt = tsk->maj_flt = 0;
tsk->nvcsw = tsk->nivcsw = 0;
#ifdef CONFIG_DETECT_HUNG_TASK
tsk->last_switch_count = tsk->nvcsw + tsk->nivcsw;
tsk->last_switch_time = 0;
#endif
tsk->mm = NULL;
tsk->active_mm = NULL;
oldmm = current->mm;
if (!oldmm)
return 0;
//Initialization of vmacache entry
vmacache_flush(tsk);
// CLONE_If a VM is configured, the calling process and child processes
//It runs in the same memory space.
if (clone_flags & CLONE_VM) {
mmget(oldmm);
//Set mm of parent process to child and good_To mm
mm = oldmm;
goto good_mm;
}
retval = -ENOMEM;
// CLONE_Duplicate the existing mm structure if the VM is not set
mm = dup_mm(tsk, current->mm);
if (!mm)
goto fail_nomem;
good_mm:
tsk->mm = mm;
tsk->active_mm = mm;
return 0;
fail_nomem:
return retval;
}
The process of copy_mm called after clone (2). It turns out that if CLONE_VM is set (flag at thread creation) it shares an address with the parent process. https://linuxjm.osdn.jp/html/LDP_man-pages/man2/clone.2.html
static struct mm_struct *dup_mm(struct task_struct *tsk,
struct mm_struct *oldmm)
{
struct mm_struct *mm;
int err;
mm = allocate_mm();
if (!mm)
goto fail_nomem;
//Copy the memory of the parent process to the generated process
memcpy(mm, oldmm, sizeof(*mm));
if (!mm_init(mm, tsk, mm->user_ns))
goto fail_nomem;
err = dup_mmap(mm, oldmm);
if (err)
goto free_pt;
mm->hiwater_rss = get_mm_rss(mm);
mm->hiwater_vm = mm->total_vm;
if (mm->binfmt && !try_module_get(mm->binfmt->module))
goto free_pt;
return mm;
free_pt:
/* don't put binfmt in mmput, we haven't got module yet */
mm->binfmt = NULL;
mm_init_owner(mm, NULL);
mmput(mm);
fail_nomem:
return NULL;
}
The main processing of copy_mm. If CLONE_VM (thread) is set at the time of the previous process, this process is not performed. Threads can access the data from which the thread was created. In the case of a process, the process of copying the memory of the parent process is performed. (To be described later.)
mm_struct --Structure that manages address space information
item | Overview |
---|---|
mmap | vm_area_Hold the beginning of the struct |
mm_rb | Red for fast search of memory area-Hold a black tree |
mmap_cache | Locality of reference for memory(locality)To speed up by taking advantage of |
mm_count | Reference counter for this structure. If 0, no object is pointed to |
mm_list | mm_struct Field for making a list of structs |
start_code, end_code | Start and end addresses of the text segment |
start_brk, brk | Heap start and end addresses |
start_stack | Start address of the stack |