Linux process and thread

Program execution:
Program Execution https://web.stanford.edu/class/cs101/software-1.html
The main difference between a process and a thread: a process is a unit of resource (storage resource) allocation, while a thread is a unit of task scheduling (cpu)

process

A process is the basic unit of resource allocation, because each process has its own virtual memory space, which is independently addressed. This is why there is a need for inter-process communication mechanisms. The address space structure of a process:
process address space
Process virtual memory space

  • Process creation:
    • Create a virtual memory space, initialize the page table directory, and map the virtual memory space to physical memory
    • Read the header of the executable file, create a mapping relationship between the executable file and the virtual memory space, and load the file from the disk into the physical memory when there is a page fault later
    • Set the cpu's instruction register as the entry address of the process execution (also involves context switching, etc.)

Executable files and process virtual memory space

  • Execution of the process :
    • After the cpu starts to execute the instruction, it finds that the page pointed to by the entry address is an empty page, so a page fault (page fault) occurs, and then the operating system finds the virtual memory area (VMA, a segment in the executable file) where the page needs to be located. According to the mapping relationship between the executable file and the virtual memory space established in the second step above, find the offset of the page in the file, then find an empty page in the physical memory, and map this area to the physical memory; then The operating system transfers control to the process, and the process starts executing from the location of the page fault just now
    • Page faults are constantly generated during execution, so the previous step is repeated

PS: segment is a concept in the loading process, unlike section, section is a concept when linking. Because during the loading process, due to page alignment, if each section occupies a page (some sections are small), it will cause a waste of space. , readable and writable, read-only) are divided into different segments. The information of these segments is stored in the program header of the executable file, which can be readelf -l <可执行文件>viewed, and the specific process can be viewed by cat /proc/<pid>/mapsviewing the virtual space distribution; the information of the section is stored in the section header, which can be readelf -S <可执行文件>viewed

  • Execute the ELF program under bash :
    • Create a child process by fork()(calling clone()the system call), and then the new process calls to execve()process the execution file, and the original bash process waits for the new process to end
    • execve()After the system call, the kernel starts to load the ELF file (map the ELF file, initialize the ELF process environment, and set the return address of the system call to the entry address of the ELF execution file)

thread

  • thread creation

    • When a thread is pthread_createcreated, it uses clone()a system call and uses a different flag from the process fork()call : CLONE_VM, etc. After setting this flag, the thread and the process (main thread) use the same address space. Compare the two programs: 1: fork processclone()

      int main()
      {
              
              
          pid_t pid;
          pid = fork();
          if (pid == 0) {
              
              
              std::cout << "child process" << std::endl;
          } else {
              
              
              std::cout << "parent process" << std::endl;
          }
      }
      

      2: pthread_create thread

      void * run(void * args)
      {
              
              
          std::cout << "child thread" << std::endl;
          sleep(50);
      }
      int main()
      {
              
              
          pthread_t ptid1;
          pthread_create(&ptid1, nullptr, run, nullptr);
          pthread_join(ptid1, nullptr);
          return 0;
      }
      

      Using strace ./a.outthese two programs, it can be found that:

      • The output of the fork program:
        clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7fd3ecd58210) = 24203
      • The output of the pthread_create program:
        clone(child_stack=0x7efd47234fb0, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tidptr=0x7efd472359d0, tls=0x7efd47235700, child_tidptr=0x7efd472359d0) = 23882
        where:
    mark meaning
    CLONE_VM shared virtual memory
    CLONE_FS Share filesystem-related attributes
    CLONE_FILES shared open file descriptor table
    CLONE_SIGHAND Shared dispositions on signals
    CLONE_THREAD placed in the thread group to which the parent process belongs
    • threading model

      There are three threading models in Linux: N:1 user threading model, 1:1 kernel threading model and N:M mixed threading model
      User-level vs. kernel-level threads

      • N:1 user thread model: LinuxThreads, the earliest model, only partially implements the POSIX Threads standard

        Created entirely by user-space libraries, the kernel is unaware of the existence of threads. The kernel only allocates one corresponding kernel thread per process for scheduling and trapping system calls. A thread in the process is selected by the library scheduler of the user space to correspond to the kernel thread of the process, and then the operating system schedules it to run on the CPU.
        Advantages: Convenient thread switching in the process
        Disadvantages: Only one thread can execute in a process at the same time
        N-1 thread model

      • 1:1 kernel threading model: NPTL, Native POSIX Thread Library, the model used since kernel 2.6 to now

        Thread creation, switching, etc. are implemented by the kernel. Each user thread corresponds to a kernel thread. A thread control block (TCB) is set for each kernel thread in the kernel space, and the kernel senses the existence of the thread and controls it according to the control block.
        Advantages: The kernel can execute multiple threads in a process in parallel
        Disadvantages: Context switching is complex This model is used
        1-1 threading model
        in versions since Linux kernel 2.6 . The created user thread is also called a lightweight process (LWP, Light Weight Process) in some documents [Does LWP refer to the user thread or the corresponding kernel thread? There are articles saying both ways]. This is because pthread_create also uses the system call clone() when creating a process, but it shares the virtual memory space of the process (main thread) when creating a user thread. Refer to the section above.pthread库pthread_create()线程创建

        #include <iostream>
        #include <sys/types.h>
        #include <unistd.h>
        #include <pthread.h>
        
        void * run(void * args)
        {
                  
                  
            std::cout << "child thread" << std::endl;
            sleep(50);
        }
        
        int main()
        {
                  
                  
            pthread_t ptid1, ptid2, ptid3;
            pthread_create(&ptid1, nullptr, run, nullptr);
            pthread_create(&ptid2, nullptr, run, nullptr);
            pthread_create(&ptid3, nullptr, run, nullptr);
            std::cout << "main thread" << std::endl;
            pthread_join(ptid1, nullptr);
            pthread_join(ptid2, nullptr);
            pthread_join(ptid3, nullptr);
            return 0;
        }
        

        Execute the above example and use the command ps -eLfto view:
        ThreadLWP
        where LWP represents the pid of the lightweight process, and NLWP represents the number of threads in the thread group. It can be seen that the pids of the four threads (one main thread) of a.out are all 4289, indicating that these threads are in a parallel relationship; the ppids are all 29129, indicating that there is no parent-child relationship between threads; each thread is associated with an LWP Correspondingly, they are 4289, 4290, 4291, 4292; these 4 threads form a thread group, so the NLWP is 4.

      • N:M mixed thread model: NGPT, Next Generation POSIX Threads, terminated

      NM threading model

    PS: Why is it a kernel thread, not a kernel process: Since the kernel thread runs in the kernel, it shares the same kernel space and kernel page table.

    • Thread stack
      Each thread has its own unique thread stack after creation. Unlike the process stack whose size grows dynamically, the thread stack size is fixed. Each thread stack has a certain size boundary. Once the thread access exceeds the boundary, an error will be reported. From this thread stack, it can be allocated close to the heap (from top to bottom near the top of the heap), or it can be allocated close to the stack (the one executed by itself is different from the one in the reference document).
      1. allocate near the heap

        Execute ulimit -ato view the stack size:

        Execute code in the threading model, by cat /proc/<pid>/mapslooking at the address space of the process.
        You can see that under the heap, in addition to the stack of the main thread, there are 3 thread stacks and their boundaries:

        insert image description here

      2. Allocate near the stack
        ulimit -s unlimitedand set the stack size to unlimited. Note that although the stack size is set to unlimited, it is actually not unlimited, but a fixed-size thread stack.
        Execute code in the threading model, by cat /proc/<pid>/mapslooking at the address space of the process.
        It is found that the relative positions of the shared memory area, stack, heap, code area, and data area have all changed.
        insert image description here
        insert image description here
        But what is certain is that each thread has its own thread stack, and the size of the thread stack is fixed.

        PS: user stack and kernel stack - each process has 2 stacks, user stack and kernel stack, when the process system calls or interrupts, it will fall into the kernel state, at this time, the user state stack will be saved in the kernel stack , and then change the stack pointer to the address of the kernel stack. Since the kernel stack is always empty when the process transfers from the user state to the kernel state, the top address of the kernel stack can be directly saved in the stack pointer register. At the same time, the process mentioned here means that there is only one main thread. Processes and threads don't share a kernel stack (perhaps one per LWP?).

