Main Page   Modules   Alphabetical List   Data Structures   File List   Data Fields   Globals   Related Pages  

task.c File Reference

Task Management. More...

#include <const.h>
#include <stdlib.h>
#include <string.h>
#include <arch/i386.h>
#include <arch/interrupt.h>
#include <arch/mem.h>
#include <arch/paging.h>
#include <arch/v86.h>
#include <kernel/console.h>
#include <kernel/clock.h>
#include <kernel/elf32.h>
#include <kernel/fat.h>
#include <kernel/kmalloc.h>
#include <kernel/queue.h>
#include <kernel/semaphore.h>
#include <kernel/shell.h>
#include <kernel/umalloc.h>
#include <kernel/task.h>

Go to the source code of this file.

Functions

void do_idle ()
 Simply do nothing...

void kpager ()
 This special kernel thread provides to free the memory space and resources owned by the zombie tasks.

pid_t new_pid ()
 Create a new pid.
Returns:
The new pid created.


pid_t get_pid ()
 Return the current task pid if there is a curr_task.
Returns:
The current task pid.


task_tget_curr_task ()
 Get the current task.
Returns:
The current task structure pointer.


char * get_pname ()
 Return the current task name if there is a curr_task.
Returns:
The current task name.


task_tcreate_kthread (void *address, char *pname)
 Create a new kernel thread.
Parameters:
address  The address of the starting point for the thread.
pname  The name of the new thread.
Returns:
A pointer to the new-created task structure.
Exceptions:
NULL  If an error occurs (in particular out-of-memory).


task_tcreate_process (void *address, void *buffer, size_t size, char *pname, int privilege)
 Create a new process.
Parameters:
address  The address of the starting point for the thread.
buffer  An address to a buffer where is placed the executable code.
size  The size of the executable code buffer.
pname  The name of the new task.
privilege  The privilege level of the new task:
Returns:
A pointer to the new-created task structure.
Exceptions:
NULL  If an error occurs (in particular out-of-memory).


size_t new_vspace ()
 Create a new virtual space.
Returns:
The physical address of the page directory for the new virtual space.
Exceptions:
NULL  If an error occurs (in particular out-of-memory).


uint32_ttask_setup_stack (int argc, char **argv, addr_t stack_start, size_t stack_size, int privilege)
 Initialize the stack for a new process.
Parameters:
argc  The number of arguments passed to the new process.
argv  A pointer to the argument strings. NOTE: The content of argv may be changed!!!
stack_start  Where the stack is placed in memory.
stack_size  The size of the stack in bytes.
privilege  The privilege of the task.
Returns:
The starting stack pointer after initialization.


task_tnew_task (char *filename, int argc, char **argv, int privilege)
 Execute a file creating a new task.
Parameters:
filename  The name of the file to execute.
argc  The number of arguments passed to the new process.
argv  The arguments passed to the new process.
argv  A pointer to the argument strings.
privilege  The privilege level of the new task:
Returns:
A pointer to the new-created task structure.
Exceptions:
NULL  If an error occurs (in particular out-of-memory).


bool kill (pid_t pid)
 Kill a task by the pid.
Parameters:
pid  The process id.
Returns:
  • TRUE if the task has been successfully killed
  • FALSE if the task cannot be killed.


void auto_kill (int n)
 Kill the current running task.
Parameters:
n  The return code.


pid_t waitpid (pid_t pid, int *status, int options)
 Wait for the exit of a child.
Parameters:
pid 
  • The child process id
  • -1 to wait for the exit of a generic child.
status  Reports the status of the child.
options  Options flags.
Returns:
The pid of the child or -1 if the process has not been found.


void sleep_task (task_t *p)
 Sleep a process if it is awake.
Parameters:
p  The structure of the task to sleep.


void wakeup_task (task_t *p)
 Wakeup a process if it is sleeping.
Parameters:
p  The structure of the task to wakeup.


void sched_enter_critical_region ()
 Disable the scheduling of the other tasks.
Warning:
ATTENTION!!! Remember to call sched_leave_critical_region() when the task exits from the critical region. Otherwise the whole system could be blocked, because the scheduling of the other tasks is disabled after a task calls this function.


void sched_leave_critical_region ()
 Re-enable scheduling for the other tasks.

bool is_sched_enabled ()
 Return if the scheduling is enabled or not.
Returns:
  • TRUE if the scheduling is enabled
  • FALSE otherwise.


__inline__ void scheduler ()
 This is a simple round robin scheduler.

void dispatcher ()
 The dispatcher.

void init_multitasking ()
 Initialize the multitasking management.

void ps ()
 Print the state of every process from the ready, wait and zombie queues.


Variables

size_t K_PDBR [PAGE_SIZE/sizeof(size_t)]
 Master page directory.

queue_tready_queue = NULL
 A pointer to ready queue. Declared in task.c.

queue_twait_queue = NULL
 Wait-queue: tasks are waiting for I/O.

queue_tzombie_queue = NULL
 Zombie-queue: tasks are dying.

task_tcurr_task = NULL
 A pointer to the current running task structure. Declared in task.c.

task_tidle_task = NULL
 IDLE task structure. Initialized in init_multitasking().

task_tkpagd = NULL
 KPager daemon.

