Programación del sistema Linux C (07) gestión de procesos control de procesos

1 identificador de proceso

1.1 Usuarios reales / grupos de usuarios; usuarios efectivos / grupos de usuarios

En el proceso de Linux / Unix, intervienen múltiples ID de usuario e ID de grupo de usuarios, que incluyen:

  1. ID de usuario real e ID de grupo de usuario real: Identifique quién soy (se dice que este es un problema filosófico pervertido, es difícil matar a un filósofo). Es decir, el uid y el gid del usuario que ha iniciado sesión. Por ejemplo, mi Linux ha iniciado sesión con taskiller. La ID de usuario real de todos los comandos que se ejecutan en Linux es el uid de taskiller, y la ID del grupo de usuarios real es el gid de taskiller (puede usar el comando id para ver) .
  2. Identificación efectiva del usuario e identificación efectiva del grupo de usuarios: los procesos se utilizan para determinar nuestro acceso a los recursos. En general, la ID de usuario efectiva es igual a la ID de usuario real, y la ID de grupo de usuario efectiva es igual a la ID de grupo de usuario real. Cuando se establece el bit de configuración de ID de usuario (SUID), la ID de usuario efectiva es igual al uid del propietario del archivo, no la ID de usuario real; de manera similar, si se establece el bit de ID de grupo de usuario de configuración (SGID), el grupo de usuarios efectivo El ID es igual al gid del propietario del archivo, no el ID del grupo de usuarios real.

1.2 ID del proceso

Los atributos básicos de un proceso son similares al número de identificación de cada persona. Según la identificación del proceso, un proceso puede determinarse con precisión y los identificadores de varios procesos pueden corresponder a un programa.

1.3 Valor de ID importante en el proceso

Cada proceso tiene 6 valores de ID importantes, a saber, ID de proceso, ID de proceso principal, ID de usuario efectivo, ID de grupo efectivo, ID de usuario real, ID de grupo de usuario real. Estas 6 ID se almacenan en la estructura de datos del núcleo. Solo a veces los usuarios necesitan estos ID. En Linux, use las funciones getpid y getppid para obtener la ID del proceso y la ID del proceso principal. El prototipo de la función es el siguiente:

#include <sys/types.h>
#include <unistd.h>
pid_t getpid(void);
pid_t getppid(void);

Consulte el manual de referencia de la función de Linux para más detalles . En Linux, use las funciones getuid y geteuid para obtener el usuario real y el usuario efectivo del proceso. El prototipo de la función es el siguiente:

#include <unistd.h>
#include <sys/types.h>
uid_t getuid(void);
uid_t geteuid(void);

Consulte el manual de referencia de la función de Linux para más detalles . En Linux, use las funciones getgid y getegid para obtener el ID de grupo de usuario real y el ID de grupo de usuario efectivo del proceso. El prototipo de la función es el siguiente:

#include <unistd.h>
#include <sys/types.h>
gid_t getgid(void);
gid_t getegid(void);

Consulte el manual de referencia de la función de Linux para más detalles. Nota:

  1. Los dos identificadores de la ID del proceso y la ID del proceso principal no se pueden cambiar, y las otras 4 ID se pueden cambiar en circunstancias apropiadas.
  2. Para un proceso general, la ID de usuario real y la ID de usuario efectiva son las mismas, solo que diferentes en algunas ocasiones especiales.    

2 Operación de proceso

2.1 La función fork crea un proceso

El proceso es la unidad básica de ejecución en el sistema. El sistema Linux permite que cualquier proceso de usuario cree un proceso hijo. Después de la creación, el proceso secundario existe en el sistema y es independiente del proceso primario. El proceso secundario puede aceptar la programación del sistema y puede asignar recursos del sistema. El sistema también puede detectar su existencia y darle el mismo poder que el proceso padre. (Nota: en Linux, todos los procesos son creados por otros procesos excepto el proceso No. 0)
En Linux, use la función fork para crear un nuevo proceso. El prototipo de la función fork:

#include <unistd.h>
pid_t fork(void);
函数执行成功有两个返回值;为0,表示子进程;为正数,表示父进程。失败则返回-1。

