Red de conocimiento informático - Problemas con los teléfonos móviles - Código fuente para la gestión de procesos de Windows; consulte a continuación para obtener más detalles.

Código fuente para la gestión de procesos de Windows; consulte a continuación para obtener más detalles.

Windows es un software comercial de código cerrado y está protegido por las leyes de derechos de autor comerciales. Sin mencionar que nadie lo tiene, incluso si lo tiene, no se lo puede dar (ser demandado por Microsoft no es un asunto menor).

Linux es de código abierto, ¿por qué no leer el código fuente de Linux?

La versión más nueva es demasiado larga para escribirla, así que les doy la versión 0.11 más clásica del código fuente de gestión de procesos. Puedes encontrar el resto tú mismo. Como buen programador, debes aprender a aprovechar al máximo los motores de búsqueda.

---------------------------------

Linux0.11 gestiona procesos a través de matrices de procesos, permitiendo hasta 64 procesos. El estado del proceso incluye listo, en ejecución, suspendido, inactivo y muerto. Los estados de sueño se pueden dividir en dos tipos: interrumpibles e ininterrumpibles. Para la gestión de procesos creo que es más claro hablar del estado del proceso.

1.0 Task

0 es bastante especial, está "hecho a mano", el siguiente es el proceso de producción

mem_init(main_memory_start,memory_end);

trap_init();

blk_dev_init();

char_dev_init();

tty_init();

time_init() ;

sched_init();

buffer_init(buffer_memory_end);

hd_init();

floppy_init();

sti();

move_to_user_mode();

Por supuesto, tantos inicios no son todos para la tarea 0, sino para inicializar el sistema. sched_init() establece manualmente el descriptor de segmento de tarea tss y el descriptor de segmento local ldt de la tarea 0, y establece los registros tr y ldtr:

set_tss_desc(gdt+ FIRST_TSS_ENTRY,&(init_task.tss));/ /Set descriptor de segmento tss

set_ldt_desc(gdt+FIRST_LDT_ENTRY,&(init_task.task.ldt));//Establecer descriptor ldt

...

ltr( 0); //cargar la dirección del descriptor tss en tr

lldt(0); //cargar la dirección del descriptor ldt en ldtr

Veamos el tss de la tarea 0 ¿Qué significa ldt? parece

/*ldt*/ {0,0},\

