Proceso e hilo de Linux

Ejecución del programa:
Ejecución del programa https://web.stanford.edu/class/cs101/software-1.html
La principal diferencia entre un proceso y un subproceso: un proceso es una unidad de asignación de recursos (recursos de almacenamiento), mientras que un subproceso es una unidad de programación de tareas (cpu)

proceso

Un proceso es la unidad básica de asignación de recursos, porque cada proceso tiene su propio espacio de memoria virtual, que se direcciona de forma independiente. Es por esto que existe la necesidad de mecanismos de comunicación entre procesos. La estructura del espacio de direcciones de un proceso:
espacio de direcciones del proceso
Procesar espacio de memoria virtual

  • Proceso de creación:
    • Cree un espacio de memoria virtual, inicialice el directorio de la tabla de páginas y asigne el espacio de memoria virtual a la memoria física
    • Lea el encabezado del archivo ejecutable, cree una relación de mapeo entre el archivo ejecutable y el espacio de la memoria virtual, y cargue el archivo desde el disco a la memoria física cuando haya una falla de página más tarde.
    • Establezca el registro de instrucciones de la CPU como la dirección de entrada de la ejecución del proceso (también implica el cambio de contexto, etc.)

Archivos ejecutables y espacio de memoria virtual de proceso

  • Ejecución del proceso :
    • Después de que la CPU comienza a ejecutar la instrucción, encuentra que la página a la que apunta la dirección de entrada es una página vacía, por lo que se produce una falla de página (falla de página), y luego el sistema operativo encuentra el área de memoria virtual (VMA, un segmento en el archivo ejecutable) donde se debe ubicar la página. De acuerdo con la relación de mapeo entre el archivo ejecutable y el espacio de memoria virtual establecida en el segundo paso anterior, encuentre el desplazamiento de la página en el archivo, luego busque una página vacía en la memoria física y asigne esta área a la memoria física; luego, el sistema operativo transfiere el control al proceso, y el proceso comienza a ejecutarse desde la ubicación de la falla de página en este momento
    • Los fallos de página se generan constantemente durante la ejecución, por lo que se repite el paso anterior

PD: el segmento es un concepto en el proceso de carga, a diferencia de la sección, la sección es un concepto cuando se vincula. Porque durante el proceso de carga, debido a la alineación de la página, si cada sección ocupa una página (algunas secciones son pequeñas), provocará una pérdida de espacio. , legibles y escribibles, solo lectura) se dividen en diferentes segmentos. La información de estos segmentos se almacena en el encabezado del programa del archivo ejecutable, que se puede readelf -l <可执行文件>ver, y el proceso específico se puede ver al cat /proc/<pid>/mapsver la distribución del espacio virtual; la información de la sección se almacena en el encabezado de la sección, que se puede ver readelf -S <可执行文件>visto

  • Ejecute el programa ELF en bash :
    • Cree un proceso secundario fork()(llamando clone()a la llamada del sistema), y luego el nuevo proceso llama para execve()procesar el archivo de ejecución, y el proceso bash original espera a que finalice el nuevo proceso
    • execve()Después de la llamada al sistema, el kernel comienza a cargar el archivo ELF (asignar el archivo ELF, inicializar el entorno del proceso ELF y establecer la dirección de retorno de la llamada al sistema en la dirección de entrada del archivo de ejecución ELF)