Consulte el manual de referencia de la función de Linux para más detalles. Nota:

  1. En circunstancias normales, cuando el programa se está ejecutando, no se puede garantizar que el proceso principal o el proceso secundario se ejecuten primero, y se deben realizar operaciones adicionales para garantizar que el proceso se esté ejecutando.
  2. Para la función fork, el proceso padre y el proceso hijo comparten el segmento de código, pero otros recursos, como el segmento de datos y el segmento de pila, se copian completamente del proceso padre.
  3. Cuando el proceso secundario hereda el proceso primario, el bloqueo de archivo, la señal de alarma no procesada y la señal pendiente no se heredarán.
  4. El kernel actual de Linux a menudo implementa la función fork cuando el proceso secundario copia recursos antes que el proceso primario. Cuando el proceso secundario modifica estos contenidos, se producirá la copia, y el núcleo asignará espacio de proceso al proceso secundario para copiar el contenido en el proceso primario , Continúe con la siguiente operación. Esta es realmente una manifestación importante de la operación de tiempo de escritura.

Situación de error de la función de horquilla:

  1. El número de procesos en el sistema excede el límite especificado por el sistema.    
  2. Hay demasiados procesos de usuario que llaman a la función fork.

2.2 vfork crea un proceso

Linux proporciona una función vfork similar a la función de la función fork, la diferencia entre ellas es:

  1. Fork es el segmento de datos y el segmento de código del proceso secundario que copia el proceso primario; vfork es el segmento de datos compartido por el proceso secundario y el proceso primario 
  2. El orden de ejecución de los procesos padre e hijo es incierto; vfork garantiza que el proceso hijo se ejecute primero. Antes de llamar a exec o exit, los datos se comparten con el proceso padre. El proceso padre se puede programar para que se ejecute después de que llame a exec o exit. 
  3. Vfork se asegurará de que el proceso secundario se ejecute primero, y el proceso principal se puede programar para que se ejecute después de que llame a exec o salga. Si el proceso hijo depende de acciones adicionales del proceso padre antes de llamar a estas dos funciones, provocará un punto muerto.

Prototipo de la función vfork:

#include <sys/types.h>
#include <unistd.h>
pid_t vfork(void);
函数执行成功有两个返回值;为0,表示子进程;为正数,表示父进程。失败则返回-1。

Consulte el manual de referencia de la función de Linux para más detalles . Nota: Para las funciones de vfork, generalmente no llame a funciones que no sean main. La razón es que el proceso secundario se ejecuta antes que el proceso primario y se sobrescribe el marco de la pila. Finalmente, se produce una falla predeterminada cuando el proceso primario opera. Es decir, la influencia del proceso hijo en el proceso padre es enorme.

2.3 Salir de un proceso

Salir de un proceso en Linux generalmente usa la función de salida. El prototipo de la función de salida:

#include <unistd.h>
void exit(int status);
参数status:表示的是进程退出的状态,这个状态值是一个整型。在shell中可以检查到退出的状态。正常退出,exit中的参数为0,异常退出为非0。

Consulte el manual de referencia de la función de Linux para más detalles . Puede usar la variable errno como parámetro para pasar a la función de salida, de modo que pueda verificar el motivo de la salida del programa después de que el programa salga. Es decir, la causa del error se puede determinar en el shell.    

La función de salida en realidad encapsula la función _exit llamada por el sistema Linux. La principal diferencia entre los dos es que la función de salida hará un trabajo de cuidado posterior en el espacio del usuario, como sincronizar el contenido en el disco, borrar el búfer del usuario, etc., y luego ingresar al núcleo para liberar El espacio de direcciones del proceso del usuario. La función _exit ingresa directamente al núcleo para liberar el espacio de direcciones del usuario, y se perderá todo el contenido del búfer del espacio del usuario.

2.4 Establecer propietario del proceso

Cada proceso tiene dos ID de usuario, la ID de usuario real y la ID de usuario efectiva. Use setuid en linux para cambiar la ID de usuario real y la ID de usuario efectiva de un proceso. Prototipo de función setuid:

#include <sys/types.h>
#include <unistd.h>
int setuid(uid_t uid);
参数uid:改变后的新用户ID;函数执行成功返回0,失败返回-1。