CPU context switch

  • Process context switching:
    1. Page table – corresponding to virtual memory resources
    2. File descriptor table/open file table – corresponding to open file resources
    3. Registers – corresponding to runtime data
    4. Signal control information / process running information
  • Thread context switching - in the same process:
    1. Stack
    2. Registers - corresponding to runtime data

PS: The article is written with reference to many articles. Since I haven't read the Linux kernel source code, I may understand something wrong. Please help to point it out.

  1. Virtual address space of Linux executable files and processes: https://cloud.tencent.com/developer/article/1624718
  2. Process address space, heap and stack relationship: https://blog.csdn.net/icandoit_2014/article/details/56666569
  3. The difference between process and thread: https://harmonyos.51cto.com/posts/659
  4. The execution process of the process in the cpu: https://blog.csdn.net/hunter___/article/details/82906540
  5. Kernel-linux process switching: https://r00tk1ts.github.io/2017/08/26/Linux%E8%BF%9B%E7%A8%8B%E5%88%87%E6%8D%A2/
  6. Concepts related to Linux process threads: https://segmentfault.com/a/1190000019066170
  7. Three implementations of threads: https://blog.csdn.net/gatieme/article/details/51892437
  8. posix thread: https://cloud.tencent.com/developer/article/1008758
  9. Kernel threads, lightweight processes, and user thread concepts: https://blog.csdn.net/gatieme/article/details/51481863
  10. Are the threads created by the pthread library user-level threads or kernel-level threads: https://www.zhihu.com/question/35128513
  11. Ordinary threads and kernel threads: https://www.cnblogs.com/alantu2018/p/8526916.html
  1. What is the difference between the user stack and the kernel stack: https://www.jianshu.com/p/6b2ec520ae02
  2. Thread and stack: https://network.51cto.com/art/202006/619565.htm
  3. How to allocate thread stack: https://ahoj.cc/2020/03/%E3%80%90%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F%E3% 80%91%E7%BA%BF%E7%A8%8B%E6%A0%88%E5%A6%82%E4%BD%95%E5%88%86%E9%85%8D/
  4. Various stacks in Linux: process stack thread stack kernel stack interrupt stack: https://juejin.cn/post/6844903686003490830
  5. The difference between user stack and kernel stack: https://www.jianshu.com/p/6b2ec520ae02
  6. User stack/kernel stack: https://www.huaweicloud.com/articles/359d7c5a96089200119047b61da76098.html
  7. This article lets you understand CPU context switching: https://zhuanlan.zhihu.com/p/52845869
  8. Method of linux thread switching and process switching

Guess you like

Origin blog.csdn.net/iamanda/article/details/116163927