study of hardware and software requirements of various operating system.
(UNIX, LINUX,
WINDOWSXP, WINDOWS7/8)
1. UNIX:
Hardware Requirements: UNIX systems have varied hardware requirements
depending on the specific distribution. UNIX-like operating systems such as Solaris,
HP-UX, and AIX are often used on server-grade hardware with multi-core processors,
ample RAM, and high-speed storage systems. For desktop use, hardware
requirements may be similar to Linux distributions.
Software Requirements: UNIX typically requires a compatible UNIX-like kernel and
supporting utilities. Additionally, specific UNIX distributions may have their own
software requirements and dependencies.
2. Linux:
Hardware Requirements: Linux distributions have a wide range of hardware
support, from lightweight distributions suitable for older hardware to enterprise-
grade distributions optimized for server deployments. Generally, Linux can run on a
variety of hardware configurations, including x86, x64, ARM, and more.
Software Requirements: Linux requires a compatible kernel and userland utilities.
Additionally, specific distributions may have additional software requirements
depending on their intended use case and installed packages.
3. Windows XP:
Hardware Requirements: Windows XP was released in 2001, so its hardware
requirements are relatively modest by modern standards. It typically requires a
Pentium 233 MHz processor or higher, 64 MB RAM (128 MB recommended), and 1.5
GB of available hard disk space. However, for better performance, higher
specifications are recommended.
Software Requirements: Windows XP comes with its own software suite, including
system utilities, drivers, and optional components. It's compatible with a wide range
of applications designed for the Windows platform.
4. Windows 7/8:
Hardware Requirements: Windows 7 and 8 have higher hardware requirements
compared to Windows XP. Windows 7 typically requires a 1 GHz processor or faster,
1 GB RAM (2 GB for 64-bit), and 16 GB of available hard disk space (20 GB for 64-bit).
Windows 8 has similar requirements.
Software Requirements: Windows 7 and 8 come with their own software suite and
are compatible with a wide range of applications designed for the Windows
platform. They also include additional features and enhancements over Windows
XP.
UNIX system calls for process management
1. fork():
Creates a new process by duplicating the calling process. The new process is called
the child process, and the original process is the parent process.
#include <stdio.h>
#include <unistd.h>
int main() {
pid_t pid;
// Fork a child process
pid = fork();
// Check if fork was successful
if (pid < 0) {
// Error occurred
fprintf(stderr, "Fork failed.\n");
return 1;
} else if (pid == 0) {
// Child process
printf("This is the child process. PID: %d\n", getpid());
} else {
// Parent process
printf("This is the parent process. Child PID: %d\n", pid);
return 0;
2. exec():
Replaces the current process image with a new process image. There are several
variants of the exec() system call, such as execve(), execl(), execv(), etc., each taking
different arguments and providing different ways to execute a new program.
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main() {
// Executing ls command
execl("/bin/ls", "ls", "-l", NULL);
// If execl returns, it indicates an error
perror("exec");
return 1;
3. wait():
Suspends the execution of the calling process until one of its child processes
terminates. It allows the parent process to wait for the completion of the child
process.
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
int main() {
pid_t pid;
// Fork a child process
pid = fork();
if (pid < 0) {
// Error occurred
perror("Fork failed");
exit(EXIT_FAILURE);
} else if (pid == 0) {
// Child process
printf("Child process executing...\n");
sleep(2); // Simulate some work in the child process
printf("Child process exiting...\n");
exit(EXIT_SUCCESS);
} else {
// Parent process
printf("Parent process waiting for child to finish...\n");
int status;
wait(&status); // Wait for child process to finish
if (WIFEXITED(status)) {
printf("Child process exited with status: %d\n", WEXITSTATUS(status));
} else {
printf("Child process exited abnormally\n");
printf("Parent process exiting...\n");
exit(EXIT_SUCCESS);
4. waitpid():
Similar to wait(), but it allows the parent process to wait for a specific child process
to terminate, specified by its process ID (PID).
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
int main() {
pid_t pid, child_pid;
int status;
// Fork a child process
pid = fork();
if (pid < 0) {
// Error occurred
perror("Fork failed");
exit(EXIT_FAILURE);
} else if (pid == 0) {
// Child process
printf("Child process executing...\n");
sleep(2); // Simulate some work in the child process
printf("Child process exiting...\n");
exit(EXIT_SUCCESS);
} else {
// Parent process
printf("Parent process waiting for child to finish...\n");
// Wait for the specific child process with pid
child_pid = waitpid(pid, &status, 0);
if (child_pid == -1) {
perror("waitpid");
exit(EXIT_FAILURE);
if (WIFEXITED(status)) {
printf("Child process with PID %d exited with status: %d\n", child_pid,
WEXITSTATUS(status));
} else {
printf("Child process with PID %d exited abnormally\n", child_pid);
printf("Parent process exiting...\n");
exit(EXIT_SUCCESS);
5. exit():
Terminates the calling process and returns an exit status to the parent process.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main() {
printf("This is before calling exit()\n");
// Calling exit() to terminate the process
exit(0);
// This line will not be executed
printf("This is after calling exit()\n");
return 0;
6. getpid():
Returns the process ID of the calling process.
#include <stdio.h>
#include <unistd.h>
int main() {
// Get the process ID of the current process
pid_t pid = getpid();
// Print the process ID
printf("Process ID: %d\n", pid);
return 0;
7. getppid():
Returns the parent process ID of the calling process.
#include <stdio.h>
#include <unistd.h>
int main() {
// Get the parent process ID of the current process
pid_t parent_pid = getppid();
// Print the parent process ID
printf("Parent Process ID: %d\n", parent_pid);
return 0;
}
8. kill():
Sends a signal to a process or a group of processes. Signals can be used for various
purposes, such as terminating a process (SIGKILL) or requesting it to terminate
gracefully (SIGTERM).
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
int main() {
pid_t pid = getpid(); // Get the PID of the current process
// Print the PID of the current process
printf("Process ID: %d\n", pid);
// Send SIGTERM signal to the current process
int result = kill(pid, SIGTERM);
// Check if kill was successful
if (result == 0) {
printf("SIGTERM signal sent successfully to process %d\n", pid);
} else {
perror("kill");
exit(EXIT_FAILURE);
return 0;
9. signal():
Sets a signal handler for a specific signal. Signal handlers define how a process
should react when it receives a particular signal.
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
// Signal handler function
void sigint_handler(int signum) {
printf("Caught SIGINT signal (%d)\n", signum);
exit(EXIT_SUCCESS);
int main() {
// Install signal handler for SIGINT (Ctrl+C)
signal(SIGINT, sigint_handler);
printf("Press Ctrl+C to send SIGINT signal...\n");
// Infinite loop to keep the program running
while (1) {
sleep(1); // Sleep for 1 second
return 0;
10. nice():
Changes the priority of a process. It allows a process to voluntarily lower its priority
to give more CPU time to other processes.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main() {
int priority;
// Get the current process's nice value
priority = nice(0);
if (priority == -1) {
perror("nice");
exit(EXIT_FAILURE);
printf("Current nice value: %d\n", priority);
// Set a new nice value for the current process
int new_priority = nice(10);
if (new_priority == -1) {
perror("nice");
exit(EXIT_FAILURE);
printf("New nice value set to: %d\n", new_priority);
return 0;
UNIX system calls for file management
1. open():
Opens or creates a file and returns a file descriptor for further operations.
Syntax: int open(const char *pathname, int flags, mode_t mode);
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
int main() {
int fd;
// Open a file in read-only mode
fd = open("example.txt", O_RDONLY);
if (fd == -1) {
perror("open");
exit(EXIT_FAILURE);
printf("File opened successfully with file descriptor: %d\n", fd);
// Close the file
if (close(fd) == -1) {
perror("close");
exit(EXIT_FAILURE);
printf("File closed successfully\n");
return 0;
2. close():
Closes a file descriptor, releasing associated resources.
Syntax: int close(int fd);
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
int main() {
int fd;
// Open a file in read-only mode
fd = open("example.txt", O_RDONLY);
if (fd == -1) {
perror("open");
exit(EXIT_FAILURE);
printf("File opened successfully with file descriptor: %d\n", fd);
// Close the file
if (close(fd) == -1) {
perror("close");
exit(EXIT_FAILURE);
printf("File closed successfully\n");
return 0;
3. read():
Reads data from a file descriptor into a buffer.
Syntax: ssize_t read(int fd, void *buf, size_t count);
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#define BUFFER_SIZE 1024
int main() {
int fd;
ssize_t bytes_read;
char buffer[BUFFER_SIZE + 1]; // +1 for null terminator
// Open a file in read-only mode
fd = open("example.txt", O_RDONLY);
if (fd == -1) {
perror("open");
exit(EXIT_FAILURE);
printf("File opened successfully\n");
// Read from the file
bytes_read = read(fd, buffer, BUFFER_SIZE);
if (bytes_read == -1) {
perror("read");
exit(EXIT_FAILURE);
// Null-terminate the buffer
buffer[bytes_read] = '\0';
printf("Bytes read from file: %zd\n", bytes_read);
printf("Data read from file:\n%s\n", buffer);
// Close the file
if (close(fd) == -1) {
perror("close");
exit(EXIT_FAILURE);
}
printf("File closed successfully\n");
return 0;
4. write():
Writes data from a buffer to a file descriptor.
Syntax: ssize_t write(int fd, const void *buf, size_t count);
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
int main() {
int fd;
ssize_t bytes_written;
char *data = "Hello, world!\n";
// Open a file in write-only mode, creating it if it doesn't exist
fd = open("output.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (fd == -1) {
perror("open");
exit(EXIT_FAILURE);
printf("File opened successfully\n");
// Write data to the file
bytes_written = write(fd, data, strlen(data));
if (bytes_written == -1) {
perror("write");
exit(EXIT_FAILURE);
printf("Bytes written to file: %zd\n", bytes_written);
// Close the file
if (close(fd) == -1) {
perror("close");
exit(EXIT_FAILURE);
printf("File closed successfully\n");
return 0;
5. lseek():
Repositions the offset of the file descriptor.
Syntax: off_t lseek(int fd, off_t offset, int whence);
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
int main() {
int fd;
off_t offset;
// Open a file in read-write mode
fd = open("example.txt", O_RDWR);
if (fd == -1) {
perror("open");
exit(EXIT_FAILURE);
// Seek to the beginning of the file
offset = lseek(fd, 0, SEEK_SET);
if (offset == -1) {
perror("lseek");
exit(EXIT_FAILURE);
printf("File offset after seeking: %ld\n", (long) offset);
// Close the file
if (close(fd) == -1) {
perror("close");
exit(EXIT_FAILURE);
return 0;
6. unlink():
Deletes a name from the file system. If that name was the last link to the file and no
processes have it open, the file is deleted.
Syntax: int unlink(const char *pathname);
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main() {
// Path to the file to be deleted
const char *filename = "example.txt";
// Attempt to delete the file
if (unlink(filename) == -1) {
perror("unlink");
exit(EXIT_FAILURE);
printf("File '%s' deleted successfully\n", filename);
return 0;
7. rename():
Renames a file, moving it between directories if required.
Syntax: int rename(const char *oldpath, const char *newpath);
#include <stdio.h>
#include <stdlib.h>
int main() {
const char *old_filename = "old_name.txt";
const char *new_filename = "new_name.txt";
// Attempt to rename the file
if (rename(old_filename, new_filename) == -1) {
perror("rename");
exit(EXIT_FAILURE);
printf("File '%s' renamed to '%s' successfully\n", old_filename, new_filename);
return 0;
}
8. mkdir():
Creates a new directory.
Syntax: int mkdir(const char *pathname, mode_t mode);
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
int main() {
const char *dirname = "new_directory";
// Attempt to create the directory
if (mkdir(dirname, 0777) == -1) {
perror("mkdir");
exit(EXIT_FAILURE);
printf("Directory '%s' created successfully\n", dirname);
return 0;
9. rmdir():
Deletes a directory.
Syntax: int rmdir(const char *pathname);
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main() {
const char *dirname = "directory_to_remove";
// Attempt to remove the directory
if (rmdir(dirname) == -1) {
perror("rmdir");
exit(EXIT_FAILURE);
printf("Directory '%s' removed successfully\n", dirname);
return 0;
10. stat() and fstat():
Retrieves information about a file (e.g., file size, permissions, modification time).
Syntax: int stat(const char *pathname, struct stat *buf); and int fstat(int fd, struct
stat *buf);
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
int main() {
const char *filename = "example.txt";
struct stat file_info;
// Use stat() to get information about a file by filename
if (stat(filename, &file_info) == -1) {
perror("stat");
exit(EXIT_FAILURE);
}
printf("Information about file '%s':\n", filename);
printf("File size: %ld bytes\n", file_info.st_size);
printf("File permissions: %o\n", file_info.st_mode & 0777);
printf("File inode number: %ld\n", (long) file_info.st_ino);
// Open the file to obtain a file descriptor
int fd = open(filename, O_RDONLY);
if (fd == -1) {
perror("open");
exit(EXIT_FAILURE);
// Use fstat() to get information about a file by file descriptor
struct stat fd_info;
if (fstat(fd, &fd_info) == -1) {
perror("fstat");
exit(EXIT_FAILURE);
printf("\nInformation about file '%s' obtained using file descriptor:\n", filename);
printf("File size: %ld bytes\n", fd_info.st_size);
printf("File permissions: %o\n", fd_info.st_mode & 0777);
printf("File inode number: %ld\n", (long) fd_info.st_ino);
// Close the file
if (close(fd) == -1) {
perror("close");
exit(EXIT_FAILURE);
return 0;
}
11. chmod():
Changes the permissions of a file.
Syntax: int chmod(const char *pathname, mode_t mode);
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
int main() {
const char *filename = "example.txt";
mode_t new_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; // Readable by
owner, writable by owner, readable by group, readable by others
// Attempt to change the permissions of the file
if (chmod(filename, new_mode) == -1) {
perror("chmod");
exit(EXIT_FAILURE);
printf("Permissions of file '%s' changed successfully\n", filename);
return 0;
12. chown():
Changes the owner and group of a file.
Syntax: int chown(const char *pathname, uid_t owner, gid_t group);
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main() {
const char *filename = "example.txt";
uid_t new_owner = 1000; // UID of the new owner
gid_t new_group = 1000; // GID of the new group
// Attempt to change the ownership of the file
if (chown(filename, new_owner, new_group) == -1) {
perror("chown");
exit(EXIT_FAILURE);
printf("Ownership of file '%s' changed successfully\n", filename);
return 0;
UNIX system calls for input/output system calls
1. open():
Opens or creates a file and returns a file descriptor for further operations.
Syntax: int open(const char *pathname, int flags, mode_t mode);
2. close():
Closes a file descriptor, releasing associated resources.
Syntax: int close(int fd);
3. read():
Reads data from a file descriptor into a buffer.
Syntax: ssize_t read(int fd, void *buf, size_t count);
4. write():
Writes data from a buffer to a file descriptor.
Syntax: ssize_t write(int fd, const void *buf, size_t count);
5. lseek():
Repositions the offset of the file descriptor.
Syntax: off_t lseek(int fd, off_t offset, int whence);
6. dup() and dup2():
Duplicates an existing file descriptor.
Syntax: int dup(int oldfd); and int dup2(int oldfd, int newfd);
For dup()
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
int main() {
int fd, new_fd;
// Open a file for reading
fd = open("example.txt", O_RDONLY);
if (fd == -1) {
perror("open");
exit(EXIT_FAILURE);
// Duplicate the file descriptor
new_fd = dup(fd);
if (new_fd == -1) {
perror("dup");
exit(EXIT_FAILURE);
printf("Original file descriptor: %d\n", fd);
printf("New file descriptor (duplicated): %d\n", new_fd);
// Close the original file descriptor
if (close(fd) == -1) {
perror("close");
exit(EXIT_FAILURE);
// Close the duplicated file descriptor
if (close(new_fd) == -1) {
perror("close");
exit(EXIT_FAILURE);
return 0;
For dup2()
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
int main() {
int fd, new_fd;
// Open a file for reading
fd = open("example.txt", O_RDONLY);
if (fd == -1) {
perror("open");
exit(EXIT_FAILURE);
// Duplicate the file descriptor
new_fd = dup2(fd, 100); // Duplicate to a specific file descriptor (100 in this case)
if (new_fd == -1) {
perror("dup2");
exit(EXIT_FAILURE);
printf("Original file descriptor: %d\n", fd);
printf("New file descriptor (duplicated): %d\n", new_fd);
// Close the original file descriptor
if (close(fd) == -1) {
perror("close");
exit(EXIT_FAILURE);
// No need to close the duplicated file descriptor (new_fd) as it's duplicated to a
specific file descriptor
return 0;
7. pipe():
Creates an interprocess communication pipe.
Syntax: int pipe(int pipefd[2]);
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main() {
int pipefd[2];
char buffer[256];
ssize_t bytes_read;
// Create a pipe
if (pipe(pipefd) == -1) {
perror("pipe");
exit(EXIT_FAILURE);
// Fork a child process
pid_t pid = fork();
if (pid == -1) {
perror("fork");
exit(EXIT_FAILURE);
} else if (pid == 0) { // Child process
// Close the write end of the pipe
close(pipefd[1]);
// Read from the pipe
bytes_read = read(pipefd[0], buffer, sizeof(buffer));
if (bytes_read == -1) {
perror("read");
exit(EXIT_FAILURE);
printf("Child process received message from parent: %s\n", buffer);
// Close the read end of the pipe
close(pipefd[0]);
exit(EXIT_SUCCESS);
} else { // Parent process
// Close the read end of the pipe
close(pipefd[0]);
// Write to the pipe
const char *message = "Hello from parent!";
if (write(pipefd[1], message, sizeof(message)) == -1) {
perror("write");
exit(EXIT_FAILURE);
printf("Parent process sent message to child: %s\n", message);
// Close the write end of the pipe
close(pipefd[1]);
return 0;
8. ioctl():
Performs device-specific I/O operations.
Syntax: int ioctl(int fd, unsigned long request, ...);
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <errno.h>
int main() {
int fd;
char device[] = "/dev/tty";
// Open the terminal device
fd = open(device, O_RDWR);
if (fd == -1) {
perror("open");
exit(EXIT_FAILURE);
// Get the terminal size
struct winsize ws;
if (ioctl(fd, TIOCGWINSZ, &ws) == -1) {
perror("ioctl");
close(fd);
exit(EXIT_FAILURE);
printf("Terminal width: %d\n", ws.ws_col);
printf("Terminal height: %d\n", ws.ws_row);
// Close the file descriptor
if (close(fd) == -1) {
perror("close");
exit(EXIT_FAILURE);
return 0;
9. fcntl():
Performs various operations on file descriptors.
Syntax: int fcntl(int fd, int cmd, ...);
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
int main() {
int fd, new_fd;
// Open a file for reading
fd = open("example.txt", O_RDONLY);
if (fd == -1) {
perror("open");
exit(EXIT_FAILURE);
// Duplicate the file descriptor
new_fd = fcntl(fd, F_DUPFD, 0);
if (new_fd == -1) {
perror("fcntl");
exit(EXIT_FAILURE);
printf("Original file descriptor: %d\n", fd);
printf("New file descriptor (duplicated): %d\n", new_fd);
// Close the original file descriptor
if (close(fd) == -1) {
perror("close");
exit(EXIT_FAILURE);
// Close the duplicated file descriptor
if (close(new_fd) == -1) {
perror("close");
exit(EXIT_FAILURE);
}
return 0;
10. select(), poll(), and epoll():
Multiplexes I/O operations on multiple file descriptors.
Syntax: int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
struct timeval *timeout); for select(), int poll(struct pollfd *fds, nfds_t nfds, int
timeout); for poll(), and int epoll_wait(int epfd, struct epoll_event *events, int
maxevents, int timeout); for epoll().
For select()
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/select.h>
int main() {
fd_set read_fds;
int max_fd, activity, client_socket[5] = {0}; // Sample client sockets
char buffer[1024] = {0};
// Initialize client sockets (sample)
// ...
while (1) {
// Clear the file descriptor set
FD_ZERO(&read_fds);
// Add client sockets to the set
max_fd = -1;
for (int i = 0; i < 5; ++i) { // Assuming 5 client sockets
if (client_socket[i] > 0) {
FD_SET(client_socket[i], &read_fds);
if (client_socket[i] > max_fd)
max_fd = client_socket[i];
// Set timeout
struct timeval timeout;
timeout.tv_sec = 5;
timeout.tv_usec = 0;
// Call select
activity = select(max_fd + 1, &read_fds, NULL, NULL, &timeout);
if (activity < 0) {
perror("select");
exit(EXIT_FAILURE);
// Check for activity
if (activity == 0) {
printf("Timeout occurred\n");
continue;
// Handle activity on sockets
for (int i = 0; i < 5; ++i) { // Assuming 5 client sockets
if (FD_ISSET(client_socket[i], &read_fds)) {
// Read data from the socket
ssize_t bytes_read = read(client_socket[i], buffer, sizeof(buffer));
if (bytes_read == -1) {
perror("read");
// Handle read error
} else if (bytes_read == 0) {
printf("Socket %d disconnected\n", client_socket[i]);
// Close socket and remove from set
close(client_socket[i]);
client_socket[i] = 0;
} else {
printf("Data received from socket %d: %s\n", client_socket[i], buffer);
// Process data
return 0;
For poll()
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <poll.h>
int main() {
struct pollfd fds[5]; // Sample file descriptors
char buffer[1024] = {0};
// Initialize file descriptors (sample)
// ...
while (1) {
// Set up file descriptors for polling
for (int i = 0; i < 5; ++i) { // Assuming 5 file descriptors
fds[i].fd = ...; // Set file descriptor
fds[i].events = POLLIN; // Set events to poll for (e.g., POLLIN for input)
fds[i].revents = 0; // Clear revents
// Call poll
int activity = poll(fds, 5, 5000); // Timeout of 5000 milliseconds (5 seconds)
if (activity == -1) {
perror("poll");
exit(EXIT_FAILURE);
} else if (activity == 0) {
printf("Timeout occurred\n");
continue;
// Check for activity on file descriptors
for (int i = 0; i < 5; ++i) { // Assuming 5 file descriptors
if (fds[i].revents & POLLIN) {
// Read data from the file descriptor
ssize_t bytes_read = read(fds[i].fd, buffer, sizeof(buffer));
if (bytes_read == -1) {
perror("read");
// Handle read error
} else if (bytes_read == 0) {
printf("File descriptor %d disconnected\n", fds[i].fd);
// Handle disconnection
} else {
printf("Data received from file descriptor %d: %s\n", fds[i].fd, buffer);
// Process data
}
return 0;
For epoll()
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/epoll.h>
int main() {
int epoll_fd, num_events;
struct epoll_event event, events[5]; // Sample events
char buffer[1024] = {0};
// Initialize events and create epoll instance (sample)
// ...
while (1) {
// Wait for events
num_events = epoll_wait(epoll_fd, events, 5, 5000); // Timeout of 5000
milliseconds (5 seconds)
if (num_events == -1) {
perror("epoll_wait");
exit(EXIT_FAILURE);
} else if (num_events == 0) {
printf("Timeout occurred\n");
continue;
}
// Handle events
for (int i = 0; i < num_events; ++i) {
if (events[i].events & EPOLLIN) {
// Read data from the file descriptor associated with the event
ssize_t bytes_read = read(events[i].data.fd, buffer, sizeof(buffer));
if (bytes_read == -1) {
perror("read");
// Handle read error
} else if (bytes_read == 0) {
printf("File descriptor %d disconnected\n", events[i].data.fd);
// Handle disconnection
} else {
printf("Data received from file descriptor %d: %s\n", events[i].data.fd,
buffer);
// Process data
return 0;
}
Lab Assessment – 2
FCFS - First Come First Serve
#include <stdio.h>
#include <stdlib.h>
#define MAX_PROCESSES 10
// Process structure
typedef struct {
int id; // Process ID
int burst_time; // CPU burst time
int arrival_time; // Arrival time of the process
int waiting_time; // Waiting time of the process
int turnaround_time;// Turnaround time of the process
} Process;
// Function to calculate waiting time and turnaround time for FCFS
void calculate_fcfs_times(Process processes[], int n) {
processes[0].waiting_time = 0;
processes[0].turnaround_time = processes[0].burst_time;
for (int i = 1; i < n; i++) {
processes[i].waiting_time = processes[i - 1].waiting_time + processes[i - 1].burst_time;
processes[i].turnaround_time = processes[i].waiting_time + processes[i].burst_time;
// Function to display process details for FCFS
void display_fcfs_processes(Process processes[], int n) {
printf("Process\tBurst Time\tArrival Time\tWaiting Time\tTurnaround Time\n");
for (int i = 0; i < n; i++) {
printf("%d\t%d\t\t%d\t\t%d\t\t%d\n", processes[i].id, processes[i].burst_time,
processes[i].arrival_time, processes[i].waiting_time,
processes[i].turnaround_time);
}
int main() {
int n; // Number of processes
Process processes[MAX_PROCESSES]; // Array of processes
printf("Enter the number of processes (up to %d): ", MAX_PROCESSES);
scanf("%d", &n);
// Input process details
for (int i = 0; i < n; i++) {
processes[i].id = i + 1;
printf("Enter CPU burst time for process %d: ", i + 1);
scanf("%d", &processes[i].burst_time);
printf("Enter arrival time for process %d: ", i + 1);
scanf("%d", &processes[i].arrival_time);
// Calculate waiting time and turnaround time for FCFS
calculate_fcfs_times(processes, n);
// Display FCFS scheduling results
printf("\nFCFS Scheduling:\n");
display_fcfs_processes(processes, n);
return 0;
Input
Output
SJF – Shortest Job First
#include <stdio.h>
#include <stdlib.h>
#define MAX_PROCESSES 10
// Process structure
typedef struct {
int id; // Process ID
int burst_time; // CPU burst time
int arrival_time; // Arrival time of the process
int waiting_time; // Waiting time of the process
int turnaround_time;// Turnaround time of the process
} Process;
// Function to sort processes by burst time (SJF)
void sort_by_burst_time(Process processes[], int n) {
// Implement any sorting algorithm here (e.g., Bubble Sort)
// For simplicity, let's use Bubble Sort
for (int i = 0; i < n - 1; i++) {
for (int j = 0; j < n - i - 1; j++) {
if (processes[j].burst_time > processes[j + 1].burst_time) {
// Swap processes
Process temp = processes[j];
processes[j] = processes[j + 1];
processes[j + 1] = temp;
}
}
// Function to calculate waiting time and turnaround time for SJF
void calculate_sjf_times(Process processes[], int n) {
processes[0].waiting_time = 0;
processes[0].turnaround_time = processes[0].burst_time;
for (int i = 1; i < n; i++) {
processes[i].waiting_time = processes[i - 1].waiting_time + processes[i - 1].burst_time;
processes[i].turnaround_time = processes[i].waiting_time + processes[i].burst_time;
// Function to display process details for SJF
void display_sjf_processes(Process processes[], int n) {
printf("Process\tBurst Time\tArrival Time\tWaiting Time\tTurnaround Time\n");
for (int i = 0; i < n; i++) {
printf("%d\t%d\t\t%d\t\t%d\t\t%d\n", processes[i].id, processes[i].burst_time,
processes[i].arrival_time, processes[i].waiting_time,
processes[i].turnaround_time);
int main() {
int n; // Number of processes
Process processes[MAX_PROCESSES]; // Array of processes
printf("Enter the number of processes (up to %d): ", MAX_PROCESSES);
scanf("%d", &n);
// Input process details
for (int i = 0; i < n; i++) {
processes[i].id = i + 1;
printf("Enter CPU burst time for process %d: ", i + 1);
scanf("%d", &processes[i].burst_time);
printf("Enter arrival time for process %d: ", i + 1);
scanf("%d", &processes[i].arrival_time);
// Sort processes based on burst time for SJF
sort_by_burst_time(processes, n);
// Calculate waiting time and turnaround time for SJF
calculate_sjf_times(processes, n);
// Display SJF scheduling results
printf("\nSJF Scheduling:\n");
display_sjf_processes(processes, n);
return 0;
Input
Output
Priority Scheduling
#include <stdio.h>
#include <stdlib.h>
#define MAX_PROCESSES 10
Input
Output
Multi-level Queue
#include <stdio.h>
#include <stdlib.h>
#define MAX_PROCESSES 10
#define NUM_QUEUES 3
// Process structure
typedef struct {
int id; // Process ID
int burst_time; // CPU burst time
int priority; // Priority of the process
int arrival_time; // Arrival time of the process
int waiting_time; // Waiting time of the process
int turnaround_time;// Turnaround time of the process
} Process;
// Function to calculate waiting time and turnaround time for a process
void calculate_times(Process *process) {
process->waiting_time = process->turnaround_time - process->burst_time;
// Function to display process details
void display_processes(Process processes[], int n) {
printf("Process\tBurst Time\tPriority\tArrival Time\tWaiting Time\tTurnaround Time\n");
for (int i = 0; i < n; i++) {
printf("%d\t%d\t\t%d\t\t%d\t\t%d\t\t%d\n", processes[i].id, processes[i].burst_time,
processes[i].priority, processes[i].arrival_time, processes[i].waiting_time,
processes[i].turnaround_time);
int main() {
int n; // Number of processes
Process processes[MAX_PROCESSES]; // Array of processes
Process *queues[NUM_QUEUES][MAX_PROCESSES]; // Multi-level queues
int front[NUM_QUEUES] = {0}; // Front pointer for each queue
int rear[NUM_QUEUES] = {0}; // Rear pointer for each queue
int total_waiting_time = 0; // Total waiting time
int total_turnaround_time = 0; // Total turnaround time
// Initialize queues
for (int i = 0; i < NUM_QUEUES; i++) {
for (int j = 0; j < MAX_PROCESSES; j++) {
queues[i][j] = NULL;
printf("Enter the number of processes (up to %d): ", MAX_PROCESSES);
scanf("%d", &n);
// Input process details
for (int i = 0; i < n; i++) {
processes[i].id = i + 1;
printf("Enter CPU burst time for process %d: ", i + 1);
scanf("%d", &processes[i].burst_time);
printf("Enter priority for process %d: ", i + 1);
scanf("%d", &processes[i].priority);
printf("Enter arrival time for process %d: ", i + 1);
scanf("%d", &processes[i].arrival_time);
// Add process to appropriate queue based on priority
if (processes[i].priority >= 0 && processes[i].priority < NUM_QUEUES) {
queues[processes[i].priority][rear[processes[i].priority]++] = &processes[i];
// Schedule processes from each queue
for (int i = 0; i < NUM_QUEUES; i++) {
for (int j = 0; j < rear[i]; j++) {
// Calculate turnaround time
queues[i][j]->turnaround_time = queues[i][j]->burst_time;
total_turnaround_time += queues[i][j]->turnaround_time;
// Calculate waiting time
if (j > 0) {
queues[i][j]->waiting_time = queues[i][j - 1]->waiting_time + queues[i][j - 1]->burst_time;
total_waiting_time += queues[i][j]->waiting_time;
} else {
queues[i][j]->waiting_time = 0;
}
// Calculate average waiting time and average turnaround time
float avg_waiting_time = (float)total_waiting_time / n;
float avg_turnaround_time = (float)total_turnaround_time / n;
// Display multi-level queue scheduling results
printf("\nMulti-level Queue Scheduling:\n");
display_processes(processes, n);
printf("\nAverage Waiting Time: %.2f\n", avg_waiting_time);
printf("Average Turnaround Time: %.2f\n", avg_turnaround_time);
return 0;
Input
Output
LAB – 3
Implement file storage allocation techniques:
(a) Contiguous (using array)
#include <stdio.h>
#include <stdbool.h>
#define DISK_SIZE 100
char disk[DISK_SIZE]; // Represents the disk
int freeSpace = DISK_SIZE; // Tracks the available free space
// Function to allocate contiguous space for a file
bool allocate(char filename, int size) {
if (size > freeSpace) {
printf("Not enough free space available on disk.\n");
return false;
}
int startIndex = -1; // Start index of contiguous free space
int count = 0; // Count of consecutive free blocks
for (int i = 0; i < DISK_SIZE; ++i) {
if (disk[i] == '\0') { // If block is free
if (startIndex == -1) {
startIndex = i; // Start of a new consecutive free space
}
++count;
if (count == size) { // If required contiguous space is found
for (int j = startIndex; j < startIndex + size; ++j) {
disk[j] = filename; // Allocate space by marking blocks with file name
}
freeSpace -= size; // Update free space count
printf("File '%c' allocated from sector %d to %d.\n", filename, startIndex, startIndex + size -
1);
return true;
}
} else {
startIndex = -1; // Reset startIndex if consecutive free space is broken
count = 0;
}
}
printf("Not enough contiguous free space available on disk.\n");
return false;
}
// Function to deallocate contiguous space allocated for a file
void deallocate(char filename) {
for (int i = 0; i < DISK_SIZE; ++i) {
if (disk[i] == filename) {
disk[i] = '\0'; // Free the block by marking it as available
++freeSpace; // Increment free space count
}
}
printf("File '%c' deallocated.\n", filename);
}
// Function to print disk status
void printDiskStatus() {
printf("Disk Status:\n");
for (int i = 0; i < DISK_SIZE; ++i) {
printf("%c ", disk[i] == '\0' ? '-' : disk[i]);
}
printf("\nFree Space: %d\n", freeSpace);
}
int main() {
// Initialize disk with free space
for (int i = 0; i < DISK_SIZE; ++i) {
disk[i] = '\0';
}
// Example usage
printf("Initial Disk Status:\n");
printDiskStatus();
allocate('A', 5);
allocate('B', 3);
printDiskStatus();
deallocate('A');
printDiskStatus();
allocate('C', 7);
printDiskStatus();
return 0;
}
(b) Linked –list (using linked list)
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
// Define a structure for a disk block
typedef struct DiskBlock {
char data;
struct DiskBlock* next;
} DiskBlock;
DiskBlock* head = NULL; // Head of the linked list representing the disk
int freeSpace = 0; // Tracks the available free space
// Function to allocate space for a file
void allocate(char filename, int size) {
if (size <= freeSpace) {
DiskBlock* newNode;
for (int i = 0; i < size; ++i) {
newNode = (DiskBlock*)malloc(sizeof(DiskBlock));
newNode->data = filename;
newNode->next = head;
head = newNode;
}
freeSpace -= size; // Update free space count
printf("File '%c' allocated using linked list.\n", filename);
} else {
printf("Not enough free space available on disk.\n");
}
}
// Function to deallocate space allocated for a file
void deallocate(char filename) {
DiskBlock *current = head, *prev = NULL;
while (current != NULL) {
if (current->data == filename) {
if (prev != NULL) {
prev->next = current->next;
} else {
head = current->next;
}
free(current);
++freeSpace; // Increment free space count
} else {
prev = current;
}
current = current->next;
}
printf("File '%c' deallocated.\n", filename);
}
// Function to print disk status
void printDiskStatus() {
printf("Disk Status:\n");
DiskBlock* current = head;
while (current != NULL) {
printf("%c -> ", current->data);
current = current->next;
}
printf("NULL\n");
printf("Free Space: %d\n", freeSpace);
}
int main() {
// Initialize free space
freeSpace = 100;
// Example usage
printf("Initial Disk Status:\n");
printDiskStatus();
allocate('A', 5);
allocate('B', 3);
printDiskStatus();
deallocate('A');
printDiskStatus();
allocate('C', 7);
printDiskStatus();
return 0;
}
(c) Indirect allocation (indexing)
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#define DISK_SIZE 100
char disk[DISK_SIZE]; // Represents the disk
int indexBlock[DISK_SIZE]; // Represents the index block
int freeSpace = DISK_SIZE; // Tracks the available free space
// Function to allocate space for a file
void allocate(char filename, int size) {
if (size <= freeSpace) {
int indices[size];
int count = 0;
for (int i = 0; i < DISK_SIZE && count < size; ++i) {
if (disk[i] == '\0') {
indices[count++] = i;
}
}
for (int i = 0; i < size; ++i) {
disk[indices[i]] = filename; // Allocate space by marking blocks with file name
}
indexBlock[filename] = indices[0];
freeSpace -= size; // Update free space count
printf("File '%c' allocated using indirect allocation.\n", filename);
} else {
printf("Not enough free space available on disk.\n");
}
}
// Function to deallocate space allocated for a file
void deallocate(char filename) {
int index = indexBlock[filename];
while (disk[index] != '\0') {
disk[index] = '\0'; // Free the block by marking it as available
++index;
++freeSpace; // Increment free space count
}
printf("File '%c' deallocated.\n", filename);
}
// Function to print disk status
void printDiskStatus() {
printf("Disk Status:\n");
for (int i = 0; i < DISK_SIZE; ++i) {
printf("%c ", disk[i] == '\0' ? '-' : disk[i]);
}
printf("\nFree Space: %d\n", freeSpace);
}
int main() {
// Initialize disk with free space
for (int i = 0; i < DISK_SIZE; ++i) {
disk[i] = '\0';
}
// Example usage
printf("Initial Disk Status:\n");
printDiskStatus();
allocate('A', 5);
allocate('B', 3);
printDiskStatus();
deallocate('A');
printDiskStatus();
allocate('C', 7);
printDiskStatus();
return 0;
}
LAB – 4
Contiguous Allocation Techniques
I. Implementation of Contiguous allocation techniques:
(a) Worst-Fit
(b) Best-Fit
(c) First-Fit
#include <stdio.h>
#define MAX_BLOCKS 100
// Structure to represent a block of memory
struct Block {
int id;
int size;
int allocated;
};
// Function prototypes
void worstFit(struct Block freeBlocks[], int n, int processes[], int m);
void bestFit(struct Block freeBlocks[], int n, int processes[], int m);
void firstFit(struct Block freeBlocks[], int n, int processes[], int m);
int main() {
// Sample data
struct Block freeBlocks[MAX_BLOCKS] = {{1, 100, 0}, {2, 200, 0}, {3, 50, 0}};
int processes[] = {20, 70, 30, 60};
int n = sizeof(freeBlocks) / sizeof(freeBlocks[0]);
int m = sizeof(processes) / sizeof(processes[0]);
printf("Worst Fit Allocation:\n");
worstFit(freeBlocks, n, processes, m);
printf("\nBest Fit Allocation:\n");
bestFit(freeBlocks, n, processes, m);
printf("\nFirst Fit Allocation:\n");
firstFit(freeBlocks, n, processes, m);
return 0;
}
void worstFit(struct Block freeBlocks[], int n, int processes[], int m) {
// Implementation of Worst-Fit allocation
}
void bestFit(struct Block freeBlocks[], int n, int processes[], int m) {
// Implementation of Best-Fit allocation
}
void firstFit(struct Block freeBlocks[], int n, int processes[], int m) {
// Implementation of First-Fit allocation
}
LAB – 5
External and Internal Fragmentation
Calculation of external and internal fragmentation.
#include <stdio.h>
#define MAX_BLOCKS 100
// Structure to represent a block of memory
struct Block {
int id;
int size;
int allocated;
};
// Function prototypes
void calculateFragmentation(struct Block freeBlocks[], int n);
int main() {
// Sample data
struct Block freeBlocks[MAX_BLOCKS] = {{1, 100, 0}, {2, 200, 0}, {3, 50, 0}};
int n = sizeof(freeBlocks) / sizeof(freeBlocks[0]);
calculateFragmentation(freeBlocks, n);
return 0;
}
void calculateFragmentation(struct Block freeBlocks[], int n) {
int externalFragmentation = 0;
int totalFreeSpace = 0;
int totalAllocatedSpace = 0;
for (int i = 0; i < n; ++i) {
if (!freeBlocks[i].allocated) {
totalFreeSpace += freeBlocks[i].size;
if (freeBlocks[i].size < 20) { // Example threshold for internal fragmentation
externalFragmentation += freeBlocks[i].size;
}
} else {
totalAllocatedSpace += freeBlocks[i].size;
}
}
int internalFragmentation = totalAllocatedSpace - totalFreeSpace;
printf("Total External Fragmentation: %d\n", externalFragmentation);
printf("Total Internal Fragmentation: %d\n", internalFragmentation);
}
LAB – 6
External and Internal Fragmentation
Implementation of Compaction for the continually changing memory layout and calculate total
movement of data.
#include <stdio.h>
#define MAX_BLOCKS 100
// Structure to represent a block of memory
struct Block {
int id;
int size;
int allocated;
};
// Function prototypes
void compactMemory(struct Block freeBlocks[], int n, int *totalMovement);
void printMemoryLayout(struct Block freeBlocks[], int n);
int main() {
// Sample data
struct Block freeBlocks[MAX_BLOCKS] = {{1, 100, 1}, {2, 200, 0}, {3, 50, 1}};
int n = sizeof(freeBlocks) / sizeof(freeBlocks[0]);
printf("Before Compaction:\n");
printMemoryLayout(freeBlocks, n);
int totalMovement = 0;
compactMemory(freeBlocks, n, &totalMovement);
printf("\nAfter Compaction:\n");
printMemoryLayout(freeBlocks, n);
printf("Total Movement of Data: %d\n", totalMovement);
return 0;
void compactMemory(struct Block freeBlocks[], int n, int *totalMovement) {
int currentPos = 0;
for (int i = 0; i < n; ++i) {
if (freeBlocks[i].allocated) {
freeBlocks[i].id = currentPos++;
*totalMovement += abs(freeBlocks[i].id - i);
void printMemoryLayout(struct Block freeBlocks[], int n) {
printf("Block ID\tSize\tAllocated\n");
for (int i = 0; i < n; ++i) {
printf("%d\t\t%d\t%d\n", freeBlocks[i].id, freeBlocks[i].size, freeBlocks[i].allocated);
}
LAB – 7
Resource Allocation Graph (RAG)
Implementation of resource allocation graph (RAG).
#include <stdio.h>
#include <stdbool.h>
#define MAX_NODES 100
// Structure to represent a node in the graph
struct Node {
int id;
bool visited;
bool allocated;
};
// Structure to represent an edge in the graph
struct Edge {
int from;
int to;
};
// Function prototypes
void initializeGraph(struct Node nodes[], int n);
void addEdge(struct Edge edges[], int *numEdges, int from, int to);
void printGraph(struct Node nodes[], struct Edge edges[], int n, int numEdges);
bool isCycleUtil(struct Node nodes[], struct Edge edges[], int n, int u);
bool isCycle(struct Node nodes[], struct Edge edges[], int n);
int main() {
struct Node nodes[MAX_NODES];
struct Edge edges[MAX_NODES];
int numNodes = 5; // Number of nodes in the graph
int numEdges = 0; // Number of edges in the graph
// Initialize nodes
initializeGraph(nodes, numNodes);
// Add edges
addEdge(edges, &numEdges, 0, 2);
addEdge(edges, &numEdges, 1, 3);
addEdge(edges, &numEdges, 2, 4);
addEdge(edges, &numEdges, 3, 4);
addEdge(edges, &numEdges, 4, 1);
// Print the graph
printGraph(nodes, edges, numNodes, numEdges);
// Check for cycle
if (isCycle(nodes, edges, numNodes)) {
printf("Cycle detected in the graph.\n");
} else {
printf("No cycle detected in the graph.\n");
return 0;
// Initialize nodes of the graph
void initializeGraph(struct Node nodes[], int n) {
for (int i = 0; i < n; ++i) {
nodes[i].id = i;
nodes[i].visited = false;
nodes[i].allocated = false;
// Add an edge to the graph
void addEdge(struct Edge edges[], int *numEdges, int from, int to) {
edges[*numEdges].from = from;
edges[*numEdges].to = to;
(*numEdges)++;
// Print the graph
void printGraph(struct Node nodes[], struct Edge edges[], int n, int numEdges) {
printf("Resource Allocation Graph:\n");
for (int i = 0; i < numEdges; ++i) {
printf("%d -> %d\n", edges[i].from, edges[i].to);
// Utility function to check for cycle recursively
bool isCycleUtil(struct Node nodes[], struct Edge edges[], int n, int u) {
if (!nodes[u].visited) {
nodes[u].visited = true;
nodes[u].allocated = true;
for (int i = 0; i < n; ++i) {
if (edges[i].from == u) {
if (!nodes[edges[i].to].visited && isCycleUtil(nodes, edges, n, edges[i].to)) {
return true;
} else if (nodes[edges[i].to].allocated) {
return true;
}
nodes[u].allocated = false;
return false;
// Check for cycle in the graph
bool isCycle(struct Node nodes[], struct Edge edges[], int n) {
for (int i = 0; i < n; ++i) {
if (!nodes[i].visited && isCycleUtil(nodes, edges, n, i)) {
return true;
return false;
}
LAB – 8
Bankers Algorithm
Implementation of Banker’s Algorithm.
#include <stdio.h>
#include <stdbool.h>
#define MAX_PROCESSES 100
#define MAX_RESOURCES 100
// Function prototypes
bool isSafeState(int available[], int max[][MAX_RESOURCES], int allocation[][MAX_RESOURCES], int
need[][MAX_RESOURCES], int processes[], int numProcesses, int numResources);
void printSafeSequence(int safeSequence[], int numProcesses);
int main() {
int available[MAX_RESOURCES];
int max[MAX_PROCESSES][MAX_RESOURCES];
int allocation[MAX_PROCESSES][MAX_RESOURCES];
int need[MAX_PROCESSES][MAX_RESOURCES];
int processes[MAX_PROCESSES];
int numProcesses, numResources;
// Input the number of processes and resources
printf("Enter number of processes: ");
scanf("%d", &numProcesses);
printf("Enter number of resources: ");
scanf("%d", &numResources);
// Input available resources
printf("Enter available resources:\n");
for (int i = 0; i < numResources; ++i) {
scanf("%d", &available[i]);
// Input maximum resources required by each process
printf("Enter maximum resources required by each process:\n");
for (int i = 0; i < numProcesses; ++i) {
printf("For process %d: ", i);
for (int j = 0; j < numResources; ++j) {
scanf("%d", &max[i][j]);
processes[i] = i; // Assigning process IDs
// Input allocated resources for each process
printf("Enter allocated resources for each process:\n");
for (int i = 0; i < numProcesses; ++i) {
printf("For process %d: ", i);
for (int j = 0; j < numResources; ++j) {
scanf("%d", &allocation[i][j]);
// Calculate need resources
need[i][j] = max[i][j] - allocation[i][j];
// Run Banker's Algorithm
if (isSafeState(available, max, allocation, need, processes, numProcesses, numResources)) {
printf("Safe state! Safe sequence: ");
printSafeSequence(processes, numProcesses);
} else {
printf("Unsafe state! Deadlock detected.\n");
}
return 0;
// Function to check if system is in safe state using Banker's Algorithm
bool isSafeState(int available[], int max[][MAX_RESOURCES], int allocation[][MAX_RESOURCES], int
need[][MAX_RESOURCES], int processes[], int numProcesses, int numResources) {
bool finish[numProcesses];
int work[MAX_RESOURCES];
int safeSequence[numProcesses];
int count = 0;
// Initialize finish array
for (int i = 0; i < numProcesses; ++i) {
finish[i] = false;
// Initialize work array
for (int i = 0; i < numResources; ++i) {
work[i] = available[i];
// Find a process which can be allocated
while (count < numProcesses) {
bool found = false;
for (int i = 0; i < numProcesses; ++i) {
if (!finish[i]) {
bool canAllocate = true;
for (int j = 0; j < numResources; ++j) {
if (need[i][j] > work[j]) {
canAllocate = false;
break;
if (canAllocate) {
// Process can be allocated
for (int j = 0; j < numResources; ++j) {
work[j] += allocation[i][j];
safeSequence[count++] = i;
finish[i] = true;
found = true;
// If no process found, break loop
if (!found) {
break;
// If all processes are not finished, then unsafe state
if (count != numProcesses) {
return false;
// Safe state
return true;
// Function to print safe sequence
void printSafeSequence(int safeSequence[], int numProcesses) {
for (int i = 0; i < numProcesses; ++i) {
printf("%d ", safeSequence[i]);
printf("\n");
}
LAB – 9
Wait Graph
Conversion of resource allocation graph (RAG) to wait-for-graph (WFG) for each type of method
used for storing graph.
#include <stdio.h>
#include <stdbool.h>
#define MAX_NODES 100
// Structure to represent a node in the graph
struct Node {
int id;
struct Node* next;
};
// Function prototypes
void convertToWaitForGraphAdjList(struct Node* rag[], struct Node* wfg[], int numProcesses);
void convertToWaitForGraphAdjMatrix(bool rag[][MAX_NODES], bool wfg[][MAX_NODES], int
numProcesses);
int main() {
// Sample Resource Allocation Graph (RAG) represented as adjacency list
struct Node* rag[MAX_NODES];
// Initialize RAG adjacency list
for (int i = 0; i < MAX_NODES; ++i) {
rag[i] = NULL;
// Assuming some resource allocation relationships
rag[0] = (struct Node*)malloc(sizeof(struct Node));
rag[0]->id = 1;
rag[0]->next = NULL;
rag[1] = (struct Node*)malloc(sizeof(struct Node));
rag[1]->id = 2;
rag[1]->next = NULL;
rag[2] = (struct Node*)malloc(sizeof(struct Node));
rag[2]->id = 0;
rag[2]->next = NULL;
// Number of processes
int numProcesses = 3;
// Convert RAG to WFG using adjacency list
struct Node* wfg[MAX_NODES];
convertToWaitForGraphAdjList(rag, wfg, numProcesses);
// Print WFG (adjacency list representation)
printf("Wait-For-Graph (Adjacency List Representation):\n");
for (int i = 0; i < numProcesses; ++i) {
printf("Process %d -> ", i);
struct Node* current = wfg[i];
while (current != NULL) {
printf("%d ", current->id);
current = current->next;
printf("\n");
// Convert RAG to WFG using adjacency matrix
bool ragMatrix[MAX_NODES][MAX_NODES] = {false}; // Assuming no relationship initially
bool wfgMatrix[MAX_NODES][MAX_NODES];
for (int i = 0; i < numProcesses; ++i) {
struct Node* current = rag[i];
while (current != NULL) {
ragMatrix[i][current->id] = true;
current = current->next;
// Convert to WFG using adjacency matrix
convertToWaitForGraphAdjMatrix(ragMatrix, wfgMatrix, numProcesses);
// Print WFG (adjacency matrix representation)
printf("\nWait-For-Graph (Adjacency Matrix Representation):\n");
for (int i = 0; i < numProcesses; ++i) {
for (int j = 0; j < numProcesses; ++j) {
printf("%d ", wfgMatrix[i][j]);
printf("\n");
return 0;
// Convert RAG to WFG using adjacency list
void convertToWaitForGraphAdjList(struct Node* rag[], struct Node* wfg[], int numProcesses) {
for (int i = 0; i < numProcesses; ++i) {
wfg[i] = NULL;
for (int j = 0; j < numProcesses; ++j) {
if (i != j) {
struct Node* current = rag[j];
while (current != NULL) {
if (current->id == i) {
// Process i is waiting for process j
struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
newNode->id = j;
newNode->next = wfg[i];
wfg[i] = newNode;
break;
current = current->next;
// Convert RAG to WFG using adjacency matrix
void convertToWaitForGraphAdjMatrix(bool rag[][MAX_NODES], bool wfg[][MAX_NODES], int
numProcesses) {
for (int i = 0; i < numProcesses; ++i) {
for (int j = 0; j < numProcesses; ++j) {
if (i != j && rag[j][i]) {
// Process i is waiting for process j
wfg[i][j] = true;
} else {
wfg[i][j] = false;
}
LAB – 10
Inter process Communication – Semaphore
Implement the solution for Bounded Buffer (Producer-Consumer) problem using inter process
communication technique – Semaphores.
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
#define BUFFER_SIZE 5
sem_t empty, full;
pthread_mutex_t mutex;
int buffer[BUFFER_SIZE];
int in = 0, out = 0;
void *producer(void *arg) {
int item;
while (1) {
item = rand() % 100; // Produce random item
sem_wait(&empty);
pthread_mutex_lock(&mutex);
buffer[in] = item;
printf("Produced: %d\n", item);
in = (in + 1) % BUFFER_SIZE;
pthread_mutex_unlock(&mutex);
sem_post(&full);
sleep(1);
}
void *consumer(void *arg) {
int item;
while (1) {
sem_wait(&full);
pthread_mutex_lock(&mutex);
item = buffer[out];
printf("Consumed: %d\n", item);
out = (out + 1) % BUFFER_SIZE;
pthread_mutex_unlock(&mutex);
sem_post(&empty);
sleep(2);
int main() {
pthread_t producer_thread, consumer_thread;
sem_init(&empty, 0, BUFFER_SIZE);
sem_init(&full, 0, 0);
pthread_mutex_init(&mutex, NULL);
pthread_create(&producer_thread, NULL, producer, NULL);
pthread_create(&consumer_thread, NULL, consumer, NULL);
pthread_join(producer_thread, NULL);
pthread_join(consumer_thread, NULL);
sem_destroy(&empty);
sem_destroy(&full);
pthread_mutex_destroy(&mutex);
return 0;
II. Implement the solution for Readers-Writers problem using inter process communication
technique – Semaphores.
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
sem_t mutex, writeblock;
int data = 0, readcount = 0;
void *reader(void *arg) {
int f;
f = *((int *)arg);
sem_wait(&mutex);
readcount++;
if (readcount == 1)
sem_wait(&writeblock);
sem_post(&mutex);
printf("Data read by the reader%d is %d\n", f, data);
sleep(1);
sem_wait(&mutex);
readcount--;
if (readcount == 0)
sem_post(&writeblock);
sem_post(&mutex);
void *writer(void *arg) {
int f;
f = *((int *)arg);
sem_wait(&writeblock);
data++;
printf("Data written by the writer%d is %d\n", f, data);
sleep(1);
sem_post(&writeblock);
int main() {
pthread_t rtid[5], wtid[5];
int i;
sem_init(&mutex, 0, 1);
sem_init(&writeblock, 0, 1);
for (i = 0; i <= 2; i++) {
pthread_create(&wtid[i], NULL, writer, (void *)&i);
pthread_create(&rtid[i], NULL, reader, (void *)&i);
for (i = 0; i <= 2; i++) {
pthread_join(wtid[i], NULL);
pthread_join(rtid[i], NULL);
return 0;
}
III. Implement the solution for Dining-Philosopher problem using inter process communication
technique – Semaphores.
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.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];
void test(int phnum) {
if (state[phnum] == HUNGRY && state[LEFT] != EATING && state[RIGHT] != EATING) {
state[phnum] = EATING;
sleep(2);
printf("Philosopher %d takes fork %d and %d\n", phnum + 1, LEFT + 1, phnum + 1);
printf("Philosopher %d is Eating\n", phnum + 1);
sem_post(&S[phnum]);
void take_fork(int phnum) {
sem_wait(&mutex);
state[phnum] = HUNGRY;
printf("Philosopher %d is Hungry\n", phnum + 1);
test(phnum);
sem_post(&mutex);
sem_wait(&S[phnum]);
sleep(1);
void put_fork(int phnum) {
sem_wait(&mutex);
state[phnum] = THINKING;
printf("Philosopher %d putting fork %d and %d down\n", phnum + 1, LEFT + 1, phnum + 1);
printf("Philosopher %d is thinking\n", phnum + 1);
test(LEFT);
test(RIGHT);
sem_post(&mutex);
void *philosopher(void *num) {
while (1) {
int *i = num;
sleep(1);
take_fork(*i);
sleep(0);
put_fork(*i);
int main() {
int i;
pthread_t thread_id[N];
sem_init(&mutex, 0, 1);
for (i = 0; i < N; i++)
sem_init(&S[i], 0, 0);
for (i = 0; i < N; i++) {
pthread_create(&thread_id[i], NULL, philosopher, &phil[i]);
printf("Philosopher %d is thinking\n", i + 1);
for (i = 0; i < N; i++)
pthread_join(thread_id[i], NULL);
}
LAB – 11
FORK and JOIN construct
I. Write a program where parent process take average of the odd numbers and child
process will take the average of even numbers present in a given Aadhar number of a
person. Use FORK and JOIN construct.
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
void calculate_average(int numbers[], int size, int is_odd) {
int sum = 0, count = 0;
for (int i = 0; i < size; i++) {
if ((numbers[i] % 2 == 0 && !is_odd) || (numbers[i] % 2 != 0 && is_odd)) {
sum += numbers[i];
count++;
}
}
if (count > 0) {
float average = (float)sum / count;
printf("Average of %s numbers: %.2f\n", (is_odd ? "odd" : "even"), average);
} else {
printf("No %s numbers found.\n", (is_odd ? "odd" : "even"));
}
}
int main() {
int numbers[] = {12, 23, 34, 45, 56, 67, 78, 89, 90, 101};
int size = sizeof(numbers) / sizeof(numbers[0]);
pid_t pid = fork();
if (pid == 0) { // Child process
calculate_average(numbers, size, 0); // Calculate average of even numbers
} else if (pid > 0) { // Parent process
wait(NULL); // Wait for child process to finish
calculate_average(numbers, size, 1); // Calculate average of odd numbers
} else { // Fork failed
fprintf(stderr, "Fork failed\n");
return 1;
}
return 0;
}
II. II. Write a program where parent process finds additive primes and child process finds
circular prime for a given prime list array. Use FORK and JOIN construct.
#include <stdio.h>
#include <unistd.h>
#include <stdbool.h>
#include <sys/wait.h>
bool is_prime(int num) {
if (num <= 1)
return false;
for (int i = 2; i * i <= num; i++) {
if (num % i == 0)
return false;
}
return true;
}
bool is_circular_prime(int num) {
int temp = num, digit_count = 0;
while (temp > 0) {
digit_count++;
temp /= 10;
}
temp = num;
for (int i = 0; i < digit_count; i++) {
if (!is_prime(temp))
return false;
int last_digit = temp % 10;
temp = last_digit * (int)(pow(10, digit_count - 1)) + temp / 10;
}
return true;
}
void find_primes(int prime_list[], int size, int is_additive_prime) {
for (int i = 0; i < size; i++) {
if ((is_prime(prime_list[i]) && is_additive_prime) || (is_circular_prime(prime_list[i])
&& !is_additive_prime))
printf("%d is %s prime.\n", prime_list[i], (is_additive_prime ? "additive" :
"circular"));
}
}
int main() {
int prime_list[] = {7, 11, 13, 17, 23, 29, 31, 37, 41, 47};
int size = sizeof(prime_list) / sizeof(prime_list[0]);
pid_t pid = fork();
if (pid == 0) { // Child process
find_primes(prime_list, size, 0); // Find circular primes
} else if (pid > 0) { // Parent process
wait(NULL); // Wait for child process to finish
find_primes(prime_list, size, 1); // Find additive primes
} else { // Fork failed
fprintf(stderr, "Fork failed\n");
return 1;
}
return 0;
}