problema de valor de retorno del sistema

Llamar a la función del sistema para ejecutar un comando de Shell devuelve -1 y el mensaje de error es que no hay procesos secundarios, pero el sistema se puede ejecutar correctamente.

La razón es que hay una declaración que ignora SIGCHLD antes de llamar al sistema.

señal(SIGCHLD, SIG_IGN);

Si el comportamiento de la señal SIGCHLD se establece en SIG_IGN, la función waitpid() puede informar un error ECHILD porque no puede encontrar el proceso hijo . Parece que hemos encontrado una solución al problema: restablecer la señal SIGCHLD al valor predeterminado, es decir, señal (SIGCHLD, SIG_DFL) antes de llamar a la función system().

O hay otra llamada waitpid en el programa mismo, y la captura de señal solo activa un lugar, lo que hace que el sistema capture internamente la señal CHILD y establezca el errno global del sistema en 10 para no generar procesos secundarios.

La solución es utilizar la función pox_system () en lugar de system (), aquí solo es necesario modificar una función y no es necesario modificar otras ubicaciones de llamada.

typedef void (*sighandler_t)(int);  
int pox_system(const char *cmd_line)  
{  
   int ret = 0;  
   sighandler_t old_handler;  
  
   old_handler = signal(SIGCHLD, SIG_DFL);  
   ret = system(cmd_line);  
   signal(SIGCHLD, old_handler);  
  
   return ret;  
}  

SIG_DFL: Controlador de señales predeterminado
SIG_IGN: Controlador de señales ignoradas

Realmente funciona después de la prueba, ¡gracias al autor de la publicación!
Artículo de referencia: http://my.oschina.net/renhc/blog/54582

El texto original es el siguiente:

Hoy, un programa que había estado funcionando durante casi un año murió repentinamente. El problema se identificó como un problema con la función system() . El uso simple de esta función se presentó en mi último artículo: http://my.oschina. net/renhc/blog/53580

Veamos primero el problema.

Simplemente encapsule la función system() :

int pox_system(const char *cmd_line)
{
    return system(cmd_line);
}

Llamada de función :

int ret = 0;
ret = pox_system("gzip -c /var/opt/I00005.xml > /var/opt/I00005.z");
if(0 != ret)
{
    Log("zip file failed\n");
}

Síntoma: zip falla cada vez que se ejecuta aquí. Sin embargo, siempre es correcto sacar este comando y ejecutarlo en el shell, de hecho, este código se ha estado ejecutando durante mucho tiempo y nunca ha tenido ningún problema.

mal registro

Al analizar el registro, solo podemos ver nuestro mensaje personalizado "Error en el archivo zip". En cuanto a por qué falló, no tenemos idea.

Bueno, primero intentemos encontrar más pistas:

int ret = 0;
ret = pox_system("gzip -c /var/opt/I00005.xml > /var/opt/I00005.z");
if(0 != ret)
{
    Log("zip file failed: %s\n", strerror(errno)); //尝试打印出系统错误信息
}

Agregamos el registro y, a través del error establecido por la función system() , obtuvimos una pista muy útil: la función system() falló debido a "No hay procesos secundarios". Continúe buscando la causa raíz.

quien se movió errno

De las pistas anteriores, sabemos que la función system() establece errno en ECHILD. Sin embargo, no podemos encontrar ninguna información sobre EHILD en el manual de la función system(). Sabemos que el proceso de ejecución de la función system() es: fork()->exec()->waitpid(). Obviamente waitpid() tiene serias sospechas. Revisemos el manual man para ver si es posible configurar ECHILD en esta función :

ENIÑO

(para waitpid() o waitid()) El proceso especificado por pid (waitpid()) o idtype e id (waitid()) no existe o no es hijo del proceso que llama. (Esto puede sucederle al propio hijo si la acción para SIGCHLD está configurada en SIG_IGN. Consulte también la sección de Notas de Linux sobre subprocesos).

Como era de esperar, si el comportamiento de la señal SIGCHLD se establece en SIG_IGN, la función waitpid() puede informar un error ECHILD porque no puede encontrar el proceso hijo . Parece que hemos encontrado una solución al problema: restablecer la señal SIGCHLD al valor predeterminado, es decir, señal (SIGCHLD, SIG_DFL) antes de llamar a la función system(). ¡Estamos tan entusiasmados que no veremos la parte de Notas de Linux por el momento y solo agregaremos pruebas de código! Cariño, ¡el problema está resuelto!

¿Es este tu estilo de manejar los problemas?

Justo cuando estábamos ansiosos por verificar el código, surgió una pregunta: "¿Por qué no ocurrió este error antes?" Sí, ¿por qué un programa que funciona bien se cuelga de repente? En primer lugar, nuestro código no ha cambiado, por lo que debe ser un factor externo. Cuando pensamos en factores externos, empezamos a quejarnos: "¡Debe ser el programa del otro grupo el que nos afecta!" Pero quejarse es inútil, si así lo crees, ¡por favor muestra evidencia! Pero después de calmarse y analizar, no es difícil encontrar que esto no puede ser la influencia de otros programas y que otros procesos no pueden afectar la forma en que nuestro proceso maneja las señales.

La función system() no daba error antes porque la función systeme() se basa en una característica del sistema, es decir, el método de procesamiento de la señal SIGCHLD cuando el kernel inicializa el proceso es SIG_DFL. ¿Qué significa esto? Es decir, el kernel envía una señal SIGCHLD al proceso después de descubrir que el proceso hijo del proceso ha terminado. Después de que el proceso recibe la señal, utiliza el método SIG_DFL para procesarla. Entonces, ¿qué es el método SIG_DFL? SIG_DFL es una macro que define un puntero de función de procesamiento de señales , de hecho, la función de procesamiento de señales no hace nada. Esta característica es exactamente lo que necesita la función system() . La función system() primero bifurca() un proceso hijo para ejecutar el comando. Después de la ejecución, la función system() utilizará la función waitpid() para matar al niño. proceso.

