Estoy leyendo "Sistema operativo: tres piezas sencillas". En el capítulo 5, hay un fragmento de código que muestra el uso de la llamada al sistema exec().
1 #include "common.h" 2 3 int main(int argc, char ** argv) { 4 printf("hello world (pid: %d)\n", (int) getpid()); 5 int rc = fork(); 6 if (rc < 0) { 7 fprintf(stderr, "fork failed\n"); 8 exit(1); 9 } else if (rc == 0) { 10 printf("hello, I am child (pid: %d)\n", (int) getpid()); 11 char *myargs[3]; 12 myargs[0] = strdup("wc"); 13 myargs[1] = strdup("p3.c"); 14 myargs[2] = NULL; 15 execvp(myargs[0], myargs); // run work count 16 printf("this shouldn't print out\n"); 17 } else { 18 int wc = wait(NULL); 19 printf("hello, I am parent of %d (wc: %d) (pid: %d)\n", rc, wc, (int) getpid()); 20 } 21 return 0; 22 } 23¿Por qué se usa strdup() en la línea 12~14? He intentado reemplazar esta parte con lo siguiente:
12 myargs[0] = "wc"; 13 myargs[1] = "p3.c"; 14 myargs[2] = NULL;Funciona igual que el anterior.
Su versión del código está bien. Contrariamente a la respuesta de Werner, no causará un comportamiento indefinido.
Según la especificación POSIX , el prototipo de función para execvp es
int execvp(const char *file, char *const argv[]); Tomado en sí mismo, esto sugeriría que el contenido de las cadenas en la matriz argv podría modificarse, aunque los elementos punteros de esa matriz no podrían modificarse. Si fuera así, sería necesario pasar una matriz de punteros a cadenas de escritura, como proporcionaría strdup , y no literales de cadena, ya que escribir en un literal de cadena es un comportamiento indefinido. Esto puede haber sido lo que estaba pensando el autor del código.
Sin embargo, el texto de la especificación nos da más información:
Las matrices de punteros argv[] y envp[] y las cadenas a las que apuntan esas matrices no se modificarán mediante una llamada a una de las funciones exec, excepto como consecuencia de la sustitución de la imagen del proceso.
Entonces, de hecho, se garantiza que execvp no modificará esas cadenas. Como tal, es perfectamente seguro pasar un literal de cadena. Es posible que el autor del código original no supiera acerca de esta garantía.
La justificación más abajo explica por qué eligieron el prototipo como lo hicieron ("La declaración acerca de que argv[] y envp[] son constantes se incluye en..."). Como sugiere Werner, const char *const argv[] parecería expresar mejor el comportamiento real. Sin embargo, las reglas de conversión de punteros de C significan que si lo hubieran hecho, entonces codificarían como
char *args[5]; args[0] = /* some writable string */; ... execvp(file, args); no compilaría, aunque en principio está perfectamente bien. La justificación incluye una diatriba cortésmente velada sobre la semántica de const en C y cómo hacen que sea imposible escribir esto correctamente.
Hay más discusión en ¿Puedo pasar una matriz const char* a execv? .
El código se compila con su cambio, pero podría desencadenar un comportamiento indefinido.
execvp espera un parámetro char *const argv[] . No es la const que falta en el char . Entonces, execvp es libre de escribir en las cadenas que se le pasan.
Los literales de cadena en C no son modificables:
Los literales de cadena no se pueden modificar (y, de hecho, se pueden colocar en la memoria de solo lectura, como .rodata). Si un programa intenta modificar la matriz estática formada por un literal de cadena, el comportamiento no está definido.
Entonces, el autor del código quiere estar seguro y strdup s los literales de cadena para obtener una versión modificable de la cadena.
Tenga en cuenta que C ++ es más estricto y que gcc emitiría
advertencia: ISO C++ prohíbe convertir una constante de cadena a 'char*' [-Wwrite-strings]
por tu código.
Si execvp no modifica las cadenas de argumentos, sería mejor si tuviera una firma que expresara esto ( const char *const argv[] ).