atomic_t last_pid
 Last used pid.

semaphore_t sched_enabled
 A semaphore to enable or disable scheduling (used to manage critical regions).


Detailed Description

Task Management.

Author:
Andrea Righi <drizzt@inwind.it>
Date:
Last update:
2003-12-15 Andrea Righi: Added temporary memory operators to get and free the temporary mapped pages.
2003-12-16 Andrea Righi: Fixed mutual exclusion bug in kill() and waitpid()
Note:
Copyright (©) 2003 Andrea Righi

Definition in file task.c.


Function Documentation

void auto_kill int    n
 

Kill the current running task.

Parameters:
n  The return code.

This routine move the current task from the ready queue to the zombie queue. Then the kpager() daemon should provide to free the memory and resources owned by this task.

Definition at line 949 of file task.c.

00950 {
00951         kill( curr_task->pid );
00952 }

task_t* create_kthread void *    address,
char *    pname
 

Create a new kernel thread.

Parameters:
address  The address of the starting point for the thread.
pname  The name of the new thread.
Returns:
A pointer to the new-created task structure.
Exceptions:
NULL  If an error occurs (in particular out-of-memory).

This routine initialize a new kernel thread structure and add this thread to the ready queue.

Definition at line 234 of file task.c.

00235 {
00236         task_t *new_task;
00237         byte *pl0_stack;
00238 
00239         sched_enter_critical_region();
00240 
00241         // --- Create the task structure -------------------------------//
00242         //!
00243         //! IA-32 Intel(R) Architecture Software Developer's Manual
00244         //! Volume 3: System Programming Guide - Chapter 6 - reports:
00245         //!
00246         //! "If paging is used, care should be taken to avoid placing a
00247         //! page boundary within the part of the TSS that the processor
00248         //! reads during a task switch (the first 104 bytes). If a page
00249         //! boundary is placed within this part of the TSS, the pages on
00250         //! either side of the boundary must be present at the same time
00251         //! and contiguous in physical memory.
00252         //!
00253         //! The reason for this restriction is that when accessing a TSS
00254         //! during a task switch, the processor reads and writes into
00255         //! the first 104 bytes of each TSS from contiguous physical
00256         //! addresses beginning with the physical address of the first
00257         //! byte of the TSS. It may not perform address translation at a
00258         //! page boundary if one occurs within this area. So, after the
00259         //! TSS access begins, if a part of the 104 bytes is not both
00260         //! present and physically contiguous, the processor will access
00261         //! incorrect TSS information, without generating a page-fault
00262         //! exception. The reading of this incorrect information will
00263         //! generally lead to an unrecoverable exception later in the
00264         //! task switch process..."
00265         new_task = kmemalign(PAGE_SIZE, sizeof(task_t));
00266         if (new_task == NULL)
00267         {
00268                 // Out of virtual memory!!! //
00269                 set_color(LIGHT_RED);
00270                 kprintf("\n\rOut of virtual memory!!! Cannot create task [%s]\n\r", pname);
00271                 set_color(DEFAULT_COLOR);
00272 
00273                 sched_leave_critical_region();
00274                 return(NULL);
00275         }
00276         memset(new_task, 0, sizeof(task_t));
00277 
00278         // --- Create the pl0-stack --- //
00279         pl0_stack = kmalloc(STACK_SIZE);
00280         if (pl0_stack == NULL)
00281         {
00282                 // Out of virtual memory!!! //
00283                 set_color(LIGHT_RED);
00284                 kprintf("\n\rOut of virtual memory!!! Cannot create task [%s]\n\r", pname);
00285                 set_color(DEFAULT_COLOR);
00286                 // Free the previous allocated space //
00287                 kfree((void *)new_task);
00288 
00289                 sched_leave_critical_region();
00290                 return(NULL);
00291         }
00292         // Null the stack to enforce the page mapping //
00293         memset(pl0_stack, 0, STACK_SIZE);
00294         // Setup the pl0-stack //
00295         new_task->tss.ss0 = new_task->tss.ss = KERNEL_STACK;
00296         new_task->tss.esp0 = new_task->tss.esp = new_task->pl0_stack = (dword)(pl0_stack+STACK_SIZE-sizeof(uint32_t));
00297 
00298         // --- Setup the TSS --- //
00299         new_task->tss_sel = setup_GDT_entry(sizeof(tss_IO_t), (dword)&(new_task->tss), TSS_SEG, 0);
00300 
00301         // Setup the task PDBR => get the kernel PDBR //
00302         new_task->tss.cr3 = GET_PDBR();
00303 
00304         // Setup the IO port mapping //
00305         new_task->tss.io_map_addr = sizeof(tss_t);
00306 
00307         // Setup general registers //
00308         new_task->tss.ds = new_task->tss.es = KERNEL_DATA;
00309         new_task->tss.fs = new_task->tss.gs = KERNEL_DATA;
00310         new_task->tss.eflags = EFLAGS_IF | 0x02;
00311 
00312         // Initialize general purpose registers //
00313         new_task->tss.eax = new_task->tss.ebx =
00314         new_task->tss.ecx = new_task->tss.edx =
00315         new_task->tss.esi = new_task->tss.edi = 0;
00316 
00317         // Initialize LDTR (Local Descriptor Table Register) //
00318         // No LDTs for now...
00319         new_task->tss.ldtr = 0;
00320 
00321         // Initialize debug trap //
00322         // If set to 1 the processor generate a debug exception when a task switch to this task occurs...
00323         new_task->tss.trace = 0;
00324 
00325         // Setup starting address //
00326         new_task->tss.cs = KERNEL_CODE;
00327         new_task->tss.eip = (dword)address;
00328 
00329         // --- Get a pid --- //
00330         new_task->pid = new_pid();
00331 
00332         // --- Store the name --- //
00333         // The last character must be ever '\0' (end of string) //
00334         strncpy(new_task->name, pname, sizeof(new_task->name)-2);
00335 
00336         // --- Set the type --- //
00337         new_task->type = KTHREAD_T;
00338 
00339         // --- Set the parent process --- //
00340         new_task->father = curr_task;
00341 
00342         // --- Set the console --- //
00343         new_task->console = curr_task->console;
00344 
00345         // --- Insert the task into the ready queue --- //
00346         new_task->state = READY;
00347         add_queue(&ready_queue, new_task);
00348 
00349         sched_leave_critical_region();
00350 
00351         return (new_task);
00352 }

