0% found this document useful (0 votes)
400 views

Lab 3 Course: Operating Systems: Thanh Le-Hai Hoang Email: Thanhhoang@hcmut - Edu.vn

This lab helps students understand processes by having them retrieve information about running processes and create programs with multiple processes. Students learn that a process is more than just a program's code and includes other information maintained by the operating system, like the process state and memory map. The lab has students use commands like ps and cat to find a process's ID and view its status. It also has them write programs using fork() to create child processes and observe that the order of output from parent and child is not guaranteed without synchronization. Finally, students examine the /proc directory and maps file to compare the code segment of a running process to the original program file.

Uploaded by

Anh Nguyễn
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
400 views

Lab 3 Course: Operating Systems: Thanh Le-Hai Hoang Email: Thanhhoang@hcmut - Edu.vn

This lab helps students understand processes by having them retrieve information about running processes and create programs with multiple processes. Students learn that a process is more than just a program's code and includes other information maintained by the operating system, like the process state and memory map. The lab has students use commands like ps and cat to find a process's ID and view its status. It also has them write programs using fork() to create child processes and observe that the order of output from parent and child is not guaranteed without synchronization. Finally, students examine the /proc directory and maps file to compare the code segment of a running process to the original program file.

Uploaded by

Anh Nguyễn
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 12

HCMC University Of Technology

Faculty of Computer Science & Engineering

Lab 3
Course: Operating Systems

Thanh Le-Hai Hoang


Email: [email protected]
April 5, 2021

Goal: This lab helps student to understand the definition of process, and how an
operating system can manage the execution of a process.

Content In detail, this lab reflects the theory of process into practical exercises. For
example:

• How to retrieve the information of running process? How is PCB table controlled
by OS?

• Create a program with multiple processes.

Result After doing this lab, student can distinguish a program and a process. They
can create a program with multiple process and retrieve the information of processes.

Requirement Student need to review the theory of process in operating system.

1. Introduction
Informally, as mentioned earlier, a process is a program in execution. A process is
more than the program code, which is sometimes known as the text section. It also
includes the current activity, as represented by the value of the program counter and

1
the contents of the processor‘s registers. A process generally also includes the process
stack, which contains temporary data (such as function parameters, return addresses,
and local variables), and a data section, which contains global variables. A process may
also include a heap, which is memory that is dynamically allocated during process run
time.
As a process executes, it changes state. The state of a process is defined in part by the
current activity of that process.

Figure 1.1: Diagram of process state.

To keep track of processes, the operating system maintains a process table (or list).
Each entry in the process table corresponds to a particular process and contains fields
with information that the kernel needs to know about the process. This entry is called
a Process Control Block (PCB). Some of these fields on a typical Linux/Unix system
PCB are:

• Machine state (registers, program counter, stack pointer)

• Parent process and a list of child processes

• Process state (ready, running, blocked)

• Event descriptor if the process is blocked

• Memory map (where the process is in memory)

• Open file descriptors

• Owner (user identifier). This determines access privileges & signaling privileges

2
• Scheduling parameters

• Signals that have not yet been handled

• Timers for accounting (time & resource utilization)

• Process group (multiple processes can belong to a common group)

A process is identified by a unique number called the process ID (PID). Some operating
systems (notably UNIX-derived systems) have a notion of a process group. A process
group is just a way to lump related processes together so that related processes can be
signaled. Every process is a member of some process group.

2. How do we find the process’ID and group?


A process can find its process ID with the getpid system call. It can find its process
group number with the getpgrp system call, and it can find its parent‘s process ID with
getppid. For example:
1 #include <s t d i o . h>
2 #include <s t d l i b . h>
3
4 int main( int argc , char ∗∗argv ) {
5 p r i n t f ( "Process␣ID : ␣%d\n" , getpid ( ) ) ;
6 p r i n t f ( "Parent␣ process ␣ID : ␣%d\n" , getppid ( ) ) ;
7 p r i n t f ( "My␣group : ␣%d\n" , getpgrp ( ) ) ;
8
9 return 0 ;
10 }

