You are on page 1of 81

OPERATING SYSTEM LAB

SYSTEM CALLS
• WRITE()/READ() SYSTEM CALL
read() and write() system calls are used to read and write data respectively to a file descriptor. To
understand the concept of write()/read() system calls let us first start with write() system call.

write() system call is used to write to a file descriptor. In other words write() can be used to write
to any file (all hardware are also referred as file in Linux) in the system but rather than specifying
the file name, you need to specify its file descriptor.

Syntax:

#include<unistd.h>

ssize_t write(int fd, const void *buf, size_t count);

The first parameter (fd) is the file descriptor where you want to write. The data that is to be written
is specified in the second parameter. Finally, the third parameter is the total bytes that are to be
written.

To understand better lets look at the first program below:

Program 1:

To write some data on the standard output device (by default – monitor)

//Name the program file as “write.c”

#include<unistd.h>
int main()
{
write(1,"hello\n",6); //1 is the file descriptor, "hello\n" is the data, 6 is the count of characters in
data
}
How it works?

The write() system call takes three parameters: “1” which is the file descriptor of the file where
we want to write. Since we want to write on standard output device which is the screen, hence the
file descriptor, in this case, is ‘1’, which is fixed (0 is the file descriptor for standard input device
(e.g. keyboard) and 2 is for standard error device)).
Next thing is what we want to write on the screen. In this case its “hello\n” i.e. hello and
newline(\n), so a total of 6 characters, which becomes the third parameter. The third parameter is
how much you want to write, which may be less than the data specified in the second parameter.
You can play around and see the change in output.

Output:

Once you compile and run this, the output on the screen will be the word “hello”, as shown below

On success, the write() system call returns the ‘number of bytes written’ i.e. the count of how many
bytes it could write. This you can save in an integer variable and checked. The write() system call
on failure returns -1.
Note: students get confused by thinking that write() return the data that is written. Remember, it
returns the count of characters written. Refer to the program below.
Program 2

#include<stdio.h

#include<unistd.h>

int main()

int count;

count=write(1,"hello\n",6);

printf("Total bytes written: %d\n",count);

How it Works?

The program is similar to the previous one except that this time it also explicitly prints the count
of bytes that write() system call was able to write on file descriptor 1.

Output:

Variations

Try making the changes as shown in the codes below and observe the output to understand the
working of the write() system call in detail.
Program 3:

#include<unistd.h>

int main()

write(1,"hello\n",60); //the bytes to be printed (third parameter) are more than the data specified in
2nd parameter

Program4:

#include<unistd.h>

int main()

write(1,"hello\n",3);//the bytes to be printed (third parameter) are less than the data specified in
2nd parameter

Program5:

#include<unistd.h>

#include<stdio.h>

int main()

int count;

count=write(3,"hello\n",6); //the file descriptor is not one of the pre-specified ones i.e., 0, 1 or 2
printf("Total bytes written: %d\n",count);

Will this call to write() be successful? If not then what will it return?

read()
The use of read() system call is to read from a file descriptor. The working is same as write(), the
only difference is read() will read the data from file pointed to by file descriptor.

Syntax:

#include<unistd.h>

ssize_t read(int fd, const void *buf, size_t count);

The first parameter is the file descriptor. The second parameter is the buffer where the read data
will be saved. Lastly, the third parameter is the number of bytes that you want to read.
Think of the buffer as a temporary storing area. As you are reading from this page and before
typing the program on your system you temporarily store it in your brain. So your brain is the
buffer. Although this page contains a lot of data, you might want to read only 20 characters. Hence,
the third parameter (count) tells, how much you want to read?

Program 6:

To read data from the standard input device and write it on the screen

//read.c

#include<unistd.h>

int main()

char buff[20];

read(0,buff,10);//read 10 bytes from standard input device(keyboard), store in buffer (buff)

write(1,buff,10);}//print 10 bytes from the buffer on the screen


How it works?

The read() system call reads the input typed by the user via the keyboard (file descriptor 0) and
stores it in the buffer (buff) which is nothing but a character array. It will read a maximum of 10
bytes (because of the third parameter). This can be less than or equal to the buffer size. No matter
how much the user types only first 10 characters will be read.
Finally, the data is printed on the screen using the write() system call. It prints the same 10 bytes
from the buffer (buff) on the screen (file descriptor 1).

Output:

Variations: you can try different values of the third parameter in read and write to understand the
working better.

The read() system call returns -1 on failure and “the count of bytes read” on success.

Important:

A common question that comes to our mind is that as a programmer you can not guarantee how
much the user will type as an input. Hence, you can not specify the correct bytes in write() systemc
all’s 3rd parameter. Hence, the output may vary from what you expect.
Now, remember what read() returns on success! the number of bytes read, and that’s the key as
demonstrated below.
Program 7:

To read data from the standard input device and write it on the screen
//read.c

#include<unistd.h>

int main()

int nread;

char buff[20];

nread=read(0,buff,10);//read 10 bytes from standard input device(keyboard), store in buffer (buff)

write(1,buff,nread);//print 10 bytes from the buffer on the screen)}

How it works?

This time we store the count of bytes read by read() in nread variable and then use variable in the
write() to print exactly the same number of bytes on the screen.

• PROGRAM ON OPEN() SYSTEM CALL


In the previous section on read/write system call we learned how to read from the standard input
device and how to write to a standard output device. But, normally we would either read from a
user-created file or write to a user-created file. Hence, the question comes that how do know the
file descriptor of these files because to read or to write the first parameter in the read()/write()
system calls is the file descriptor. We can use the open() system call to get the file descriptor of
any file.

Syntax

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

int open(const char *pathname, int flags);


int open(const char *pathname, int flags, mode_t mode);

The open() system call has two syntax. We discuss the first one here:

int open(const char *pathname, int flags);

The first parameter is the name of the file that you want to open for reading/writing. The second
parameter is the mode in which to open the file i.e., for reading or for writing. For reading from a
file, the flag used is O_RDONLY, for writing O_WRONLY and for both reading and writing
O_RDWR.
Other commonly used flags are O_CREAT, O_APPEND, O_TRUNC, O_EXCL
On success, the open() system call return the file descriptor of the file. This descriptor now can be
used in read()/write() system call for further processing. The value of the file descriptor will always
be a positive number greater than 2. Whereas on failure it returns -1.

Program 1:

Write a program using open() system call to read the first 10 characters of an existing file “test.txt”
and print them on screen.

//open.c

#include<stdio.h>

#include<unistd.h>

#include<sys/types.h>

#include<sys/stat.h>

#include<fcntl.h>

int main()

int n,fd;

char buff[50];
fd=open("test.txt",O_RDONLY); //opens test.txt in read mode and the file descriptor is saved in
integer fd.

