There are many way to manage each process's pid. Here is the way I do it.
I decided to make minimal modification to $OS161_SRC/kern/thread/thread.c,
in case anything is ruined. So I only add two things to the thread module. One
is I add a t_pid field to struct thread so that getpid system call is
trivial. Another is I add a call of pid_alloc in thread_alloc to initialize
new thread's t_pid. That's it. No more touch on the thread module.
The process Structure
In os161, we stick to the 1:1 process:thread model. That is, a process has and
only has one thread. Thus process and thread are basically the same thing in
this scenario. However, I still decided to use a struct process to do process
bookkeeping stuff. It's independent to struct thread and outside the thread
module. Thus when a thread exits and its thread structure is destroyed. I
still have its meta-data (e.g. exitcode) stored in the process structure.
So, what should we record about a process? As we already have the struct
thread to record most of the information about a thread, we just use a pointer
to struct thread to get all these information. What we do in struct process
is mainly for our waitpid and exit system call. So we should keep the
information of:
-
Its parent's (if any) pid
-
Whether a process has exited
-
If this process has exited, then the exitcode
-
Synchronous facilities to protect the exit status (lock, cv, samophore, etc)
-
Of course a pointer to
struct thread
So the structure looks like:
struct process {
pid_t ppid;
struct semphore* exitsem;
bool exited;
int exitcode;
struct thread* self;
};
Pid allocation
For convenience and simplicity, I decided to support a maximum number of
MAX_RUNNING_PROCS (256) processes in the OS, regardless the __PID_MAX (32767)
macro in $OS161_SRC/kern/inlude/kern/limits.h. So I just use a global static
array of struct process* to maintain all the processes in system. Of course
it's very dumb but hope it's sufficient for a toy OS like 161.
Then allocate a pid is very easy, just scan the process array and find a
available slot (NULL). One important thing to note is that leave pid=0
alone and do not use it. Since in /kern/include/kern/wait.h, there are two
special MACROs:
#define WAIT_ANY (-1)
#define WAIT_MYPGRP (0)
That is, pid = 0 has a special meaning. So we'd better not use it, staring
allocate pid from 1. We can also see this from the __PID_MIN (2) macro in
$OS161_SRC/kern/inlude/kern/limits.h.
Once a available slot is found, we need to create a struct process and
initialize it appropriately, especially it's ppid (-1 or other invalid value).