task_t* create_process void *    address,
void *    buffer,
size_t    size,
char *    pname,
int    privilege
 

Create a new process.

Parameters:
address  The address of the starting point for the thread.
buffer  An address to a buffer where is placed the executable code.
size  The size of the executable code buffer.
pname  The name of the new task.
privilege  The privilege level of the new task:
Returns:
A pointer to the new-created task structure.
Exceptions:
NULL  If an error occurs (in particular out-of-memory).

This routine initializes the new task structure, copies the content of the buffer to the entry point address and then add the new task to the ready queue.

Definition at line 374 of file task.c.

00375 {
00376         task_t *new_task;
00377         byte *pl0_stack;
00378         dword *PDBR;
00379         dword PDBR_frame, i;
00380         register dword cr3;
00381 
00382         sched_enter_critical_region();
00383 
00384         // --- Create the task structure -------------------------------//
00385         //!
00386         //! IA-32 Intel(R) Architecture Software Developer's Manual
00387         //! Volume 3: System Programming Guide - Chapter 6 - reports:
00388         //!
00389         //! "If paging is used, care should be taken to avoid placing a
00390         //! page boundary within the part of the TSS that the processor
00391         //! reads during a task switch (the first 104 bytes). If a page
00392         //! boundary is placed within this part of the TSS, the pages on
00393         //! either side of the boundary must be present at the same time
00394         //! and contiguous in physical memory.
00395         //!
00396         //! The reason for this restriction is that when accessing a TSS
00397         //! during a task switch, the processor reads and writes into
00398         //! the first 104 bytes of each TSS from contiguous physical
00399         //! addresses beginning with the physical address of the first
00400         //! byte of the TSS. It may not perform address translation at a
00401         //! page boundary if one occurs within this area. So, after the
00402         //! TSS access begins, if a part of the 104 bytes is not both
00403         //! present and physically contiguous, the processor will access
00404         //! incorrect TSS information, without generating a page-fault
00405         //! exception. The reading of this incorrect information will
00406         //! generally lead to an unrecoverable exception later in the
00407         //! task switch process..."
00408         new_task = kmemalign(PAGE_SIZE, sizeof(task_t));
00409         if (new_task == NULL)
00410         {
00411                 // Out of virtual memory!!! //
00412                 set_color(LIGHT_RED);
00413                 kprintf("\n\rOut of virtual memory!!! Cannot create task [%s]\n\r", pname);
00414                 set_color(DEFAULT_COLOR);
00415 
00416                 sched_leave_critical_region();
00417                 return(NULL);
00418         }
00419         memset(new_task, 0, sizeof(task_t));
00420 
00421         // --- Create the pl0-stack --- //
00422         pl0_stack = kmalloc(STACK_SIZE);
00423         if (pl0_stack == NULL)
00424         {
00425                 // Out of virtual memory!!! //
00426                 set_color(LIGHT_RED);
00427                 kprintf("\n\rOut of virtual memory!!! Cannot create task [%s]\n\r", pname);
00428                 set_color(DEFAULT_COLOR);
00429                 // Free the previous allocated space //
00430                 kfree((void *)new_task);
00431 
00432                 sched_leave_critical_region();
00433                 return(NULL);
00434         }
00435         // Null the stack to enforce the page mapping //
00436         memset(pl0_stack, 0, STACK_SIZE);
00437         // Setup the pl0-stack //
00438         new_task->tss.ss0 = KERNEL_STACK;
00439         new_task->tss.esp0 = new_task->pl0_stack = (dword)(pl0_stack+STACK_SIZE-sizeof(uint32_t));
00440 
00441         // --- Setup the TSS --- //
00442         new_task->tss_sel = setup_GDT_entry(sizeof(tss_IO_t), (dword)&(new_task->tss), TSS_SEG, 0);
00443 
00444         // --- Create the virtual space of the task ------------------- //
00445         PDBR = (dword *)get_temp_page();
00446         if ( PDBR == NULL )
00447         {
00448                 // Out of memory!!! Free the previous allocated memory. //
00449                 set_color(LIGHT_RED);
00450                 kprintf("\n\rOut memory!!! Cannot create task [%s]\n\r", pname);
00451                 set_color(DEFAULT_COLOR);
00452                 // Free the previous allocated space //
00453                 kfree((void *)(pl0_stack));
00454                 kfree((void *)new_task);
00455                 // Destroy the TSS //
00456                 remove_GDT_entry(new_task->tss_sel);
00457                 // Free the
00458 
00459                 sched_leave_critical_region();
00460                 return(NULL);
00461         }
00462         PDBR_frame = vir_to_phys( (dword)PDBR );
00463 
00464         // Initialize PDBR //
00465         memset(PDBR, 0, PAGE_SIZE);
00466         for (i=K_VIR_START/(PAGE_SIZE*1024); i<1024; i++)
00467                 PDBR[i] = K_PDBR[i];
00468 
00469         // Map page directory into itself //
00470         PDBR[1023] = PDBR_frame | P_PRESENT | P_WRITE;
00471 
00472         // Unmap the temporary page, without free the physical frame    //
00473         free_temp_page(PDBR, FALSE);
00474 
00475         // Temporary switch to the new address space                    //
00476         //                                                              //
00477         // QUESTION: cr3 is in the stack... if I switch into the new    //
00478         // address space can I ever see the old cr3 value???            //
00479         //                                                              //
00480         // RE: Yes, but only if cr3 is stored into a register...        //
00481         // And if the value should be stored into the stack, here we    //
00482         // are at CPL0, so we're using the 0-privileged stack, so,      //
00483         // since the 0-privileged stack space is shared between every   //
00484         // task and also the kernel, we're ever able to see the stored  //
00485         // cr3 value...                                                 //
00486         __asm__ __volatile__ ("movl %%cr3, %0" : "=r"(cr3) : );
00487         __asm__ __volatile__ ("movl %0, %%cr3" : : "r"(PDBR_frame));
00488 
00489         // --- Create the user space --- //
00490 
00491         // Create the task stack //
00492         new_task->tss.ss = (privilege == KERNEL_PRIVILEGE) ? KERNEL_STACK : USER_STACK | 3;
00493         if ( privilege == KERNEL_PRIVILEGE )
00494                 new_task->tss.esp = new_task->pl0_stack;
00495         else
00496                 new_task->tss.esp = (dword)(TASK_STACK_START-sizeof(size_t));
00497 
00498         // Map the user code and data space.                            //
00499         // ("address" is the virtual task space, "buffer" is the        //
00500         // kernel space where the task-code is located).                //
00501         if (address != buffer)
00502         {
00503                 memcpy(address, buffer, size);
00504         }
00505 
00506         // Restore the old address space //
00507         __asm__ __volatile__ ("movl %0, %%cr3" : : "r"(cr3));
00508 
00509         // Setup the task PDBR //
00510         new_task->tss.cr3 = PDBR_frame;
00511 
00512         // Setup the IO port mapping //
00513         new_task->tss.io_map_addr = sizeof(tss_t);
00514 
00515         // Setup general registers //
00516         if ( privilege == KERNEL_PRIVILEGE )
00517                 new_task->tss.ds = new_task->tss.es =
00518                 new_task->tss.fs = new_task->tss.gs = KERNEL_DATA;
00519         else
00520                 new_task->tss.ds = new_task->tss.es =
00521                 new_task->tss.fs = new_task->tss.gs = USER_DATA | 3;
00522 
00523         new_task->tss.eflags = EFLAGS_IF | 0x02;
00524         if ( privilege == USER_PRIVILEGE )
00525                 // User tasks must have the I/O privilege level         //
00526                 // at the lower level                                   //
00527                 new_task->tss.eflags |= EFLAGS_IOPL3;
00528 
00529         // Initialize general purpose registers //
00530         new_task->tss.eax = new_task->tss.ebx =
00531         new_task->tss.ecx = new_task->tss.edx =
00532         new_task->tss.esi = new_task->tss.edi = 0;
00533 
00534         // Initialize LDTR (Local Descriptor Table Register) //
00535         // No LDTs for now...
00536         new_task->tss.ldtr = 0;
00537 
00538         // Initialize debug trap //
00539         // If set to 1 the processor generates a debug exception when a //
00540         // task switch to this task occurs...                           //
00541         new_task->tss.trace = 0;
00542 
00543         // Setup starting address //
00544         new_task->tss.cs = (privilege == KERNEL_PRIVILEGE) ? KERNEL_CODE : USER_CODE | 3;
00545         new_task->tss.eip = (dword)address;
00546 
00547         // --- Get a pid --- //
00548         new_task->pid = new_pid();
00549 
00550         // --- Store the name --- //
00551         // The last character must be ever '\0' (end of string) //
00552         strncpy(new_task->name, pname, sizeof(new_task->name)-2);
00553 
00554         // --- Set the type --- //
00555         new_task->type = PROCESS_T;
00556 
00557         // --- Set the parent process --- //
00558         new_task->father = curr_task;
00559 
00560         // --- Set the console --- //
00561         if( curr_task )
00562                 new_task->console = curr_task->console;
00563 
00564         // --- Insert the task into the ready queue --- //
00565         new_task->state = READY;
00566         add_queue(&ready_queue, new_task);
00567 
00568         sched_leave_critical_region();
00569         return (new_task);
00570 }

