This is a small programming assignment about upcalls or signals. The program has a parent and a child process. Both processes have a global integer variable proc_state
, initially set to 0. Our task is to modify the program so that the parent can change the value of the proc_state
of the child.
-
Global variable
proc_state
: Notice that both the parent and child processes have their ownproc_state
variable. -
Signal handler (
sig_handler
): This is the function that will increment theproc_state
when a signal is received. -
Parent process: The parent has a for-loop where each pass will cause the
proc_state
of the child to increment by 1. This is done by sending aSIGUSR1
signal to the signal handler of the child. Each pass is delayed by 1 second, and it displays the value and address of the parent’sproc_state
. -
Child process: The child should register its signal handler with the kernel using the system call
signal()
. The child has a for-loop where each pass is delayed by 1 second and displays the value and address of itsproc_state
. Theproc_state
of the child will increment as it receivesSIGUSR1
signals from the parent.
The current program doesn’t work. It neither sends signals from the parent nor receives them in the child. Modify the program so that it works.
When the program is working correctly, the output should look something like this (though it may differ slightly since the processes are running in parallel):
Child begins: address of proc_state = 404068
Child: proc_state = 0, address = 0x404068
Child: proc_state = 1, address = 0x404068
Child: proc_state = 2, address = 0x404068
Child: proc_state = 3, address = 0x404068
Child: proc_state = 3, address = 0x404068
Child: proc_state = 3, address = 0x404068
Child: bye bye
Parent begins: address of proc_state = 0x404068
Parent: proc_state = 0, address = 0x404068
Parent: proc_state = 0, address = 0x404068
Parent: proc_state = 0, address = 0x404068
Parent: proc_state = 0, address = 0x404068
Parent: bye bye
Did you notice?...Why Do Parent and Child Processes Have proc_state
at the Same Address but with Different Values?
This behavior occurs due to the nature of process forking in operating systems like Linux.
-
Separate Address Spaces: When a process is forked, the parent and child processes have independent memory spaces, even though they initially share the same virtual memory layout. Each process can modify its own variables without affecting the other.
-
Same Virtual Address, Different Physical Memory: Both processes have
proc_state
at the same virtual address, but due to address space isolation, they point to different physical memory locations after the fork. -
Copy-on-Write (COW): The operating system employs a technique called Copy-on-Write (COW). Initially, the parent and child share the same physical memory pages. However, when one process modifies
proc_state
, the OS creates a separate copy of the memory page for that process.
While the address appears the same, the values differ because the parent and child have independent copies of proc_state
.
When working with signals and processes in C, especially in parent-child relationships, there are several key points and best practices to keep in mind. These tips can help improve the reliability and clarity of your program.
-
Use the
signal()
function to register a signal handler for a specific signal (e.g.,SIGUSR1
). -
The signal handler should be simple and efficient because it executes asynchronously. Avoid calling non-reentrant functions like
printf()
inside the handler. Instead, use simple, signal-safe operations like modifying variables. -
Example for setting up the signal handler:
signal(SIGUSR1, sig_handler); // Register sig_handler for SIGUSR1
-
Parent Process: Use
kill()
to send signals from the parent to the child. Thekill()
function requires the process ID of the child and the signal you want to send.kill(child_pid, SIGUSR1); // Send SIGUSR1 signal to child
-
Child Process: The child needs to listen for signals and modify its state upon receiving them using the signal handler.
- Both the parent and child processes have their own copies of global variables. Even though they share the same addresses (since they start from the same memory layout after
fork()
), the variables are not shared between processes. They exist in separate memory spaces. This explains whyproc_state
has the same address in both parent and child but different values.
-
Always check for errors after system calls like
fork()
andsignal()
. Iffork()
fails, it returns-1
, and you should handle this case properly. -
Use
perror()
to output error messages for system call failures.if (fork() == -1) { perror("fork failed"); exit(EXIT_FAILURE); // Exit if fork fails }
-
Ensure that the parent waits for the child to finish by calling
wait(NULL)
. This prevents zombie processes and ensures proper process cleanup.wait(NULL); // Wait for child to finish