hilo

  • creación de hilos

    • Cuando se crea un subproceso pthread_create, utiliza clone()una llamada al sistema y usa una marca diferente de la fork()llamada al proceso: CLONE_VM, etc. Después de establecer esta marca, el subproceso y el proceso (subproceso principal) usan el mismo espacio de direcciones. Compare los dos programas: 1: proceso de bifurcaciónclone()

      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 subproceso

      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;
      }
      

      Usando strace ./a.outestos dos programas, se puede encontrar que:

      • La salida del programa tenedor:
        clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7fd3ecd58210) = 24203
      • La salida del programa pthread_create:
        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
        donde:
    marca significado
    CLONE_VM memoria virtual compartida
    CLONE_FS Compartir propiedades relacionadas con el sistema de archivos
    CLONE_FILES tabla de descriptores de archivos abiertos compartidos
    CLONE_SIGHAND Disposiciones compartidas sobre señales
    CLONE_THREAD colocado en el grupo de subprocesos al que pertenece el proceso padre
    • modelo de roscado

      Hay tres modelos de subprocesos en Linux: modelo de subprocesos de usuario N: 1, modelo de subprocesos de kernel 1: 1 y modelo de subprocesos mixtos N: M
      Subprocesos a nivel de usuario frente a nivel de kernel

      • Modelo de subprocesos de usuario N:1: LinuxThreads, el modelo más antiguo, solo implementa parcialmente el estándar POSIX Threads

        Creado completamente por bibliotecas de espacio de usuario, el núcleo no tiene conocimiento de la existencia de subprocesos. El kernel solo asigna un subproceso del kernel correspondiente por proceso, que se utiliza para programar y atrapar llamadas al sistema. El planificador de la biblioteca del espacio de usuario selecciona un subproceso en el proceso para que se corresponda con el subproceso del núcleo del proceso y luego el sistema operativo lo programa para que se ejecute en la CPU.
        Ventajas: Conveniente cambio de subprocesos en un proceso
        Desventajas: Solo un subproceso puede ejecutarse en un proceso al mismo tiempo
        Modelo de rosca N-1

      • Modelo de subprocesamiento del kernel 1:1: NPTL, biblioteca de subprocesos POSIX nativa, el modelo utilizado desde el kernel 2.6 hasta ahora

        El kernel implementa la creación de subprocesos, el cambio, etc. Cada subproceso de usuario corresponde a un subproceso del kernel. Se establece un bloque de control de subprocesos (TCB) para cada subproceso del kernel en el espacio del kernel, y el kernel detecta la existencia del subproceso y lo controla de acuerdo con el bloque de control.
        Ventajas: El kernel puede ejecutar múltiples hilos en un proceso en paralelo
        Desventajas: El cambio de contexto es complejo Este modelo se usa
        modelo de rosca 1-1
        en versiones desde Linux kernel 2.6 . El subproceso de usuario creado también se denomina proceso ligero (LWP, Light Weight Process) en algunos documentos [¿LWP se refiere al subproceso de usuario o al subproceso de kernel correspondiente? Hay artículos que dicen en ambos sentidos]. Esto se debe a que pthread_create también usa la llamada al sistema clone() cuando crea un proceso, pero comparte el espacio de memoria virtual del proceso (hilo principal) cuando crea un hilo de usuario. Consulte la sección anterior.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;
        }
        

        Ejecute el ejemplo anterior y use el comando ps -eLfpara ver:
        SubprocesoLWP
        donde LWP representa el pid del proceso ligero y NLWP representa la cantidad de subprocesos en el grupo de subprocesos. Se puede ver que los pids de los cuatro subprocesos (un subproceso principal) de a.out son todos 4289, lo que indica que estos subprocesos están en una relación paralela; los pids son todos 29129, lo que indica que no existe una relación padre-hijo entre subprocesos; cada subproceso está asociado con un LWP En consecuencia, son 4289, 4290, 4291, 4292; estos 4 subprocesos forman un grupo de subprocesos, por lo que el NLWP es 4.

      • Modelo de subproceso mixto N:M: NGPT, subprocesos POSIX de próxima generación, terminado

      Modelo de roscado NM

    PD: ¿Por qué es un subproceso del kernel, no un proceso del kernel? Dado que el subproceso del kernel se ejecuta en el kernel, comparte el mismo espacio del kernel y la misma tabla de páginas del kernel.

    • Pila de subprocesos
      Cada subproceso tiene su propia pila de subprocesos única después de la creación. A diferencia de la pila de procesos cuyo tamaño crece dinámicamente, el tamaño de la pila de subprocesos es fijo. Cada pila de subprocesos tiene un límite de tamaño determinado. Una vez que el acceso del subproceso supera el límite, se informará un error. Desde esta pila de subprocesos, se puede asignar cerca del montón (de arriba a abajo cerca de la parte superior del montón), o se puede asignar cerca de la pila (el que se ejecuta solo es diferente del que se encuentra en el documento de referencia ).
      1. asignar cerca del montón

        Ejecutar ulimit -apara ver el tamaño de la pila:

        Ejecute código en el modelo de subprocesamiento, observando cat /proc/<pid>/mapsel espacio de direcciones del proceso.
        Puede ver que debajo del montón, además de la pila del subproceso principal, hay 3 pilas de subprocesos y sus límites:

        inserte la descripción de la imagen aquí

      2. Asigne cerca de la pila
        ulimit -s unlimitedy establezca el tamaño de la pila en ilimitado. Tenga en cuenta que aunque el tamaño de la pila se establece en ilimitado, en realidad no es ilimitado, sino una pila de subprocesos de tamaño fijo.
        Ejecute código en el modelo de subprocesamiento, observando cat /proc/<pid>/mapsel espacio de direcciones del proceso.
        Se encuentra que las posiciones relativas del área de memoria compartida, la pila, el montón, el área de código y el área de datos han cambiado.
        inserte la descripción de la imagen aquí
        inserte la descripción de la imagen aquí
        Pero lo que es seguro es que cada subproceso tiene su propia pila de subprocesos y el tamaño de la pila de subprocesos es fijo.

        PD: pila de usuario y pila de kernel: cada proceso tiene 2 pilas, pila de usuario y pila de kernel, cuando el sistema de proceso llama o interrumpe, caerá en el estado del kernel, en este momento, la pila de estado del usuario se guardará en el kernel stack y luego cambie el puntero de la pila a la dirección de la pila del kernel. Dado que la pila del kernel siempre está vacía cuando el proceso se transfiere del estado del usuario al estado del kernel, la dirección superior de la pila del kernel se puede guardar directamente en el registro del puntero de la pila. Al mismo tiempo, el proceso mencionado aquí significa que solo hay un hilo principal. Los procesos y subprocesos no comparten una pila de kernel (¿quizás una por LWP?).