Consulte el manual de referencia de la función de Linux para más detalles . Solo dos tipos de usuarios pueden modificar la ID de usuario real y la ID de usuario efectiva del proceso: seguir al usuario y al usuario igual a la ID de usuario real del proceso.
La situación general es que un proceso debe tener una cierta autoridad, establecer la identificación de usuario efectiva a una ID de usuario con dicha autoridad. Cuando el proceso no necesita dicha autoridad, el proceso restaura su identificación de usuario efectiva para restaurar su autoridad. Para la función seteuid (uid_t euid), solo se cambia la ID de usuario efectiva. La misma serie de funciones también tiene funciones setgid y setegid, que son similares a las funciones setuid y seteuid, respectivamente, excepto que la identificación del grupo se ve afectada.

2.5 Depuración de múltiples procesos

Hay dos formas de depurar multiprocesos con gdb:
@ 1 Establecer la secuencia de seguimiento: El método de configuración es el siguiente:
establecer el modo seguir-tenedor [principal | secundario]
Seleccione un proceso para realizar un seguimiento, el otro proceso no se ve afectado. Luego establezca un punto de interrupción en el código del subproceso.
Si desea desconectar la prueba de un proceso después de la función fork, use el comando:

    set detach-on-fork [on,off]
    #若选中on,则断开调试follow-fork-mode指定的进程。
    #若选中off,gdb将控制父进程和子进程

@ 2 Use el comando de conexión: El comando de conexión en el depurador gdb puede depurar un programa que ya se está ejecutando. Después de que el proceso llama a la función fork, puede usar el comando de conexión para depurar el subproceso. La premisa es conocer el ID de proceso del proceso secundario, y el proceso secundario puede esperar el inicio de la depuración, por lo tanto, agregue código auxiliar cuando use el comando adjuntar.


3 procedimientos de ejecución

3.1 funciones familiares ejecutivas

Use la función exec para ejecutar un nuevo programa en el entorno Linux. Esta función busca el archivo del sistema en la ruta especificada y copia el contenido del archivo en el espacio de direcciones de la función exec para reemplazar el contenido del proceso original. El contenido del espacio del proceso, excepto que el segmento de código y el segmento de datos del proceso han sido reemplazados. (Nota: la función exec no crea un proceso nuevo. Aunque el contenido del proceso ha cambiado, la ID del proceso no ha cambiado y sigue siendo un proceso).
Hay seis funciones de la familia exec:

  1. El sufijo l indica la lista, lo que indica que los parámetros de la línea de comandos del programa de ejecución se proporcionan en una lista y terminan en NULL, pero el número de parámetros no está limitado. Reciba una lista de parámetros separados por comas.
  2. El vector tiene el sufijo v, lo que indica que los parámetros de la línea de comandos del programa de ejecución se proporcionan en forma de matriz bidimensional. Recibe un puntero a una serie de cadenas que terminan en NULL.
  3. El sufijo e representa el entorno, que representa la lista de variables de entorno pasadas al nuevo programa. Esta lista es una matriz bidimensional, y cada fila es una variable de entorno.
  4. La función exec que termina con p indica que el primer parámetro no es un nombre de ruta completo, sino un nombre de programa. Esto requiere que la variable de entorno PATH y este parámetro se combinen en una ruta completa.

El prototipo de la función de familia ejecutiva es el siguiente:

#include <unistd.h>
extern char **environ;
int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg, ..., char * const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execve(const char *filename, char *const argv[], char *const envp[]);

Consulte el manual de referencia de la función de Linux para más detalles .

3.2 Ejecución de comandos de shell en el programa
Use la función del sistema para llamar a comandos de shell en Linux El prototipo de la función del sistema es el siguiente:

#include <stdlib.h>
int system(const char *command);

Consulte el manual de referencia de la función de Linux para más detalles . El comando de parámetro es el comando a ejecutar. El valor de retorno de la función es más complicado. De hecho, la función del sistema encapsula las tres llamadas del sistema fork, exec y waitpid. El valor de retorno también debe discutirse de acuerdo con la situación de estas llamadas del sistema:

  1. Si las funciones fork y waitpid no se ejecutan, la función del sistema devuelve -1.
  2. Si la función exec no se ejecuta, la función devuelve que el archivo no es ejecutable.
  3. Si las tres funciones se ejecutan con éxito, la función del sistema vuelve al estado de terminación del programa de ejecución.
  4. Si el valor del comando de parámetro es NULL, la función del sistema devuelve 1, de hecho, esto puede usarse para probar si el sistema admite la función del sistema.

