un determinado proceso con el estado TASK_RUNNING indica que esta ejecutandose(actualmente)o en espera
de ejecución, al terminar ésta porción de tiempo se recoje otro proceso de esa cola.
Es cierto que podemos tener un arbol de 200 procesos(o tantos como se quiera) corriendo a la vez
y paresca que todos están en TASK_RUNNING ésto no es cierto ya que el procesador utiliza esa cola para
determinar en que momento se acaba esa ejecución y empieza una nueva.
La funcion schedule() permite "liberar" al procesador para otros usos en cuanto a procesos (ésto no se limita solo
a éste ejemplo), claro que esto tiene un costo y es poner a aquéllos procesos a dormir o TASK_INTERRUPTIBLE,
solo si el proceso contiene el estado TASK_RUNNING, esto quiere decir que éstan a la espera de un evento,
ya sea de hardware o una "llamada" nuestra, que podría ser, por ejemplo los timers del kernel(tema que ya trate en posts anteriores).
Al usar una función como wake_up_process() estamos por obviedad, cambiando el estado de aquel proceso a TASK_RUNNING
"reinsertandolo" en la cola de ejecución de espera.
Existen 2 tipos de sueños:
1. INTERRUPTIBLE - el proceso puede ser despertado por señales o interrupciones del hardware y regresa a TASK_RUNNING
2. UNINTERRUPTIBLE - el proceso no puede ser despertado por el programador, si no por un evento de forma explícita.
Una señal "ininterrumpible" podrían ser aquella que se realizan en el contexto atomico(tema que ya toque también).
Entonces decidí programar ésta pequeña función como prueba de concepto, su uso es enviar una señal por medio del kernel a
tal proceso, y se configura con 3 señas FREEZE_PROCESS(congela un proceso), REFRIGERATE_PROCESS(lo refrigera si ésta congelado
aunque no funcióna en todos los casos), KILL_PROCESS(mata un proceso).
Código:
/*--------------------------------------------
Autor: NvK
Descripción: Enviar señales a un proceso para tomar control de el.Se lo puede matar, congelar o refrigerar.
Fecha: 27-1-2014
----------------------------------------------*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#define REFRIGERATE_PROCESS 18
#define FREEZE_PROCESS 19
#define KILL_PROCESS 15
static uint8_t send_signal_process(__u32 _pid,
struct pid *pid_struct,
struct task_struct *_ktask,
__u8 _signal)
{
atomic_t pid_status;
pid_struct= task_pid(_ktask);
if( ((*_ktask).pid==_pid)&&(pid_struct!=NULL) )
{
_ktask->state= TASK_INTERRUPTIBLE;
schedule();
kill_pid(pid_struct, _signal, TASK_INTERRUPTIBLE);
atomic_set(&pid_status, SIGHUP);
}
else
{
atomic_set(&pid_status, SIGABRT);
}
return atomic_read(&pid_status);
}
#define freeze_process(_pid, pid_struct, _ktask) send_signal_process(_pid, pid_struct, kern_ts, FREEZE_PROCESS)
#define refrigerate_process(_pid, pid_struct, _ktask) send_signal_process(_pid, pid_struct, kern_ts, REFRIGERATE_PROCESS)
#define kill_process(_pid, pid_struct, _ktask) send_signal_process(_pid, pid_struct, kern_ts, KILL_PROCESS)
Ejemplo - 1
// Se necesitan definir éstas estructuras.
struct task_struct *kern_ts;
static struct pid *pid_struct;
unsigned int pid;
static int __section(.init.text) __cold notrace
INIT_KERNEL(void)
{
printk(KERN_INFO "[Driver OK]\n");
kern_ts= &init_task;
for(;(kern_ts=next_task(kern_ts))!=&init_task;)
{
send_signal_process(1787, pid_struct, kern_ts, KILL_PROCESS); // esto matara al proceso con el PID 1787
}
return 0;
}
Ejemplo - 2 (lo mismo pero con macros, más facil)
for(;(kern_ts=next_task(kern_ts))!=&init_task;)
{
kill_process(1968, pid_struct, kern_ts); // mata
freeze_proces(3638, pid_struct, kern_ts); // congela
refrigerate_process(3638, pid_struct, kern_ts); // descongela
}
Conclusión: Al moverse fuera de la cola de ejecución(cualquiera sea sus estados), deja de planificarse para esa ejecución
lo que por logica básica lo pone a dormir.