Analysis of MINIX3 IPC

The examples of this section are the solutions of exercise 44 on page 219 of book [MINIX3_OS_BOOK]. The exact wording of the exercise, cited from the book [MINIX3_OS_BOOK], is the following: „Add code to the MINIX 3 kernel to keep track of the number of messages sent from process (or task) i to process (or task) j. Print this matrix when the F4 key is hit.”

In the author's Operating System courses between 2008 and 2010, the solutions of this task were used as typical examples to illustrate the Minix kernel programming. At that time, the Minix kernel was 3.1, and our students were helped to solve this exercise by the following notes: DEIK_MIPPOS_2008tavasz_BN_KiemeltOttoni_OR168_38.pdf. But remember, Minix is a living system, its code is changing rapidly, and therefore we may find several points, in this linked document, where the source code has already changed. This is a common phenomenon in informatics. For example, being restricted to Minix only, in the Minix book we can read about managing penalty points in sched() and we can see it in source code in the appendix of the book, but it is not included in the current version of the Minix kernel.

A solution with modifying the PCB

In this section we are working with MINIX 3.2.1. At the time of the writing of this book, it is the current version and it has just been installed in the section „The installation of MINIX3 system”

The PCB is an abstraction of the processes of the operating system, which is typically realised as a C structure. In Minix this structure is the struct proc that is defined in kernel/proc.h source file. The process table in Minix is simply a structure array of NR_TASKS + NR_PROCS PCB elements.

Acquiring routine

If you have no experience in Minix kernel programming we suggest that you should suspend the elaboration of this example and first start by solving the following exercise.

300 processes

When you log into the machine a special process called the shell will be created. If you enter a command in the shell, a (child) process will be created to execute the command. Web servers may fork processes to handle client requests and the list goes on. Many situations can be listed in which processes play significant roles. Actually, it is not suprising because processes are abstractions of computer programs. But it is obvious that the number of processes is limited by the size of the process table array anyway. Therefore, it is a legitimate exercise to increase the size of the process table: set the number of possible processes to 300 (from 256).

We may set the value of NR_PROCS in the source file include/minix/sys_config.h

  1. Set the value of NR_PROCS to 300 from 256.

    Figure 2.15. 

    Setting the preprocessor macro NR_PROCS.

  2. After setting the NR_PROCS, it will be interesting to see the size of the process table. It can be done easily by following the previous exercise in Section The first MINIX kernel hacking.

    Figure 2.16. Printing out the size of the PCB and the size of the process table.

    Printing out the size of the PCB and the size of the process table.

Example 2.1. Number of processes

We have also done this exercise ourselves.

After the above two steps we can see in the next figure that the size of the PCB is equal to 1664 bytes and the size of the process table is 507520 (=1664*(5+300)). If the reader completes this exercise by himself/herself, he or she will see that the result will be different due to the fact that we have already used a modified version of PCB. Because our PCB has already contains a vector in which the number of elements is dependent on the value of NR_PROCS too.

Figure 2.17. Read the size of the PCB and the size of the process table.

Read the size of the PCB and the size of the process table.



Example 2.2. The size of the PCB

Print the size of the PCB and the size of the process table from kernel.

This exercise was solved as an implicit part of the previous exercise.


Returning to the original exercise, the MINIX PCB is being extended with an array of integers (called in Hungrian uzenetszam that means the number of messages). The i-th element of this array says how many messages were sent to the i-th process.

Figure 2.18. Extending the MINIC PCB.

Extending the MINIC PCB.

In principle, we know where to store the number of sent messages, now we are going to program the counting procedure itself.

          ++(caller_ptr->uzenetszam[dst_ptr->p_nr + NR_TASKS]);
          

The appropriate element of the uzenetszam array in the caller's PCB is being incremented. Here, it is necessary to add + NR_TASKS to the array index because the numbering of processes starts from -NR_TASKS.

To avoid complication (in the first approach), this increment statement has been placed into the source file kernel/proc.c, into the begining of the function mini_send.

Figure 2.19. Counting massages.

Counting massages.

We have already mentioned that the numbering of processes starts from -NR_TASKS. It can be seen not only in the kernel sources but also if you press F1 key:

Figure 2.20. The kernel process table.

The kernel process table.


In GNU/Linux systems, the /proc virtual file system serves for monitoring the running kernel. Minix kernel has no such tool like this. We can obtain information about the running kernel over the Information Server (is), which is located in the server part of the Minix microkernel architecture. If we are going to debug the running kernel (in our case to print out the added PCB vectors), we need to use Information Server. We can link debug functions to function keys in the source file servers/is/dmp.c, right now to the F4.

Figure 2.21. Connecting a debug function to a function key.

Connecting a debug function to a function key.

