2 Systems Programming

You might also like

Download as pdf or txt
Download as pdf or txt
You are on page 1of 3

Systems Programming

2 Process model and related system calls

Process tree ◦ User ID running the current process can be obtained


using getuid() system call
◦ Every programming running on a UNIX based system
is called a process
◦ If a system call returns an error, the code for the er-
◦ Every process has a process number (pid), a parent pro- ror can be accessed using errno variable. System calls
cess (ppid), the user running it (uid), the source of the return a negative value on error.
process (the program), file handles that are open, and
zero or more child processes
◦ The error text can be printed on the screen using
◦ Exception to above rule is init, which has no parent perror(string ) function from stdio.h
as it is the root of the processes

◦ Process numbers are sequential and the process number


for init is 1 Creating new process
◦ init starts with the operating system and dies with it
! <unistd.h>, <sys/types.h>
◦ init is replaced with systemd in modern Linux systems
but have the same semantics and often still called init
◦ fork(): splits a new process from the current one
◦ init starts system services, then the login manager:
effectively starting the system
◦ Copies file descriptors, environment, and signals
◦ Processes on Linux are lightweight

◦ Also a process can wait for its children to end ◦ Code stays the same, execution continues from the next
line for child and parent
◦ A process can be terminated while its children is left
running
◦ Two processes will have the same value for all variables
◦ If parent of a process is terminated while the process is at the creation time, but they are not shared and may
still running: diverge
 The child becomes orphan
 Child processes are immediately adopted by init ◦ Only way to differentiate between child and parent is
the return value of fork() system call
 Child processes will continue running under init
without any issues
 Once finished, init will terminate the child ◦ If there is an error fork() returns a negative number
and child process will not be created
◦ When a child process is finished and its parent is not
waiting, the process becomes zombie process
◦ fork() returns a positive number to the parent process.
 Reason behind this behavior is to allow parent pro- This number is the pid of the child that is created
cess to later read the return value of the child pro-
cess
◦ fork() returns 0 to the child process, if necessary, child
 In modern systems child is almost terminated in can access pid of its parent by called getppid().
this state and takes up very few amount of re-
sources
 Parent can call to terminate the child ◦ The following program will create a child process, both
child and the parent will display process information
 If parent never calls wait and exists, init will reap
the zombie
 Even if a parent constantly creates children with- int pid = fork();
out waiting for them to finish, system will run out
if(pid == -1) {
of process numbers before running out of memory
perror("Fork error");

return -1;
Getting information }
else if(pid == 0) {
◦ It is possible to get process id (pid) of the current pro- printf("I am the child process with PID: %d, PPID: %d
cess by calling getpid() system call }
else {
◦ Parent process id (ppid) can be accessed using printf("I am the parent process with PID: %d, PPID: %
getppid() system call }

1/3
Waiting for child process Executing other applications
! <wait.h> <sys/types.h> ! <unistd.h>
◦ Parent processes can wait for its child processes to finish ◦ It is possible to replace currently running code with an-
◦ This allows parent to get return code of the child as well other
waiting for it finish
◦ Effectively this switches to another application
◦ pid = wait(status ) is used for this task, pid is the
child that is finished. In case of an error pid will be neg- ◦ The functions that are used to perform this operation
ative. This includes when there is no children to wait. is called exec family system calls
status is the termination status of the child. There
are two macros to analyze this status. It is a pointer to
◦ If there are no errors, an exec call will not terminate
integer and can be left NULL if not needed. ◦ When the executed application finishes, the child pro-
◦ In order to determine if the child process has crashed, cess will be terminated
WIFEXITED(status macro can be used. If this macro
returns 0 the child has crashed
◦ Unless specified otherwise, file descriptors, environ-
ment, and signals will not be modified when the process
◦ Return code of a child that has returned (as opposed is executed
to crashing) can be obtained by WEXITSTATUS(status )
macro ◦ execl or execlp(progam , arguments ..., NULL): ex-
ecutes a program from current path (execl) or from
◦ The following code will run a child process, wait for it the system path (execlp) by specifying arguments as
to exit and at the end it will show the state of the child. parameters to this function. First argument should be
Child will ask a value and divide 10 to that value and the name of the program while last argument should be
returns the result. If provided value is 0, child will crash NULL.
due to division by 0.
◦ execv or execvp(program , arguments ): executes a
if(fork()) { program by giving out an argument vector (NULL ter-
int status;
minated). Useful when arguments are constructed step
wait(&status);
by step.

if(WIFEXITED(status)) {
◦ Following code segment will execute ls -l command
printf("Child has finished with %d\n",
which will display detailed list of files in the current
WEXITSTATUS(status)); directory:
}
else { if(fork()) {
printf("Child has crashed.\n"); wait(NULL);
} }
} else {
else { execlp("ls", "ls", "-la", NULL);
int x; perror("Cannot run ls");
scanf("%d", &x); }

return 10 / x;
} Environment variables
! <stdlib.h>

Process tree ◦ Used to store variable that are passed to child processes

◦ Important tool to understand what is going on ◦ Useful to probe slow changing system info

◦ sleep(seconds ) makes the process sleep for given sec- ◦ They are not automatically updated, only newly cre-
onds, good for testing but should never be used for pro- ated direct children of the modifier will be affected
cess synchronization
◦ C Library provides these functions
◦ Zombie and orphan graphs
◦ Trace the following:
◦ getenv(name ): returns the value for the given variable
name. Environment variables are generally capitalized
for(int i=0; i<4; i++) { and cannot contain = sign
int pid = fork();
◦ setenv(name , value , overwrite ): sets the value of
if(pid && i%2) the variable with the given name. If variable exists and
return 0; overwrite parameter is set to 0, it will not be changed.

printf("X"); ◦ In the terminal variables can be set using X="abcd"


} syntax while can be access like $X

2/3
◦ exec family functions that have an additional e at the int main() {
end (like execvpe) takes an additional vector whose signal(SIGINT, &sig_int);
members are in form of VAR=value. This vector will while(1) {
be used for environment variables. printf("Sleeping...");
sleep(1);
◦ Following code prints the name of the current user: }

printf("Username: %s\n", getenv("USER")); return 0;


}

Signals
! <signal.h>

◦ Signals are used to control processes

◦ Most signals are generated by the OS, however, it is


possible for any process to send signals to any other
process if it is not denied for security

◦ If a process does handle a specific signal, default action


for the signal is performed. For most signals default
action is to terminate the application.

◦ The following table contains commonly used signals

Signal Default Description


SIGINT Terminate Interrupt from keyboard
SIGTERM Terminate Program is asked to terminate
SIGKILL Terminate Program is killed instantly
SIGFPE Terminate Float point exception, integer
division by zero
SIGSGV Terminate Access to unauthorized mem-
ory location
SIGCHLD Nothing Called when a child process is
terminated

◦ raise(signal ): send the given signal to the current


process

◦ kill(pid , signal ): send the given signal to pro-


cess with the given pid

◦ You may use console command kill -signal pid to


send a signal to any process

◦ Use old = signal(signal , new ) system call to spec-


ify a new handler for the signal .

◦ Use of signal system function requires a handler func-


tion which takes an integer parameter and does not re-
turn anything.

◦ Apart from SIGINT and SIGCHLD, you should terminate


your application at the end of the signal. Save the cur-
rent state, cleanup any resources you use and end your
application.

◦ The following code will handle SIGINT, use CTRL + C


in terminal to send SIGINT to the running program.

void sig_int(int) {
printf("Interrupted...\n");
exit(0);
}

3/3

You might also like