Ver la versión completa : [Ayuda] Linux y programación en C: ¿cómo modifico el nombre de un proceso "desde dentro"?
3L_S4N70
23/04/2010, 15:30
Hola gente:
En esto de Linux soy practicamente por completo autodidacta, y por tanto, algunas cosas se me escapan. Por eso no he sabido muy bien cómo explicar mi duda en el título. Os comento lo que quiero hacer, cómo lo tengo hecho hasta ahora, y mi duda, de tal manera que si me echáis por tierra lo que ya tengo hecho en pro de una mejor idea, yo la acepto.
Lo que quiero es que mediante un programa (llamémosle de control, al que mando comandos por una tubería no bloqueante de sólo lectura), ejecute otros en función de esos comandos.
- Por una parte lanzo unos en cuyo nombre llevan un identificador (un número al final) y que por tanto a la hora de matar (también por medio de otro comando) sólo he de buscar el que tenga tal nombre y matarlo.
- Por otra tengo otro que lanzo tantas veces como se le pida, al que se le manda un argumento (que define la IP a la que se conectará debido a que es un cliente de una estructura de sockets, pero digamos que esto es secundario). Es aquí donde surge mi problemilla: a la hora de matarlo, si lo busco por nombre, como lo lance las veces que lo lance, por mucho que el argumento sea distinto, el nombre será igual, así que cuando busque los PID para cepillármelo, me los cepillaré todos. Entonces pensaba en si se podría añadir en el "nombre de ejecución" (no sé cómo se llama, el nombre que aparece tras un ps) al final, por ejemplo, el agumento. Ejemplo:
./programa 23
Que en un ps, me dijese:
1567 (bueno, lo que diga, no tengo la máquina UNIX ahora) programa23
en lugar de:
1567 (bueno, lo que diga, no tengo la máquina UNIX ahora) programa
Y bueno, eso, si véis que lo que tengo hecho, se puede hacer mejor de otra manera, o queréis más información, o lo que sea, os agradeceré infinito que me lo comentéis.
Molto grazie de antemano.
Diría que lo mejor es que registres el PID de cada proceso al inicio con la función getpid(), y no que lo busques cuando quieras matarlo porque como dices es difícil de distinguir :) Otra opción es que inicies los procesos desde un programa que controles tú, y así al iniciarlos sabrás sus PIDs sin tener que buscarlos después.
Diría que el nombre del proceso no se puede cambiar en tiempo de ejecución porque esa es información interna que mantiene el kernel.
Ah, pues sí que hay una forma, aunque confieso que me ha parecido tan compleja que no la he leido en detalle :) http://stupefydeveloper.blogspot.com/2008/10/linux-change-process-name.html
3L_S4N70
23/04/2010, 16:08
Diría que lo mejor es que registres el PID de cada proceso al inicio con la función getpid(), y no que lo busques cuando quieras matarlo porque como dices es difícil de distinguir :) Otra opción es que inicies los procesos desde un programa que controles tú, y así al iniciarlos sabrás sus PIDs sin tener que buscarlos después.
Sí, el programa que lanza los programas es mío también, así que eso sí que lo podría hacer, sería un cambio en la filosofía, y que yo creo que me evitaría las movidas que me hago ahora para matar los malditos procesos, que a veces no me deja ni ná. Te agradecería que me ampliases un poco la info de cómo coger el PID a la hora de iniciarlo :D
Diría que el nombre del proceso no se puede cambiar en tiempo de ejecución porque esa es información interna que mantiene el kernel.
Ah, pues sí que hay una forma, aunque confieso que me ha parecido tan compleja que no la he leido en detalle :) http://stupefydeveloper.blogspot.com/2008/10/linux-change-process-name.html
Sí, creo que mejor paso de cambiar el nombre :lamer:
Qué maldita locura, jaja.
Tu programa controlador sería algo así (ojo que no lo he probado):
#define NUM_PROCESS 5
int main(){
int* pids=int[NUM_PROCESS]; // aquí guardaremos los PIDs
for(int i=0; i<NUM_PROCESS; i++){
pids[i]=fork(); // Llamada a fork! Ahora tenemos dos programas ejecutándose a la vez, padre e hijo
if(pids[i]==0){
// somos el hijo: nos cambiamos por el nuevo programa
execl("./programa", "./programa","argumento1","argumento2",(char *)0);
// ya deberíamos ser el nuevo programa, así que si seguimos aquí es por un error
printf("Error al ejecutar el programa\n");
// Los hijos nunca deberían salir de este if, así que por si alguno se escapa lo terminamos
exit(1);
}else if(pids[i]<0){
// somos el padre y hemos fallado al crear el hijo.
printf("Error en el fork()");
}else{
// Somos el padre y todo fue bien:
printf("He parido un nuevo retoño con PID=%d\n", pids[i]);
}
}
// Resto de código del controlador una vez que haya creado a sus hijos.
// Recuerda que hemos tenido cuidado para que los hijos nunca lleguen aquí
// Recuerda que los PIDs están en el array pids[]
// Y finalmente, recuerda que puedes esperar a que los hijos acaben con wait() y waitpid()
patata();
}
3L_S4N70
23/04/2010, 16:32
Mil gracias, juanvvc, a ver si luego le echo unas pruebas. Tengo que parir los programas en función de lo que me llegue por una tubería, pero bueno, yo creo que fusionando lo que me das y lo que tengo puedo llegar a algo :D
Si me surge cualquier cosilla lo comento.
Un saludete
Propeller
23/04/2010, 17:00
argv[0] es el nombre del proceso. Cámbialo y listo.
Propeller
3L_S4N70
23/04/2010, 17:15
argv[0] es el nombre del proceso. Cámbialo y listo.
Propeller
Ostras, no había caído en el detalle. Sólo que no sabía que cambiando el nombre a argv[0] afectaba (vamos, si me llegan a preguntar hubiera jurado que no hace caso y punto, jajaja).
Bien, probaré eso, aunque es probable que reestructure todo y acabe moviéndome por PID más que por nombre de proceso.
De todas maneras, muchísimas gracias por el comentario, Propeller.
Un saludo
Pero como bien sabe propeller, eso no funciona más que dentro del programa y además corres el riesgo de jorobar toda la cabecera como el nuevo nombre tenga más letras que el antiguo.
De todas formas lo acabo de probar porsiaca, y ps no hace caso de un nombre cambiado por argv[0] :)
(propeller, tienes la oportunidad de vengarte en el hilo de las estafas :))
3L_S4N70
23/04/2010, 17:35
Pero como bien sabe propeller, eso no funciona más que dentro del programa y además corres el riesgo de jorobar toda la cabecera como el nuevo nombre tenga más letras que el antiguo.
De todas formas lo acabo de probar porsiaca, y ps no hace caso de un nombre cambiado por argv[0] :)
(propeller, tienes la oportunidad de vengarte en el hilo de las estafas :))
Jajaja, ok, gracias por ahorrarme el currelo, juan :D
Nada, a ver si luego puedo probar lo de los execlv, fork, getpid y esas cosucas.
Propeller
23/04/2010, 17:36
Hombre, si usas el modificador 'u' para el ps, ves la modificación (al menos en Linux).
Propeller
Esta es la salida de uname -a (Debian sid): Linux currito 2.6.32-3-amd64 #1 SMP Wed Feb 24 18:07:42 UTC 2010 x86_64 GNU/Linux
Este es el programa que he probado, compilado con gcc -o prueba prueba.c
#include <stdio.h>
int main(int argc, char** argv){
printf("Nombre del programa %s\n", argv[0]);
argv[0]="hola";
printf("Nombre del programa %s\n", argv[0]);
while(1);
}
Y esta es la salida del comando "ps u" mientras está ejecutándose el anterior:
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
juanvi 4360 0.0 0.1 21536 4596 pts/2 Ss 18:13 0:00 /bin/bash
juanvi 4447 99.9 0.0 3652 404 pts/2 R+ 18:13 5:54 ./prueba
juanvi 4531 6.0 0.1 21544 4608 pts/1 Ss 18:18 0:00 /bin/bash
juanvi 4618 0.0 0.0 16076 1124 pts/1 R+ 18:18 0:00 ps u
¿Puede ser que no haya entendido tu propuesta?
Pues sí, id6490, tienes toda la razón. No sé porqué había pensado que strcpy() no era necesario, pero es que, claro, queremos que argv[0] siga apuntando a donde estaba apuntando.
3L_S4N70, te confirmo que como dicen con argv[0] se puede cambiar el nombre de un programa si usas strcpy, y al menos ps lo lista con el nuevo nombre. Bueno, en realidad también hay que cambiar la cabecera para que sea consciente del nuevo número de caracteres del nombre, pero si usas un nombre con el mismo número no parece que vayas a tener problemas.
tSuKiYoMi
23/04/2010, 23:00
una cosa para controlar el reconocimiento de la muerte de procesos me imagino que tendrás algun manejador de señales como este o parecido, no?
/*Tratamiento de la señal SIGTERM*/
void manejador_SIGTERM(int sig)
{
int error;
do
{
error = wait3(NULL,WNOHANG|WUNTRACED,NULL);
}while(error == ERROR || errno == EINTR);
}//fin de la funcion
lo digo porque si no gestionas bien la señal sigterm cuando matas un proceso puedes tener problemas.
3L_S4N70
23/04/2010, 23:59
Pues sí, id6490, tienes toda la razón. No sé porqué había pensado que strcpy() no era necesario, pero es que, claro, queremos que argv[0] siga apuntando a donde estaba apuntando.
3L_S4N70, te confirmo que como dicen con argv[0] se puede cambiar el nombre de un programa si usas strcpy, y al menos ps lo lista con el nuevo nombre. Bueno, en realidad también hay que cambiar la cabecera para que sea consciente del nuevo número de caracteres del nombre, pero si usas un nombre con el mismo número no parece que vayas a tener problemas.
Bueno, el tema es que a mi me interesa que el nombre crezca, luego me tocaría andar metiendo mano en la cabecera, así que me tiraré al tema de generar procesos hijos y tal (a ver cómo me las arreglo).
una cosa para controlar el reconocimiento de la muerte de procesos me imagino que tendrás algun manejador de señales como este o parecido, no?
/*Tratamiento de la señal SIGTERM*/
void manejador_SIGTERM(int sig)
{
int error;
do
{
error = wait3(NULL,WNOHANG|WUNTRACED,NULL);
}while(error == ERROR || errno == EINTR);
}//fin de la funcion
lo digo porque si no gestionas bien la señal sigterm cuando matas un proceso puedes tener problemas.
Ehm... pues no. Como comentaba al principio, estoy bastante en pañales en estos temas, entonces me has pillado completamente out. Hasta ahora, y os ruego me indiquéis la mejor manera, lo que hacía era cascar con un system() un kill a todos los procesos con el nombre que le indicase, pero muchas veces casca, o ni funciona, y no se cepilla los procesos.
tSuKiYoMi
24/04/2010, 00:16
para matar procesos puedes usar la función:
kill(pid_proceso ,señal);
KILL no sirve para matar procesos de forma explicita, sino para enviar señales a procesos, por tanto si quieres terminar con alguno (martarlo en la jerga) debes ejecutar:
kill(pid_proceso ,SIGTERM);, resulta que en algunas ocasiones cuando le envias esta señal de terminación al proceso este puede estar terminando de ejecutar algunas instrucciones por lo que no termina definitivamente hasta que acaba dichas instrucciones, sin embargo la señal la sigue recibiendo, una vez acabado si el proceso padre que lo creo no reconoce su "muerte" este proceso se queda en lo que se denomina estado "ZOMBI" es decir la PCB (PROCESS CONTROL BLOCK) del proceso sigue exsitiendo con todos sus descriptores de fichero abiertos (incluidos los sockets que son tmb despcriptores pero especiales ya que se encargan de la comuninicación entre maquinas) lo que implica que consume memoria de forma innecesaria. Por tanto necesitas crearte un manejador para que el proceso padre reconozca la muerte de sus hijos. Como puedes ver en el código que te ponía antes esto sea hace con las funciones de la familia WAIT. Yo te he puesto wait3 que es la que suelo usar yo, pero si te vas al MAN y buscas Wait te vienen todas las funciones de dicha familia y el significado de las constantes que puedes usar.
Otra recomendación que te hago es que te leas el MAN de SIGNAL y de KILL. Y tmb que si vas a lanzar otras señales que no sean SIGTERM como por ejemplo SIGINT,SIGCHLD, SIGUSR1, SIGUSR2, u otras te crees un manejador explicito para cada una de ellas, porque es la forma más comoda y segura de hacer las cosas bien.
Otra duda que tengo con tu programa es si los procesos que crean comparten variables o ficheros o ke????, si es esí los mismo te conviene crear un espacio de memoria común entre ellos para poder compartir descriptores de ficheros y de sockets de forma más sencilla y amena. PAra hacerlo en ese caso necesitaras usar la función:
mmap()
si es tu caso y vas compartir memoria entre porcesos tmb te recomiendo que te mires la documentación en MAN asociada ha esta función. De todas formas seguro que buscando en google encuentras ejemplos que te pueden aclarar más aun las cosas que te comento.:awesome:
3L_S4N70
24/04/2010, 00:37
para matar procesos puedes usar la función:
kill(pid_proceso ,señal);
KILL no sirve para matar procesos de forma explicita, sino para enviar señales a procesos, por tanto si quieres terminar con alguno (martarlo en la jerga) debes ejecutar:
kill(pid_proceso ,SIGTERM);, resulta que en algunas ocasiones cuando le envias esta señal de terminación al proceso este puede estar terminando de ejecutar algunas instrucciones por lo que no termina definitivamente hasta que acaba dichas instrucciones, sin embargo la señal la sigue recibiendo, una vez acabado si el proceso padre que lo creo no reconoce su "muerte" este proceso se queda en lo que se denomina estado "ZOMBI" es decir la PCB (PROCESS CONTROL BLOCK) del proceso sigue exsitiendo con todos sus descriptores de fichero abiertos (incluidos los sockets que son tmb despcriptores pero especiales ya que se encargan de la comuninicación entre maquinas) lo que implica que consume memoria de forma innecesaria. Por tanto necesitas crearte un manejador para que el proceso padre reconozca la muerte de sus hijos. Como puedes ver en el código que te ponía antes esto sea hace con las funciones de la familia WAIT. Yo te he puesto wait3 que es la que suelo usar yo, pero si te vas al MAN y buscas Wait te vienen todas las funciones de dicha familia y el significado de las constantes que puedes usar.
Otra recomendación que te hago es que te leas el MAN de SIGNAL y de KILL. Y tmb que si vas a lanzar otras señales que no sean SIGTERM como por ejemplo SIGINT,SIGCHLD, SIGUSR1, SIGUSR2, u otras te crees un manejador explicito para cada una de ellas, porque es la forma más comoda y segura de hacer las cosas bien.
Otra duda que tengo con tu programa es si los procesos que crean comparten variables o ficheros o ke????, si es esí los mismo te conviene crear un espacio de memoria común entre ellos para poder compartir descriptores de ficheros y de sockets de forma más sencilla y amena. PAra hacerlo en ese caso necesitaras usar la función:
mmap()
si es tu caso y vas compartir memoria entre porcesos tmb te recomiendo que te mires la documentación en MAN asociada ha esta función. De todas formas seguro que buscando en google encuentras ejemplos que te pueden aclarar más aun las cosas que te comento.:awesome:
Ostras, veo que manejas bastante tío. Pues verás, los procesos que genero no comparten nada entre sí, si acaso que se conecten al mismo servidor, pero nada más.
Me tengo que mirar bastante más a fondo lo que me has contado (ya he visto cositas), pero te quería preguntar una cosilla: mira, ahora mismo cuando mato los programas, lo hago a saco, pero estos programas son clientes de un programa que utiliza una estructura cliente servidor por sockets; el tema está en que si me lo cepillo a saco, no lo desconecto del servidor... Mi tema es si hay alguna manera enviarle (y gestionar) una señal que le haga por ejemplo salirse de un while() o un if(), y antes de morir, se desconecte expresamente.
Muchas gracias por la info que ya me has dado, y gracias de antemano por la que me puedas dar :brindis:
Un saludo
tSuKiYoMi
24/04/2010, 01:00
ok no compartes memoria. Sino lo he captado mal el problema que tienes es al cerrar las conexiones de los clientes con el servidor. una pregunta: el servidor es el mismo para todos lo clientes??? lo digo por varias cosas:
1- me imagino que el servidor será concurrente para atender de forma paralela todas las peticiones que le envias, no??, o tu no has escrito el servidor y ya te venia hecho de antemano.
2- en C el mecanismo de gestión de Sokects es a pelo es decir tienes que decirle explicitamente al sistema que quieres cerrar una conexión TCP e n un momento dado, eso lo haces con la función close() del socket, de todas formas aqui te pongo las funciones mas habituales de sockects en ANSI C para linux:
int s = socket(PF_INET, SOCK_STREAM, 0 /*TCP*/); //donde s es el descriptor de socket
bind(s, (struct sockaddr *)&direccionLocal, sizeof(direccionLocal) //te permite crear un contexto de comunicacón asociando IP
close(s) //te permite cerrar EL DESCRIPTOR DE SOCKET y por tanto cerrar la conexión TCP.
de todas formas te recomiendo que te leas el MAN de sockets de linux, (ya que se que me pongo muy pesado con el MAN pero es que estoy acostumbrado a él y para mi es la bilbia).
Hay una cosa de lo que me dices que no me convence. Eso de destruir conexiones a mansalba no suena bien tio. Te lo digo porque las cosas tienen un orden, y más en un lenguaje no orientado a objetos como es C donde la programación es estructurada, esto implica que variable que te crees, variable que tienes que liberar de forma correcta. Acuerdate, esto no es JAva donde tienes al amigo "Garbage Collector" en segundo plano ejecutando "destructores" implicitos (que ni siquiera escribiste tu porque los heredan automaticamente de java.lang.Object) para liberar memoria. Aqui tienes que liberar la memoria a mano, y para el caso de los descriptores de fichero o desockets tienes que hacer el close().
lo que me comentas de salir de un while() forzando...... bueno no me gusta lo que te voy a decirte pero si haces:
while(condiciion_del_while){
.....instrucciones
if(condición){
break;
}
}
o algo parecido, te digo que no me gusta porque poner breaks no es una programación limpia y rompe la linea de ejecución de un programa, cosa que es catastrofico. Los breaks solo deben usarse en un bloque switch. PEro me imagino que tu while es un bucle infinito, no??, en ese caso si quieres salir usa break, pero ojo si te preguntan yo no te lo he dicho.....jajaja. es broma. En lugar de usar break puedes hacer que tu manejador de SIGTERM o SIGINT (sigint es equivalente a pulsar en el teclado CRTL+Z, de hecho si tienes tu programa rulando y pulsas esa secuencia el programa terminara, como termine dependera de como tengas gestionado el manejador de esa señal) cambie alguna varible (que debe ser global) de condición que use el bucle para forzar la salida.
por ejemplo:
/*Tratamiento de la señal SIGINT*/
void manejador_SIGNIT(int sig)
{
terminar = 1;
printf("\nSeñal SIGINT DETECTADA");
}//fin de la funcion
y en el bucle tendrías:
while((terminar == 0){
...instrucciones de tu programa
}
pero ojo!!!!, que no se te olvide cerrar los sockets con close(s en algun sitio) porque sino casca de igual forma.
espero haberte solucionado alguna de tus dudas. si tienes más ya sabes.
3L_S4N70
24/04/2010, 01:24
ok no compartes memoria. Sino lo he captado mal el problema que tienes es al cerrar las conexiones de los clientes con el servidor. una pregunta: el servidor es el mismo para todos lo clientes??? lo digo por varias cosas:
1- me imagino que el servidor será concurrente para atender de forma paralela todas las peticiones que le envias, no??, o tu no has escrito el servidor y ya te venia hecho de antemano.
2- en C el mecanismo de gestión de Sokects es a pelo es decir tienes que decirle explicitamente al sistema que quieres cerrar una conexión TCP e n un momento dado, eso lo haces con la función close() del socket, de todas formas aqui te pongo las funciones mas habituales de sockects en ANSI C para linux:
int s = socket(PF_INET, SOCK_STREAM, 0 /*TCP*/); //donde s es el descriptor de socket
bind(s, (struct sockaddr *)&direccionLocal, sizeof(direccionLocal) //te permite crear un contexto de comunicacón asociando IP
close(s) //te permite cerrar EL DESCRIPTOR DE SOCKET y por tanto cerrar la conexión TCP.
de todas formas te recomiendo que te leas el MAN de sockets de linux, (ya que se que me pongo muy pesado con el MAN pero es que estoy acostumbrado a él y para mi es la bilbia).
Hay una cosa de lo que me dices que no me convence. Eso de destruir conexiones a mansalba no suena bien tio. Te lo digo porque las cosas tienen un orden, y más en un lenguaje no orientado a objetos como es C donde la programación es estructurada, esto implica que variable que te crees, variable que tienes que liberar de forma correcta. Acuerdate, esto no es JAva donde tienes al amigo "Garbage Collector" en segundo plano ejecutando "destructores" implicitos (que ni siquiera escribiste tu porque los heredan automaticamente de java.lang.Object) para liberar memoria. Aqui tienes que liberar la memoria a mano, y para el caso de los descriptores de fichero o desockets tienes que hacer el close().
lo que me comentas de salir de un while() forzando...... bueno no me gusta lo que te voy a decirte pero si haces:
while(condiciion_del_while){
.....instrucciones
if(condición){
break;
}
}
o algo parecido, te digo que no me gusta porque poner breaks no es una programación limpia y rompe la linea de ejecución de un programa, cosa que es catastrofico. Los breaks solo deben usarse en un bloque switch. PEro me imagino que tu while es un bucle infinito, no??, en ese caso si quieres salir usa break, pero ojo si te preguntan yo no te lo he dicho.....jajaja. es broma. En lugar de usar break puedes hacer que tu manejador de SIGTERM o SIGINT (sigint es equivalente a pulsar en el teclado CRTL+Z, de hecho si tienes tu programa rulando y pulsas esa secuencia el programa terminara, como termine dependera de como tengas gestionado el manejador de esa señal) cambie alguna varible (que debe ser global) de condición que use el bucle para forzar la salida.
por ejemplo:
/*Tratamiento de la señal SIGINT*/
void manejador_SIGNIT(int sig)
{
terminar = 1;
printf("\nSeñal SIGINT DETECTADA");
}//fin de la funcion
y en el bucle tendrías:
while((terminar == 0){
...instrucciones de tu programa
}
pero ojo!!!!, que no se te olvide cerrar los sockets con close(s en algun sitio) porque sino casca de igual forma.
espero haberte solucionado alguna de tus dudas. si tienes más ya sabes.
Jajaja, sé lo que me dices del MAN, es un crack el tío, sabe de todo. A mi también me va sacando de atolladeros, pero hay veces que me meto en grandes fregaos :S
Bueno, te comento cómo es más o menos, para que sepas de qué va. Tengo un programa que utiliza la estructura cliente servidor por sockets, que conforma (el servidor) una capa de abstracción del hardware para robots. De tal manera que este servidor corre sobre una máquina, y los clientes sobre otra. En mi caso no pretendo que haya más de un cliente corriendo a la vez, conectado al servidor, pero no hay problema de que haya más de uno, el sistema tiene sus propias librerías para los sockets y tal. (No obstante, diferencia los robots por puertos, así que si conectase más robots, tiraría a distintos puertos en la misma IP sin mayor problema). Por la parte del cliente, tengo dos tipos, uno que es autónomo (él se lo guisa, él se lo come, para que el robot siga una línea); otro por telecontrol, al que envío los comandos con una fifo no bloqueante de sólo lectura.
Entonces el tema es que lo de los sockets lo tengo apañadico. Incluso poniéndose quisquillosos, en el de telecontrol, podría endiñarle un comando que cambiase un flag que es el que valida un while que contenga a su vez el programa principal, ¿no? Pero en el autónomo sería más movida, luego ahí sería interesante tirar a por lo último que me has comentado (aunque bueno, la movida es que pretende ser un sistema en que alumnos suban sus programas, por tanto tendría que exhortárseles a poner el manejador y el while() al programa para no dejar el servidor lleno de mierda, o peor, con algún programa que se replique hasta el infinito, o a saber).
No sé, bueno, ese es el tema. Me gusta lo del manejador que me has comentado, imaginaba que existía alguna manera de cambiar el comportamiento por defecto de un programa al cascarle un Ctrl+Z, pero no la conocía. (¿Y qué diferencia hay entre Ctrl+Z y Ctrl+C? Esto ya es más casi curiosidad a saciar).
Un saludo
EDITO: Fallo en que caí más tarde, me refería a Ctrl+D que me comentaron (pero vamos, tras probarlo, nada que ver, jaja).
tSuKiYoMi
24/04/2010, 16:21
No sé, bueno, ese es el tema. Me gusta lo del manejador que me has comentado, imaginaba que existía alguna manera de cambiar el comportamiento por defecto de un programa al cascarle un Ctrl+Z, pero no la conocía. (¿Y qué diferencia hay entre Ctrl+Z y Ctrl+C? Esto ya es más casi curiosidad a saciar).
Un saludo
tienes un buen programa entre manos amigo. Antes cuando te comentaba la señal de finalización de SIGINT, he cometido un gazapo del que me acabo de dar cuenta. SIGINT detecta la secuencia CRTL+C que es la terminación de programa no la CRTL+Z que sería para detener un proceso de forma incondicional, es decir que tu porgrma recibe la señal SIGSTOP. De todas formas aqui te pongo la lista de señales que se manejan de forma habitual en los sistemas operativos de la familia UNIX:
SIGABRT - Proceso abortado.
SIGALRM - Señal de alarma, salta al expirar el timer del sistema. Reprogramable.
SIGCHLD - Proceso hijo terminado, detenido (*o que continúa). Tratamiento por defecto: ignorar. Reporgramable.
SIGCONT - Continúa si estaba parado.Tratamiento por defecto: continuar. Reprogramable.
SIGFPE - Excepción de coma flotante -- "erroneous arithmetic operation"(SUS).
SIGHUP - Hangup, al salir de la sessión se envía a los processos en Background. Tratamiento por defecto: exit. Reprogramable.
SIGILL - Instrucción ilegal.
SIGINT - Interrupción, se genera al pulsar "^c" durante la ejecución. Tratamiento por defecto:exit. Reprogramable.
SIGKILL - Destrucción inmediata del proceso.Tratamiento:exit. No reprogramable, no ignorable.
SIGPIPE - Se genera al escribir sobre la pipe sin lector. Tratamiento por defecto:exit. Reprogramable.
SIGQUIT - Terminar.
SIGSEGV - segmentation violation.Salta con dirección de memoria ilegal.
Tratamiento por defecto:exit + volcado de memoria. Reprogramable.
SIGSTOP - Detiene el proceso. Se genera al pulsar "^z" durante la ejecución. No reprogramable, no ignorable.
SIGTERM - Terminación. Tratamiento por defecto:exit. Reprogramable.
SIGTSTP - Parada de terminal.
SIGTTIN - Proceso en segundo plano intentando leer ("in").
SIGTTOU - Proceso en segundo plano intentando escribir ("out").
SIGUSR1 - User defined 1. señal definida por el usuario.Tratamiento por defecto: exit. Reprogramable.
SIGUSR2 - User defined 2. Señal definida por el usuario.Tratamiento por defecto: exit. Reprogramable.
SIGURG - datos urgentes disponibles en socket
SIGXCPU - excedido límite de tiempo de CPU
tmb se me olvido comentarte que para que el sistema reconozca tus propios manejadores de señal, deberías ejecutar la función siguiente al inicio de tu programa:
signal("tipo_señal", "nombre de la función manejadora");
los parametros sin comillas por supuesto., un ejemplo de esto sería:
if(signal(SIGINT, manejador_SIGINT)==SIG_ERR){
perror("\nERROR en signal");
return 1;
}
o si lo prefieres puedes cargar varios manejadores de señal al mismo tiempo:
if(signal(SIGINT, manejador_SIGINT)== SIG_ERR || signal(SIGTERM, manejador_SIGINT)==SIG_ERR || signal(SIGCHLD, manejador_SIGTERM)==SIG_ERR)
{
perror("\nERROR en signal");
return 1;
}
este IF es necesario para realizar la comprobación de que al cargar el manejador de señal a la señal deseada no entra en conflicto con manejadores ya existentes.
Si te fijas en el segundo IF que te pongo meto el mismo manejador de señal para SIGTERM y para SIGCHLD. Si recuerdas el codigo de manejador SIGTERM que te pase en uhn POST anterior veras que hay se hace el reconocimiento de la muerte de los procesos hijos mediante WAIT3(), el motivo de poner en mismo manejador para ambas señales es que en esencia van a realizar la misma opoeración ya que si el programa recibe sigterm tiene que terminar casi al instante de recibir la orden, pero para que no casque el tema de la memoria se debe reconocer la muerte de los procesos hijos que el padre tuviese pendiente en ese momento. Mientrás que en el caso de la señal SIGCHLD es el codigo que ejectura siempre que el proceso hijo lance dicha señal para avisar de que terminó. Como puedes ver esto de las señales es todo un mundillo pero una vez que te ahces un par de ejemplos no es demasiada cosa.
Otra cosa que se me paso comentarte es que hay dos señales programables por el usuario SIGUSR1, y SIGUSR2 en donde por defecto al recibirlas el programa termina, pero en sus respectivos manejadores puedes ejecutar codigo de guarda o lo que tu quieras ya que son completamente reprogramables.
Otra señal que te debrías mirar es la de SIGURG, ya que estas usando sockets y conexiones. Esta señal avisa cuando en un socket ya sea de cliente o de servidor has recibido segmentos TCP con marca urgente y por tanto son los que tiene mayor prioridad a la hora de ser tratados. Si el servidor los has escrito tu, puedes modificar la funciones que te pase en el post anterior para marcar datos TCP como urgentes de tal forma que cuando cierras un programa cliente pero estas a mitad de enviarle un comendo importante a un ROBOT pongas esa info como urgente para que antes de cerrar lo envie. Como puedes ver con las señales se pueden programar comportamientos más complejos.
Una ultima cuestión que te quería pregutnar es, si no te combiene mejor usar programación multihilo en lugar de multiproceso, lo digo porque los hilos de un mismo proceso son más fáciles de manejar y permiten compartición de variables, descritpores, y demás información de forma bastante sencilla.
SALUDOS.
< - >
he estado pensando un poco en tu programa y como tienes varios clientes (precesos) que lanzar, lo mismo te interesa crearte una estrutura de datos a modo de lista donde poder alamacenar información relativa a los procesos que lanzas, yo te pongo aqui un ejemplo pero le puedes meter tantos campos como quieras:
typedef struct{
int state;//estado actual del proceso
int pid;//identificador del proceso
}PCliente;
luego te puedes definir la lista de procesos como
PCliente* tablaClientes;
y posteriormente te creas un espacio de memoria compartido para la tabla donde puedes intecambiar de forma más sencilla información entre procesos:
if((tabla = (PCliente*)mmap(NULL,(pmax*sizeof(PCliente)),PROT_ READ|PROT_WRITE, MAP_SHARED|MAP_ANON, -1, 0)) == MAP_FAILED) return1;
esto solo sería una forma mas limpia de hacerlo pero hay muchas opciones de implementarlo.:gatito:
(¿Y qué diferencia hay entre Ctrl+Z y Ctrl+C? Esto ya es más casi curiosidad a saciar).
Ctrl+Z es solo pausar un programa, en cualquier momento podrías continuarlo. Ctrl+C es matar el programa, sin posibilidad de continuarlo :)
Si lo haces desde una consola, podrás continuar un programa pausado con Ctrl+Z mediante la instrucción fg (se continuará inmediatamente, no se vuelve a la consola) o la instrucción bg (se sigue ejecutando en paralelo, se vuelve inmediatamente a la consola)
Prueba las tres cosas (Ctrl+C, Ctrl+Z y luego fg, Ctrl+Z y luego bg) por ejemplo con el programa firefox llamándolo desde una consola, y verás cómo responde cada vez.
tSuKiYoMi
24/04/2010, 23:18
para mejorar la gestión lo suyo sería que te hicieras la tabla de procesos como te comentaba anteriormente, si eso le sumas la forma que te puso en un mensaje anterior juanvvc para crear procesos y ejecutar los programas con execl, yo creo que más o menos lo tendrías hecho.
jduranmaster
25/04/2010, 12:35
Las señales SIGUSR1 y SIGUSR2 son definibles por el usuario? osea que podríamos meter el código que quisieramos en el manejador para ejecutarlo cuando se reciba la señal?
tSuKiYoMi
25/04/2010, 12:50
Las señales SIGUSR1 y SIGUSR2 son definibles por el usuario? osea que podríamos meter el código que quisieramos en el manejador para ejecutarlo cuando se reciba la señal?
como ponia antes:
SIGUSR1 - User defined 1. señal definida por el usuario.Tratamiento por defecto: exit. Reprogramable.
SIGUSR2 - User defined 2. Señal definida por el usuario.Tratamiento por defecto: exit. Reprogramable.
creo que con eso queda bastante claro, no?. El tema es que son dos señales que el sistema deja que los usuarios las utilizen en sus programas como mejor les convengan, si no las reprogramas por defecto al recibiir cualquiera de ellas se trata como una exit(). En cualquier caso se puede meter el código que más te convenga en sus respectivos manejadores de señal. Por ejemplo puede ser util lanzar esa señal al proceso principal para que finalize (con lo que acabaría el programa) y este en el manejador respectivo puede salvar estados de ficheros, liberar memoria, etc, etc, etc..... en fin lo que quieras ponerle ahi. Eso depende de como sea tu programa y las necesidades u operaciones tenga.
SALUDOS.
jduranmaster
25/04/2010, 17:53
ok, Y estas señales son sincronas, es decir las puedo generar en cualquier momento, como el resto, no?
tSuKiYoMi
25/04/2010, 17:58
ok, Y estas señales son sincronas, es decir las puedo generar en cualquier momento, como el resto, no?
por norma general todas las señales a procesos e hilos dentro de un sistema son ¡¡¡¡¡¡¡¡ASÍNCRONAS!!!!!!!!!!! es decir que efectivamente las puedes generar en cualquier instante (pero no solo tu desde programa, ya que el sistema operativo para gestionar los procesos tmb lo hace). De todas formas si tienes más dudas con el tema de las señales, buscando en GOOGLE seguro que encuentras más información al respecto. PEro tmb te recomiendo leerte de forma concienzuda los MAN de WAIT(), SIGNAL(), y KILL(), que son los que más tienen que ver con este tema.
jduranmaster
25/04/2010, 22:03
Ok, gracias por los consejos. Es que el tema este de señales me interesa porque estoy programando un Servidor Web en C y tiene que ser multihilo (logicamente) de tal forma que que cuendo se cierra y termina para que no haya problemas todos los hilos que atienden peticiones WEB y que pertenecen al proceso que los creo deben haber acabado correctamente.
tSuKiYoMi
25/04/2010, 22:13
Ok, gracias por los consejos. Es que el tema este de señales me interesa porque estoy programando un Servidor Web en C y tiene que ser multihilo (logicamente) de tal forma que que cuendo se cierra y termina para que no haya problemas todos los hilos que atienden peticiones WEB y que pertenecen al proceso que los creo deben haber acabado correctamente.
si el servidor es multihilo, en lugar de multi proceso, tmb puedes enviar señales a los hilos que crea el proceso principal, pero no es necesario reconocer su muerte mediente la función WAIT(), WAIT3() o similares ya que WAIT es para procesos no para hilos.
La programación multihilos o concurrente suele ser más compleja que la multiproceso por el tema de las regiones criticas. En programación multiproceso por lo general puedes crearte un espacio de memoria compartido entre procesos mediante la función mmap(), en esta región se te garantiza que un proceso no accede a un determinado recurso mientrás otro lo este utilizando. Pero con programación multihilo esto no es asi. Los hilos o procesos ligeros tienen acceso a todas las variables, descriptores de ficheros y sockets que el proceso principal tenga abiertos y es el programador el que a pelo debe poner las restricciones necesarias para a modo de regiones criticas para que no existan inconsistencias en el código pues si un hilo esta leyendo mientras otro esta escribiendo se produce un error, y además bastante gordo.
PAra ello se usan el mecanismo de las regiones criticas, para evitar estos problemas. En unix puedes usar la API de POSIX que es lo mas comun para estos casos. Aqui te pongo las funciones que forman parte del API de POSIX, pero lo mejor es que busques algun tutorial para enterarte mejor:
int pthread_attr_destroy(pthread_attr_t *);
int pthread_attr_getdetachstate(const pthread_attr_t *, int *);
int pthread_attr_getguardsize(const pthread_attr_t *, size_t *);
int pthread_attr_getinheritsched(const pthread_attr_t *, int *);
int pthread_attr_getschedparam(const pthread_attr_t *,
struct sched_param *);
int pthread_attr_getschedpolicy(const pthread_attr_t *, int *);
int pthread_attr_getscope(const pthread_attr_t *, int *);
int pthread_attr_getstackaddr(const pthread_attr_t *, void **);
int pthread_attr_getstacksize(const pthread_attr_t *, size_t *);
int pthread_attr_init(pthread_attr_t *);
int pthread_attr_setdetachstate(pthread_attr_t *, int);
int pthread_attr_setguardsize(pthread_attr_t *, size_t);
int pthread_attr_setinheritsched(pthread_attr_t *, int);
int pthread_attr_setschedparam(pthread_attr_t *,
const struct sched_param *);
int pthread_attr_setschedpolicy(pthread_attr_t *, int);
int pthread_attr_setscope(pthread_attr_t *, int);
int pthread_attr_setstackaddr(pthread_attr_t *, void *);
int pthread_attr_setstacksize(pthread_attr_t *, size_t);
int pthread_cancel(pthread_t);
void pthread_cleanup_push(void*), void *);
void pthread_cleanup_pop(int);
int pthread_cond_broadcast(pthread_cond_t *);
int pthread_cond_destroy(pthread_cond_t *);
int pthread_cond_init(pthread_cond_t *, const pthread_condattr_t *);
int pthread_cond_signal(pthread_cond_t *);
int pthread_cond_timedwait(pthread_cond_t *,
pthread_mutex_t *, const struct timespec *);
int pthread_cond_wait(pthread_cond_t *, pthread_mutex_t *);
int pthread_condattr_destroy(pthread_condattr_t *);
int pthread_condattr_getpshared(const pthread_condattr_t *, int *);
int pthread_condattr_init(pthread_condattr_t *);
int pthread_condattr_setpshared(pthread_condattr_t *, int);
int pthread_create(pthread_t *, const pthread_attr_t *,
void *(*)(void *), void *);
int pthread_detach(pthread_t);
int pthread_equal(pthread_t, pthread_t);
void pthread_exit(void *);
int pthread_getconcurrency(void);
int pthread_getschedparam(pthread_t, int *, struct sched_param *);
void *pthread_getspecific(pthread_key_t);
int pthread_join(pthread_t, void **);
int pthread_key_create(pthread_key_t *, void (*)(void *));
int pthread_key_delete(pthread_key_t);
int pthread_mutex_destroy(pthread_mutex_t *);
int pthread_mutex_getprioceiling(const pthread_mutex_t *, int *);
int pthread_mutex_init(pthread_mutex_t *, const pthread_mutexattr_t *);
int pthread_mutex_lock(pthread_mutex_t *);
int pthread_mutex_setprioceiling(pthread_mutex_t *, int, int *);
int pthread_mutex_trylock(pthread_mutex_t *);
int pthread_mutex_unlock(pthread_mutex_t *);
int pthread_mutexattr_destroy(pthread_mutexattr_t *);
int pthread_mutexattr_getprioceiling(const pthread_mutexattr_t *,
int *);
int pthread_mutexattr_getprotocol(const pthread_mutexattr_t *, int *);
int pthread_mutexattr_getpshared(const pthread_mutexattr_t *, int *);
int pthread_mutexattr_gettype(const pthread_mutexattr_t *, int *);
int pthread_mutexattr_init(pthread_mutexattr_t *);
int pthread_mutexattr_setprioceiling(pthread_mutexattr _t *, int);
int pthread_mutexattr_setprotocol(pthread_mutexattr_t *, int);
int pthread_mutexattr_setpshared(pthread_mutexattr_t *, int);
int pthread_mutexattr_settype(pthread_mutexattr_t *, int);
int pthread_once(pthread_once_t *, void (*)(void));
int pthread_rwlock_destroy(pthread_rwlock_t *);
int pthread_rwlock_init(pthread_rwlock_t *,
const pthread_rwlockattr_t *);
int pthread_rwlock_rdlock(pthread_rwlock_t *);
int pthread_rwlock_tryrdlock(pthread_rwlock_t *);
int pthread_rwlock_trywrlock(pthread_rwlock_t *);
int pthread_rwlock_unlock(pthread_rwlock_t *);
int pthread_rwlock_wrlock(pthread_rwlock_t *);
int pthread_rwlockattr_destroy(pthread_rwlockattr_t *);
int pthread_rwlockattr_getpshared(const pthread_rwlockattr_t *,
int *);
int pthread_rwlockattr_init(pthread_rwlockattr_t *);
int pthread_rwlockattr_setpshared(pthread_rwlockattr_t *, int);
pthread_t
pthread_self(void);
int pthread_setcancelstate(int, int *);
int pthread_setcanceltype(int, int *);
int pthread_setconcurrency(int);
int pthread_setschedparam(pthread_t, int ,
const struct sched_param *);
int pthread_setspecific(pthread_key_t, const void *);
void pthread_testcancel(void);
una cosa que te recomiendo para tu servidor es que tengas un hilo que acepte las peticiones y multiples hilos que atiendan las peticiones y que sean creados por el hilo aceptador. Este esquema es el más utilizado.:awesome:
jduranmaster
25/04/2010, 22:51
ok. Bueno la API de POSIX ya me la conocia.:awesome:
tSuKiYoMi
25/04/2010, 23:08
bueno, en cualquier caso espero haberte ayudado.:gatito:
SplinterGU
26/04/2010, 01:38
si estas matando los procesos por nombre, entonces deduzco que no lo estas haciendo desde codigo C, sino los matarias por PID.
Entonces, si quieres saber que PID corresponde a cada proceso para cada IP... tienes el siguiente comando unix...
ps aux
esto te da no solo el pid y nombre del proceso, sino tambien los parametros con los que fue arrancado, entonces, podrias hacer...
ps aux | grep <ip> | grep -v grep
incluso, si encima queres saber solo el PID:
ps aux | grep <ip> | grep -v grep |cut -f3 -d\
(nota: la barra escapea un espacio, es "-d\ ", sin las comillas)
saludos.
3L_S4N70
26/04/2010, 10:51
tienes un buen programa entre manos amigo. Antes cuando te comentaba la señal de finalización de SIGINT, he cometido un gazapo del que me acabo de dar cuenta. SIGINT detecta la secuencia CRTL+C que es la terminación de programa no la CRTL+Z que sería para detener un proceso de forma incondicional, es decir que tu porgrma recibe la señal SIGSTOP. De todas formas aqui te pongo la lista de señales que se manejan de forma habitual en los sistemas operativos de la familia UNIX:
SIGABRT - Proceso abortado.
SIGALRM - Señal de alarma, salta al expirar el timer del sistema. Reprogramable.
SIGCHLD - Proceso hijo terminado, detenido (*o que continúa). Tratamiento por defecto: ignorar. Reporgramable.
SIGCONT - Continúa si estaba parado.Tratamiento por defecto: continuar. Reprogramable.
SIGFPE - Excepción de coma flotante -- "erroneous arithmetic operation"(SUS).
SIGHUP - Hangup, al salir de la sessión se envía a los processos en Background. Tratamiento por defecto: exit. Reprogramable.
SIGILL - Instrucción ilegal.
SIGINT - Interrupción, se genera al pulsar "^c" durante la ejecución. Tratamiento por defecto:exit. Reprogramable.
SIGKILL - Destrucción inmediata del proceso.Tratamiento:exit. No reprogramable, no ignorable.
SIGPIPE - Se genera al escribir sobre la pipe sin lector. Tratamiento por defecto:exit. Reprogramable.
SIGQUIT - Terminar.
SIGSEGV - segmentation violation.Salta con dirección de memoria ilegal.
Tratamiento por defecto:exit + volcado de memoria. Reprogramable.
SIGSTOP - Detiene el proceso. Se genera al pulsar "^z" durante la ejecución. No reprogramable, no ignorable.
SIGTERM - Terminación. Tratamiento por defecto:exit. Reprogramable.
SIGTSTP - Parada de terminal.
SIGTTIN - Proceso en segundo plano intentando leer ("in").
SIGTTOU - Proceso en segundo plano intentando escribir ("out").
SIGUSR1 - User defined 1. señal definida por el usuario.Tratamiento por defecto: exit. Reprogramable.
SIGUSR2 - User defined 2. Señal definida por el usuario.Tratamiento por defecto: exit. Reprogramable.
SIGURG - datos urgentes disponibles en socket
SIGXCPU - excedido límite de tiempo de CPU
tmb se me olvido comentarte que para que el sistema reconozca tus propios manejadores de señal, deberías ejecutar la función siguiente al inicio de tu programa:
signal("tipo_señal", "nombre de la función manejadora");
los parametros sin comillas por supuesto., un ejemplo de esto sería:
if(signal(SIGINT, manejador_SIGINT)==SIG_ERR){
perror("\nERROR en signal");
return 1;
}
o si lo prefieres puedes cargar varios manejadores de señal al mismo tiempo:
if(signal(SIGINT, manejador_SIGINT)== SIG_ERR || signal(SIGTERM, manejador_SIGINT)==SIG_ERR || signal(SIGCHLD, manejador_SIGTERM)==SIG_ERR)
{
perror("\nERROR en signal");
return 1;
}
este IF es necesario para realizar la comprobación de que al cargar el manejador de señal a la señal deseada no entra en conflicto con manejadores ya existentes.
Si te fijas en el segundo IF que te pongo meto el mismo manejador de señal para SIGTERM y para SIGCHLD. Si recuerdas el codigo de manejador SIGTERM que te pase en uhn POST anterior veras que hay se hace el reconocimiento de la muerte de los procesos hijos mediante WAIT3(), el motivo de poner en mismo manejador para ambas señales es que en esencia van a realizar la misma opoeración ya que si el programa recibe sigterm tiene que terminar casi al instante de recibir la orden, pero para que no casque el tema de la memoria se debe reconocer la muerte de los procesos hijos que el padre tuviese pendiente en ese momento. Mientrás que en el caso de la señal SIGCHLD es el codigo que ejectura siempre que el proceso hijo lance dicha señal para avisar de que terminó. Como puedes ver esto de las señales es todo un mundillo pero una vez que te ahces un par de ejemplos no es demasiada cosa.
Otra cosa que se me paso comentarte es que hay dos señales programables por el usuario SIGUSR1, y SIGUSR2 en donde por defecto al recibirlas el programa termina, pero en sus respectivos manejadores puedes ejecutar codigo de guarda o lo que tu quieras ya que son completamente reprogramables.
Otra señal que te debrías mirar es la de SIGURG, ya que estas usando sockets y conexiones. Esta señal avisa cuando en un socket ya sea de cliente o de servidor has recibido segmentos TCP con marca urgente y por tanto son los que tiene mayor prioridad a la hora de ser tratados. Si el servidor los has escrito tu, puedes modificar la funciones que te pase en el post anterior para marcar datos TCP como urgentes de tal forma que cuando cierras un programa cliente pero estas a mitad de enviarle un comendo importante a un ROBOT pongas esa info como urgente para que antes de cerrar lo envie. Como puedes ver con las señales se pueden programar comportamientos más complejos.
Una ultima cuestión que te quería pregutnar es, si no te combiene mejor usar programación multihilo en lugar de multiproceso, lo digo porque los hilos de un mismo proceso son más fáciles de manejar y permiten compartición de variables, descritpores, y demás información de forma bastante sencilla.
SALUDOS.
< - >
he estado pensando un poco en tu programa y como tienes varios clientes (precesos) que lanzar, lo mismo te interesa crearte una estrutura de datos a modo de lista donde poder alamacenar información relativa a los procesos que lanzas, yo te pongo aqui un ejemplo pero le puedes meter tantos campos como quieras:
typedef struct{
int state;//estado actual del proceso
int pid;//identificador del proceso
}PCliente;
luego te puedes definir la lista de procesos como
PCliente* tablaClientes;
y posteriormente te creas un espacio de memoria compartido para la tabla donde puedes intecambiar de forma más sencilla informaciñon entre procesos:
if((tablaTrabajadores = (PCliente*)mmap(NULL,(pmax*sizeof(PCliente)),PROT_ READ|PROT_WRITE, MAP_SHARED|MAP_ANON, -1, 0)) == MAP_FAILED) return1;
esto solo sería una forma mas limpia de hacerlo pero hay muchas opciones de implementarlo.:gatito:
Bueno, eres un crack. Gracias por mencionarme las señales más usadas, he de reconocer que me puse a buscarlas, y no encontré un sitio donde las explicasen así de bien (puede ser que no buscase bien también, pero el caso es que eran bastante escuetos en las descripciones).
Respecto a lo que me comentas de multihilo y multiproceso, sinceramente, no sabría decirte. Te comento con más detalle mi caso, y a ver qué te parece a ti:
Por una parte tengo el programa de control, el que lanza otros programas, que puede lanzar por una parte un programa conocido, en el que según el argumento enviado se conectará a una IP concreta (como forman todos parte de la misma red, lo que hago es enviar el último octeto de la IP(v4)), ya que ese programa puede estar funconando tantas veces como robots haya, con distinta IP, claro; y que recibe por una tubería comandos (el programa es de telecontrol, luego recibe información de hacie dónde mover el robot); pero por otra lanza un programa que es compilado en el momento de ser lanzado. Me explico: pretende ser una especie de plataforma de aprendizaje, de tal manera que los alumnos puedan subir sus programitas en C (habrá que decirles que introduzcan el tema del manejo de la señal, pero eso puede dárseles como un esqueleto), compilarlos (tengo una interfaz web en la que lanzo el gcc, y que compilo el programa añadiéndole al final del nombre del mismo el último octeto de la IP del robot seleccionado, de tal manera que aunque dos usen el mismo nombre de programa, no haya colisión) y lanzarlos.
Es a la hora de lanzarlos donde vuelve a entrar en juego el programa de control.
Y luego, no sé si ya lo mencioné, al programa de control le son enviados los distintos comandos por una tubería, y es función de tales comandos que realiza la acción (definida en unos if encadenados, aunque supongo que me pasaré a un switch-case que es más limpio :D)
Bueno el caso es ese, y que además una vez lanzados, son egoístas en cuanto a su memoria, variables, y demás cosillas: se las quedan para ellos, no las tienen que compartir. Por eso no sé si con el multiproceso voy por buen camino, o mejor por multihilo.
Me mola lo de la estructura, aunque tengo que mirarlo bien, porque a lo mejor con sólo el PID me basta y sobra, tengo que estudiarlo bien.
Ctrl+Z es solo pausar un programa, en cualquier momento podrías continuarlo. Ctrl+C es matar el programa, sin posibilidad de continuarlo :)
Si lo haces desde una consola, podrás continuar un programa pausado con Ctrl+Z mediante la instrucción fg (se continuará inmediatamente, no se vuelve a la consola) o la instrucción bg (se sigue ejecutando en paralelo, se vuelve inmediatamente a la consola)
Prueba las tres cosas (Ctrl+C, Ctrl+Z y luego fg, Ctrl+Z y luego bg) por ejemplo con el programa firefox llamándolo desde una consola, y verás cómo responde cada vez.
Cáspita, soy un poco canelo, no quise referirme a Ctrl+Z (así me pasa, que alguna vez he dejado algún proceso gilipolllas con Ctrl+Z, y no he sabido sacarlo de su gilipollez). ¿Osea que escribiendo los comandos fg o bg así sin más, puedo mandar a foregroud o background el proceso sin mayor problema? Pues bueno es saberlo, le echaré un vistazo para aprender cómo va.
Gracias, le echaré un vistazo.
si estas matando los procesos por nombre, entonces deduzco que no lo estas haciendo desde codigo C, sino los matarias por PID.
Entonces, si quieres saber que PID corresponde a cada proceso para cada IP... tienes el siguiente comando unix...
ps aux
esto te da no solo el pid y nombre del proceso, sino tambien los parametros con los que fue arrancado, entonces, podrias hacer...
ps aux | grep <ip> | grep -v grep
incluso, si encima queres saber solo el PID:
ps aux | grep <ip> | grep -v grep |cut -f3 -d\
(nota: la barra escapea un espacio, es "-d\ ", sin las comillas)
saludos.
Sí, hasta ahora me los cepillaba por nombre, aunque supongo que me pasaré a registrar el pid al lanzarlos, y matarlos por pid (si hace menos de una semana me oigo diciendo esta última frase, pensaría que estoy balbuceando y diciendo cosas sin sentido, jajaja). De todas maneras, muchas gracias SplinterGU, es una manera alternativa a la marranada peligrosa que quería hacer de cambiar el nombre al proceso :D
Muchas gracias a los tres. :brindis:
Un saludo
SplinterGU
26/04/2010, 17:44
mas alla de todo esto, si manejas una comunicacion mediante tuberias con solo romper la tuberia en cuestion podriamos manejar de esta forma la muerte del proceso... el padre rompe la tuberia, el hijo detecta el corte de la misma y en consecuencia se mata a si mismo...
3L_S4N70
26/04/2010, 18:56
Me encuentro ante una tesitura: veamos, yo en principio paso de wait y derivados, ¿no? Osea, mi programa de control ejecuta los programas, y se desentiende de ellos, tan sólo se acuerda de ellos cuando le diga que se los cepille. Entonces me encuentro ante unas dudas: según os he explicado, y lo que me habéis comentado, lo que debo hacer viene a ser asociar el último octeto de la IP con con el pid del proceso, de tal manera que cuando me digan que lo mate, yo miro a qué pid corresponde ese octeto, y me lo cargo, ¿o no?
< - >
mas alla de todo esto, si manejas una comunicacion mediante tuberias con solo romper la tuberia en cuestion podriamos manejar de esta forma la muerte del proceso... el padre rompe la tuberia, el hijo detecta el corte de la misma y en consecuencia se mata a si mismo...
Ya, lo he pensado, pero no me conviene (creo), porque utilizo tuberías ya hechas de antemano, no las genero cuando las uso, sólo las abro y las cierro. Sácame del error si estoy equivocado :D
< - >
Y otro detalle que no termino de ver claro es cómo voy a registrar los PID y tal si lanzo el programa con distintas condiciones en el cuerpo del programa de control; y la otra es que no entiendo muy bien cómo identifica el programa si soy el hijo o el padre.
Respecto a lo de las señales, ya me voy empapando, y ¡es la leche en vinagre! Una verdadera maravilla, jeje.
< - >
Me encuentro ante otro tema que no termino de dilucidar (creo que llevo rato dándole y ya no pienso con claridad): si no lanzo los programas al principio, y no sé cuántos van a estar corriendo a la vez, es complicado que me haga una matriz con los pid de los procesos que he ido lanzando, ¿no? ¿Cómo narices lo hago entonces? ¿Qué utilizo?
Respecto a lo de reconocer al proceso hijo o padre, ya he caído en la cuenta de que el hijo tiene de pid, entiendo que al hacer el fork, lo que hay son dos programas, el padre y el hijo, que están en la instrucción inmediatamente inferior (la siguiente, vamos) a aquella en la que se hizo el fork, y por eso, el pid en ese momento es cero para el hijo, y el pid del hijo para el padre.
SplinterGU
26/04/2010, 19:43
para el tema del pid, te sugiero ver el modulo exec de bennu, mod_exec...
con respecto a la tuberias... yo hice hace muchos años un launcher generico, donde por configuracion en una db decias que procesos lanzar, que se comunica con estos procesos por medio de pipes (con los que tenia soporte)... y estos pipes servian para enviar comandos (incluso un comando MATATE/KILL) y tambien para saber cuando un proceso moria por si solo, ya que al morir el proceso se recibe en el padre una señal de SIGPIPE... incluso en el hijo al recibir un SIGPIPE, podes saber que el padre murio y por ende morir o que el padre pide que se mate la instancia del hijo... y el esquema funciona perfectamente...
imagino que tus procesos estan en select() durante el momento IDLE o espera de comandos, entonces ahi podes estar escuchando tanto socket, como pipes a la vez... y podes saber cuando un pipe se corta o te llega un mensaje del mismo o cuando te llega una desconexion socket o lo mismo para recibir un mensaje...
creo que es la mejor opcion...
3L_S4N70
26/04/2010, 20:08
para el tema del pid, te sugiero ver el modulo exec de bennu, mod_exec...
con respecto a la tuberias... yo hice hace muchos años un launcher generico, donde por configuracion en una db decias que procesos lanzar, que se comunica con estos procesos por medio de pipes (con los que tenia soporte)... y estos pipes servian para enviar comandos (incluso un comando MATATE/KILL) y tambien para saber cuando un proceso moria por si solo, ya que al morir el proceso se recibe en el padre una señal de SIGPIPE... incluso en el hijo al recibir un SIGPIPE, podes saber que el padre murio y por ende morir o que el padre pide que se mate la instancia del hijo... y el esquema funciona perfectamente...
imagino que tus procesos estan en select() durante el momento IDLE o espera de comandos, entonces ahi podes estar escuchando tanto socket, como pipes a la vez... y podes saber cuando un pipe se corta o te llega un mensaje del mismo o cuando te llega una desconexion socket o lo mismo para recibir un mensaje...
creo que es la mejor opcion...
Bueno, le he echado una pensada, y el tema está en que la comunicación por fifos no la hacen entre procesos, al menos no entre procesos hechos por mi en C. Los comandos se envían a lo bestia, con echo "dato" > tuberia; luego no tengo manera de cortar la tubería.
Me da la sensación de que tendré que hacerlo registrando el pid a la hora de cascar un fork. Y es ahí donde me asalta una de las dudas, no veo claro cómo hacerlo, con el ejemplo que se me puso antes tiro de un for() con un número de procesos predeterminado... ¿cómo lo hago en mi caso? (Numerosos procesos, pero número indefinido de ellos, y de sólo dos tipos, es decir, lanzados desde dos posibles sitios, con distinto argumento o distinto nombre).
Tampoco alcanzo a ver claro otro tema, ¿cómo mato el proceso? ¿Sencillamente con un kill(nº_pid, SIGINT) en C?
tSuKiYoMi
26/04/2010, 21:37
Bueno, le he echado una pensada, y el tema está en que la comunicación por fifos no la hacen entre procesos, al menos no entre procesos hechos por mi en C. Los comandos se envían a lo bestia, con echo "dato" > tuberia; luego no tengo manera de cortar la tubería.
Me da la sensación de que tendré que hacerlo registrando el pid a la hora de cascar un fork. Y es ahí donde me asalta una de las dudas, no veo claro cómo hacerlo, con el ejemplo que se me puso antes tiro de un for() con un número de procesos predeterminado... ¿cómo lo hago en mi caso? (Numerosos procesos, pero número indefinido de ellos, y de sólo dos tipos, es decir, lanzados desde dos posibles sitios, con distinto argumento o distinto nombre).
Tampoco alcanzo a ver claro otro tema, ¿cómo mato el proceso? ¿Sencillamente con un kill(nº_pid, SIGINT) en C?
no!!!, para matar el proceso desde programa usa kill(nºPID,SIGTERM); porque SIGINT es la que se corresponde con la secuencia CRTL+C
respecto al tema de las tuberias, aqui te pongo un segemento de código de un Servidor que me hice ya bastante tiempo:
//Funcion
(nota: ya se que el codigo esta muy guarro pero a veces cuando consigo que me funcionen las cosas no me molesto en quitarle los comentarios chorras y demas)
como puedes ver se trata de una función que se encarga de procesar páginas CGI dentro de un conexto de servidor WEB, puedes ver en el código que cada vez que le llega una petición de cGI el proceso padre crea un proceso hijo y duplica el descriptor de la tuberia para atender la petición mediante la llamada a execl() a la cual se le pasa el nombre de la programa a ejectuar a traves de una estructura de datos llamada ARGUMENTO. Como ves para discriminar entre PADRE e HIJO se utiliza un SWITCH, de tal forma que al proceso HIJO se le devuelve un 0 tras el FORK() y al padre se le devuelve el PID del hijo.
Si no quires complicarte la vida con el tema de la concurrencia yo te recomendaría usar la estrcutura de datos que te proponia en un POST anterior en donde le pongas a cada proceso el PID y el ESTADO en el ques se encuentra. El ESTADO te lo puedes definir mediante ctes de tipo INT:
#define USADO 1
#define NOUSADO 2
#define INACTIVO 3
#define ACTIVO 4
o algo así, no tiene porque ser asi precisamente, sino acorde a las necesidades de tu programa.
Si no sabes a priori el número de procesos máximo que vas a manejar ponte un cte lo suficientemente alta como para que no casque nunca, pero con algo de sentido común claro. Antes decias que era una plataforma de enseñanza o no se que, eso siginifca que tus alumnos tmb podrán crear y destruir procesos me imagino, en ese contexto deberás indicarles las limitaciones del sistema encuanto a memoria, y que según vayan probando sus programas, mientras no funcionen bien que borren los procesos que se crean con sus programas a mano.
SALUDOS.:awesome:
3L_S4N70
26/04/2010, 21:56
no!!!, para matar el proceso desde programa usa kill(nºPID,SIGTERM); porque SIGINT es la que se corresponde con la secuencia CRTL+C
Hola artista, gracias por el comentario. La nomenclatura no la termino de dominar, quería referorme a eso, a mandarle un Ctrl+C. SIGTERM no es reprogramable, ¿verdad? Por eso he pensado en SIGINT.
Respecto a lo que me mandas, después me meto más a fondo que ahora estoy con otra cosilla, mi tema es ese, registrar el PID con exactitud para no cepillarme el que no debo, jaja. De todas maneras suena cachondo lo de mis alumnos, jaja (aunque no te digo que no molaría...), no es cierto, yo vengo a ser un poco el que hace el trabajo sucio, un profesor usará esto. Sí, en principio es eso, pero mi idea es darles las estructura concreta a meter para la gestión de la señal, de tal manera que como es mi programa, si ellos respetan el meterle el manejador bien, y teniendo en cuenta de que es gente seria y no va a hacer el burro, osea, que harán programas medianamente funcionales (antes probados en simulador si puede ser), entonces no me preocupa tanto que tengan cuidado con la memoria y tal (no sé si me columpio mucho, para mi este tema es un poco fangoso todavía).
Pero eso, que aún no he mirado tu código, cuando me lo mire podré decir más.
Gracias. Un saludo
tSuKiYoMi
26/04/2010, 22:01
Hola artista, gracias por el comentario. La nomenclatura no la termino de dominar, quería referorme a eso, a mandarle un Ctrl+C. SIGTERM no es reprogramable, ¿verdad? Por eso he pensado en SIGINT.
Respecto a lo que me mandas, después me meto más a fondo que ahora estoy con otra cosilla, mi tema es ese, registrar el PID con exactitud para no cepillarme el que no debo, jaja. De todas maneras suena cachondo lo de mis alumnos, jaja (aunque no te digo que no molaría...), no es cierto, yo vengo a ser un poco el que hace el trabajo sucio, un profesor usará esto. Sí, en principio es eso, pero mi idea es darles las estructura concreta a meter para la gestión de la señal, de tal manera que como es mi programa, si ellos respetan el meterle el manejador bien, y teniendo en cuenta de que es gente seria y no va a hacer el burro, osea, que harán programas medianamente funcionales (antes probados en simulador si puede ser), entonces no me preocupa tanto que tengan cuidado con la memoria y tal (no sé si me columpio mucho, para mi este tema es un poco fangoso todavía).
Pero eso, que aún no he mirado tu código, cuando me lo mire podré decir más.
Gracias. Un saludo
SIGTERM si es reprogramable, sino lo fuese no podrías escribirte un manejador para ella como si que te he puesto en algunos ejemplos de post ánteriores. De hecho tu programa debería tener al menos los dos manejadores a capón (SIGNIT + SIGTERM) más otros manejadores de otras señales que consideres que vas a manejar.
ok miratelo tranquilamente.
saludos.:awesome:
3L_S4N70
26/04/2010, 22:04
SIGTERM si es reprogramable, sino lo fuese no podrías escribirte un manejador para ella como si que te he puesto en algunos ejemplos de post ánteriores. De hecho tu programa debería tener al menos los dos manejadores a capón (SIGNIT + SIGTERM) más otros manejadores de otras señales que consideres que vas a manejar.
ok miratelo tranquilamente.
saludos.:awesome:
Coñe, tienes toda la razón. Si lo he probado, no sé dónde tengo la cabeza ya. Lo que pasa que como me era más cómodo cascarle un Ctrl+C a mano para las pruebas, se me ha ido el santo al cielo. Mil perdones, maestro :D
En principio le puedo cascar SIGINT y SIGTERM, con eso yo creo que me bastaría.
Lo de las tubeŕias y pid luego lo miro y si caigo en algo te comento.
Gracias.
tSuKiYoMi
26/04/2010, 22:07
de todas formas como creas otros procesos tu programa debería manejar tmb la señal SIGCHLD.:awesome:
3L_S4N70
27/04/2010, 10:35
de todas formas como creas otros procesos tu programa debería manejar tmb la señal SIGCHLD.:awesome:
Sí, parece que tiene sentido. ¿Eso qué haría? ¿Eso me informaría de que me he cepillado un hijo? Osea, cuando yo digo que le casco un SIGINT, o un SIGTERM, etc., SIGCHLD se activa, ¿no? No sé si termino de verlo claro, la verdad.
Respecto a las tuberías, me he estado rayando ratos largos, porque me he encontrado ante algo que me parecía poco eficiente. Veréis, si tengo un sólo programa de control, y los clientes (que como comenté se conectan a una iterfaz web, y es desde ésta que se introducen datos a una tubería) me mandan comandos, ¿no es más que probable que tenga problemas si dos me intentan mandar información a la vez (la tubería es la misma)? Me puse a pensar y dije: si me hago tantas tuberías como potenciales clientes, es harto ineficiente.
E inspeccionando el código que me pusiste de tu servidor, veo lo siguiente:
int tuberia_entrada[2];//tuberia_entrada con dos descriptores
Eso, ¿viene a ser como una "matriz de tuberías"? Eso podría interesarme... El caso es que yo las tuberias las tengo ya hechas, y las abro por medio de open, leo con read, cierro con close (todo con un "puntero" de tipo int -el puntero es cuando se usa fopen, fclose, etc.; éste usa int-). La razón de tenerlas ya hechas era porque en principio me iba a mover en memoria flash, y si bien la información iría por RAM, lo que viene siendo la fifo en sí estaría en flash, y generarla cada vez que la necesitase haría que al final ma cargarse la memoria.
Ahora, todo el tinglado éste correrá presumiblemente sobre un PC, con su disco duro de verdad, luego no sé si es efciente o ineficiente o lo que sea tener ya hechas las tuberías.
Otro tema al respecto es que como la información en todos los casos fluye desde la web (y más concretamente desde un cgi en shell scripting) hacia el programa en C, no puedo usar funciones de C para hacer la fifo ni nada por el estilo; sólo abrirla, leerla, cerrarla. Entonces, no sé si replantearlo todo o qué.
Por último también primordial en este tema es que como los CGI bloquean el servidor (mientras el CGI se está ejecutando, el servidor se queda en espera), lo qu hago es abrir las tuberías como no bloqueantes, para que la información sea dejada en ella por el CGI, y se largue.
Así que no sé si seguir en esta línea, replantearlo todo, usar algo que aún no conozco pero que amablemente me váis a indicar :brindis;... Como véis, cada vez que conoco nuevos métodos de hacer las cosas, le saco más problemas a lo que tengo hecho.
Un saludo
tSuKiYoMi
27/04/2010, 21:59
Sí, parece que tiene sentido. ¿Eso qué haría? ¿Eso me informaría de que me he cepillado un hijo? Osea, cuando yo digo que le casco un SIGINT, o un SIGTERM, etc., SIGCHLD se activa, ¿no? No sé si termino de verlo claro, la verdad.
Un saludo
SIGCHLD se activa cuando un proceso hijo va a terminar su ejecución y va a liberar sus descriptores, memoria, etc. El rollo es que si el padre no reconoce su muerte mediante la ejecución de WAIT() o cualquiera de las funciones de esa misma familia el proceso hijo queda en estado Zombi ocupando recursos, lo que es causa de que los programas peten. En uno de los post anteiores te comentaba como usarlo, miratelo.:awesome:
3L_S4N70
29/04/2010, 02:09
SIGCHLD se activa cuando un proceso hijo va a terminar su ejecución y va a liberar sus descriptores, memoria, etc. El rollo es que si el padre no reconoce su muerte mediante la ejecución de WAIT() o cualquiera de las funciones de esa misma familia el proceso hijo queda en estado Zombi ocupando recursos, lo que es causa de que los programas peten. En uno de los post anteiores te comentaba como usarlo, miratelo.:awesome:
Hey. He andado de viaje, así que siento no haber contestado antes.
El tema de usar wait() es que no puedo quedarme esperando, es decir, el programita de control está escuchando por una tubería los comandos que le mandan todos los usuarios (esto aún no lo tengo muy claro, después lo comento otra vez), y esperando si mata el programa, lanza el otro, no lanza ninguno, etc. Con wait y demáses, entiendo que se quedaría "clavado", esperando a que de la manera que fuese, el programa muriese, y como es él quien lo tiene que matar, no moriría nunca.
Lo que comentaba también es que en principio habría un sólo programa de control corriendo, y éste atendería todas las órdenes, pero hay un problema: las ordenes se reciben por una tubería... ¿y si dos (o más) escriben en la tubería a la vez? Entonces, ¿qué me sugerís que haga? ¿Sigo con este sistema, metiendo por la tubería identificador de quien me lo manda y qué me manda? ¿Múltiples programas de control? ¿Alguna otra manera que incluya el tema de que el programa de control se quedaría parado si pongo wait()? La verdad es que le he estado dando vueltas, pero no sé me ocurre una buena manera que evite colisiones entre usuarios y además evite zombis (de hecho, no se me ocurren buenas ideas para evitar los problemas por separado).
Cualquier sugerencia será bienvenida, por mi parte seguiré empollándome el man de las distintas funciones que puedo usar, y dándole vueltas.
Muchísimas gracias
tSuKiYoMi
29/04/2010, 21:34
ten encuenta que el programa principal se quedará esperando la muerte de sus procesos hijos pero cuando digo esperar no me refiero a 30 minutos, 5 minutos, joer ni si quiera se me pasa por la mente que se quede esperando más de 5 segundos, y mira que estoy siendo bueno. Si el sistema esta bien hecho tras la finalización de un hijo no debería tardar practicamente en reconocer su muerte.:awesome:
3L_S4N70
30/04/2010, 10:34
ten encuenta que el programa principal se quedará esperando la muerte de sus procesos hijos pero cuando digo esperar no me refiero a 30 minutos, 5 minutos, joer ni si quiera se me pasa por la mente que se quede esperando más de 5 segundos, y mira que estoy siendo bueno. Si el sistema esta bien hecho tras la finalización de un hijo no debería tardar practicamente en reconocer su muerte.:awesome:
Vale, captado, había entendido mal el propósito de la función wait() y sus hermanas.
Respecto al tema que comentaba sobre el programa de control y tal, a vr qué opináis: quizá sea más interesante que cada vez que se conecte al sistema un nuevo alumno, se ejecute un programa de control asociado a éste, que ejecute las órdenes relativas a telecontrol del robot, o genere como hijo el programa que el alumno suba, ¿no? De tal manera tendría controlada la muerte del hijo, y el padre no tendría colisiones al recibir datos de otros, porque no los recibiría.
Lo único que me sigue mosqueado de esto, y que no sé si se os ocurre algun manera mejor, es que necesitaría una tubería por potencial nuevo alumno, ¿verdad? Y teniendo en cuenta que los datos van desde scripts de consola (en CGIs) a la tubería, deberé tener las tuberías ya hechas.
No sé, vertid vuestras ideas sobre eso.
Un saludo y muchas gracias por la ayuda que me váis brindando
tSuKiYoMi
30/04/2010, 10:55
Vale, captado, había entendido mal el propósito de la función wait() y sus hermanas.
Respecto al tema que comentaba sobre el programa de control y tal, a vr qué opináis: quizá sea más interesante que cada vez que se conecte al sistema un nuevo alumno, se ejecute un programa de control asociado a éste, que ejecute las órdenes relativas a telecontrol del robot, o genere como hijo el programa que el alumno suba, ¿no? De tal manera tendría controlada la muerte del hijo, y el padre no tendría colisiones al recibir datos de otros, porque no los recibiría.
Lo único que me sigue mosqueado de esto, y que no sé si se os ocurre algun manera mejor, es que necesitaría una tubería por potencial nuevo alumno, ¿verdad? Y teniendo en cuenta que los datos van desde scripts de consola (en CGIs) a la tubería, deberé tener las tuberías ya hechas.
No sé, vertid vuestras ideas sobre eso.
Un saludo y muchas gracias por la ayuda que me váis brindando
creo que es buena ida que haya un programa de control por cada alumno, quizas en este ámbito más que tener un programa de control por alumno lo mejor sería tener un hilo de control por alumno, pero sino quieres liarte con el tema de la programación concurrente, lo mejor será que el progama principal cree un proceso de control por alumno y listo.
saludos.:awesome:
Powered by vBulletin® Version 4.2.5 Copyright © 2025 vBulletin Solutions Inc. All rights reserved.