A través del análisis anterior, podemos saber claramente que antes de que se ejecute system(), el método de procesamiento de la señal SIGCHLD debe haber cambiado. Ya no es SIG_DFL. En cuanto a en qué se convertirá, aún no lo sabemos. De hecho, No necesitamos saberlo, solo debemos recordar cambiar explícitamente el método de procesamiento de señal SIGCHLD al método SIG_DFL antes de usar la , registrar el método de procesamiento original al mismo tiempo y luego configurarlo al procesamiento original. método después de usar system(). De esta manera, podemos proteger el impacto causado por actualizaciones del sistema o cambios en los métodos de procesamiento de señales.

Verificar conjetura 

Nuestra empresa adopta un modelo de integración continua + desarrollo ágil. Un equipo dedicado es responsable de las pruebas de casos automatizadas todos los días. Cada vez se llama compilación. Analizamos la versión del sistema utilizada en esta compilación y en la compilación anterior, y descubrimos que la versión tiene de hecho ha sido actualizado. Entonces encontramos al equipo relevante para la verificación, describimos el problema en detalle y la otra parte rápidamente dio su opinión. La siguiente es la respuesta por correo electrónico original:

LIBGEN ha agregado recientemente el procesamiento SIGCHLD. Ignoralo. Para evitar la generación de procesos zombies.

¡Parece que nuestra suposición era correcta! Después de analizar el problema aquí, la solución es clara, por lo que modificamos nuestra función pox_system() :

typedef void (*sighandler_t)(int);
int pox_system(const char *cmd_line)
{
    int ret = 0;
    sighandler_t old_handler;	
    old_handler = signal(SIGCHLD, SIG_DFL);
    ret = system(cmd_line);
    signal(SIGCHLD, old_handler);
    return ret;
}

Creo que esta es una solución perfecta para llamar al sistema (). Al mismo tiempo, usar la encapsulación de la función pox_system () brinda una gran facilidad de mantenimiento. Aquí solo necesitamos modificar una función , y no es necesario modificar otros lugares de llamada. .

Más tarde, verifiqué el código modificado por la otra parte y encontré la respuesta en el código:


/* Ignore SIGCHLD to avoid zombie process */
if (signal(SIGCHLD, SIG_IGN) == SIG_ERR) {
    return -1;
} else {
    return 0;
}

Otros pensamientos

El código de nuestra empresa se gestiona mediante el proceso SVN. Hasta ahora, hay muchas ramas. Poco a poco, casi todas las ramas tienen el problema anterior, así que solucioné este problema en cada rama una por una. Me tomó casi un día solucionarlo. porque algunas ramas han sido bloqueadas. Si quiero fusionar el código, tengo que encontrar a la persona a cargo correspondiente para explicar la gravedad del problema y probarlo en diferentes entornos. Mientras hacía esto, pensé, ¿es apropiado ¿Actualizar el sistema de esta manera?

En primer lugar, debido a la actualización del sistema, se descubrieron problemas en nuestro código durante las pruebas y nos apresuramos a solucionarlos en ese momento, lo que nos hizo ser pasivos. Creo que fue un error de su parte. La mejora que realices debe considerar el impacto en otros equipos, ¿verdad? Es más, lo que estás haciendo es una actualización del sistema. Antes de actualizar, es necesario realizar una evaluación de riesgos e informar a todos sobre los posibles impactos. Esto es sólo profesional.

Además, según ellos, el método de procesamiento de señales se modificó para evitar procesos zombies. Por supuesto, la intención original era buena, pero dicha actualización afectó el uso de algunas funciones, como la función system(), la función wait( ) , y waipid ( ), función fork () , estas funciones están relacionadas con el proceso hijo. Si desea utilizar wait () o waitpid () para recopilar el cadáver del proceso hijo, debe utilizar el método introducido anteriormente: antes llamando (en realidad antes de fork() ) Configure la señal SIGCHLD en el modo de procesamiento SIG_DFL y luego configure el modo de procesamiento de señal en el valor anterior después de la llamada (de hecho, después de wait()/waitpid()). La actualización de su sistema obliga a todos a mejorar el código, lo que de hecho ha mejorado la calidad del código, pero no estoy de acuerdo con esta actualización. Piénselo, ¿cuántos códigos ha visto que configuran la señal SIGCHLD antes y después de fork()? ->esperapid()?

Sugerencias sobre el uso de la función system()

El artículo anterior proporciona una forma relativamente segura de llamar a la función system(), pero el uso de la función system() aún es propenso a errores. Ese es el valor de retorno de la función system() . Para obtener una introducción a su valor de retorno , consulte el artículo anterior. La función system() a veces es conveniente, ¡pero no se debe abusar de ella!

1. Se recomienda que la función system() solo se use para ejecutar comandos de shell, porque en términos generales, si el valor de retorno de system() no es 0, significa que se ha producido un error;

2. Se recomienda monitorear el valor de errno después de que se complete la ejecución de la función system () y esforzarse por proporcionar más información útil cuando ocurre un error;

3. Se recomienda considerar la función de reemplazo popen() de la función system() ;

Supongo que te gusta

Origin blog.csdn.net/HideInTime/article/details/130867057
Recomendado
Clasificación