void dispatcher  
 

The dispatcher.

This routine supplies to change the context of the tasks. It is invoked at every clock tick.
You can change the frequency of the ticks changing the value of the HZ definition.

Definition at line 1131 of file task.c.

01132 {
01133         task_t *prev_task;
01134 
01135         // Call the clock task at every tick                            //
01136         clock_thread();
01137 
01138         // Can we switch to another process?!                           //
01139         if ( atomic_read(&sched_enabled) )
01140         {
01141                 // Store the previous task                              //
01142                 prev_task = curr_task;
01143                 // Select a new task                                    //
01144                 scheduler();
01145 
01146                 if ( prev_task != curr_task )
01147                 {
01148                         if ( prev_task->tss.cr3 != curr_task->tss.cr3 )
01149                         {
01150                                 // Update the page directory of the     //
01151                                 // new task with the kernel page        //
01152                                 // directory in the kernel memory area. //
01153                                 __asm__ __volatile__ (
01154                                         "movl %%eax, %%cr3\n"
01155                                         "cld\n"
01156                                         "rep movsl\n"
01157                                         "movl %%ebx, %%cr3" : :
01158                                         "a"(curr_task->tss.cr3),
01159                                         "b"(prev_task->tss.cr3),
01160                                         "D"((dword *)PAGE_DIR_MAP+(K_VIR_START/(PAGE_SIZE*1024))),
01161                                         "S"((dword *)K_PDBR+(K_VIR_START/(PAGE_SIZE*1024))),
01162                                         "c"((PAGE_DIR_MAP/(PAGE_SIZE*1024)) - (K_VIR_START/(PAGE_SIZE*1024)))
01163                                 );
01164                                 __asm__("" : : : "%eax", "%ebx", "%ecx", "%edi", "%esi");
01165                         }
01166 
01167                         // Re-enable master interrupt controller        //
01168                         outportb(PORT_8259_M, EOI);
01169                         // Perform the task switch                      //
01170                         jmp_to_tss(curr_task->tss_sel);
01171                 }
01172         }
01173 }