printf("The file descriptor of the file is: %d\n,fd); // the value of the file descriptor is printed.

n=read(fd,buff,10);//read 10 characters from the file pointed to by file descriptor fd and save them
in buffer (buff)

write(1,buff,n); //write on the screen from the buffer

How it works?

First, create a file “test.txt” and write some content into it(more than 10 characters). The open()
system call opens the file test.txt in read-only mode and returns the file descriptor. This file
descriptor is saved in variable ‘fd’. You can print it to check the value of file dexcriptor of the file.
next, use read() to read 10 characters from the file into the buffer. Finally, the buffer values are
printed on the screen. The file descriptor used here is ‘0’ which is the file descriptor for standard
output device.

Output

Step1: create the file test.txt and write “1234567890abcdefghij54321” into it


$nano test.txt
Step2: compile the program
$gcc open.c
Step3: run
$./a.out

Syntax 2:

The second syntax is used when the file involved does not already exist in the system and you
want to create it on the go.

int open(const char *pathname, int flags, mode_t mode);


The first parameter is the file name. The second parameter is the mode (read/write). In this case
apart from writing O_RDONLY, O_WRONLY and O_RDWR you also need to write O_CREAT,
to create the file. The third parameter secifies the permissions on the created file
(read/write/execute).
Note: O_RDONLY and O_WRONLY flags are different from read and write permission in the
manner that even if a file has write permision on it it will not open in write mode until you specify
the O_WRONLY mode.

Now, we extend Program 1 to read from file text.txt and write the contents into a npon existing
file “towrite.txt”

Program2:

To read 10 characters from file “test.txt” and write them into non-existing file “towrite.txt”

//open2.c

#include<unistd.h>

#include<sys/types.h>

#include<sys/stat.h>

#include<fcntl.h>

int main()

int n,fd,fd1;

char buff[50];

fd=open("test.txt",O_RDONLY);

n=read(fd,buff,10);

fd1=open("towrite.txt",O_WRONLY|O_CREAT,0642);//use the pipe symbol (|) to separate


O_WRONLY and O_CREAT
write(fd1,buff,n);

How it works?

In this we open the “towrite.txt” in write mode and also use O_CREAT to create it with read and
write permission for user, read for the group and write for others. The data is read from test.txt
using file descriptor fd and stored in buff. It is then written from buff array into file towrite.txt
using file descriptor fd1.

Output

• LSEEK() SYSTEM CALL


lseek() system call repositions the read/write file offset i.e., it changes the positions of the
read/write pointer within the file. In every file any read or write operations happen at the position
pointed to by the pointer. lseek() system call helps us to manage the position of this pointer within
a file.
e.g., let’s suppose the content of a file F1 is “1234567890” but you want the content to be
“12345hello”. You simply can’t open the file and write “hello” because if you do so then “hello”
will be written in the very beginning of the file. This means you need to reposition the pointer after
‘5’ and then start writing “hello”. lseek() will help to reposition the pointer and write() will be used
to write “hello”

Syntax

#include <sys/types.h>

#include <unistd.h>

off_t lseek(int fd, off_t offset, int whence);


The first parameter is the file descriptor of the file, which you can get using open() system call.
the second parameter specifies how much you want the pointer to move and the third parameter is
the reference point of the movement i.e., beginning of file(SEEK_SET), current
position(SEEK_CUR) of pointer or end of file(SEEK_END).

Examples:

lseek(fd,5,SEEK_SET) – this moves the pointer 5 positions ahead starting from the

beginning of the file
• lseek(fd,5,SEEK_CUR) – this moves the pointer 5 positions ahead from the current
position in the file
• lseek(fd,-5,SEEK_CUR) – this moves the pointer 5 positions back from the current
position in the file
• lseek(fd,-5,SEEK_END) -> this moves the pointer 5 positions back from the end of the
file
On success, lseek() returns the position of the pointer within the file as measured in bytes from the
beginning of the file. But, on failure, it returns -1.

To understand the working of lseek() system call lets first write two programs: Program1 without
using lseek() and Program2 using lseek().

Pre-requisite: Create a file “seeking” and write “1234567890abcdefghijxxxxxxxxxx” into it.

Program1:

Program using lseek() system call that reads 10 characters from file “seeking” and print on screen.
Again read 10 characters and write on screen.

#include<unistd.h>

#include<fcntl.h>

#include<sys/types.h

#include<sys/stat.h>

#int main()

int n,f;
char buff[10];

f=open("seeking",O_RDWR);

read(f,buff,10);

write(1,buff,10);

read(f,buff,10);

write(1,buff,10);

Output

Q. What you think should be the output of this program?


Ans: it will be
1234567890abcdefghij
Because when the file opens the pointer is in the start by default. read() reads 10 characters
“1234567890”. The pointer is now positioned at ‘a’ i.e., 10 positions ahead. write() then writes the
characters on screen. read() again reads next 10 characters “abcdefghij” and write() writes on the
screen.

Program2:

Program using lseek() system call that reads 10 characters from file “seeking” and print on screen.
Skip next 5 characters and again read 10 characters and write on screen.

#include<unistd.h>

#include<fcntl.h>

#include<sys/types.h

#include<sys/stat.h>

int main()

{
int n,f;

char buff[10];

f=open("seeking",O_RDWR);

read(f,buff,10);

write(1,buff,10);

lseek(f,5,SEEK_CUR);//skips 5 characters from the current position

read(f,buff,10);

write(1,buff,10);

Output

This time the output will be the first 10 characters “1234567890” followed by “fghijxxxxx”. The
inbetween 5 characters are skipped because we used lseek to reposition the pointer 5 characters
ahead from the current (SEEK_CUR) position.

Here’s another program to understand the working better

Program3:

Write a program to print 10 characters starting from the 10th character from a file “seeking”.

//Let the contents of the file F1 be “1234567890abcdefghijxxxxxxxx”. This means we want the
output to be “abcdefghij”.
//Note: the first character ‘1’ is at 0th position

#include<unistd.h>
#include<fcntl.h>

#include<sys/types.h

#include<sys/stat.h>

#include<stdio.h>

int main()

int n,f,f1;

char buff[10];

f=open("seeking",O_RDWR);

f1=lseek(f,10,SEEK_SET);

printf("Pointer is at %d position\n",f1);

read(f,buff,10);

write(1,buff,10);

How it works?

lseek is used to position the cursor at 10th position from the starting, hence the use of SEEK_SET.
f1 saves the current position of the pointer which is printed. Then the next 10 characters are read
using read() and printed on screen using write().
Output

• PROGRAM FOR DUP() SYSTEM CALL


Before starting with the program to use dup() system call in linux, let’s first understand the use
and syntax of dup()

Use

dup() system call is used to duplicate a file descriptor. It creates a copy of the old file descriptor to
a new file descriptor. The new file descriptor can be system-generated or a number of your choice
depending upon either you use dup() or dup2().

Syntax

#include<unistd.h>

int dup(int oldfd);

int dup2(int oldfd, int newfd);

dup() takes the old file descriptor as the input parameter. On success, it returns the lowest-
numbered unused file descriptor as the new file descriptor.
Now, we can use the old and the new file descriptor because they refer to the same file.
Note: Both these file descriptor share the same file offset i.e., if you use old file descriptor to read
from the file and the file pointer is positioned at 10th position. Even if you use new file descriptor
the reading/writing within the file will start from 10th position only.

dup2() system call is same as dup() with the difference that here you can give the value of new file
descriptor of your own choice. If the new file descriptor is already open, it is silently closed and
then reopened for reuse.
Programs

Program 1:

Program for dup() system call in C to duplicate a file descriptor.

//dup.c

#include<unistd.h>

#include<stdio.h>

3include<fcntl.h>

int main()

int old_fd, new_fd;

old_fd=open("test.txt",O_RDWR);

printf("File descriptor is %d\n",old_fd);

new_fd=dup(old_fd);

printf("New file descriptor is %d\n",new_fd);

How it works?

open() system call returns the file descriptor of the file “test.txt”. The old_fd variable stores the
value of this file descriptor. Next, we duplicate the file descriptor by using dup(), which assign the
lowest unused file descriptor value. Finally, the new_fd variable stores the value of the new file
descriptor. Now, both old_fd and new_fd point to the same file “test.txt”
Output

Program 2:

Program to use dup2() system call in linux to duplicate a file descriptor.

//dup2.c

#include<unistd.h>

#include<stdio.h>

3include<fcntl.h>

int main()

int old_fd, new_fd;

old_fd=open("test.txt",O_RDWR);

printf("File descriptor is %d\n",old_fd);

new_fd=dup2(old_fd,7);

printf("New file descriptor is %d\n",new_fd);

How it works?

it works in the same manner as Program 1 with the only difference that now the value of new file
descriptor will be ‘7’.
Output

Program 3:

Program to show that both file descriptor point to the same file and same pointer position is
maintained

//create a file test.txt with the content "1234567890abcdefghij54321"

#include<unistd.h>

#include<stdio.h>

#include<fcntl.h>

int main()

int old_fd, new_fd;

char buff[10];

old_fd=open("test.txt",O_RDWR);

read(old_fd,buff,10);//read first 10 characters using old file descriptor

write(1,buff,10);//prints them on screen

new_fd=dup(old_fd);//duplicates file descriptor

read(old_fd,buff,10);//this read will read the next 10 characters even if new file descriptor is used
write(1,buff,10);

How it works?

The file “test.txt” is opened with the pointer positioned at ‘1’. The read() system call reads the first
10 characters i.e., 1234567890 and write() prints them on screen. The call to read() leaves the
pointer pointing to ‘a’ (the 11th character of the file). Next, the file descriptor is duplicated and
read() is used to read 10 more characters. Since, the pointer is at ‘a’ it reads the next 10 characters
starting ‘a’ which are “abcdefghij”. Hence, the output “1234567890abcdefghij”

Output

PROCESS
• PROGRAM FOR FORK() SYSTEM CALL
fork() is a system call used to create a new process. The new process is called a child process and
the original process is called the parent process. The child process by default is a duplicate of the
parent process. By duplicate we mean that the child process has the same code as the parent process
but the memory space of both the processes is separate. The syntax of using fork() is :

Syntax of fork() system call

#include<unistd.h>

pid_t fork(void);

fork() returns -1 on failure; On success it returns ‘0’ in the child process and process-id of the child
in the parent process.

What is the need to duplicate a process?

It may happen that a process is required to do two tasks that are independent. Since the tasks are
to be done by the same process, they can be executed one after the other only. Now to do the
same task in roughly half the time the process (parent) creates a new process (child). One of the
task is performed by the parent and the other by the child. Consider it as a situation where you are
supposed to do to two tasks and to complete them in quick time you take help of your friend which
does one of the task for you.
We know that a process is program in execution. For the parent process the program is written by
the programmer but from where the child gets its program? The child runs the same code as its
parent, hence, it is called a duplicate process.

If the same code is run both by the parent and the child then isn’t the task done twice? It’s the job
of the programmer to write the code in such a manner that only one of the task is done when the
parent process runs the code and the other task is done when the child runs the code.

Program for fork() system call

#include<stdio.h>

#include<unistd.h>

#include<sys/types.h>

int main()

pid_t p;

printf("before fork\n");

p=fork();

if(p==0)

printf("I am child having id %d\n",getpid());

printf("My parent's id is %d\n",getppid());

else{

printf("My child's id is %d\n",p);


printf("I am parent having id %d\n",getpid());

printf("Common\n");

Output:

Working of fork() system call:

After compiling the program with gcc it creates an output file “a.out”. The moment you run a.out
using the command, ./a.out, a new process is created (parent). This process gets the process id
(PID) 27. The PID will differ from system to system and each time you run the program. The
process starts to run and it prints before fork. Next it executes the fork() system call. If it gets
executed a child process is created having a different PID. Now there are two process in the system
both having the same code to run. But since the code has been run till this line the execution will
continue from the next line in both the process. fork() on success returns either 0 or a non-zero
value. Since, the same code is both the processes the variable ‘p’ will have some value in both the
process. In the parent process it gets a non-zero positive value (which actually is the PID of the
child). In the child process ‘p’ gets the value ‘0’.

Hence the next lines of code has been written to check the value of ‘p’. When the if-else code runs
from within the parent the condition becomes false and the else part is run. So, the lines

My child’s id is 28

I am parent having id 27
Are printed by the parent process.

When the if-else case runs from within the child the if condition becomes true and hence the lines

I am child having id 28

My parent’s id is 27

Are printed by the child process.

Since the printf(“common\n”) line was out of the if-else it has been printed twice; once by the
parent process and once by the child process.

By analysing the PID printed by each process the parent-child relationship can also be verified
between them. The process with PID 27 says its child has a PID 28. Similarly, the process with
PID 28 says its parent has a PID 27.

Note: The function getpid() is used to print the PID of a process while the function getppid() is
used to print the PID of the parent process.

Point to Remember:

Whatever task you want the parent to perform should be written in the else part and the task for
the child process should be written in p==0 part. Anything outside the if-else will be performed
by both parent and child.

Why the output is not in an order?

If the code is run multiple times, the order of the output lines may differ. Since there are two
processes, the processor can be assigned to any of them. One of them can pre-empt the other and
hence the output may overlap. The order of the output will differ. If you want the parent to execute
first or the child to execute first then either wait() or sleep() functions can be used depending on
the requirement.

• PROGRAM FOR WAIT() SYSTEM CALL


When a process creates a child process, sometimes it becomes necessary that the parent process
should execute only after the child has finished. wait() system call does exactly this. It makes the
parent process wait for child process to finish and then the parent continues its working from the
statement after the wait().
To be exact wait makes the parent wait for the child to change state. The state change can be : the
child terminated; the child was stopped by a signal; or the child was resumed by a signal.
Synopsis

#include <sys/types.h>

#include <sys/wait.h>

pid_t wait(int *wstatus);

wait() system call takes only one parameter which stores the status information of the process.
Pass NULL as the value if you do not want to know the exit status of the child process and are
simply concerned with making the parent wait for the child. On success, wait returns the PID of
the terminated child process while on failure it returns -1.

Important: wait() can be used to make the parent wait for the child to terminate(finish) but not
the other way around.

Program for wait() system call which makes the parent process wait for the child to finish.

#include<unistd.h>

#include<sys/types.h>

#include<stdio.h>

#include<sys/wait.h>

int main()

pid_t p;

printf("before fork\n");

p=fork();

if(p==0)//child
{

printf("I am child having id %d\n",getpid());

printf("My parent's id is %d\n",getppid());

else//parent

wait(NULL);

printf("My child's id is %d\n",p);

printf("I am parent having id %d\n",getpid());

printf("Common\n");

Output
How it works?

The execution begins by printing “before fork”. Then fork() system call creates a child process.
wait() system call is added to the parent section of the code. Hence, the moment processor starts
processing the parent, the parent process is suspended because the very first statement is
wait(NULL). Thus, first, the child process runs, and the output lines are all corresponding to the
child process. Once the child process finishes, parent resumes and prints all its printf() statements.
The NULL inside the wait() means that we are not interested to know the status of change of state
of child process.

• PROGRAM TO CREATE AN ORPHAN PROCESS


An orphan process is a process whose parent has finished. Suppose P1 and P2 are two process such
that P1 is the parent process and P2 is the child process of P1. Now, if P1 finishes before P2
finishes, then P2 becomes an orphan process. The following programs we will see how to create
an orphan process.

Program1:

To create a normal child (duplicate) process (no orphan process in this case)

#include<stdio.h

#include<unistd.h>

#include<sys/types.h>

int main()

pid_t p;

p=fork();

if(p==0) //child

printf("I am child having PID %d\n",getpid());

printf("My parent PID is %d\n",getppid());


}

else //parent

printf("I am parent having PID %d\n",getpid());

printf("My child PID is %d\n",p);

Output:

Process duplication
This is a normal duplication of process. The Parent process having PID 130 creates a child process
with PID 131.

Program 2:

Program to create an orphan process

#include<stdio.h

#include<unistd.h>

#include<sys/types.h>

int main()
{

pid_t p;

p=fork();

if(p==0)

sleep(5); //child goes to sleep and in the mean time parent terminates

printf("I am child having PID %d\n",getpid());

printf("My parent PID is %d\n",getppid());

else

printf("I am parent having PID %d\n",getpid());

printf("My child PID is %d\n",p);

}
Output:

Orphan Process
How it Works?

In this code, we add sleep(5) in the child section. This line of code makes the child process go to
sleep for 5 seconds and the parent starts executing. Since, parent process has just two lines to print,
which it does well within 5 seconds and it terminates. After 5 seconds when the child process
wakes up, its parent has already terminated and hence the child becomes an orphan process. Hence,
it prints the PID of its parent as 1 (1 means the init process has been made its parent now) and not
138.

What is a Zombie Process?


A zombie is a process which has terminated but its entry still exists in the process table until the
parent terminates normally or calls wait(). Suppose you create a child process and the child finishes
before the parent process does. If you run the ps command before the parent gets terminated the
output of ps will show the entry of a zombie process(denoted by defunct ). This happens because
the child is no longer active but its exit code needs to be stored in case the parent subsequently
calls wait.

Program

//zombie.c

#include<stdio.h>

#include<unistd.h>

int main()

{
pid_t t;

t=fork();

if(t==0)

printf("Child having id %d\n",getpid());

else

printf("Parent having id %d\n",getpid());

sleep(15); // Parent sleeps. Run the ps command during this time

Output:

$gcc zombie.c

$./a.out &
How it works?

Now Compile the program and run it using ./a.out &. The ‘&’ sign makes it run in the background.
After you see the output of both the printf statement, note that the parent will go into sleep for 15
seconds. During this time type the command ‘ps’. The output will contain a process
with defunct written in the end. Hence, this is the child process (can be verified from PID) which
has become zombie.

How to prevent the creation of a Zombie Process?

In order to prevent this use the wait() system call in the parent process. The wait() system call
makes the parent process wait for the child process to change state.

Program to replace process image using execl( )


There is a family of function which can be used for replacing the current process with a new
process. They differ in the number of arguments and the way they start a new process. The various
functions are execl, execlp, execle, execv and execvp. In this post we will write a Program to
replace process image using execl( )

We will discuss the use of execl and the others can be used on similar lines to replace process
image. The syntax for execl is

int execl(const char *path, const char *arg0, …, (char *)0);

The 1st argument is the PATH for the program while the last argument will be NULL.

Program:

Program to replace process image using execl( ). Process ps will replace the image of current
process

#include<stdio.h>

#include<unistd.h>

int main()

{
printf("Before execl\n");

execl("/bin/ps","ps","-a",NULL);//

printf("After execlp\n");

Output

$gcc -o exec execl.c

$./exec

How it Works?

The program prints the first message and then calls execl. execl searches for the program ps in the
PATH variable and executes it replacing the original program. Hence the second message doesn’t
get printed. Also, It can be seen in the output that the name of the process running is ps and not
the user process exec.

Normally the exec() of function is to replace the image of a child process. By default the child
process duplicates the parent which means that the child will have exactly the same code as the
parent. Now, if you want a different code in child then use execl() function.

• DIFFERENCE BETWEEN SYSTEM() AND EXECL() FUNCTIONS


In this post, we will discuss the difference between system() and execl() functions

execl() or execlp() functions replace the image of the current process with a new process image.
In other words the current process code gets replaced by the new process code.

Syntax of execl()

#include<unistd.h>

int execl(const char *path, const char *arg, . . . /* (char *) NULL */);

Let us take an example:


Program 1:

#include<stdio.h>

int main()

printf(“Before\n”);

printf(After\n”);

Program 2:

#include<stdio.h>

#include<unistd.h>

int main()

printf(“Before\n”);

execl(“/bin/ps”,”ps”,NULL);

printf(After\n”);

Output:
How it works?

Output of Program 1 is trivial. It prints both the printf() statements. In Program 2 the statement
printf(“Before\n”) is printed and then execl() is called which replaces the current process image
with the new process i.e. ps. Hence, the output of ps command is printed and not the second printf()
statement. The output of ps also shows entry for ps only and not a.out (the original process)
because the image of a.out is replaced by the image of ps process.

execl() can be used within a child process also to replace the image of the child process with a new
process.

system() function in Linux

system() function on the other hand is used to execute a shell command from within a process.

Syntax:

#include<stdlib.h>

int system(const char *command);

In the background system() calls fork() to create a child process that executes the shell command
specified in command using execl(3). In other words system will create a child process and replace
its image with the image of a new process passed as its argument.

Program 3:

#include<stdio.h>

#include<stdlib.h>

int main()

printf(“Before\n”);

system(”ps”);

printf(After\n”);
}

Output:

How it works?

system() does not does not replace the image of the current process (a.out). So the first printf() gets
printed then another process (ps) is created using system() which continues independently printing
the output of ps command while a.out also continues and prints After using the second printf()
statement. As seen from the out of ps command there were two process a.out (having PID 202)
and ps (having PID 204).

Conclusion:

execl() system()

Replaces the image of the current process Does not replace the image of the current process

Does not create new process Creates a new process

THREAD
• PROGRAM TO CREATE THREADS IN LINUX
In this post we discuss the use of pthread_create function create threads in linux. Various program
to create threads in Linux are discussed below that shows how to create threads, how to pass input
to thread and how to return value from thread.
Syntax

#include<pthread.h>

int pthread_create(pthread_t *thread, const pthread_attr_t *attr,

void *(*start_routine) (void *), void arg);

The first parameter is the buffer which will contain the ID of the new thread, if pthread_create is
successful. The second parameter specifies the attributes of the thread. This parameter is generally
NULL until you want to change the default settings. The third parameter is the name the function
which the thread will execute. Hence, everything that you want the thread to do should be defined
in this function. Lastly, the fourth parameter is the input to the function in the third parameter. If
the function in the third parameter does not take any input then the fourth parameter is NULL.

Program 1:

Program to create threads in linux. Thread prints 0-4 while the main process prints 20-24

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
void*thread_function(void*arg);
inti,j;
intmain(){
pthread_t a_thread; //thread declaration

pthread_create(&a_thread,NULL,thread_function,NULL);//threadiscreated
pthread_join(a_thread, NULL); //process waits for thread to finish . //Comment this line to see the
difference
printf("InsideMainProgram\n");
for(j=20;j<25;j++)
{
printf("%d\n",j);
sleep(1);
}
}

void*thread_function(void*arg){
// the work to be done by the thread is defined in this function
printf("InsideThread\n");
for(i=0;i<5;i++)
{
printf("%d\n",i);
sleep(1);
}
}

Note: To compile any program which involves creation of thread(s) use pthread library (lpthread)
Suppose the above program is named “Thread.c”, then to compile write
$gcc Thread.c -lpthread
To run the command remains same
$./a.out

Output

Inside Main Program

20

21

22

23

24

How it works?

pthread_create() creates a new thread which starts to execute thread_function. This function
creates a loop which prints 0-4. The sleep function makes the thread go to sleep after each digit is
printed. pthread_join() makes the main function wait until the newly created thread finishes its
execution. So the control returns to the main function only when the thread finishes. Then the main
function prints “Inside Main program” and executes the loop from 20-24.

How a thread returns a value to the main process?

pthread_exit() is used to return a value from the thread back to the main process. The program
below shows this. The program also shows how to pass value to a thread from the main process.

Program 2:

Program to create a thread. The thread prints numbers from zero to n, where value of n is passed
from the main process to the thread. The main process also waits for the thread to finish first and
then prints from 20-24.

#include<stdio.h>

#include<stdlib.h>

#include<unistd.h>

#include<pthread.h>

#include<string.h>

void *thread_function(void *arg);

int i,n,j;

int main() {

char *m="5";

pthread_t a_thread; //thread declaration

void *result;

pthread_create(&a_thread, NULL, thread_function, m); //thread is created

pthread_join(a_thread, &result);
printf("Thread joined\n");

for(j=20;j<25;j++)

printf("%d\n",j);

sleep(1);

printf("thread returned %s\n",(char *)result);

void *thread_function(void *arg) {

int sum=0;

n=atoi(arg);

for(i=0;i<n;i++)
{
printf("%d\n",i);
sleep(1);
}
pthread_exit("Done");//Threadreturns"Done"
}

How it works?

When we call pthread_create() function a value if passed to the thread by passing that value as the
fourth parameter of the pthread_create() function.
How to pass multiple values to a thread using structure?

Program 3:

Program to create a thread. The thread is passed more than one input from the main process. For
passing multiple inputs we need to create structure and include all the variables that are to be
passed in this structure.

#include<stdio.h

#include<pthread.h>

struct arg_struct { //structure which contains multiple variables that are to passed as input to the
thread

int arg1;

int arg2;

};

void *arguments(void *arguments)

struct arg_struct *args=arguments;

printf("%d\n", args -> arg1);

printf("%d\n", args -> arg2);

pthread_exit(NULL);

intmain()
{
pthread_tt;
structarg_structargs;
args.arg1=5;
args.arg2=7;
pthread_create(&t,NULL,arguments,&args);
//structurepassedas4thargument
pthread_join(t,NULL);/*Waituntilthreadisfinished*/
}

DEADLOCK
• PROGRAM TO CREATE DEADLOCK USING C IN LINUX
Deadlock in operating system is a situation which occurs when a process or thread enters a waiting
state because a resource requested is being held by another waiting process, which in turn is
waiting for another resource held by another waiting process. In a deadlock state a process is
unable to change its state(waiting) indefinitely because the resources requested by it are being used
by another waiting process.

Setup

To simulate deadlock in the system we will create the above shown situation.

P1 and P2 will be represented by two thread one and two.


The two resources R1 and R2 will be represented by the two lock
variables first_mutex and second_mutex
First thread one will acquire lock first_mutex and then thread two will acquire
lock second_mutex. Hence, both the threads have acquired one resource each. Next,
thread one will try to acquire lock second_mutex while the second thread, thread two will try to
acquire lock first_mutex. Since the resources are already occupied by the other thread, both the
threads will get into a deadlock.

Note: You must know how to create Threads to understand this program

Program to create Deadlock Using C in Linux using Mutex Locks and threads

#include<stdio.h>
#include<pthread.h>
#include<unistd.h>
void*function1();
void*function2();
pthread_mutex_tfirst_mutex;//mutexlock
pthread_mutex_t second_mutex;

intmain(){
pthread_mutex_init(&first_mutex,NULL);//initializethelock
pthread_mutex_init(&second_mutex,NULL);
pthread_tone,two;
pthread_create(&one,NULL,function1,NULL);//createthread
pthread_create(&two,NULL,function2,NULL);
pthread_join(one,NULL);
pthread_join(two,NULL);
printf("Threadjoined\n");
}

void *function1( ) {

pthread_mutex_lock(&first_mutex); // to acquire the resource/mutex lock

printf("Thread ONE acquired first_mutex\n");

sleep(1);

pthread_mutex_lock(&second_mutex);

printf("Thread ONE acquired second_mutex\n");

pthread_mutex_unlock(&second_mutex); // to release the resource

printf("Thread ONE released second_mutex\n");


pthread_mutex_unlock(&first_mutex);

printf("Thread ONE released first_mutex\n");

void*function2(){
pthread_mutex_lock(&second_mutex);
printf("ThreadTWOacquiredsecond_mutex\n");
sleep(1);
pthread_mutex_lock(&first_mutex);
printf("ThreadTWOacquiredfirst_mutex\n");
pthread_mutex_unlock(&first_mutex);
printf("ThreadTWOreleasedfirst_mutex\n");
pthread_mutex_unlock(&second_mutex);
printf("Thread TWO released second_mutex\n");

SYNCHRONIZATION
• PROGRAM TO SIMULATE RACE CONDITION
A situation where several processes access and manipulate the same data concurrently and the
outcome of the execution depends on the particular order in which the access takes place is called
a race condition. In this post we will write a Program using threads to simulate race condition.

Program to create two threads: one to increment the value of a shared variable and second to
decrement the value of shared variable. Both the threads are executed, so the final value of shared
variable should be same as its initial value. But due to race condition it would not be same. */

#include<pthread.h>
#include<stdio.h>
#include<unistd.h>
void*fun1();
void*fun2();
intshared=1;//sharedvariable
intmain()
{
pthread_tthread1,thread2;
pthread_create(&thread1,NULL,fun1,NULL);
pthread_create(&thread2,NULL,fun2,NULL);
pthread_join(thread1,NULL);
pthread_join(thread2,NULL);
printf("Final value of shared is %d\n",shared); //prints the last updated value of shared variable
}

void *fun1()

int x;

x=shared;//thread one reads value of shared variable

printf("Thread1 reads the value of shared variable as %d\n",x);

x++; //thread one increments its value

printf("Local updation by Thread1: %d\n",x);

sleep(1); //thread one is preempted by thread 2

shared=x; //thread one updates the value of shared variable

printf("Value of shared variable updated by Thread1 is: %d\n",shared);

void*fun2()
{
inty;
y=shared;//threadtworeadsvalueofsharedvariable
printf("Thread2readsthevalueas%d\n",y);
y--;//threadtwoincrementsitsvalue
printf("LocalupdationbyThread2:%d\n",y);
sleep(1);//threadtwoispreemptedbythread1
shared=y;//threadoneupdatesthevalueofsharedvariable
printf("ValueofsharedvariableupdatedbyThread2is:%d\n",shared);
}

Note: the final value of shared variable should have been 1 but it will be either 2 or 0 depending
upon which thread executes first. This happened because the two processes were not synchronized.
When one thread was modifying the value of shared variable the other thread must not have read
its value for modification. This can be achieved using locks or semaphores.
Output:

Race condition
How it Works?

Thread1 reads the value of shared variable as 1 and then increments it to 2. Now, before it could
update the shared variable, Thread1 is preempted (using sleep()) by Thread2 which reads the
unstable value of shared variable as 1. Thread2 then decrements it to 0. Then, both the threads
prints the updated value of shared variable. Since, the final updation is done by Thread1, so the
final value comes out to be 2

• PROGRAM FOR PROCESS SYNCHRONIZATION USING MUTEX LOCKS


Program create two threads: one to increment the value of a shared variable and second to
decrement the value of shared variable. Both the threads make use of locks so that only one of the
threads is executing in its critical section */

#include<pthread.h>
#include<stdio.h>
#include<unistd.h>
void*fun1();
void*fun2();
intshared=1;//sharedvariable
pthread_mutex_tl;//mutexlock
intmain()
{
pthread_mutex_init(&l,NULL);//initializingmutexlocks
pthread_tthread1,thread2;
pthread_create(&thread1,NULL,fun1,NULL);
pthread_create(&thread2,NULL,fun2,NULL);
pthread_join(thread1,NULL);
pthread_join(thread2,NULL);
printf("Final value of shared is %d\n",shared); //prints the last updated value of shared variable
}

void*fun1()
{
intx;
printf("Thread1tryingtoacquirelock\n");
pthread_mutex_lock(&l); //thread one acquires the lock. Now thread 2 will not be able to
acquirethelock

//untilitisunlockedbythread1
printf("Thread1acquiredlock\n");
x=shared;//threadonereadsvalueofsharedvariable
printf("Thread1readsthevalueofsharedvariableas%d\n",x);
x++;//threadoneincrementsitsvalue
printf("LocalupdationbyThread1:%d\n",x);
sleep(1);//threadoneispreemptedbythread2
shared=x;//threadoneupdatesthevalueofsharedvariable
printf("ValueofsharedvariableupdatedbyThread1is:%d\n",shared);
pthread_mutex_unlock(&l);
printf("Thread1releasedthelock\n");
}

void*fun2()
{
inty;
printf("Thread2tryingtoacquirelock\n");
pthread_mutex_lock(&l);
printf("Thread2acquiredlock\n");
y=shared;//threadtworeadsvalueofsharedvariable
printf("Thread2readsthevalueas%d\n",y);
y--;//threadtwoincrementsitsvalue
printf("LocalupdationbyThread2:%d\n",y);
sleep(1);//threadtwoispreemptedbythread1
shared=y;//threadoneupdatesthevalueosharedvariable
printf("ValueofsharedvariableupdatedbyThread2is:%d\n",shared);
pthread_mutex_unlock(&l);
printf("Thread2releasedthelock\n");
}

The final value of shared variable will be 1. When any one of the threads acquires the lock and is
making changes to shared variable the other thread (even if it preempts the running thread) is not
able to acquire the lock and thus not able to read the inconsistent value of shared variable. Thus
only one of the thread is running in its critical section at any given time */
Sample Output

• PROGRAM FOR PROCESS SYNCHRONIZATION USING SEMAPHORES


Semaphore is an integer variable which is accessed or modified by using two atomic
operations: wait() and signal(). In C program the corresponding operations
are sem_wait() and sem_post(). Here, we write a Program for Process Synchronization using
Semaphores to understand the implementation of sem_wait() and sem_signal() to avoid a race
condition.

Program 1:

Program for process synchronization using semaphores

Q. Program creates two threads: one to increment the value of a shared variable and second to
decrement the value of the shared variable. Both the threads make use of semaphore variable so
that only one of the threads is executing in its critical section

#include<pthread.h>

#include<stdio.h>

#include<semaphore.h>

#include<unistd.h>

void *fun1();

void *fun2();

int shared=1; //shared variable


sem_t s; //semaphore variable

int main()

sem_init(&s,0,1); //initialize semaphore variable - 1st argument is address of variable, 2nd is


number of processes sharing semaphore, 3rd argument is the initial value of semaphore variable

pthread_t thread1, thread2;

pthread_create(&thread1, NULL, fun1, NULL);

pthread_create(&thread2, NULL, fun2, NULL);

pthread_join(thread1, NULL);

pthread_join(thread2,NULL);

printf("Final value of shared is %d\n",shared); //prints the last updated value of shared variable

void *fun1()

int x;

sem_wait(&s); //executes wait operation on s

x=shared;//thread1 reads value of shared variable

printf("Thread1 reads the value as %d\n",x);

x++; //thread1 increments its value

printf("Local updation by Thread1: %d\n",x);


sleep(1); //thread1 is preempted by thread 2

shared=x; //thread one updates the value of shared variable

printf("Value of shared variable updated by Thread1 is: %d\n",shared);

sem_post(&s);

void *fun2()

int y;

sem_wait(&s);

y=shared;//thread2 reads value of shared variable

printf("Thread2 reads the value as %d\n",y);

y--; //thread2 increments its value

printf("Local updation by Thread2: %d\n",y);

sleep(1); //thread2 is preempted by thread 1

shared=y; //thread2 updates the value of shared variable

printf("Value of shared variable updated by Thread2 is: %d\n",shared);

sem_post(&s);

The final value of the variable shared will be 1. When any one of the threads executes the wait
operation the value of “s” becomes zero. Hence the other thread (even if it preempts the running
thread) is not able to successfully execute the wait operation on “s“. Thus not able to read the
inconsistent value of the shared variable. This ensures that only one of the thread is running in its
critical section at any given time. The output is as shown below. The working of the program is
also discussed in detail.

Output

Process synchronization using semaphore


How it works?

The process initializes the semaphore variable s to ‘1’ using the sem_init() function. The initial
value is set to ‘1’ because binary semaphore is used here. If you have multiple instances of the
resource then counting semaphores can be used. Next, the process creates two
threads. thread1 acquires the semaphore variable by calling sem_wait(). Next, it executes
statements in its critical section part. We use sleep(1) function to preempt thread1 and
start thread2. This simulates a real-life scenario. Now, when thraed2 executes sem_wait() it will
not be able to do so because thread1 is already in the critical section. Finally, thread1
calls sem_post() function. Now thread2 will be able to acquire s using sem_wait(). This ensures
synchronization among threads.

• PROGRAM ON DINING PHILOSOPHER PROBLEM


In this post we are going to understand the C program implementing the solution to the Dining
Philosopher Problem. The Dining Philosopher Problem states that there are five philosophers
which do two thinks: think and eat. They share a table having a chair for each one of them. In the
center of the table there is a bowl of rice and the table is laid with 5 single chopsticks.
When a philosopher thinks, he does not interact with others. When he gets hungry, he tries to pick
up the two chopsticks that are near to him. For example, philosopher 1 will try to pick chopsticks
1 and 2. But the philosopher can pickup only one chopstick at a time. He can not take a chopstick
that is already in the hands of his neighbour. The philosopher stars to eat when he has both his
chopsticks in his hand. After eating the philosopher puts down both the chopsticks and starts to
think again.

Solution to Dining Philosopher Problem

Represent each chopstick with a semaphore. Each philosopher first picks up the left chopstick and
then the right chopstick using the wait() operation each semaphore. After eating he puts down the
chopsticks by using the signal() operation on each chopstick.

#include<stdio.h>

#include<stdlib.h>

#include<pthread.h>

#include<semaphore.h>
#include<unistd.h>

sem_t chopstick[5];

void * philos(void *);

void eat(int);

int main()

int i,n[5];

pthread_t T[5];

for(i=0;i<5;i++)

sem_init(&chopstick[i],0,1);

for(i=0;i<5;i++){

n[i]=i;

pthread_create(&T[i],NULL,philos,(void *)&n[i]);

for(i=0;i<5;i++)

pthread_join(T[i],NULL);

void * philos(void * n)

int ph=*(int *)n;


printf("Philosopher %d wants to eat\n",ph);

printf("Philosopher %d tries to pick left chopstick\n",ph);

sem_wait(&chopstick[ph]);

printf("Philosopher %d picks the left chopstick\n",ph);

printf("Philosopher %d tries to pick the right chopstick\n",ph);

sem_wait(&chopstick[(ph+1)%5]);

printf("Philosopher %d picks the right chopstick\n",ph);

eat(ph);

sleep(2);

printf("Philosopher %d has finished eating\n",ph);

sem_post(&chopstick[(ph+1)%5]);

printf("Philosopher %d leaves the right chopstick\n",ph);

sem_post(&chopstick[ph]);

printf("Philosopher %d leaves the left chopstick\n",ph);

void eat(int ph)

printf("Philosopher %d begins to eat\n",ph);

}
How it works?

Now let us understand how the code works

#include<stdio.h>

#include<stdlib.h>

#include<pthread.h>

#include<semaphore.h>

#include<unistd.h>

sem_t chopstick[5];

void * philos(void *);

void eat(int);

sem_t chopstick[5] is used to declare 5 semaphore variable, one for each of the five chopsticks.
Next, two are the prototypes for functions defined below.

In the main() function there are three for loops. The first loop is used to initialize each semaphore
variable with initial value to 1. The second for loop is used to create five threads which will act as
the five philosophers. The third for loop uses the pthread_join function which makes the parent
program wait for each of the thread to finish.

Next is the philos function. The philos function when called by each thread receives the same value
as the thread number. For example if thread one runs, the variable ph in the philos function is
assigned the value n. This is done because each philosopher n before eating will pick two
chopstick, n and (n+1)%5.

Next, the sem_wait function is used on the left chopstick first

sem_wait(&chopstick[ph]);

If successful, the thread executes the sem_wait function on the right chopstick

sem_wait(&chopstick[(ph+1)%5]);
These two operations are equivalent to picking the left chopstick and then the right chopstick. If
both these operations are successful this means that the philosopher is able to pick both the
chopsticks and hence will start to eat by calling the eat() function. After eating both the chopsticks
are release by using the sem_post() function.

Output

Here’s a sample output. Your output might differ each type you run the program. This is because
the sequence of execution of threads will be different. Try to understand the output below and then
relate it with what you get.
Here philosopher(thread) 0 tries to eat first. So, it tries to pick the left chopstick, which it does.
Then the right one. Since it picks both the chopstick so philosopher 0 starts to eat. Now, refer to
the image at the beginning of the post. If philosopher 0 starts to eat this means chopstick 0 and 1
are busy hence, philosopher 1 and 4 can not eat until philosopher 0 puts down the chopsticks. Read
the output now, next philosopher wants to eat. It tries to pick the left chopstick (i.e. chopstick 1)
but is not successful because chopstick 1 is already with philosopher 0. Similarly, you can
understand the rest of the output.

INTER-PROCESS COMMUNICATION (IPC)


• PROGRAM FOR IPC USING POPEN()
IPC (Inter-process communication) can be achieved using popen/pclose
functions. popen() function opens a process by creating a pipe, forking and invoking the shell. This
pipe is a unidirectional pipe. Hence, it can be used either for reading or writing purpose only. We
understand this concept with the help of a Program for IPC using popen()

Syntax

#include<stdio.h>

FILE *popen(const char *command, const char *type)

The first argument specifies the name of the process with which the communication is to take place
and the second argument tells whether your current process is going to send data (writing into
pipe) or receive data (reading from pipe).

We will demonstrate the use of popen using two different programs. One to send data to another
process and second to receive data from the other process.

//Q. Program to write into a pipe i.e to send data from one process to another process.
//ipc1.c

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>

int main()

FILE *rd;
char buffer[50];

sprintf(buffer,"name first");

rd=popen("wc -c","w"); // wc -c -> is the process which counts the number of characters passed.
2nd parameter is "w" which means pipe is opened in writing mode

fwrite(buffer,sizeof(char),strlen(buffer),rd); // to write the data into the pipe

pclose(rd);

How it Works?

There are two programs “ipc1.c” (which will send the data) and “wc” command(which will receive
the data). As the data will be sent to another process, so the mode of opening the pipe is writing
mode “w”. popen() establishes the pipe between ipc1.c and wc. fwrite() function writes data into
this pipe.
ipc1.c stores some data in the buffer, then it connects with “wc” using popen. Finally,
the fwrite() function writes data into the pipe.
The data is received by “wc” which then counts the number of characters in the input and prints it

Output:

Theoutputwillbe10
because ipc1.c passed “name first” which contains 10 characters. So, when wc -c reads this it prints
the count of number of characters which is 10

Program 2:

//Q. Program to read from a pipe i.e. to receive data from another process
// ipc2.c

#include<stdio.h>

#include<stdlib.h>

#include<unistd.h>

#include<string.h>
int main()

FILE *rd;

char buffer[50];

rd=popen("ls","r"); //pipe opened in reading mode

fread(buffer, 1, 40, rd); //will read only 50 characters

printf("%s\n", buffer);

pclose(rd);

How it works?

In this case, we establish the pipe between “ipc2.c” and “ls”. Since, the data will be read, hence,
the pipe is opened in reading mode “r”. ls sends the data through the pipe. This data will be read
by ipc2.c. So, this time our program is the one receiving the data. ls will send the list of files in
current working directory. ipc2.c will read that, save it in buffer and then finally print it.

Remember: The ls command generates the list of all files in the current working directory but our
process will read only first 40 character as specified in the fread() function. So, the output will
consist of 40 characters only.

Output
PROGRAM FOR IPC USING PIPE() FUNCTION
The second method for IPC is using the pipe() function. Before writing a program for IPC using
pipe() function let us first understand its working.

Syntax:

#include<unistd.h>
int pipe(int pipefd[2]);

pipe() function creates a unidirectional pipe for IPC. On success it return two file descriptors
pipefd[0] and pipefd[1]. pipefd[0] is the reading end of the pipe. So, the process which will receive
the data should use this file descriptor. pipefd[1] is the writing end of the pipe. So, the process that
wants to send the data should use this file descriptor.

The program below creates a child process. The parent process will establish a pipe and will send
the data to the child using writing end of the pipe and the child will receive that data and print on
the screen using the reading end of the pipe.

//Q. Program to send a message from parent process to child process using pipe()

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
intmain()
{
intfd[2],n;
charbuffer[100];
pid_tp;
pipe(fd);//createsaunidirectionalpipewithtwoendfd[0]andfd[1]
p=fork();
if(p>0)//parent
{
printf("ParentPassingvaluetochild\n");
write(fd[1],"hello\n",6);//fd[1]isthewriteendofthepipe
wait();
}
else//child
{
printf("Childprintingreceivedvalue\n");
n=read(fd[0],buffer,100);//fd[0]isthereadendofthepipe
write(1,buffer,n);
}
}

How it works?

The parent process create a pipe using pipe(fd) call and then creates a child process using fork().
Then the parent sends the data by writing to the writing end of the pipe by using the fd[1] file
descriptor. The child then reads this using the fd[0] file descriptor and stores it in buffer. Then the
child prints the received data from the buffer onto the screen.

Output

• PROGRAM FOR IPC USING NAMED PIPES (MKFIFO())


The third method for IPC is using mkfifo() function. mkfifo() creates a named pipe which can be
used exactly like a file. So, if you know how to read/write in a file this is a convenient method for
IPC

Syntax:

#include<sys/types.h>
#include<sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);

mkfifo() makes a FIFO special file with the name specified by pathname and the permissions are
specified by mode. On success mkfifo() returns 0 while on error it returns -1.

The advantage is that this FIFO special file can be used by any process for reading or writing just
like a normal file. This means to sender process can use the write() system call to write data into
the pipe and the receiver process can use the read() system call to read data from the pipe, hence,
completing the communication.

It is same as a pipe except that it is accessed as part of the filesystem. Multiple process can access
it for writing and reading. When the FIFO special files is used for exchange of data by process, the
entire data is passed internally without writing it on the filesystem. Hence, if you open this special
file there will be no content written in it.
Note: The FIFO pipe works in blocked mode(by default) i.e., the writing process must be present
on one end while the reading process must be present on the other side at the same time else the
communication will not happen. Operating the FIFO special file in non-blocking mode is also
possible.

Program1:

Creating fifo/named pipe ( 1.c )

#include<stdio.h>

#include<sys/types.h>

#include<sys/stat.h>

int main()

int res;

res = mkfifo("fifo1",0777); //creates a named pipe with the name fifo1

printf("named pipe created\n");

How it works?

This will simply create a named pipe (fifo1) with read, write and execute permission for all users.
You can change this to whatever you prefer

Step 2 is to create a process which will use this pipe to send data. The below program will do that.

Program2:

Writing to a fifo/named pipe ( 2.c )

#include<unistd.h>
#include<stdio.h>

#include<fcntl.h>

int main()

int res,n;

res=open("fifo1",O_WRONLY);

write(res,"Message",7);

printf("Sender Process %d sent the data\n",getpid());

How it works?

The above code opens the pipe created previously in writing mode (because it wants to send data).
Then it uses “write” system call to write some data into it. Finally, it prints a message using printf.
But when you compile and run it, it won’t run because by default the sender runs in BLOCKING
mode which means that until the receiver is not there the sender process gets blocked. Hence, you
need a receiver process also.

The third step is to create the receiver process. The below program does so.

Program 3:

Reading from the named pipe ( 3.c )

#include<unistd.h>

#include<stdio.h>

#include<fcntl.h>

int main()
{

int res,n;

char buffer[100];

res=open("fifo1",O_RDONLY);

n=read(res,buffer,100);

printf("Reader process %d started\n",getpid());

printf("Data received by receiver %d is: %s\n",getpid(), buffer);

How it works?

This program connects to the pipe in reading mode and reads the data into buffer and prints it. But
again this program will not run. Because the receiver is BLOCKED until the sender is there.

You will see the output as

Named pipes
• PROGRAM FOR IPC USING SHARED MEMORY
The following post explains the Program for IPC using shared memory concept

Shared Memory is the fastest inter-process communication (IPC) method. The operating system
maps a memory segment in the address space of several processes so that those processes can read
and write in that memory segment.The overview is as shown below:
Two functions:shmget() and shmat() are used for IPC using shared memory. shmget() function is
used to create the shared memory segment while shmat() function is used to attach the shared
segment with the address space of the process.

Syntax (shmget()):

#include <sys/ipc.h>

#include <sys/shm.h>

int shmget(key_t key, size_t size, int shmflg);

The first parameter specifies the unique number (called key) identifying the shared segment. The
second parameter is the size of the shared segment e.g. 1024 bytes or 2048 bytes. The third
parameter specifies the permissions on the shared segment. On success the shmget() function
returns a valid identifier while on failure it return -1.

Syntax (shmat()):

#include <sys/types.h>

#include <sys/shm.h>

void *shmat(int shmid, const void *shmaddr, int shmflg);


shmat() is used to attach the created shared segment with the address space of the calling process.
The first parameter here is the identifier which shmget() function returns on success. The second
parameter is the address where to attach it to the calling process. A NULL value of second
parameter means that the system will automatically choose a suitable address. The third parameter
is ‘0’ if the second parameter is NULL, otherwise, the value is specified by SHM_RND.

We will write two program for IPC using shared memory. Program 1 will create the shared
segment, attach to it and then write some content into it. Then Program 2 will attach itself to the
shared segment and read the value written by Program 1.

Program 1:

This program creates a shared memory segment, attaches itself to it and then writes some content
into the shared memory segment.

#include<stdio.h>

#include<stdlib.h>

#include<unistd.h>

#include<sys/shm.h>

#include<string.h>

int main()

int i;

void *shared_memory;

char buff[100];

int shmid;

shmid=shmget((key_t)2345, 1024, 0666|IPC_CREAT); //creates shared memory segment with


key 2345, having size 1024 bytes. IPC_CREAT is used to create the shared segment if it does not
exist. 0666 are the permisions on the shared segment
printf("Key of shared memory is %d\n",shmid);

shared_memory=shmat(shmid,NULL,0); //process attached to shared memory segment

printf("Process attached at %p\n",shared_memory); //this prints the address where the segment is
attached with this process

printf("Enter some data to write to shared memory\n");

read(0,buff,100); //get some input from user

strcpy(shared_memory,buff); //data written to shared memory

printf("You wrote : %s\n",(char *)shared_memory);

How it works?

shmget() function creates a segment with key 2345, size 1024 bytes and read and write permissions
for all users. It returns the identifier of the segment which gets store in shmid. This identifier is
used in shmat() to attach the shared segment to the address space of the process. NULL in shmat()
means that the OS will itself attach the shared segment at a suitable address of this process.
Then some data is read from the user using read() system call and it is finally written to the shared
segment using strcpy() function.

Output

Program 2:

This program attaches itself to the shared memory segment created in Program 1. Finally, it reads
the content of the shared memory
#include<stdio.h>

#include<stdlib.h>

#include<unistd.h>

#include<sys/shm.h>

#include<string.h>

int main()

int i;

void *shared_memory;

char buff[100];

int shmid;

shmid=shmget((key_t)2345, 1024, 0666);

printf("Key of shared memory is %d\n",shmid);

shared_memory=shmat(shmid,NULL,0); //process attached to shared memory segment

printf("Process attached at %p\n",shared_memory);

printf("Data read from shared memory is : %s\n",(char *)shared_memory);

How it works?

shmget() here generates the identifier of the same segment as created in Program 1. Remember to
give the same key value. The only change is, do not write IPC_CREAT as the shared memory
segment is already created. Next, shmat() attaches the shared segment to the current process.
After that, the data is printed from the shared segment. In the output, you will see that it is the
same data that you have written while executing the Program 1.

Output

• PROGRAM FOR IPC USING MESSAGE QUEUES


Program for IPC using Message queues are almost similar to named pipes with the exception that
they do not require the opening and closing of pipes. But, they face one similar problem like named
pipes; blocking on full pipes. Message queues send blocks of data from one process to another.
Each block of data is considered to have a type. There is an upper limit on the maximum size of
each block and also a limit on the maximum total size of all blocks on all queues in the system.

Message Queue Functions

There are 4 important functions that we will use in the programs to achieve IPC using message
queues

• int msgget(key_t key, int msgflg);


• int msgsnd(int msqid, const void *msg_ptr, size_t msg_sz, int msgflg);
• int msgrcv(int msqid, void *msg_ptr, size_t msg_sz, long int msgtype, int msgflg);
• int msgctl(int msqid, int command, struct msqid_ds *buf);
Now let’s understand each of these functions in detail.

int msgget(key_t key, int msgflg);

To create and access a message queue, we use the msgget function. It takes two parameters. The
first parameter is a key that names a message queue in the system. The second parameter is used
to assign permission to the message queue and is ORed with IPC_CREAT to create the queue if it
doesn’t already exist. if the queue already exists then IPC_CREAT is ignored. On success, the
msgget function returns a positive number which is the queue identifier while on failure it returns
-1.

The second functions is msgsnd

int msgsnd(int msqid, const void *msg_ptr, size_t msg_sz, int msgflg);
This function allows us to add a message to the message queue. The first parameter (msgid) is the
message queue identifier returned by the msgget function. The second parameter, is the pointer to
the message to be sent, which must start with a long int type. The third parameter, is the size of
the message. It must not include the long int message type. The fourth and final parameter controls
what happens if either the message queue is full or the system limit on queued messages is reached.
The function on success returns 0 and place the copy of message data on the message queue. On
failure it returns -1.

There are two constraints related to the structure of the message. First, it must be smaller than the
system limit and,
second, it must start with a long int. This long int is used as a message type in the receive function.
Since the message_type is used in message reception, you can’t simply ignore it.
your data structure to include it, it’s also wise to initialize it, so that it contains a known value.

The third function is msgrcv

int msgrcv(int msqid, void *msg_ptr, size_t msg_sz, long int msgtype, int msgflg);

This function retrieves messages from a message queue. The first parameter (msgid) is the message
queue identifier returned by the msgget function. The second parameter, is the pointer to the
message to be received, which must start with a long int type as explained above. The third
parameter is the size of the message.

The fourth parameter allows implementing priority. If the value is 0, the first available message in
the queue is retrieved. But if the value is greater than 0 then the first message with the same
message type is retrieved. If the value is less than 0 then the first message having the type value
same as the absolute value of msgtype is retrieved. In simple words 0 value means to receive the
messages in the order in which they were sent and non zero means receive the message with a
specific message type. The final parameter controls what happens if either the message queue is
full or the system limit on queued messages is reached. The function on success returns 0 and place
the copy of message data on the message queue. On failure it returns -1.

The final function is msgctl, which is the control function.

int msgctl(int msqid, int command, struct msqid_ds *buf);

The first parameter is the identifier returned by msgget function. The second parameter can have
one out of the below three values
Command Description

Sets the data in the msqid_ds structure to reflect the values associated with the
IPC_STAT
message queue.

If the process has permission to do so, this sets the values associated with the
IPC_SET
message queue to those provided in the msqid_ds data structure.

IPC_RMID Deletes the message queue.

The msgctl function returns 0 on success and -1 on error. The send or receive function will fail if
a message queue is deleted while a process is waiting in a msgsnd or msgrcv function.

Program 1:

Program for IPC using Message Queues To send data to a message queue

#include<stdlib.h>

#include<stdio.h>

#include<string.h>

#include<unistd.h>

#include<sys/types.h>

#include<sys/ipc.h>

#include<sys/msg.h>

#define MAX_TEXT 512 //maximum length of the message that can be sent allowed

struct my_msg{

long int msg_type;

char some_text[MAX_TEXT];
};

int main()

int running=1;

int msgid;

struct my_msg some_data;

char buffer[50]; //array to store user input

msgid=msgget((key_t)14534,0666|IPC_CREAT);

if (msgid == -1) // -1 means the message queue is not created

printf("Error in creating queue\n");

exit(0);

while(running)

printf("Enter some text:\n");

fgets(buffer,50,stdin);

some_data.msg_type=1;

strcpy(some_data.some_text,buffer);
if(msgsnd(msgid,(void *)&some_data, MAX_TEXT,0)==-1) // msgsnd returns -1 if the
message is not sent

printf("Msg not sent\n");

if(strncmp(buffer,"end",3)==0)

running=0;

Output:

How it Works?

The structure my_msg declares the long int variable and the char array to store the data to be sent
to the message queue. Then the message queue is created using the msgget() function. Next, read
data from the user into the buffer using fgets() and then copy it into the variable some_text of the
structure some_data. Finally, send the data to the queue using the msgsnd() function. The strcmp
function is used to stop sending the data by comparing the first three characters of the data. If the
data starts with “end” this means no more data is to be sent.
Program 2:

Program for IPC using Message Queues To receive/read message from the above-created
message queue

#include<stdlib.h>

#include<stdio.h>

#include<string.h>

#include<unistd.h>

#include<sys/types.h>

#include<sys/ipc.h>

#include<sys/msg.h>

struct my_msg{

long int msg_type;

char some_text[BUFSIZ];

};

int main()

int running=1;

int msgid;

struct my_msg some_data;

long int msg_to_rec=0;


msgid=msgget((key_t)14534,0666|IPC_CREAT);

while(running)

msgrcv(msgid,(void *)&some_data,BUFSIZ,msg_to_rec,0);

printf("Data received: %s\n",some_data.some_text);

if(strncmp(some_data.some_text,"end",3)==0)

running=0;

msgctl(msgid,IPC_RMID,0);

Output:

How it works?

The msg_to_rec variable is set to 0 so that the data is received in the same order as sent (refer the
theory above for more details). The while is used to continuous receive the data using the mgrcv()
function until the text received is “end”, which we check using the strcmp function. The data is
read using the structure my_msg.
DISK SCHEDULING ALGORITHMS
• FCFS ALGORITHM PROGRAM IN C
Disk scheduling algorithms are used to come with an order in which the I/O read/write requests by
the different process should be satisfied. The aim is to minimize the seek time i.e., the time to
move from one sector to another. First Come First Serve (FCFS) disk scheduling algorithm serves
the incoming requests in order i.e., the request which came first will be satisfied first. For example,
if the order of I/O requests is 10, 40, 20, 50, 35 and the current head position is 45, then the order
of fulfilling the requests will be
45 -> 10 -> 40 -> 20 -> 50 ->35
because the first request was for sector 10, then 40 and so on. For more details refer Page
Replacement Algorithms. FCFS algorithm program in C is implemented below

Variable description for the program for FCFS disk scheduling algorithm

req[] – array for taking the request, mov – to calculate total head movement

FCFS Disk Scheduling Algorithm Program in C

#include<math.h>

#include<stdio.h>

#include<stdlib.h>

int main()

int i,n,req[50],mov=0,cp;

printf("enter the current position\n");

scanf("%d",&cp);

printf("enter the number of requests\n");

scanf("%d",&n);

printf("enter the request order\n");


for(i=0;i<n;i++)

scanf("%d",&req[i]);

mov=mov+abs(cp-req[0]); // abs is used to calculate the absolute value

printf("%d -> %d",cp,req[0]);

for(i=1;i<n;i++)

mov=mov+abs(req[i]-req[i-1]);

printf(" -> %d",req[i]);

printf("\n");

printf("total head movement = %d\n",mov);

}
Output

How it works?

In FCFS disk scheduling algorithm program the order of requests is stored in the req[] array. The
movement between the requests is calculated by taking the absolute value of the difference
between the values at consecutive index positions of the array. Finally, all the movements are
added to get the total head movement.

• SSTF ALGORITHM PROGRAM IN C


Before implementing SSTF Algorithm Program in C, let us understand the working of SSTF
algorithm. SSTF selects the request with the least seek time from the current head position. SSTF
chooses the pending request closest to the current head position.

e.g. for the same question (as in FCFS), the order of request serviced using SSTF will be:
53 -> 65 -> 67 -> 37 -> 14 -> 98 -> 122 -> 124 -> 183
Thus, the total head movement is |65-53|+|67-65|+|37-67|+|14-37|+|98-14|+|122-98|+|124-
122|+|183-124| = 12+2+30+23+84+24+2+59 = 236

Variable Description

req[] – array for taking the request, index[] – array for store the distance of each request from
current position, a[] – array to store the final sequence in which requests should be fulfilled, min
– to find the nearest request from the index array, mini – stores the index of nearest request, mov
– to calculate total head movement, cp1 – to save the initial value of current position

SSTF Algorithm Program in C

#include<math.h>

#include<stdio.h>
#include<stdlib.h>

int main()

int i,n,k,req[50],mov=0,cp,index[50],min,a[50],j=0,mini,cp1;

printf("enter the current position\n");

scanf("%d",&cp);

printf("enter the number of requests\n");

scanf("%d",&n);

cp1=cp;

printf("enter the request order\n");

for(i=0;i<n;i++)

scanf("%d",&req[i]);

for(k=0;k<n;k++)

for(i=0;i<n;i++)

index[i]=abs(cp-req[i]); // calculate distance of each request from current position

}
// to find the nearest request

min=index[0];

mini=0;

for(i=1;i<n;i++)

if(min>index[i])

min=index[i];

mini=i;

a[j]=req[mini];

j++;

cp=req[mini]; // change the current position value to next request

req[mini]=999;

} // the request that is processed its value is changed so that it is not processed again

printf("Sequence is : ");

printf("%d",cp1);

mov=mov+abs(cp1-a[0]); // head movement

printf(" -> %d",a[0]);


for(i=1;i<n;i++)

mov=mov+abs(a[i]-a[i-1]); ///head movement

printf(" -> %d",a[i]);

printf("\n");

printf("total head movement = %d\n",mov);

Output

You might also like