3. Creating a process
The f ork system call clones a process into two processes running the same code. F ork
returns a value of 0 to the child and a value of the process ID number (pid) to the parent.
A value of -1 is returned on failure.
1 #include <s t d i o . h>
2 #include <s t d l i b . h>
3
4 int main( int argc , char ∗∗argv ) {
5 switch ( fork ( ) ) {
6 case 0 :
7 p r i n t f ( " I ␣am␣the␣ c h i l d : ␣pid=%d\n" , getpid ( ) ) ;
8 break ;
9 default :

3
10 p r i n t f ( " I ␣am␣the␣parent : ␣pid=%d\n" , getpid ( ) ) ;
11 break ;
12 case −1:
13 perror ( "Fork␣ f a i l e d " ) ;
14 }
15 return 0 ;
16 }

4. Basics of Multi-process programming


Multi-process programming Implement a program using fork() command to cre-
ate child process. Student can check the number of forked processes using ps command.
1 #include <s t d l i b . h>
2 #include <s t d i o . h>
3 #include <unistd . h> /∗ d e f i n e s fork ( ) , and pid_t . ∗/
4
5 int main( int argc , char ∗∗ argv ) {
6
7 pid_t child_pid ;
8
9 /∗ l e t s fork o f f a c h i l d process . . . ∗/
10 child_pid = fork ( ) ;
11
12 /∗ check what the fork () c a l l a c t u a l l y did ∗/
13 i f ( child_pid == −1) {
14 perror ( " fork " ) ; /∗ p r i n t a system−defined error message ∗/
15 exit (1);
16 }
17
18 i f ( child_pid == 0) {
19 /∗ fork () succeeded , we ’ re i n s i d e the c h i l d process ∗/
20 p r i n t f ( "Hello , ␣" ) ;
21 f f l u s h ( stdout ) ;
22 }
23 else {
24 /∗ fork () succeeded , we ’ re i n s i d e the parent process ∗/
25 p r i n t f ( "World ! \ n" ) ;
26 f f l u s h ( stdout ) ;
27 }
28
29 return 0 ;
30 }

4
Compile and run the program observing the output of the program and giving
the conclusion about the order of letters “Hello, World!”.

This is because of the independent among porcesses, so that the order of execution is not
guaranteed. It need a mechanisim to suspend the process until its child process finised.
The system call wait() is used in this case. The example of wait() is presented in
appendix A.
In case of expanding the result to implement the reverse order which child process is
suspended until its father process is finished. This case is not recommended due to the
orphan process status. An example of such programs is presented in appendix B

5. Retrieve the code segment of process


5.1. The information of process
You need a program with the execution time being enough long.
1 /∗ Source code o f l o o p _ p r o c e s s . c ∗/
2 #include < s t d l i b . h>
3 #include <s t d i o . h>
4
5 int main ( int argc , char ∗∗ argv ) {
6 int timestamp =0;
7 while ( 1 ) {
8 p r i n t f ( "Time : ␣%5d\n" , timestamp ++);
9 sleep (1);
10 }
11 return 0 ;
12 }

$ g c c −o l o o p _ p r o c e s s l o o p _ p r o c e s s . c

$ ./ loop_process
Time : 0
Time : 1
Time : 2
Time : 3
Time : 4
...

Find process ID ps command is used to find (process ID - pid) of a process.


$ ps −a | g r e p l o o p _ p r o c e s s
7277 t c ./ loop_process

5
7279 t c grep loop_process

Retrieve PCB information of process on Linux system, each running process


is reflected in the folder of filesystem /proc. Information of each process is associated
with the folder called /proc/<pid>, where pid is process ID. For example, with the pid
of the process above, ./loop_process the directory containing the information of this
process is /proc/7277.
$ l s / p r o c/<pid>
autogroup environ mountstats smaps
auxv exe net / stat
cgroup fd / ns / statm
clear_refs fdinfo / oom_adj status
cmdline limits oom_score syscall
comm maps oom_score_adj task /
coredump_filter mem pagemap
cpuset mountinfo personality
cwd mounts root

Using commands such as cat, vi to show the information of processes.


$ cat / p r o c/<pid >/c m d l i n e
./ loop_process

$ cat / p r o c/<pid >/ s t a t u s


Name : loop_process
State : S ( sleeping )
Tgid : 7277
Pid : 7277
PPid : 6760
TracerPid : 0
Uid : 1001 1001 1001 1001
Gid : 50 50 50 50
FDSize : 32
...

5.2. Comparing the code segment of process and program