void do_idle  
 

Simply do nothing...

This is the default thread to execute when the system has to do nothing.

Definition at line 73 of file task.c.

00074 {
00075         while(TRUE)
00076         {
00077                 enable();
00078                 idle();
00079         }
00080 }

task_t* get_curr_task  
 

Get the current task.

Returns:
The current task structure pointer.

Definition at line 203 of file task.c.

00204 {
00205         return( curr_task );
00206 }

pid_t get_pid  
 

Return the current task pid if there is a curr_task.

Returns:
The current task pid.

Definition at line 186 of file task.c.

00187 {
00188         if ( !curr_task ) return( NULL );
00189         return( curr_task->pid );
00190 }

char* get_pname  
 

Return the current task name if there is a curr_task.

Returns:
The current task name.

Definition at line 210 of file task.c.

00211 {
00212         if ( !curr_task ) return( NULL );
00213         return( curr_task->name );
00214 }

void init_multitasking  
 

Initialize the multitasking management.

Definition at line 1185 of file task.c.

01186 {
01187         // Initialize pid counter.
01188         atomic_set(&last_pid, 0);
01189 
01190         // Initialize scheduler critical region flag.
01191         INIT_MUTEX( &sched_enabled );
01192 
01193         // Create the "init" task.
01194         curr_task = create_process(NULL, NULL, 0, "init", KERNEL_PRIVILEGE);
01195         // Set the console.
01196         curr_task->console=1;
01197         // Set the type.
01198         curr_task->type = KTHREAD_T;
01199 
01200         // Load task register.
01201         __asm__ __volatile__ ("ltr %%ax" : : "a" (curr_task->tss_sel));
01202         __asm__ __volatile__ ("" : : : "ax");
01203 
01204         // Load virtual space.
01205         __asm__ __volatile__ ("movl %0, %%cr3" : : "r"(curr_task->tss.cr3));
01206 
01207         // Initialize the IDLE task.
01208         // The IDLE task is a special task,
01209         // it's always ready but never present in the ready queue.
01210         idle_task = create_kthread(&do_idle, "idle");
01211         idle_task->console=0;
01212         rem_queue(&ready_queue, idle_task);
01213 
01214         // Initialize KPager thread
01215         kpagd = create_kthread(&kpager, "kpagerd");
01216         kpagd->console = 0;
01217 
01218         // Install the dispatcher.
01219         install_irq_handler( TIMER_IRQ, (void *)&dispatcher );
01220 }

bool is_sched_enabled  
 

Return if the scheduling is enabled or not.

Returns:
  • TRUE if the scheduling is enabled
  • FALSE otherwise.

Definition at line 1070 of file task.c.

01071 {
01072         if( atomic_read(&sched_enabled) )
01073                 return( TRUE );
01074         else
01075                 return( FALSE );
01076 }

bool kill pid_t    pid
 

Kill a task by the pid.

Parameters:
pid  The process id.
Returns:
  • TRUE if the task has been successfully killed
  • FALSE if the task cannot be killed.