Cambio de contexto de la CPU

  • Cambio de contexto de proceso:
    1. Tabla de páginas: correspondiente a los recursos de memoria virtual
    2. Tabla de descriptores de archivos/tabla de archivos abiertos: correspondiente a los recursos de archivos abiertos
    3. Registros: correspondientes a los datos de tiempo de ejecución
    4. Información de control de señales/información de ejecución del proceso
  • Cambio de contexto de subprocesos: en el mismo proceso:
    1. Pila
    2. Registros: correspondientes a datos de tiempo de ejecución

PD: El artículo está escrito con referencia a muchos artículos. Dado que no he leído el código fuente del kernel de Linux, puedo entender algo mal. Por favor, ayúdame a señalarlo.

  1. Espacio de direcciones virtuales de procesos y archivos ejecutables de Linux: https://cloud.tencent.com/developer/article/1624718
  2. Procesar espacio de direcciones, montón y relación de pila: https://blog.csdn.net/icandoit_2014/article/details/56666569
  3. La diferencia entre proceso e hilo: https://harmonyos.51cto.com/posts/659
  4. El proceso de ejecución del proceso en la cpu: https://blog.csdn.net/hunter___/article/details/82906540
  5. Cambio de proceso Kernel-Linux: https://r00tk1ts.github.io/2017/08/26/Linux%E8%BF%9B%E7%A8%8B%E5%88%87%E6%8D%A2/
  6. Conceptos relacionados con los subprocesos de proceso de Linux: https://segmentfault.com/a/1190000019066170
  7. Tres implementaciones de hilos: https://blog.csdn.net/gatieme/article/details/51892437
  8. subproceso posix: https://cloud.tencent.com/developer/article/1008758
  9. Subprocesos del núcleo, procesos ligeros y conceptos de subprocesos de usuario: https://blog.csdn.net/gatieme/article/details/51481863
  10. Son los subprocesos creados por la biblioteca pthread subprocesos de nivel de usuario o subprocesos de nivel de kernel: https://www.zhihu.com/question/35128513
  11. Hilos ordinarios e hilos del kernel: https://www.cnblogs.com/alantu2018/p/8526916.html
  1. ¿Cuál es la diferencia entre la pila de usuario y la pila del núcleo? https://www.jianshu.com/p/6b2ec520ae02
  2. Hilo y pila: https://network.51cto.com/art/202006/619565.htm
  3. Cómo asignar la pila de subprocesos: 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. Varias pilas en Linux: pila de procesos, pila de hilos, pila de núcleo, pila de interrupción: https://juejin.cn/post/6844903686003490830
  5. La diferencia entre la pila de usuario y la pila de kernel: https://www.jianshu.com/p/6b2ec520ae02
  6. Pila de usuario/pila de kernel: https://www.huaweicloud.com/articles/359d7c5a96089200119047b61da76098.html
  7. Este artículo le permite comprender el cambio de contexto de la CPU: https://zhuanlan.zhihu.com/p/52845869
  8. Método de conmutación de subprocesos de Linux y conmutación de procesos

Supongo que te gusta

Origin blog.csdn.net/iamanda/article/details/116163927
Recomendado
Clasificación