/proc/<pid>/ stores the information of code in maps
$ cat /proc/<pid>/maps
08048000−08049000 r−xp 00000000 00:01 30895 /home/.../loop_process
08049000−0804a000 rwxp 00000000 00:01 30895 /home/.../loop_process
b75e0000−b75e1000 rwxp 00000000 00:00 0

6
b75e1000−b76f8000 r−xp 00000000 00:01 646 /lib/libc −2.17.so
b76f8000−b76fa000 r−xp 00116000 00:01 646 /lib/libc −2.17.so
b76fa000−b76fb000 rwxp 00118000 00:01 646 /lib/libc −2.17.so
b76fb000−b76fe000 rwxp 00000000 00:00 0
b7705000−b7707000 rwxp 00000000 00:00 0
b7707000−b7708000 r−xp 00000000 00:00 0 [vdso]
b7708000−b7720000 r−xp 00000000 00:01 648 /lib/ld−2.17.so
b7720000−b7721000 r−xp 00017000 00:01 648 /lib/ld−2.17.so
b7721000−b7722000 rwxp 00018000 00:01 648 /lib/ld−2.17.so
bf9a8000−bf9c9000 rw−p 00000000 00:00 0 [ stack ]

Comparing with the program (executable binary file) using ldd to read executable
binary file and readelf to list libraries that are used.
$ ldd loop_process
linux−gate . so .1 (0xb77dc000)
l i b c . so .6 => / l i b / l i b c . so .6 (0xb76b6000)
/ l i b /ld−linux . so .2 (0xb77dd000)

$ readelf −Ws / l i b / l i b c . so .6 | grep sleep


388: 000857f0 105 FUNC WEAK DEFAULT 11 nanosleep@@GLIBC_2.0
660: 000857f0 105 FUNC WEAK DEFAULT 11 __nanosleep@@GLIBC_2. 2 . 6
803: 000bda48 121 FUNC GLOBAL DEFAULT 11 clock_nanosleep@@GLIBC_2.17
1577: 000bda48 121 FUNC GLOBAL DEFAULT 11 __clock_nanosleep@@GLIBC_PRIVATE
1651: 000aa8f8 43 FUNC GLOBAL DEFAULT 11 usleep@@GLIBC_2.0
1959: 00085564 542 FUNC WEAK DEFAULT 11 sleep@@GLIBC_2.0

Following that, we can see the consistency of code segment between program and process.

6. Exercise
6.1. Questions
1. What the output will be at LINE A?
#i n c l u d e <s y s / t y p e s . h>
#i n c l u d e < s t d i o . h>
#i n c l u d e <u n i s t d . h>

int value = 5;

i n t main ( )
{
pid t pid ;

7
pid = f o r k ( ) ;
i f ( p i d == 0 ) { /∗ c h i l d p r o c e s s ∗/
v a l u e += 1 5 ;
return 0 ;
}
e l s e i f ( p i d > 0 ) { /∗ p a r e n t p r o c e s s ∗/
wait (NULL ) ;
p r i n t f ( "PARENT: ␣ v a l u e ␣=␣%d" , v a l u e ) ; /∗ LINE A ∗/
return 0 ;
}
}

2. When a process creates a new process using the fork() operation, which of the
following states is shared between the parent process and the child process?
A. Stack
B. Heap
C. Shared memory segments

6.2. Programming exercises (required)


Problem 1 (5 points) Given a file named "numbers.txt" containing multiple lines
of text. Each line is a non-negative integer. Write a C program that reads integers
listed in this file and stores them in an array (or linked list). The program then uses the
f ork() system call to create a child process. The parent process will count the numbers
of integers in the array that are divisible by 2. The child process will count numbers
divisible by 3. Both processes then send their results to the stdout. For examples, if the
file "numbers.txt" contains the following lines
12
3
4
10
11

After executing the program, we must see the following lines on the screen (in any order)
3
2

Problem 2 (5 points) The relationship between processes could be represented by


a tree. When a process uses f ork system call to create another process then the new
process is a child of this process. This process is the parent of the new process. For
examples, if process A uses two f ork system calls to create two new processes B and C

8
then we could display their relationship by a tree in figure 6.1. B is a child process of
A. A is the parent of both B and C.

A B C

Figure 6.1: Creating processing with f ork().

Write a program that uses f ork system calls to create processes whose relationship is
similar to the one showed in Figure 6.2. Note: If a process has multiple children then
its children must be created from left to right. For example, process A must creates B
first then create C and finally D.