This routine move the task having the pid from the ready queue to the zombie queue. Then the kpager() daemon should provide to free the memory and resources owned by this task.

Definition at line 881 of file task.c.

00882 {
00883         // Queues where to search the processes                         //
00884         static queue_t **q[] = { &ready_queue, &wait_queue };
00885         task_t *p = curr_task;
00886         bool retval = FALSE;
00887         register int i, j, n;
00888 
00889         // No running processes(???) => quit!                           //
00890         if ( p==NULL )
00891                 return(FALSE);
00892 
00893         sched_enter_critical_region();
00894 
00895         // Explore every task queue                                     //
00896         for ( i=0; i<_countof(q); i++)
00897                 for ( j=0, n=count_queue(q[i]); j<n; j++ )
00898                 {
00899                         // Get the next task in the queue               //
00900                         p = pick_queue( q[i] );
00901 
00902                         if ( p->pid == pid )
00903                         {
00904                                 // Move the task from the queue to the  //
00905                                 // zombie queue                         //
00906                                 if ( rem_queue(q[i], p) )
00907                                 {
00908                                         add_queue(&zombie_queue, p);
00909                                         p->state = ZOMBIE;
00910                                         // Wake-up the kpager daemon    //
00911                                         wakeup_task( kpagd );
00912                                         // Wake-up the father           //
00913                                         if ( p->father != NULL )
00914                                                 wakeup_task( p->father );
00915                                         // Task killed!                 //
00916                                         retval = TRUE;
00917                                 }
00918                         }
00919                         else
00920                         {
00921                                 // If the process p is a child of the   //
00922                                 // process to kill we have to set its   //
00923                                 // father to NULL                       //
00924                                 if ( p->father != NULL )
00925                                         if ( (p->father)->pid == pid )
00926                                                 p->father = NULL;
00927                         }
00928                 }
00929 
00930         // Check if the task has killed itself                          //
00931         if (curr_task->pid == pid)
00932         {
00933                 sched_leave_critical_region();
00934                 // Wait for the dispatcher                              //
00935                 do_idle();
00936         }
00937 
00938         sched_leave_critical_region();
00939         return( retval );
00940 }

pid_t new_pid  
 

Create a new pid.

Returns:
The new pid created.

This routine generate a pid using an atomic increment.

Definition at line 178 of file task.c.

00179 {
00180         atomic_inc(&last_pid);
00181         return(atomic_read(&last_pid));
00182 }

task_t* new_task char *    filename,
int    argc,
char **    argv,
int    privilege
 

Execute a file creating a new task.

Parameters:
filename  The name of the file to execute.
argc  The number of arguments passed to the new process.
argv  The arguments passed to the new process.
argv  A pointer to the argument strings.
privilege  The privilege level of the new task:
Returns:
A pointer to the new-created task structure.
Exceptions:
NULL  If an error occurs (in particular out-of-memory).

This routine can execute only ELF32 files. The file is loaded in a temporary buffer, then the sections are copied into memory and the task structure is initialized. At the end of this the new task is putted into the ready queue.

Definition at line 697 of file task.c.

