Unit4 - Subjective Questions
CSE325 • Practice Questions with Detailed Answers
Explain the fork() system call with respect to process creation. What are its possible return values?
Description:
The fork() system call is used to create a new process by duplicating the calling process. The new process is referred to as the child process, and the calling process is referred to as the parent process.
Mechanism:
- The child process and the parent process run in separate memory spaces.
- At the time of
fork(), both memory spaces have the same content.
Return Values:
fork() returns an integer value:
- Negative Value: Creation of a child process was unsuccessful.
- Zero: Returned to the newly created child process.
- Positive Value: Returned to the parent process. The value contains the Process ID (PID) of the newly created child process.
Differentiate between the fork() and exec() system calls.
| Feature | fork() |
exec() |
|---|---|---|
| Function | Creates a new process by duplicating the calling process. | Replaces the current process image with a new program image. |
| PID | The child process gets a new, unique PID. | The PID remains the same; only the program changes. |
| Memory | Creates a separate memory space (copy of parent). | Overwrites the existing memory space (text, data, stack, heap). |
| Return | Returns twice (once in parent, once in child). | Does not return if successful (process is replaced). |
| Usage | Used to create parallel processing or concurrent tasks. | Used to run a different program within a process. |
What is a Zombie Process? How is it created and how can it be removed from the system?
Definition:
A Zombie process (or defunct process) is a process that has completed execution (via exit()) but still has an entry in the process table. This occurs because the parent process has not yet retrieved the child's exit status.
Creation:
It happens when a child process terminates, but the parent is busy performing other tasks and does not call wait() to read the child's exit code.
Removal:
- Normal way: The parent process calls
wait()orwaitpid()to read the exit status, releasing the process table entry. - If parent dies: If the parent terminates without waiting, the zombie child is adopted by the
initprocess (PID 1), which periodically callswait()to clean up zombies.
Describe the concept of an Orphan Process. What happens to an orphan process in a Linux/Unix system?
Definition:
An Orphan process is a running process whose parent process has finished or terminated, but the child process is still executing.
Fate of an Orphan:
- In Unix-like operating systems, orphan processes are immediately adopted by the special
initprocess (usually PID 1). - The
initprocess becomes the new parent of the orphan. - When the orphan eventually finishes,
initautomatically performs thewait()system call to collect the exit status and prevents the orphan from becoming a zombie.
Derive the number of processes created by a program containing consecutive fork() system calls. Provide an example with .
Formula:
The number of processes created by fork() calls is given by the formula:
Where includes the original parent process. If counting only new child processes, the formula is .
Example ():
c
fork(); // 2 processes (1 Parent, 1 Child)
fork(); // 4 processes (Previous 2 duplicate)
fork(); // 8 processes (Previous 4 duplicate)
Calculation:
Thus, 8 processes are running in total.
Explain the purpose of the wait() system call. What information does it provide to the parent process?
Purpose:
The wait() system call blocks the calling process (parent) until one of its child processes exits or a signal is received.
Functionality:
- Synchronization: It forces the parent to wait for the child to finish.
- Resource Reclaiming: It allows the system to release resources associated with the child process (preventing zombies).
- Status Retrieval: It takes an integer pointer argument (e.g.,
wait(&status)). The integerstatusis populated with information regarding how the child terminated (normal exit, killed by signal, etc.).
Discuss the exec() family of functions. List at least four variants and explain the meaning of the suffixes (l, v, p, e).
The exec() family replaces the current process image with a new process image.
Common Variants:
execl(const char *path, const char *arg, ...)execv(const char *path, char *const argv[])execlp(const char *file, const char *arg, ...)execvp(const char *file, char *const argv[])
Suffix Meanings:
- l (list): Arguments are passed as a comma-separated list, terminated by NULL.
- v (vector): Arguments are passed as an array (vector) of pointers.
- p (path): The function uses the
PATHenvironment variable to find the file (filename needed instead of full path). - e (environment): An array of environment variables is passed explicitly to the new process.
What are Signals in an operating system? Name three common signals and their default actions.
Definition:
A signal is a software interrupt delivered to a process by the OS to report an exceptional situation, error, or external event. It is a limited form of inter-process communication.
Common Signals:
- SIGINT (Signal Interrupt):
- Triggered by:
Ctrl+C - Default Action: Terminate process.
- Triggered by:
- SIGKILL (Signal Kill):
- Triggered by:
kill -9 <pid> - Default Action: Immediate termination (cannot be caught or ignored).
- Triggered by:
- SIGSEGV (Segmentation Violation):
- Triggered by: Invalid memory access.
- Default Action: Terminate process and dump core.
Write a C code snippet to demonstrate the creation of an Orphan Process.
c
include <stdio.h>
include <stdlib.h>
include <unistd.h>
int main() {
pid_t pid = fork();
if (pid > 0) {
// Parent Process
printf("Parent Process (PID: %d) finishes execution.
", getpid());
exit(0); // Parent dies immediately
}
else if (pid == 0) {
// Child Process
sleep(5); // Sleep to ensure parent dies first
printf("Child Process (PID: %d) is now an Orphan.", getpid());
printf("New Parent PID: %d
", getppid()); // Should be init/systemd
}
return 0;
}
Write a C code snippet to demonstrate the creation of a Zombie Process.
c
include <stdio.h>
include <stdlib.h>
include <unistd.h>
int main() {
pid_t pid = fork();
if (pid > 0) {
// Parent Process
printf("Parent (PID: %d) sleeping... check 'ps' for zombie.
", getpid());
sleep(20); // Parent sleeps, doesn't call wait()
}
else if (pid == 0) {
// Child Process
printf("Child (PID: %d) exiting.
", getpid());
exit(0); // Child dies, becomes Z+ state until parent wakes/terminates
}
return 0;
}
How does waitpid() differ from the standard wait() system call? Explain the significance of the pid argument in waitpid().
Difference:
wait()blocks until any child terminates.waitpid()can be used to wait for a specific child process or process group, and it offers options (like non-blocking wait).
Significance of pid argument in waitpid(pid_t pid, ...):
- pid < -1: Wait for any child process whose process group ID is equal to the absolute value of
pid. - pid == -1: Wait for any child process (same behavior as
wait()). - pid == 0: Wait for any child process whose process group ID is equal to that of the calling process.
- pid > 0: Wait for the child whose process ID is equal to the value of
pid.
Explain the concept of Copy-on-Write (COW) in the context of the fork() system call.
Concept:
When fork() is called, the OS does not immediately physically copy all of the parent's memory pages (text, data, heap, stack) to the child. Instead, it marks the pages as read-only and shares them between parent and child.
Mechanism:
- Both processes read from the same physical memory.
- If either the parent or the child attempts to write to a shared page, a hardware trap occurs.
- The OS kernel then allocates a new physical copy of that specific page for the writing process.
Advantage:
This significantly reduces the overhead of process creation, as physical copying is delayed and only performed for pages that are actually modified.
Explain the use of the kill() system call. What are its arguments?
Purpose:
The kill() system call is used to send a signal to a process or a group of processes. Despite its name, it is not only used to kill processes but to send any valid signal.
Syntax:
int kill(pid_t pid, int sig);
Arguments:
- pid: Specifies the process or group to receive the signal.
pid > 0: Send to specific process ID.pid == 0: Send to every process in the calling process's group.
- sig: The signal number to send (e.g.,
SIGKILL,SIGTERM,SIGINT). Ifsigis 0, no signal is sent, but error checking is performed (useful to check if a process exists).
What macros are used to interpret the status integer returned by wait()? Explain WIFEXITED and WEXITSTATUS.
The integer status returned by wait(&status) encodes the termination reason. Macros are required to decode it.
Common Macros:
WIFEXITED(status):- Returns true (non-zero) if the child terminated normally (i.e., called
exit()or returned frommain).
- Returns true (non-zero) if the child terminated normally (i.e., called
WEXITSTATUS(status):- Returns the exit status (lower 8 bits) of the child. This macro should only be used if
WIFEXITEDreturned true.
- Returns the exit status (lower 8 bits) of the child. This macro should only be used if
Other Macros:
WIFSIGNALED(status): True if child was killed by a signal.WTERMSIG(status): Returns the signal number that caused termination.
Compare the Default Action, Ignoring, and Handling (Catching) of signals.
When a signal is delivered to a process, one of three things can happen:
-
Default Action:
- The kernel performs the standard action defined for that signal (e.g., Terminate for
SIGTERM, Ignore forSIGCHLD, Core Dump forSIGSEGV).
- The kernel performs the standard action defined for that signal (e.g., Terminate for
-
Ignoring the Signal:
- The process explicitly tells the kernel to discard the signal (
SIG_IGN). - Exception:
SIGKILLandSIGSTOPcannot be ignored.
- The process explicitly tells the kernel to discard the signal (
-
Handling (Catching) the Signal:
- The process defines a custom function (Signal Handler) that executes when the signal arrives.
- When the signal occurs, the normal execution is paused, the handler runs, and then execution resumes.
Explain the role of the signal() system call with a syntax example.
Role:
The signal() system call is the traditional method used to set a disposition for a signal (i.e., telling the OS what to do when a specific signal arrives).
Syntax:
sighandler_t signal(int signum, sighandler_t handler);
Arguments:
signum: The signal number (e.g.,SIGINT).handler: The action to take. It can be:SIG_DFL: Default action.SIG_IGN: Ignore the signal.- A pointer to a user-defined function.
Example:
c
void my_handler(int signum) { printf("Caught signal %d
", signum); }
// Inside main:
signal(SIGINT, my_handler);
A process executes the following code:
c
fork();
fork();
execlp("echo", "echo", "Hello", NULL);
fork();
How many times will "Hello" be printed? Explain your reasoning.
Answer: 4 times.
Reasoning:
- First
fork(): Creates 1 child. Total processes: 2. - Second
fork(): Both processes fork. Total processes: 4. execlp(...): ALL 4 processes executeexeclp.execreplaces the process image with theechoprogram.- Once
execis successful, the remaining code in the original program is never executed.
- Third
fork(): This line is unreachable because the program memory was overwritten byecho.
Therefore, 4 processes run echo "Hello", print "Hello", and terminate.
How does a shell (like bash) use fork(), exec(), and wait() to execute a command typed by the user?
When a user types a command (e.g., ls -l) into a shell, the shell acts as the parent process and performs the following steps:
-
Fork:
- The shell calls
fork()to create a child process. This child is currently a copy of the shell.
- The shell calls
-
Exec:
- The child process calls a variant of
exec()(e.g.,execvp). - It loads the executable file for the command (e.g.,
/bin/ls) into memory, replacing the child's shell code with thelscommand code.
- The child process calls a variant of
-
Wait:
- While the child runs the command, the parent (shell) calls
wait(). - This pauses the shell prompt until the command finishes execution.
- Once the child exits,
wait()returns, and the shell prints the prompt again for the next command.
- While the child runs the command, the parent (shell) calls
What is the SIGCHLD signal? When is it generated and how should a parent process handle it?
Definition:
SIGCHLD is a signal sent to a parent process whenever one of its child processes terminates or stops.
Generation:
- When a child calls
exit(). - When a child is killed by a signal.
- When a child is stopped (suspended) or continued.
Handling:
- By default,
SIGCHLDis ignored. - However, to avoid Zombie processes without blocking the parent (as
wait()does), a parent can install a signal handler forSIGCHLD. - Inside the handler, the parent calls
waitpid()with theWNOHANGoption to reap the child's status immediately (asynchronously) without blocking the main execution flow.
Distinguish between getpid() and getppid().
getpid():
- Stands for Get Process ID.
- Returns the unique integer identifier (PID) of the calling process (the process currently executing the code).
- Syntax:
pid_t getpid(void);
getppid():
- Stands for Get Parent Process ID.
- Returns the unique integer identifier (PID) of the parent of the calling process.
- Syntax:
pid_t getppid(void);
Usage Context:
These are often used inside fork() logic to distinguish logs or actions between the parent and the child.