B C D

E F G

Figure 6.2: The tree of running processes.

Submission The source code for problem 1 and 2 must be written in two single files
named "ex1.c" and "ex2.c, respectively. Those file are placed in a directory whose name
is your MSSV. Before submitting your work, please compress this directory in ZIP format
(has .zip extension) and name the compression file by your MSSV. You must submit the
ZIP file to Sakai.

9
A. System Call wait()
This example uses system call to guarantee the order of running processes.
1 #include < s t d l i b . h>
2 #include <s t d i o . h>
3 #include <u n i s t d . h> /∗ d e f i n e s f o r k ( ) , and pid_t . ∗/
4
5 int main ( int argc , char ∗∗ argv ) {
6
7 pid_t c h i l d _ p i d ;
8
9 /∗ l e t s f o r k o f f a c h i l d p r o c e s s . . . ∗/
10 child_pid = fork ( ) ;
11
12 /∗ c h e c k what t h e f o r k ( ) c a l l a c t u a l l y d i d ∗/
13 i f ( c h i l d _ p i d == −1) {
14 perror (" fork " ) ;
15 exit (1);
16 }
17
18 i f ( c h i l d _ p i d == 0 ) {
19 /∗ f o r k ( ) s u c c e e d e d , we ’ r e i n s i d e t h e c h i l d p r o c e s s ∗/
20 p r i n t f ( " Hello , ␣" ) ;
21 f f l u s h ( stdout ) ;
22 }
23 else {
24 /∗ f o r k ( ) s u c c e e d e d , we ’ r e i n s i d e t h e p a r e n t p r o c e s s ∗/
25 wait(NULL); /∗ w a i t t h e c h i l d
26 e x i t ∗/
27 p r i n t f ( "World ! \ n" ) ;
28 f f l u s h ( stdout ) ;
29 }
30
31 return 0 ;
32 }

10
B. Signal
This example illustrate the using of IPC signal() and kill() routines to suspend child
process until its parent process is finished.
1 #include < s t d l i b . h>
2 #include <s t d i o . h>
3 #include <u n i s t d . h> /∗ d e f i n e s f o r k ( ) , and pid_t . ∗/
4
5 int main ( int argc , char ∗∗ argv ) {
6
7 pid_t c h i l d _ p i d ;
8 s i g s e t _ t mask , oldmask ;
9
10 /∗ l e t s f o r k o f f a c h i l d p r o c e s s . . . ∗/
11 child_pid = fork ( ) ;
12
13 /∗ c h e c k what t h e f o r k ( ) c a l l a c t u a l l y d i d ∗/
14 i f ( c h i l d _ p i d == −1) {
15 p e r r o r ( " f o r k " ) ; /∗ p r i n t a system−d e f i n e d e r r o r
16 message ∗/
17 exit (1);
18 }
19
20 i f ( c h i l d _ p i d == 0 ) {
21 /∗ f o r k ( ) s u c c e e d e d , we ’ r e i n s i d e t h e c h i l d p r o c e s s ∗/
22 s i g n a l ( SIGUSR1 , p a r e n t d o n e ) ; /∗ s e t up a s i g n a l ∗/
23 /∗ S e t up t h e mask o f s i g n a l s t o t e m p o r a r i l y b l o c k . ∗/
24 s i g e m p t y s e t (&mask ) ;
25 s i g a d d s e t (&mask , SIGUSR1 ) ;
26
27 /∗ Wait f o r a s i g n a l t o a r r i v e . ∗/
28 s i g p r o c m a s k (SIG_BLOCK, &mask , &oldmask ) ;
29 while ( ! u s r _ i n t e r r u p t )
30 s i g s u s p e n d (&oldmask ) ;
31 s i g p r o c m a s k (SIG_UNBLOCK, &mask , NULL ) ;
32
33
34 p r i n t f ( "World ! \ n" ) ;
35 f f l u s h ( stdout ) ;
36 }
37 else {
38 /∗ f o r k ( ) s u c c e e d e d , we ’ r e i n s i d e t h e p a r e n t p r o c e s s ∗/
39 p r i n t f ( " Hello , ␣" ) ;

11
40 f f l u s h ( stdout ) ;
41 k i l l ( c h i l d _ p i d , SIGUSR1 ) ;
42 w a i t (NULL ) ;
43 }
44
45 return 0 ;
46 }

12

You might also like