Processes
This session is dedicated to the study of processes.You don't need to submit source files or reports for this sessions. For this session, you will need to log into a PC running Linux. If you are at Eurecom, simply log on a PC of rooms 52 or 53.
I. Concepts
- Open the slides Processes
- Watch the video on processes, part 1. There is an exercise to be done during the video (pause the video whenever necessary).
- Exercise on states. Use the "Help me" only once you have found answers. You may obvisouly use the web to find answer.
What are the reasons to go from: - Running to Ready ("Ready" is sometimes called "Runnable")? To find the solution, you may want to run:
- Running to Blocked?
- Blocked to Ready?
- Running to Terminated
- Ready to Terminated?
- Blocked to Terminated?
- Why there is no transition from "Ready" to "Blocked"?
$ man pthread_yield
(Help me!)
The process was preempted by the scheduler, or it decided to stop its execution with e.g. yield()
(Help me!)
The process tries to make an input/output operation, e.g. read data from disk, but data are not yet available. Or the process decides to sleep(). Or it waits for a lock ...
(Help me!)
The blocking condition was resolved.
(Help me!)
The process reached its last instruction, or called exit(), or was killed from another processor/core
(Help me!)
You can find part of the answer from the help on Running to Terminated.
(Help me!)
You can find part of the answer from the help on Running to Terminated.
II. Programming processes
- Open the slides Processes
- Watch the video on processes, part 2. Don't forget to pause the video and to make the exercises (compiling and executing code).
- What happens if you execute the following code? I don't ask you to try, but to think about what will happen in the system. You may try if you really want to, but save all your work before ^^
- What happens if you replace fork() by vfork() in the previous code?
int main(void) { while(1) { fork() } }
III. Analyzing processes
The purpose of this exercise is to learn the spawning of new processes. During the lecture on processes, you have been presented how to start a new process from a running one, using the fork() system call. You will practice this system call in this exercise. Don't rush: take your time to really understand what you are doing. Use the "help" only once you have tried your best :)- Logging onto a remote machine. For this exercise, you need to compile, execute your programs and run various commands from another computer named "calibra". Start a terminal, and start a remote login to calibra:
- OS and hardware. Which operating system runs on calibra? What is the hardware of this machine?
- System calls and fork. Now, try to run the following command:
- C code. Save the fork01.c file into your work directory, open it and understand its code.
- Compile fork01.c to a binary file with a name of your choice, but please, select a name that another student won't take, so that you can identify your executable file with a unique name in the system. Let's call it myfork for the description of the lab, but again, name it differently:
- Warnings. Now, try to compile myfork01.c with the -Wall gcc option:
- Working with strings. Uncomment the line commented in fork01.c, compile and run again. Do you get an error? Let's try to better investigate the problem together. In which section of the process are the "child" and "parent" strings stored? You can use the following commands. First, determine where the strings are stored in the executable file.
- Writing robust code. We now assume that the system has reached its maximum number of processes: What happens if you execute myfork? Modify your code so as to take into account that problem.
(Help me!) If no more fork is possible in the system, the fork() function returns an error value. Currently, the code I provide does not check the return value of fork(). So, you need to test this value. Probably, "man fork" could help you. - Analyzing calls to library functions and to system calls The application you have just programmed performs library function calls and system calls, e.g., to start the new process, to print information, etc.. A utility provided with Solaris 10/11 - called dtrace i.e. "dynamic tracing" - makes it possible to trace all functions and system calls performed by applications while the latter is running. Documentation on dtrace:
- Advanced call analysis
- List the number of probes supported by dtrace
- List the number of entry probes supported by dtrace that contains the name "fork"
- Get the number of bytes printed by all processes running in the system
- Get the number of bytes printed by your myfork process and its son
- List of processes started by all users of the system
- Print the name of all processes executing a fork
$ ssh calibraIn order to ssh to calibra, you may have to update the configuration of your ssh client because calibra's ssh server uses a former authentication methods. A workaround consists in forcing your client to use the ssh-rsa method for calibra. For this, add the following to your ~/.ssh/config file (create the file if it does not exist yet):
Host calibra micra HostKeyAlgorithms ssh-rsa
(Help me!)
Use the uname command
$ vmstat -sWhat information has been provided to you? More specifically, where do you find the number of forks executed in the system since bootup? Also, how do you find the number of systems calls executed since bootup?
Now, run the following command:
$ vmstat -s | grep forkRe-run the same command several times. What to do you observe? What happened in the system?
(Help me!)
This number of fork should increase of at least 2 ... So, probably, each time you execute this command, the shell need to perfrom two forks ... I let you find why...
Are you alone in using calibra? Try this:
$ who
$ gcc -o myfork fork01.cWhat happened? Why did you get error messages?
(Help me!)
They are "#include" missing ... you can find which ones with "man". There is also a slight syntax error to correct.
How can you resolve these errors? Re-compile and execute myfork. What did the program print, and why?
$ gcc -Wall -o myfork fork01.cWhat warning(s) did you obtain, and how did you resolve them?
$ strings -t x myforkNow, list the code/variable sections of the executable file, and deduce in which sections are stored the strings:
$ readelf -S myforkSo, what is the nature of the section that contains the strings? You may use the name, and the flags of the section to deduce its nature. Then, you should be able to conclude on the reason for the error.
(Help me!)
string gives you the address of e.g. "parent". Then, the readelf command tells that at this address, the section is "rodata" ... which means "read only data"
Also, one way to confirm the content of a section is to use objdump (replace "section" with the correct section name):
$ objdump -s -j section myfork
You can now simply remove the faulty line that was commented at first in fork01.c
If you wanted to keep that line, how would you need to change your program?
dtrace-cheatsheet
Full documentation on dtrace
Try the following command:
$ dtrace -c myfork -n 'pid$target:::entry'Solaris lists all library functions and system calls performed by your application. Which are a few system calls in this list? What are their purposes? You may use manual pages to see whether a name provided in the list corresponds to a function or a system call. For example, if you do:
$ man fflushAt the beginning of the manual page, it is presented as a "Standard C Library Function": so, it is not a system call.
You can also try to monitor all fork systems calls that are executed in the system. To do this, run dtrace as follows (all processes are monitored, and not only your processes):
$dtrace -n syscall::forksys:entryWhat information do you obtain? How many system calls does Solaris implement?
Now, let's try to focus on the system calls only. Let's see which ones are used in your program. In the following command, replace "321" with the process id of your fork process. Also, enter CTRL-C at the end to obtain the result of the tracing. Note: you may have to modify the code of your process so as to have the time to enter the pid in the following command before the process makes the call to fork().
$dtrace -n syscall:::entry'/pid == 321/{ @syscalls[probefunc] = count(); }'Which system calls are executed, and to which C function calls do they correspond?
For such complex dtrace functions, it is sometimes easier to specify the dtrace arguments as an executable script. Edit the following file (name it e.g. mydtracescript):
#!/usr/sbin/dtrace -s pid$1:::entry { @count_table[probefunc]=count(); }Then, make it executable:
$chmod u+x mydtracescriptAnd finally, run it:
$./mydtracescriptWhat happens? What are you expected to provide to the script? Understand it, and run it on your fork program. What information do you obtain?
Using dtrace, find a way to do the following. Do not rush to the "help me!": the idea of this exercise is to discover commands on your own. Also, test your dtrace command by executing the relevant processes and actions.
(Help me!)
/usr/sbin/dtrace -l
(Help me!)
/usr/sbin/dtrace -l -n syscall:::entry|grep fork
(Help me!)
/usr/sbin/dtrace -n 'sysinfo:::writech { @bytes[execname] = sum(arg0); }'
(Help me!)
/usr/sbin/dtrace -n 'sysinfo:::writech { @bytes[execname] = sum(arg0); }'|grep "myfork"
(Help me!)
/usr/sbin/dtrace -n 'proc:::exec-success { trace(curpsinfo->pr_psargs); }'
(Help me!)
/usr/sbin/dtrace -q -n 'syscall::forksys:entry { printf("%-16s\n",execname); }'
- Terminating a process. Now, the father process should terminate (or kill) its son process before it itself terminates. To do so, you could use the kill system call, with as argument the pid of the son process, and the SIGKILL signal. Program this, and test. Also, use dtrace to figure out at which moment the "kill" system call is actually executed. Also, how do you verify that the son process is really killed, that is, that it has not terminated its execution before being killed?
- Monitoring signals. Now, we would like to monitor the SIGKILL signals which are sent to processes. Make the following dtrace script, and start it in a terminal:
#!/usr/sbin/dtrace -wqs proc:::signal-send /args[2] == SIGKILL/ { printf("SIGKILL was sent to %s by ", args[1]->pr_fname); system("getent passwd %d | cut -d: -f5", uid); }
Then, execute your fork program to see that this script can monitor SIGKILL signals. What information do you get when a process is killed?
- Now, we would like to create more than one process The main process should have three sons. The first and the last sons should also have two sons. [In this question, you are not asked to use the kill function, but if you feel at ease with this lab, you may try to have the main father killing all his sons and grandsons.]
- Let's get more information on this Solaris computer! Consult the manual pages of psrinfo command. Using this command, what is the number and types of processors installed on your system?
Modify fork01.c accordingly. Start this program several times. What do you observe? Use the ps command to verify that all processes are created. Also, verify the number of times the fork system call is performed by typing:
$ dtrace -c ./myfork -n 'pid$target::fork:entry'Trace another system call of your choice with the same approach, and do the same for one function of the C library. What results do you obtain?
(Help me!)
You could try to replace "fork" with "exit" and "printf" in the previous dtrace command
(Help me!)
$psrinfo -v
Now, we want to be sure that all processes of myfork are bound to one given CPU: 0 or 1, for example. Consult the command pbind and map your application to one given CPU. What command did you use? You may check that your application is really executed on a given cpu by using the prstat command.
(Help me!)
$./myfork&
$pbind -b -c 2 -i pid 2507
$prstat -P 2 -p 2507
$pbind -b -c 2 -i pid 2507
$prstat -P 2 -p 2507