(Think of time-out interrupt as HARDWARE interrupt (such as timer))
(Think of event wait as system call)
Note that these are not real OS states.
For a new state:
- Assign PCB (Process Control Block)
- Assign Process ID (as part of the PCB)
New->Ready: Scheduler decided to schedule P0, but P0 is in the NEW state. Next step is to prep the context for P0.
- Context is set up. (User registers (inlcuding FPs, PC, SP), status flags and other special registers)
Ready->Running: When the scheduler schedules the process to be used by the CPU.
- Using the CPU
Running -> Waiting: System calls
For example, scanf;
scanf->clib->system call-> more calls -> read(I/O)
Then an interrupt happens (pressed enter)
Waiting goes into Ready at the state/context at the point of read
- Doing I/O or certain system calls
- Exception (divide by 0, for example)
- exit system call
- return 0 from main
NOTE: Exception Interrupt will result the program going from running to terminated.
In a terminated state, you only have a process ID and PCB left. The terminated state is for the parent to know how his children died. The operating system is interested in returning the exit status to the parent, which it gets from the PCB. The way this is done is if the exit status is in the PCB and the process is terminated.
This occurs for child processes, where the entry is still needed to allow the parent process to read its child's exit status: once the exit status is read via the wait system call, the zombie's entry is removed from the process table and it is said to be "reaped".
Zombie status is when the process is dead, but the parent has gotten the exit status, then the PCB is removed and the process is dead.
If the parent dies before the child dies, the child gets adopted by the init (root process)
If the parent dies after the child dies, everything is freed and okay
If the parent does not die and does not pick up the child, there's a PCB leak.
If the parent dies and does not pick up the child, it will be picked up by init and the child will be discarded.
Process is something that has a PCB (Process Control Block) and process ID (which, again, is part of the PCB).
Scheduler looks at all of the processes that are ready and chooses one to use the CPU.
Processes only go from ready to running because of the scheduler.
Therefore the OS is not a process. (Referring to what is in the kernel mode) It is only invoked via interrupts and system calls.
In linux, when a system call is made, the process is considered to be still running. For example, if get_pid() is called, why would the process need to stop running or have a state change?
1. Assign new PID
2. Create PCB
3. Run a new executable
Windows does all 3 in CreateProcess, but for Linux, the first two are done using a command called fork and the 3rd is exec.
For linux, parent fork child, creates clone of the parent (of the PCB, stack, heap, data, text, etc) (exactly the same except for the pid), returns the new pid. The new process has the exact same context. PC for both processes continue off at the same relative position in the Text.
x = fork();
The x (which is an int) for the parent will be the child's process pid, for the child the x will be 0.
The code that calls the fork will then enter the “ready” state (because it has been interrupted) or “waiting” (during some system calls, especially I/O and process synchronization)
fork() and exec() are POSIX commands.
Exec comes in a variety
L variety - exec end with l. This is referring to exec calls that take in a list of arguments. List comes in a comma separated list.
int execl(const char *path, const char *arg, ...);
/usr/bin/gcc - the pathname for the gcc executable.
nullptr at end of execl to signify the end of the argument list.
exec transforms the process into another process, and of course, the size of the memory differs from the original, stack pointer will differ also
Environmental variables for the new program are, by default (and if not specified), taken from the parent process.
execlp -> find the executable from the path environment
int execlp(const char *file, const char *arg, ...);
execle -> set the environment variables before running
int execle(const char *path, const char *arg,..., char * const envp);
V variety - arguments passed as an array of strings
int execv(const char *path, char *const argv);
execv is for unknown number of arguments at compile-time (rather than execl)
The execle() and execvpe() functions allow the caller to specify the environment of the executed program via the argument envp. The envp argument is an array of pointers to null-terminated strings and must be terminated by a NULL pointer. The other functions take the environment for the new process image from the external variable environ in the calling process.
l - takes in arguments
p - finds exec from path env
e - set env vars before running
v - arguments passed as array of strings
l and v cannot be together
execve does not exist cause it's the exact system call.
int execvp(const char *file, char *const argv);
wait (int * status);
The system call wait() is easy. This function blocks the calling process until one of its child processes exits or a signal is received. For our purpose, we shall ignore signals. wait() takes the address of an integer variable and returns the process ID of the completed process. Some flags that indicate the completion status of the child process are passed back with the integer pointer. One of the main purposes of wait() is to wait for completion of child processes.
The execution of wait() could have two possible situations.
1. If there are at least one child processes running when the call to wait() is made, the caller will be blocked until one of its child processes exits. At that moment, the caller resumes its execution.
2. If there is no child process running when the call to wait() is made, then this wait() has no effect at all. That is, it is as if no wait() is there.
pid_t waitpid(pid_t pid, int *status, int options);
The waitpid() system call suspends execution of the calling process until a child specified by pid argument has changed state. By default,waitpid() waits only for terminated children, but this behavior is modifiable via the options argument, as described below.
The call wait(&status) is equivalent to:waitpid(-1, &status, 0);
print ("Ni Hao!\n");
4 Ni Haos will be printed.
4 Ni Haos will be printed here too.
Zombie processes are created if you create a child process and the parent process goes into an infinite loop - the resources for the child process stays, and the whole thing becomes a zombie.
CreateProcess (fork and exec in one)
Upon successful completion, fork() returns a value of 0 to the child
process and returns the process ID of the child process to the parent
process. Otherwise, a value of -1 is returned to the parent process, no
child process is created, and the global variable [errno] is set to indi-
cate the error.
therefore, if(fork()) will be from the parent process
return exits from the function while exit exits from the program.
In main function executing return 0; statement or calling exit(0) function will call the registered atexit handlers and will cause program termination.