{0x9f. La dirección base es 0 y DPL=3. Esto indica que aunque el código para la tarea 0 todavía está en el segmento del kernel, el nivel de la tarea ya es modo de usuario. Esto también se puede ver en tss, donde tanto el código como los datos están configurados en 0x17, que es la entrada 2 de la tabla de descriptores de segmento local. También puede ver que la pila del kernel de la tarea 0 está configurada en la página después de init_task, y la pila del modo de usuario es la pila del modo kernel antes de move_too_user_mode. Esto es diferente de un proceso normal, donde la pila del modo de usuario está al final del espacio de direcciones de 64 Mb.

move_to_user_mode crea la ilusión de cambio de tarea en la pila, usando iret para saltar a la capa externa 3, de modo que la CPU cargará automáticamente tss de acuerdo con tr e inicializará varios registros para ejecutar la tarea 0. Por lo tanto, la tarea 0 es en realidad una tarea de espacio de usuario en el espacio del kernel.

2. Creación del proceso

La creación del proceso se completa mediante la llamada al sistema sys_fork, utilizando principalmente dos funciones find_empty_process y copy_process. El primero busca el número de proceso no utilizado en la matriz del proceso y se lo entrega al proceso hijo. El segundo completa la creación de la información del proceso hijo, principalmente copiando la información del proceso padre.

struct task_struct *p;

int i;

struct file *f

// Primero asigna un bloque de memoria para el descriptor de proceso del proceso hijo

p=(struct task_struct *)get_free_page();

if(!p)

return -EAGAIN; p>

// Agrega un puntero a la nueva estructura de tarea a la matriz. Agregar a la matriz

task[nr]=p;

// Copiar la estructura de tareas del usuario actual al proceso secundario.

Por supuesto, la pila no se copiará

*p=*current;

// Establece el estado del proceso secundario en espera ininterrumpida para evitar que se envíe ahora< p->state=TASK_UNINTERRUPTIBLE;

P->pid=last_pid;

p->father=current->pid;

p - >cuenta=p->prioridad;

p->señal=0;

p->alarma=0;

p->líder=0

p->utime=p->stime=0;

p->cutime=p->cstime=0;

p->start_time; = jiffies;

p->tss.back_link=0;

//La pila de estado del kernel del nuevo proceso se encuentra al final de la página del descriptor del proceso

p-> tss.eip=eip;

// El valor de retorno de fork es 0 para el proceso hijo y su pid para el proceso padre. A través de esta diferencia, los segmentos de código del. El proceso padre y el proceso hijo están en Se divide después de que regresa la llamada fork

p->tss.

p->tss.esi=esi;

p->tss.edi=edi;

p->tss.es=es&0xffff;

p->tss.cs=cs&0xffff;

p ->tss .ss=ss&0xffffff;

p>

p->tss.ds=ds&0xffffff;

p->tss.fs=fs&0xffffff;

p ->tss.gs=gs&0xffffff;

p->tss.ldt=_LDT(nr);

p->tss.trace_bitmap=0x80000000;

// Si la tarea principal usa un coprocesador, también la guardará en tss

if(last_task_used_math==current )

_asm("clts;fnsave %0"::" m"(p->tss.i387));

//Establece nuevas direcciones de base de códigos y segmentos de datos para nuevas tareas. Tenga en cuenta que, dado que Linux 0.11 solo admite un espacio de proceso de 64 M, el espacio de direcciones lineal del proceso se encuentra en el límite de 64 M.

// Luego copie la tabla de páginas del proceso principal para la nueva tarea. Usando copy_page_tales, tarea principal y subtarea ****, en este momento solo se lee el segmento de datos del código. Durante una operación de escritura, la copia en escritura solicitará nuevas páginas físicas para el nuevo proceso.

if(copy_mem(nr,p)){

tarea[nr]=NULL;

free_page((long)p);

return -EAGAIN;

}

for(i=0;i

if(f=p->filp)

f->f_count++;

if(current->pwd)

current->pwd->i_count++;

if( actual ->root)

actual->root->i_count++;

if(actual->ejecutable)

actual->ejecutable->i_count++;

//Establecer descriptores tss y ldt para nuevas tareas

set_tss_desc(gdt+(nr<<1)+FIRST_TSS_ENTRY,&(p->tss));

set_ldt_desc ( gdt+(nr<<1)+ FIRST_LDT_ENTRY,&(p->ldt));

return last_pid;

return last_pid;

}

son todos valores en la pila. nr es el valor de find_empty_process antes de llamar a copy_process y el valor de retorno del proceso es el número de serie de la matriz de tareas.

3. Programación del proceso

El proceso no se ejecutará inmediatamente después de su creación. El sistema llamará a la función del programador del sistema en el momento apropiado y seleccionará el proceso apropiado para ejecutar. Las funciones de programación se pueden llamar en múltiples ocasiones, como después de una llamada al sistema, cuando el proceso está en pausa/suspensión/salida o cuando se interrumpe el reloj. La función de programación verifica principalmente el intervalo de tiempo del proceso y luego selecciona el proceso con el tiempo de ejecución más corto para ejecutar. Si no se encuentra, se ejecuta la llamada al sistema de pausa inactiva y luego se vuelve a llamar a la función de programación durante la pausa. El siguiente es el código principal

while(1){

c=-1;

next=0;

i= NR_TASKS;

p=&task[NR_TASKS];

/// Encuentra la tarea con el menor tiempo de ejecución entre las tareas listas

while(--i ){

if(!(*--p))

continuar;

if((*p)->estado==TASK_RUNNING&&(*p )- >counter>c)

c=(*p)->counter,next=i;

}

// Si lo encuentra, salga. De lo contrario, vuelva a calcular el intervalo de tiempo de la tarea.

Tenga en cuenta que si no hay ninguna tarea, la siguiente siempre es 0 y el resultado cambia a la tarea 0

if(c)break;

for(p=&LAST_TASK;p>&FIRST_TASK;- -p)

if(*p)

(*p)->.counter=((*p)->contador>>1)+(*)->prioridad ;

}

switch_to(next);//Cambia la tarea a la nueva tarea con un salto largo.

}

La interrupción del reloj es una medida importante para realizar la programación. Es la ejecución continua de la programación la que permite al sistema cambiar entre múltiples tareas y completar el objetivo de un sistema operativo multitarea. Cuando se inicializa el programador, además de configurar manualmente la tarea 0, la interrupción del temporizador 8253 y el sistema deben "encenderse". Llame a la función do_timer desde una interrupción. Ahora bien, esta es una función muy corta:

void do_timer(long cpl)

{

...

// Según Tiempo de ejecución del proceso de ajuste de prioridad

if(cpl)

current->utime++;

else

current->time++;< / p>

if( next_timer){

next_timer->jiffies--;

while(next_timer&&next_timer->jiffies<=0){

vacío (*fn)(void).

fn=next_timer->fn;

next_timer->fn=NULL;

next_timer=next_timer->siguiente;

(fn)();

}

}

...

// De restar 1 del intervalo de tiempo del proceso. Si es mayor que 0, significa que el intervalo de tiempo no se ha agotado, continúe

if((--current->counter>0)return;

// Tenga en cuenta que las tareas del modo kernel no se pueden adelantar. En 0.11, a menos que la tarea del kernel abandone activamente el procesador, continuará ejecutándose

if(!cpl)return;

// programar

programar();

}

3.

4. Terminación del proceso

El proceso puede terminarse a sí mismo o ser terminado por una señal de terminación. Si tiene un proceso hijo, el proceso hijo quedará huérfano y su proceso hijo se delegará al proceso 1 (el proceso de inicio también lo notificará después de que muera). el proceso padre, que puede estar esperándolo.