Lab 9: Subprocess
You will work on a worksheet that will prepare you for your subprocess assignment. Please attend the section you are registered for to receive your lab checkoff.
In the first part of the lab, you will invoke the kill system call without using standard library wrappers and test it.
In the second part, you will review system calls that will be useful for your upcoming Subprocess assignment.
Part 1
As you recall from lecture, processes run in an “restricted environment” called the user space. On their own, a process can run computation instructions and access its own memory. However, it cannot directly perform privileged operations such as reading files or creating processes. To request such privileged operations, a process must use system calls, which transfer control to the operating system kernel to perform the requested action safely on its behalf.
In this part of the lab, you will learn how the kill system call works and use it to send signals between processes.
System Calls
Signals are a way for processes to communicate with each other and notify each other of events. For example, when you press Ctrl+C in the terminal, a SIGINT signal is sent to the foreground process to interrupt it. For this exercise, we will write a function called void send_sigint(int pid) that sends a SIGINT signal to the process with the given PID.
Step 1 Learn more about kill
In rv-debug, run the following commands to see how the kill system call is defined by the linux kernel.
cat /usr/include/asm-generic/unistd.h | grep __NR_killcat /usr/include/signal.h | grep kill
You should find the kill signal system call number as well as the two arguments it accepts.
Step 2 Write the send_sigint function in RISC-V assembly.
# in send_sigint.s
.global send_sigint
send_sigint:
# the first argument, pid, is already in the correct place (a0)
# the second argument, SIGINT, is 2, and must be stored in the a1 register
addi a1, x0, 2
# load the syscall number for kill into a7
# - write the correct line below
#### BEGIN TODO ####
### END TODO ###
ecall
ret
To test the assembly code, we use the C code below.
// in send_sigint.c
extern void send_sigint(int pid);
int my_atoi(const char *s) {
int n=0, sign=1;
if(*s=='-'||*s=='+') sign=(*s++=='-')?-1:1;
while(*s>='0'&&*s<='9') n=n*10+(*s++-'0');
return sign*n;
}
int main(int argc, char* argv[]) {
if (argc < 2) return 0;
send_sigint(my_atoi(argv[1]));
return 0;
}
Step 3 Compile and run the program.
Ensure you are in the rv-debug docker container. To compile, run
root@1ab8790910e4:~/test# gcc -o send_sigint send_sigint.s send_sigint.c
In a second terminal, run docker exec -it testing /usr/bin/env bash and spawn a new python process
root@1ab8790910e4:~# python3
Python 3.10.12 (main, Sep 11 2024, 15:47:36) [GCC 11.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import os
>>> os.getpid()
378
If we run qemu ./send_sigint 378, replacing 378 with the PID that you see, we can see that the python process is will print keyboard interrupt. The program you wrote sent a signal to another process without importing any headers!