// caos85 kernel data structures extern struct PCB * current ; extern struct PCB * pcb_head ; extern struct IVT * ivt ; // ivt = NULL ; typedef enum { INIT, READY, RUNNING, SLEEP, ZOMBIE } ThreadState ; struct PCB { // PCB points struct PCB * next ; struct PCB * next_sibling ; struct PCB * parent_process ; int pid ; // process ID // process context int process_state ; int process_priority ; // for scheduling struct TCB * threads ; struct MMAP * memory_map ; // other int exit_status ; } ; struct TCB { // thread information ThreadState thread_state ; struct TCP * next ; struct PCB * parent ; int tid ; // thread ID // register save area int reg_BC ; int reg_DE ; int reg_EF ; int reg_HL ; int thread_PC ; int thread_SP ; int reg_Flags ; } ; struct MMAP { struct PTE[VM_SIZE/PAGE_SIZE] ; } ; struct PTE { void * physical_address ; // also contains flags } ; struct CALL_GATE { void * base_interrupt_stack ; void * handler_entry_point ; } ; struct IVT { struct CALL_GATE [INTERRUPT_LEVELS] } ;
The fundamental abstraction of an operating system is the process. The process is the context in which a program runs. In the kernel, the process is represented by a kernel data structure called the process control block, PCB. Typically, all the PCB's are arranged in a linked list. The data stored in the PCB includes,
Each process contains at least one thread. A thread represents the actual running of code. The PCB and TCP's cross link, so that a TCP can reference data about its containing process, and the process can enumerate the threads it contains.
It sets the new PCB and TCP to READY and returns to the calling process.
The global current is set to the PCB pointer.
When the interrupt returns, the thread resumes running.
On interrupts, the priority of the current thread is considered with other threads to determine to preempt the current. If so, the registers on the interrupt stack are copied into the TCB and the status of the thread is set to READY.
A new thread is selected, and is made running.
Sleeping thread set to ready on completion of read. TCB TCB +-----+ +-----+ ---->| +-------->| +--------> ... +-----+ +-----+ | | | | +-----+ +-----+ |READY| |SLEEP|<---+ +-----+ +-----+ | | | | | | ... ... | | | | | +-----+ +-----+ | | | | | | +-----+ +-----+ | send signal | | | | |<=== (2) +-----+ +-----+ | | complete read | (1) ===+ +---|---+ V | + | +--------------------+-------+ | target of read data buffer | +----------------------------+
The return to ready will be event driven. The satisfaction of the request upon which the thread waits will be associated with some interrupt. For example, in the case of a disk read wait, with the interrupt from the disk control signalling it has finished the read and is ready for a new read request.
The read is completed by copying the requested disk block into a kernel disk buffer. Associated with this buffer is a pointer to the TCB waiting on the completion of the disk read. As part of the interrupt the pointer will be followed, and the state of the TCP referenced is updated.
The point of the zombie is to allow the parent process to request the exit status of the process. If a process is waiting on child death, its interest is registered so that it can be set to ready, by following the parent pointer of the process moving to a zombie state to update the parent's status.
If ever a process goes into a zombie state, its child processes are orphans. They must be reparented to the parent of the parent. Process with PID 1 is the ancestor of all processes. And it cannot exit.
Author: Burton Rosenberg
Created: 16 September 2019
Last Update: 3 October 2019