The debug function uzenetszam_dmp first prints the process names of the non-empty slots of the process table as a header line. We may have noticed that we are in the server layer of the kernel tree to be more precise we have worked in the is server, where to the process table is copied from the bottom layer of the kernel by the system call sys_getproctab. The uzenetszam_dmp function works this copied instance of the process table, because the bottom layer of the kernel cannot be seen from the layer of servers.

Figure 2.22. The beginning of the function uzenetszam_dmp.

The beginning of the function uzenetszam_dmp.

After the header line, the next lines will contain the vectors of non-empty PCBs.

Figure 2.23. The midst of the function uzenetszam_dmp.

The midst of the function uzenetszam_dmp.

At the end of printing the matrix, the mennyitol (in English it means „from number”) variable will be incremented by 21, it implements a kind of paging for displaying the „IPC matrix”. To be more precise, if the F4 key has been pressed, a submatrix of size 22x9 will be printed from the actual value of mennyitol.

Figure 2.24. The end of the function uzenetszam_dmp.

The end of the function uzenetszam_dmp.

Since the function uzenetszam_dmp is also used outside of the source file servers/is/dmp_kernel.c, we must declare it, therefore we give its prototype in the source file servers/is/proto.h.

Figure 2.25. The prototype of the function uzenetszam_dmp.

The prototype of the function uzenetszam_dmp.

After this, all we have to do is to compile the kernel, then to boot the new kernel. And after the login, if we press F4 the following debug screen appears:

Figure 2.26.  Displaying the IPC matrix.

Displaying the IPC matrix.

Example 2.3. Non-empty slots

Print out the non-empty slots of the kernel process table in the form of [process name]-[p_quantum_size_ms]-[p_cpu_time_left] when F1 key is pressed (that is using the is Information Server). By solving this exercise, you must know the struct proc structure members p_quantum_size_ms and p_cpu_time_left defined in the kernel part of Minix PCB in the source file kernel/proc.h.

Figure 2.27. List of the non-empty slots from the process table.

List of the non-empty slots from the process table.



Another solution by introducing a new system call

In the previous section, we modified the PCB. Then (using the sys_getproctab system call) we copied the kernel process table to the layer of server processes. Now, outside the PCB, but inside the kernel we create a data structure: a matrix of integers to store the „IPC matrix”. To access this matrix, we create a new system call.

Define a 2 dimensional array of size (NR_TASKS + NR_PROCS)*(NR_TASKS + NR_PROCS) outside the PCB structure

Figure 2.28. An array to store the „IPC matrix”.

An array to store the „IPC matrix”.

Counting of IPC messages will be done in the same manner as described in the previous section. But now, the uzenetszam array will be accessed directly and not through the PCB.

Figure 2.29. Incrementing of the appropriate element of the uzenetszam array.

Incrementing of the appropriate element of the uzenetszam array.

The uzenetszam array of the kernel is also defined with the same name in the server layer.

Figure 2.30. This is the uzenetszam that we defined in the server layer.

This is the uzenetszam that we defined in the server layer.

Use the sys_getmatrix system call that will be newly developed for copying the IPC matrix from kernel level to the server level.

Figure 2.31. Usage of the sys_getmatrix system call that will be newly developed.

Usage of the sys_getmatrix system call that will be newly developed.

Printing the matrix has changed very little only in one line:

printf("%7d|", uzenetszam[rp->p_nr +
          NR_TASKS][i]);

Figure 2.32. Printing the matrix.

Printing the matrix.

In the source file include/minix/syslib.h, write the sys_getmatrix macro. This macro will call the system call do_getinfo that will do the real work. The sys_getmatrix macro helps simplify using the do_getinfo.

Figure 2.33. Defining the sys_getmatrix macro.

Defining the sys_getmatrix macro.

The GET_MATRIX constant (used in the sys_getmatrix macro) is defined in source include/minix/com.h, its value is chosen to be equal to 255.

Figure 2.34. The GET_MATRIX macro.

The GET_MATRIX macro.

There is not much for us to do to add a case branch to the switch of do_getinfo in order to handle the calls of the sys_getmatrix.

Figure 2.35. Supplying the do_getinfo system call.

Supplying the do_getinfo system call.

Now we are ready to recompile and boot the new kernel. Then, after pressing F4, we can see the fruits of our labour.

Figure 2.36. Displaying the IPC matrix in the second solution.

Displaying the IPC matrix in the second solution.

It is interesting to compare the two solutions. For example, what happens if processes are created then destroyed after they have been finished and so on.

The availability of this case study.

The two presented solutions are available in a VirtualBox virtual machine that can be found at http://www.inf.unideb.hu/~nbatfai/MINIX3.2.ova