El uso de la función del sistema requiere una mirada cuidadosa al análisis de requisitos. En general, el sistema tiene las siguientes ventajas:

  1. La función del sistema agrega operaciones de manejo de errores.
  2. La función del sistema agrega operaciones de procesamiento de señal
  3. La función del sistema llama a la función de espera para garantizar que no haya procesos zombies.

4 operaciones relacionales

Para el proceso secundario, el proceso primario puede obtener el estado en el momento de su salida. La operación de obtener la información de inicio del proceso se denomina operación relacional. El kernel de Linux guarda una cierta cantidad de información para cada proceso hijo terminado, incluida la ID del proceso, el estado de finalización del proceso y las estadísticas del proceso. Esta información es obtenida por el proceso principal y procesada en consecuencia

4.1 Dos funciones esperando que el proceso salga

Use la función de espera y la función waitpid en Linux para obtener algunas estadísticas del proceso secundario. El prototipo de las funciones wait y waitpid:

#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status);
pid_t waitpid(pid_t pid, int *status, int options);

Consulte el manual de referencia de la función de Linux para más detalles .
En comparación con la función waitpid y wait, existen los siguientes tres puntos:

  1. La función waitpid puede especificar un proceso hijo.
  2. La función waitpid puede esperar un proceso sin bloqueo.
  3. La función waitpid admite el control de trabajos.

4.2 Proceso zombie

Cuando el proceso secundario sale, la información del estado de salida del proceso se almacena en el núcleo. En este momento, el proceso primario no llama a la función de espera para procesar. El ID del proceso secundario también se guarda en la lista de procesos del sistema. El proceso en este momento se llama Proceso zombie. El proceso zombie es una gran amenaza para el sistema. Ocupa recursos del sistema pero no hace nada. Crear un proceso zombie es llamar primero a la función fork, el proceso padre no necesita la función de espera. El proceso zombie se indica como Z cuando se ve.

Formas de resolver el proceso zombie:

  1. El proceso primario espera a que el proceso secundario finalice a través de funciones como wait y waitpid, lo que hace que el proceso primario se bloquee.
  2. Si el proceso primario está ocupado, puede usar la función de señal para instalar un controlador para SIGCHLD, porque después de que finaliza el proceso secundario, el proceso primario recibirá la señal y puede llamar a esperar en el controlador para reciclar.
  3. Si el proceso padre no le importa cuándo terminará el proceso hijo, puede usar la señal (SIGCHLD, SIG_IGN) para notificar al kernel que no está interesado en el final del proceso hijo, luego de que el proceso hijo finalice, el núcleo se reciclará y ya no enviará señales al padre .
  4. Bifurca dos veces, el proceso padre bifurca un proceso hijo, y luego continúa trabajando, el proceso hijo bifurca un gran proceso y luego se cierra, luego el gran proceso es asumido por init, y después de que el gran proceso termina, init será reciclado. Sin embargo, el reciclaje del proceso secundario debe hacerlo usted mismo.

4.3 Estadísticas del proceso de salida

Las funciones wait3 y wait4 son básicamente equivalentes a la función wait y waitpid, la diferencia es que las funciones wait3 y wait4 también pueden obtener información más detallada. La mayor parte de esta información es sobre el núcleo, que se devuelve al usuario a través de la estructura. Los prototipos de las funciones wait3 y wait4 son los siguientes:

#include <sys/types.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/wait.h>
pid_t wait3(int *status, int options,struct rusage *rusage);
pid_t wait4(pid_t pid, int *status, int options, struct rusage *rusage);

Consulte el manual de referencia de la función de Linux para más detalles . Para obtener información más detallada, debe usar el parámetro de resultado del valor, la estructura correspondiente es Rusage.     

Publicado 289 artículos originales · elogiados 47 · 30,000+ vistas

Supongo que te gusta

Origin blog.csdn.net/vviccc/article/details/105153559
Recomendado
Clasificación