00698 {
00699         uint32_t temp_PDBR;
00700         task_t  *task;
00701         byte    *pl0_stack;
00702         size_t  PDBR, entry;
00703         void    *filebuffer;
00704         int     filesize = get_file_size(filename);
00705 
00706         if ( filesize < 0 )
00707                 return(NULL);
00708 
00709         // Load the ELF32 file into the buffer //
00710         filebuffer = kmalloc(filesize);
00711         if ( elf32_load_file(filename, filebuffer)<0 )
00712         {
00713                 kfree(filebuffer);
00714                 return(NULL);
00715         }
00716 
00717         sched_enter_critical_region();
00718 
00719         // Create the task structure //
00720         task = kmemalign(PAGE_SIZE, sizeof(task_t));
00721         if ( task == NULL )
00722         {
00723                 sched_leave_critical_region();
00724                 kfree(filebuffer);
00725                 return(NULL);
00726         }
00727         memset(task, 0, sizeof(task_t));
00728 
00729         // Create the PL0-stack //
00730         pl0_stack = kmalloc(STACK_SIZE);
00731         if (pl0_stack == NULL)
00732         {
00733                 // Out of virtual memory!!! //
00734                 // Free the previous allocated space //
00735                 kfree((void *)task);
00736                 kfree(filebuffer);
00737 
00738                 sched_leave_critical_region();
00739                 return(NULL);
00740         }
00741         // Null the stack to enforce the page mapping //
00742         memset(pl0_stack, 0, STACK_SIZE);
00743 
00744         // Setup the PL0-stack //
00745         task->tss.ss0 = KERNEL_STACK;
00746         task->tss.esp0 = task->pl0_stack = (dword)(pl0_stack+STACK_SIZE-sizeof(uint32_t));
00747 
00748         // Setup the TSS //
00749         task->tss_sel = setup_GDT_entry(sizeof(tss_IO_t), (dword)&(task->tss), TSS_SEG, 0);
00750 
00751         // Create the new virtual space //
00752         PDBR = new_vspace();
00753         if ( PDBR == NULL )
00754         {
00755                 // Out of memory !!! //
00756 
00757                 // Destroy the TSS //
00758                 remove_GDT_entry(task->tss_sel);
00759                 // Free the previous allocated space //
00760                 kfree((void *)(pl0_stack));
00761                 kfree((void *)task);
00762                 kfree(filebuffer);
00763 
00764                 sched_leave_critical_region();
00765                 return(NULL);
00766         }
00767 
00768         // Temporary switch to the new address space //
00769         __asm__ __volatile__ ("movl %%cr3, %0" : "=r"(temp_PDBR) : );
00770         __asm__ __volatile__ ("movl %0, %%cr3" : : "r"(PDBR));
00771 
00772         // Copy the file sections into memory //
00773         entry = elf32_copy_sections(filebuffer);
00774         if ( entry == NULL )
00775         {
00776                 // Restore the old address space //
00777                 __asm__ __volatile__ ("movl %0, %%cr3" : : "r"(temp_PDBR));
00778 
00779                 // Free the PDBR frame //
00780                 push_frame(PDBR/PAGE_SIZE);
00781                 // Destroy the TSS //
00782                 remove_GDT_entry(task->tss_sel);
00783                 // Free the previous allocated space //
00784                 kfree((void *)(pl0_stack));
00785                 kfree((void *)task);
00786                 kfree(filebuffer);
00787 
00788                 sched_leave_critical_region();
00789                 return(NULL);
00790         }
00791 
00792         // Create the task stack.
00793         task->tss.ss = (privilege == KERNEL_PRIVILEGE) ? KERNEL_STACK : USER_STACK | 3;
00794         if ( privilege == KERNEL_PRIVILEGE )
00795                 task->tss.esp = (uint32_t)task_setup_stack(argc, argv, task->pl0_stack, STACK_SIZE, privilege);
00796         else
00797                 task->tss.esp = (uint32_t)task_setup_stack(argc, argv, TASK_STACK_START, STACK_SIZE, privilege);
00798 
00799         // Initialize the user heap.
00800         umalloc_init( TASK_HEAP_START, TASK_HEAP_DEFAULT_SIZE );
00801 
00802         // Restore the old address space //
00803         __asm__ __volatile__ ("movl %0, %%cr3" : : "r"(temp_PDBR));
00804 
00805         // Setup the task PDBR //
00806         task->tss.cr3 = PDBR;
00807 
00808         // Setup the IO port mapping //
00809         task->tss.io_map_addr = sizeof(tss_t);
00810 
00811         // Setup general registers //
00812         if ( privilege == KERNEL_PRIVILEGE )
00813                 task->tss.ds = task->tss.es =
00814                 task->tss.fs = task->tss.gs = KERNEL_DATA;
00815         else
00816                 task->tss.ds = task->tss.es =
00817                 task->tss.fs = task->tss.gs = USER_DATA | 3;
00818 
00819         task->tss.eflags = EFLAGS_IF | 0x02;
00820         if ( privilege == USER_PRIVILEGE )
00821                 // User privilege must be the I/O privilege set to the  //
00822                 // lower level                                          //
00823                 task->tss.eflags |= EFLAGS_IOPL3;
00824 
00825         // Initialize general purpose registers //
00826         task->tss.eax = task->tss.ebx =
00827         task->tss.ecx = task->tss.edx =
00828         task->tss.esi = task->tss.edi = 0;
00829 
00830         // Initialize LDTR (Local Descriptor Table Register) //
00831         // No LDTs for now...
00832         task->tss.ldtr = 0;
00833 
00834         // Initialize debug trap //
00835         // If set to 1 the processor generate a debug exception when a task switch to this task occurs...
00836         task->tss.trace = 0;
00837 
00838         // Setup starting address //
00839         task->tss.cs = (privilege == KERNEL_PRIVILEGE) ? KERNEL_CODE : USER_CODE | 3;
00840         task->tss.eip = entry;
00841 
00842         // Get a pid //
00843         task->pid = new_pid();
00844 
00845         // Store the name //
00846         // The last character must be ever '\0' (end of string) //
00847         strncpy(task->name, filename, sizeof(task->name)-2);
00848 
00849         // Set the type //
00850         task->type = PROCESS_T;
00851 
00852         // Set the parent process //
00853         task->father = curr_task;
00854 
00855         // Set the console //
00856         task->console = curr_task->console;
00857 
00858         // Insert the task into the ready queue //
00859         task->state = READY;
00860         add_queue(&ready_queue, task);
00861 
00862         // Well done! //
00863         kfree(filebuffer);
00864         sched_leave_critical_region();
00865 
00866         return( task );
00867 }

void ps  
 

Print the state of every process from the ready, wait and zombie queues.

Definition at line 1234 of file task.c.

