Lab Programs
Lab Programs
SUB CODE:
III SEMESTER B.E
INSTRUCTOR MANUAL
2023-24
Vision
Mission
COURSE OUTCOMES
At the end of the course, the student will be able to –
1 Demonstrate the fundamental concepts of operating system like process management, file
. management, memory management and issues of synchronization.
2 Analyze and interpret operating system concepts to acquire a detailed understanding of the
. course.
3 Apply the operating systems concepts to address related new problems in computer science
. Domain.
4 Design or develop solutions to solve applicable problems in operating systems domain.
.
5 Extend the theoretical knowledge acquired through the course to understand the real-world
. implementation of operating system.
LIST OF PROGRAMS
Part – A
Students are expected to identify suitable application and implement solution for the
topics listed below:
Sl. Program
No.
1 Implementation of basic UNIX commands using file APIs- Write a program to
implement commands ls( -l option), cp, rm and mv using UNIX file APIs.
2 Apply the concepts of Process control system calls to build applications to demonstrate
use of fork, execve, wait, getpid, exit system calls
3. Apply the pthread library to build Applications to demonstrate use of pthread library
functions to create and manage threads.
4. Apply the concepts of Process/Thread synchronization to build Applications to
demonstrate process/thread synchronization using semaphores and mutex. Implement
Dining philosophers problem, reader-writer and producer-consumer.
5. Apply the concepts of Process/Thread synchronization for file access to build
applications to demonstrate process/thread synchronization using file locks.
6. Apply the concepts of Static and Shared libraries to write a program to create and use
static and shared libraries. Demonstrate the advantage of shared libraries over static
libraries in terms of memory usage.
INDEX
Sl.
Program Name.
No.
6. Memory Management
PROGRAM – 1
Write a program to implement commands ls( -l option), cp, rm and mv using UNIX file
APIs.
ls –l command
#include<stdio.h>
#include<dirent.h>
#include<sys/stat.h>
#include<pwd.h>
#include<grp.h>
#include<time.h>
int main()
{
DIR *d;
struct dirent *de;
struct stat buf;
int i,j;
char P[10]="rwxrwxrwx",AP[10]=" ";
struct passwd *p;
struct group *g;
struct tm *t;
char time[26];
d=opendir(".");
readdir(d);
readdir(d);
while( (de=readdir(d))!=NULL)
{
stat(de->d_name,&buf);
// File Type
if(S_ISDIR(buf.st_mode))
printf("d");
else if(S_ISREG(buf.st_mode))
printf("-");
else if(S_ISCHR(buf.st_mode))
printf("c");
else if(S_ISBLK(buf.st_mode))
printf("b");
else if(S_ISLNK(buf.st_mode))
printf("l");
else if(S_ISFIFO(buf.st_mode))
printf("p");
else if(S_ISSOCK(buf.st_mode))
printf("s");
//File Permissions P-Full Permissions AP-Actual Permissions
for(i=0,j=(1<<8);i<9;i++,j>>=1)
AP[i]= (buf.st_mode & j ) ? P[i] : '-' ;
printf("%s",AP);
//No. of Hard Links
printf("%5d",buf.st_nlink);
//User Name
p=getpwuid(buf.st_uid);
printf(" %.8s",p->pw_name);
//Group Name
g=getgrgid(buf.st_gid);
printf(" %-8.8s",g->gr_name);
//File Size
printf(" %8d",buf.st_size);
//Date and Time of modification
t=localtime(&buf.st_mtime);
strftime(time,sizeof(time),"%b %d %H:%M",t);
printf(" %s",time);
//File Name
printf(" %s\n",de->d_name);
}
}
cp command
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <unistd.h>
/* Copy process */
while((ret_in = read (input_fd, &buffer, BUF_SIZE)) > 0){
ret_out = write (output_fd, &buffer, (ssize_t)
ret_in);
if(ret_out != ret_in){
/* Write error */
perror("write");
return 4;
}
}
return (EXIT_SUCCESS);
}
mv command
rm command
output_fd = unlink(argv[1]);
if(output_fd == -1){
perror("unlink error");
return 3;
}
$./myls
$gcc -o mycp cp.c
$./myrm a.c
PROGRAM – 2
AHost Program
#include<stdio.h>
#include<string.h>
#include<sys/types.h>
#include<stdlib.h>
#include<unistd.h>
#include<wait.h>
if(pid<0){
printf("Error in fork operation\n");
}
if(pid==0){
printf("PID for Child process: %d\nPID of its parent process: %d\n",getpid(),getppid());
execl("./binsearch",argv[1],NULL);
}
else{
printf("PID of parent process: %d\n",getpid());
wait(&retval);
if(WIFEXITED(retval)==1)
{
printf("Child terminated normally\n");
}
else{
printf("Child terminated abnormally\n");
exit(0);
}
}
return 0;
}
#include<stdio.h>
int main(void){
Output
PROGRAM – 3
Implementation:
#include<pthread.h>
#include<sys/types.h>
#include<stdlib.h>
#include<unistd.h>
#include<math.h>
#include<stdio.h>
int a[4][4],b[4][4];
pthread_exit(NULL);
return 0;
}
Output:
OUTPUT:
$ cc threads.c -pthread
$./a.out
PROGRAM – 4
Process/Thread synchronization
Application to demonstrate process/thread synchronization using semaphores and mutex.
Implement either Dining philosophers problem, reader-writer or producer-consumer.
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include<semaphore.h>
int count=0,rcount=0;
sem_t mutex,wr;
void* writer(void *p){
int* i =(int*)p;
sem_wait(&wr);
printf("\nWriter %d writes page number %d",*i,++count);
sem_post(&wr);
}
void* reader(void* p){
int* i =(int*)p;
sem_wait(&mutex);
rcount++;
if(rcount==1)
sem_wait(&wr);
sem_post(&mutex);
printf("\nReader %d reads page number %d ",*i,count);
sem_wait(&mutex);
rcount--;
if(rcount==0)
sem_post(&wr);
sem_post(&mutex);
}
int main(){
sem_init(&mutex,0,1);
sem_init(&wr,0,1); int a[6]={1,2,3,1,2,3};
pthread_t p[6];
for(int i=0;i<3;i++) pthread_create(&p[i],NULL,writer,&a[i]);
for(int i=3;i<6;i++) pthread_create(&p[i],NULL,reader,&a[i]);
for(int i=0;i<6;i++) pthread_join(p[i],NULL);
}
Producer consumer:
#include<stdio.h>
#include<semaphore.h>
#include<pthread.h>
#include<stdlib.h>
#define buffersize 10
pthread_mutex_t mutex;
pthread_t tidP[20],tidC[20];
sem_t full,empty;
int counter;
int buffer[buffersize];
void initialize()
{
pthread_mutex_init(&mutex,NULL);
sem_init(&full,1,0);
sem_init(&empty,1,buffersize);
counter=0;
}
int read()
{
return(buffer[--counter]);
}
int main()
{
int n1,n2,i;
initialize();
printf("\nEnter the no of producers: ");
scanf("%d",&n1);
printf("\nEnter the no of consumers: ");
scanf("%d",&n2);
for(i=0;i<n1;i++)
pthread_create(&tidP[i],NULL,producer,NULL);
for(i=0;i<n2;i++)
pthread_create(&tidC[i],NULL,consumer,NULL);
for(i=0;i<n1;i++)
pthread_join(tidP[i],NULL);
for(i=0;i<n2;i++)
pthread_join(tidC[i],NULL);
//sleep(5);
exit(0);
}
OUTPUT:
Enter the no of producers: 3
#include <pthread.h>
#include <semaphore.h>
#include <stdio.h>
#define N 5
#define THINKING 2
#define HUNGRY 1
#define EATING 0
#define LEFT (phnum + 4) % N
#define RIGHT (phnum + 1) % N
int state[N];
int phil[N] = { 0, 1, 2, 3, 4 };
sem_t mutex;
sem_t S[N];
sleep(2);
// take up chopsticks
void take_fork(int phnum)
{
sem_wait(&mutex);
sem_post(&mutex);
sleep(1);
}
sem_wait(&mutex);
test(LEFT);
test(RIGHT);
sem_post(&mutex);
}
while (1) {
int* i = num;
sleep(1);
take_fork(*i);
sleep(0);
put_fork(*i);
}
}
int main()
{
int i;
pthread_t thread_id[N];
OUTPUT :
Philosopher 1 is thinking
Philosopher 2 is thinking
Philosopher 3 is thinking
Philosopher 4 is thinking
Philosopher 5 is thinking
Philosopher 1 is Hungry
Philosopher 2 is Hungry
Philosopher 3 is Hungry
Philosopher 4 is Hungry
Philosopher 5 is Hungry
Philosopher 5 takes fork 4 and 5
Philosopher 5 is Eating
Philosopher 5 putting fork 4 and 5 down
Philosopher 5 is thinking
Philosopher 4 takes fork 3 and 4
Philosopher 4 is Eating
Philosopher 1 takes fork 5 and 1
Philosopher 1 is Eating
Philosopher 4 putting fork 3 and 4 down
Philosopher 4 is thinking
Philosopher 3 takes fork 2 and 3
Philosopher 3 is Eating
Philosopher 1 putting fork 5 and 1 down
Philosopher 1 is thinking
Philosopher 5 is Hungry
Philosopher 5 takes fork 4 and 5
Philosopher 5 is Eating
Philosopher 3 putting fork 2 and 3 down
Philosopher 3 is thinking
Philosopher 2 takes fork 1 and 2
Philosopher 2 is Eating
Philosopher 4 is Hungry
Philosopher 1 is Hungry
Philosopher 5 putting fork 4 and 5 down
Philosopher 5 is thinking
Philosopher 4 takes fork 3 and 4
Philosopher 4 is Eating
Philosopher 3 is Hungry
Philosopher 2 putting fork 1 and 2 down
Philosopher 2 is thinking
PROGRAM – 5
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
int main(int argc,char *argv[])
{
int fd;
char buffer[255];
struct flock fvar;
if(argc==1)
{
printf("usage: %s filename\n",argv[0]);
return -1;
}
if((fd=open(argv[1],O_RDWR))==-1)
{
perror("open");
exit(1);
}
fvar.l_type=F_WRLCK;
fvar.l_whence=SEEK_END;
fvar.l_start=SEEK_END-100;
fvar.l_len=100;
printf("press enter to set lock\n");
getchar();
printf("trying to get lock..\n");
if((fcntl(fd,F_SETLK,&fvar))==-1)
{ fcntl(fd,F_GETLK,&fvar);
printf("\nFile already locked by process (pid):
\t%d\n",fvar.l_pid);
return -1;
}
printf("locked\n");
if((lseek(fd,SEEK_END-50,SEEK_END))==-1)
{
perror("lseek");
exit(1);
}
if((read(fd,buffer,100))==-1)
{
perror("read");
exit(1);
}
printf("data read from file..\n");
puts(buffer);
printf("press enter to release lock\n");
getchar();
fvar.l_type = F_UNLCK;
fvar.l_whence = SEEK_SET;
fvar.l_start = 0;
fvar.l_len = 0;
if((fcntl(fd,F_UNLCK,&fvar))==-1)
{
perror("fcntl");
exit(0);
}
printf("Unlocked\n");
close(fd);
return 0;
}
Terminal 1
$./a.out foo.txt
Terminal 2
$./a.out foo.txt
PROGRAM -6
ar -t libctest.a
cc -o executable-name prog.c
-L/path/to/library-directory -lctest
● size executable-name
● export LD_LIBRARY_PATH=.:LD_LIBRARY_PATH
● Linking with the library:
● size prog
Compare the size of shared and static libraries using size command
VIVA Questions
1)Explain the main purpose of an operating system?
Operating systems exist for two main purposes. One is that it is designed to make sure a computer
system performs well by managing its computational activities. Another is that it provides an
environment for the development and execution of programs.
Demand paging is referred when not all of a process’s pages are in the RAM, then the OS brings
the missing(and required) pages from the disk into the RAM.
With an increased number of processors, there is a considerable increase in throughput. It can also
save more money because they can share resources. Finally, overall reliability is increased as
well.
4) What is kernel?
A kernel is the core of every operating system. It connects applications to the actual processing of
data. It also manages all communications between software and hardware components to ensure
usability and reliability.
Real-time systems are used when rigid time requirements have been placed on the operation of a
processor. It has well defined and fixed time constraints.
Virtual memory is a memory management technique for letting processes execute outside of
memory. This is very useful especially is an executing program cannot fit in the physical memory.
The main objective of multiprogramming is to have a process running at all times. With this
design, CPU utilization is said to be maximized.
In a Time-sharing system, the CPU executes multiple jobs by switching among them, also known
as multitasking. This process happens so fast that users can interact with each program while it is
running.
9) What is SMP?
In asymmetric clustering, a machine is in a state known as hot standby mode where it does
nothing but to monitor the active server. That machine takes the active server’s role should the
server fails.
A thread is a basic unit of CPU utilization. In general, a thread is composed of a thread ID,
program counter, register set, and the stack.
FCFS stands for First-come, first-served. It is one type of scheduling algorithm. In this scheme,
the process that requests the CPU first is allocated the CPU first. Implementation is managed by a
FIFO queue.
16) What are necessary conditions which can lead to a deadlock situation in a system?
Deadlock situations occur when four conditions occur simultaneously in a system: Mutual
exclusion; Hold and Wait; No preemption; and Circular wait.
One is that it depends on how often a deadlock is likely to occur under the implementation of this
algorithm. The other has to do with how many processes will be affected by deadlock when this
algorithm is applied.
20) State the main difference between logical from physical address space.
Logical address refers to the address that is generated by the CPU. On the other hand, physical
address refers to the address that is seen by the memory unit.
21) How does dynamic loading aid in better memory space utilization?
With dynamic loading, a routine is not loaded until it is called. This method is especially useful
when large amounts of code are needed in order to handle infrequently occurring cases such as
error routines.
22) What are overlays?
Overlays are used to enable a process to be larger than the amount of memory allocated to it. The
basic idea of this is that only instructions and data that are needed at any given time are kept in
memory.
Paging is a memory management scheme that permits the physical address space of a process to
be noncontiguous. It avoids the considerable problem of having to fit varied sized memory
chunks onto the backing store.
Fragmentation is memory wasted. It can be internal if we are dealing with systems that have
fixed-sized allocation units, or external if we are dealing with systems that have variable-sized
allocation units.
During regular intervals that are set by the operating system, processes can be copied from main
memory to a backing store, and then copied back later. Swapping allows more operations to be
run that can fit into memory at one time.
Direct Access method is based on a disk model of a file, such that it is viewed as a numbered
sequence of blocks or records. It allows arbitrary blocks to be read or written. Direct access is
advantageous when accessing large amounts of information.
Thrashing refers to an instance of high paging activity. This happens when it is spending more
time paging instead of executing.
30) What is the best page size when designing an operating system?
The best paging size varies from system to system, so there is no single best when it comes to
page size. There are different factors to consider in order to come up with a suitable page size,
such as page table, paging time, and its effect on the overall efficiency of the operating system.
20) State the main difference between logical from physical address space.
Logical address refers to the address that is generated by the CPU. On the other hand, physical
address refers to the address that is seen by the memory unit.
21) How does dynamic loading aid in better memory space utilization?
With dynamic loading, a routine is not loaded until it is called. This method is especially useful
when large amounts of code are needed in order to handle infrequently occurring cases such as
error routines.
Overlays are used to enable a process to be larger than the amount of memory allocated to it. The
basic idea of this is that only instructions and data that are needed at any given time are kept in
memory.
Paging is a memory management scheme that permits the physical address space of a process to
be noncontiguous. It avoids the considerable problem of having to fit varied sized memory
chunks onto the backing store.
Fragmentation is memory wasted. It can be internal if we are dealing with systems that have
fixed-sized allocation units, or external if we are dealing with systems that have variable-sized
allocation units.
During regular intervals that are set by the operating system, processes can be copied from main
memory to a backing store, and then copied back later. Swapping allows more operations to be
run that can fit into memory at one time.