01235 {
01236         task_t *p;
01237         int count, i;
01238 
01239         if ((p = curr_task)==NULL) return;
01240 
01241         // Header //
01242         kprintf("\n\rPID       STATE       CONSOLE     CMD");
01243 
01244         sched_enter_critical_region();
01245 
01246         // Ready processes //
01247         for(i=0, count=count_queue(&ready_queue); i<count; i++)
01248         {
01249                 // Get the next task in the ready queue //
01250                 p = pick_queue(&ready_queue);
01251 
01252                 // Print process informations //
01253                 kprintf("\n\r%u", p->pid);
01254                 gotoxy(12, -1);
01255                 kputchar('R');
01256                 gotoxy(22, -1);
01257                 if (p->console)
01258                         kprintf("tty%u", p->console);
01259                 else
01260                         kputchar('?');
01261                 gotoxy(34, -1);
01262                 kprintf("%s", p->name);
01263         }
01264 
01265         // Wait processes //
01266         for(i=0, count=count_queue(&wait_queue); i<count; i++)
01267         {
01268                 // Get the next task in the wait queue //
01269                 p = pick_queue(&wait_queue);
01270 
01271                 // Print process informations //
01272                 kprintf("\n\r%u", p->pid);
01273                 gotoxy(12, -1);
01274                 kputchar('W');
01275                 gotoxy(22, -1);
01276                 if (p->console)
01277                         kprintf("tty%u", p->console);
01278                 else
01279                         kputchar('?');
01280                 gotoxy(34, -1);
01281                 kprintf("%s", p->name);
01282         }
01283 
01284         // Zombie processes //
01285         for(i=0, count=count_queue(&zombie_queue); i<count; i++)
01286         {
01287                 // Get the next task in the zombie queue //
01288                 p = pick_queue(&zombie_queue);
01289 
01290                 // Print process informations //
01291                 kprintf("\n\r%u", p->pid);
01292                 gotoxy(12, -1);
01293                 kputchar('Z');
01294                 gotoxy(22, -1);
01295                 if (p->console)
01296                         kprintf("tty%u", p->console);
01297                 else
01298                         kputchar('?');
01299                 gotoxy(34, -1);
01300                 kprintf("%s", p->name);
01301         }
01302 
01303         sched_leave_critical_region();
01304 
01305         kprintf("\n\r");
01306 }

void sched_enter_critical_region  
 

Disable the scheduling of the other tasks.

Warning:
ATTENTION!!! Remember to call sched_leave_critical_region() when the task exits from the critical region. Otherwise the whole system could be blocked, because the scheduling of the other tasks is disabled after a task calls this function.

Definition at line 1055 of file task.c.

01056 {
01057         DOWN( &sched_enabled );
01058 }

void sched_leave_critical_region  
 

Re-enable scheduling for the other tasks.

Definition at line 1061 of file task.c.

01062 {
01063         UP( &sched_enabled );
01064 }

void sleep_task task_t   p
 

Sleep a process if it is awake.

Parameters:
p  The structure of the task to sleep.

Definition at line 1024 of file task.c.

01025 {
01026         p->state = WAIT;
01027 }

pid_t waitpid pid_t    pid,
int *    status,
int    options
 

Wait for the exit of a child.

Parameters:
pid 
  • The child process id
  • -1 to wait for the exit of a generic child.
status  Reports the status of the child.
options  Options flags.
Returns:
The pid of the child or -1 if the process has not been found.

Todo:
status flags and options flags not yet implemented!

Definition at line 966 of file task.c.

00967 {
00968         // Queues where to search the processes                         //
00969         static queue_t **q[] = { &ready_queue, &wait_queue };
00970         task_t *p;
00971         int i, j, n;
00972         bool found = FALSE;
00973         dword IF = GET_IF();
00974 
00975         // A task can't wait for yourself!                              //
00976         if (curr_task->pid == pid) return(0);
00977 
00978 repeat:
00979         sched_enter_critical_region();
00980         // Search into every task queue                                 //
00981         for( i=0; i<_countof(q); i++ )
00982         {
00983                 for( j=0, n = count_queue( q[i] ); j<n; j++ )
00984                 {
00985                         p = pick_queue( q[i] );
00986                         if ( (p->father == curr_task) && ((pid==-1) || (p->pid==pid)) )
00987                         {
00988                                 // Found!                               //
00989                                 found = TRUE;
00990 
00991                                 // Update the pid of the child that we  //
00992                                 // are waiting (when pid is -1).        //
00993                                 pid = p->pid;
00994 
00995                                 // Move the current task (father) into  //
00996                                 // the wait queue and wait              //
00997                                 if ( rem_queue(&ready_queue, curr_task) )
00998                                 {
00999                                         add_queue(&wait_queue, curr_task);
01000                                         curr_task->state = WAIT;
01001                                 }
01002                                 sched_leave_critical_region();
01003 
01004                                 // Sleeped!                             //
01005                                 // Now wait for the dispatcher          //
01006                                 enable();
01007                                 idle();
01008                                 // Task waked-up! Check if the child    //
01009                                 // that we are waiting is dead          //
01010                                 goto repeat;
01011                         }
01012                 }
01013 
01014         }
01015         sched_leave_critical_region();
01016         SET_IF(IF);
01017 
01018         // Return the child pid or -1 if the process has not been found //
01019         return( (found) ? pid : -1 );
01020 }

void wakeup_task task_t   p
 

Wakeup a process if it is sleeping.

Parameters:
p  The structure of the task to wakeup.

Definition at line 1031 of file task.c.

01032 {
01033         p->state = READY;
01034 }


Variable Documentation

size_t K_PDBR[PAGE_SIZE/sizeof(size_t)]
 

Master page directory.

Definition at line 36 of file task.c.


Generated on Fri Feb 20 15:32:20 2004 for Minirighi by doxygen1.2.18