OS Lab Report: Shell Scripting and IPC
OS Lab Report: Shell Scripting and IPC
The 'fork()' system call in C is crucial for process creation and management as it creates a new child process by duplicating the calling process. This is evident in examples where it's used to create multiple child processes, each with its own process ID (PID) but sharing the same code as the parent. Programs that utilize 'fork()' demonstrate how each call results in a child process that can execute different branches of code, handle unique tasks, or collaborate with other child processes through shared memory or pipes. In more complex applications, 'fork()' is combined with other system calls like 'exec()' to replace the child process's memory space, or 'wait()' to manage and synchronize the completion of child processes, preventing zombie processes (i.e., defunct processes lingering after termination).
The Operating Systems lab assignments cover a range of system programming tasks that build students' skills progressively. They are structured as follows: Assignment 0 focuses on exploring terminal commands in UNIX and Windows, while Assignment 1 introduces basic shell programming, including arithmetic operations, conditionals, loops, and data structures like arrays. Later assignments delve into complex topics such as inter-process communication, file descriptor management, fork and exec programming, and CPU scheduling algorithms. Specific tasks include creating and verifying directories, process management through fork(), exec(), and vfork(), handling child processes with wait(), and demonstrating inter-process communication via pipes, FIFOs, and message queues. Advanced topics like deadlock prevention using Bankers Algorithm and page replacement strategies are also covered .
Using 'dup()' and 'dup2()' for file descriptor management in systems programming effectively allows for resource sharing and redirection in UNIX-like operating systems. 'dup()' creates a copy of a file descriptor, which is useful for writing to the same file with different handlers, as seen in examples where both the original and duplicated file descriptors are used to write distinct messages to the same file. 'dup2()', on the other hand, offers more control by allowing a programmer to specify the new file descriptor number, which is particularly beneficial for redirecting standard outputs (such as stdout) to a file, enabling applications like logging or redirecting outputs in shell scripts. These methods streamline the manipulation and control of I/O operations, making them integral to building flexible and efficient scripts and programs .
Semaphores play a critical role in solving the basic producer-consumer problem by providing a signaling mechanism to synchronize access to a shared buffer. They ensure that the producer does not add more items than the buffer can hold and that the consumer does not attempt to consume items from an empty buffer. Semaphores are implemented in a program as counters that track the number of available resources and usage permissions. By utilizing two semaphores—one for tracking filled slots (items) and another for tracking empty slots in the buffer—proper coordination between producer and consumer threads is achieved without data races. The producer must wait for the presence of available empty slots before producing, while the consumer waits for filled slots to become available before consuming, thereby maintaining flow control and data integrity .
The main memory allocation methods for fixed partitioning include First Fit, Best Fit, Worst Fit, and Next Fit, each differing in their approach to managing memory. First Fit allocates the first available partition large enough to accommodate a process, optimizing speed as it doesn't require searching the entire list. Best Fit seeks the smallest available partition that fits, aiming to minimize wasted space, but potentially increasing fragmentation and allocation time. Worst Fit allocates the largest available partition to a process, maximizing leftover space but often leading to inefficient space utilization and increased fragmentation. Next Fit operates similarly to First Fit but resumes searching from the last allocated block, balancing speed with more even distribution of free memory blocks. Each method offers trade-offs between speed of allocation, memory waste, and fragmentation .
The 'wait()' system call is essential when a parent process must ensure the completion of its child processes before proceeding, particularly in scenarios where resource cleanup and process synchronization are critical. By calling 'wait()', a parent process effectively suspends execution until a child process exits, allowing the system to reclaim resources used by the child. This process not only facilitates synchronization between parent and child processes but also prevents the occurrence of zombie processes, which happen when a terminated child's exit status remains unread by the parent. Without 'wait()', these defunct processes would continue to occupy the process table, leading to resource leakage and system instability over time .
The 'vfork()' system call offers performance advantages over 'fork()' in process creation by allowing the child process to share the parent's address space until exec() or _exit() is called, which can significantly reduce overhead and resource usage for processes that are about to execute a new program. This makes 'vfork()' particularly efficient for creating child processes that immediately call exec(). However, 'vfork()' comes with potential drawbacks, such as the parent process being suspended until the child process executes an exec() or _exit(), and any modification of shared data can lead to undefined behavior. Hence, 'vfork()''s efficient use is limited to scenarios where the immediate execution of a new program follows process creation, minimizing risks associated with shared address space .
Message queues facilitate inter-process communication (IPC) by providing a robust framework for sending and receiving messages between processes, even without a direct parent-child relationship. Unlike pipes, which are limited to byte streams and require a direct linkage between processes, message queues allow discrete messages to be exchanged, offering higher-level communication semantics. Compared to shared memory that necessitates synchronization mechanisms to avoid race conditions, message queues inherently manage message order and delivery, reducing the overhead of explicit data management. Message queues support asynchronous process communication and come with queuing and prioritization capabilities, making them more suitable for complex and scalable systems requiring reliable and ordered message exchange .
Mutexes, or mutual exclusion locks, are used in multi-threaded programs to ensure that only one thread can access a shared resource at a time, effectively preventing data corruption due to concurrent read/write operations. By locking before accessing the resource and unlocking afterward, threads synchronize their execution—this ensures data integrity. Mutexes are particularly useful in scenarios involving critical sections, where inconsistent data states caused by simultaneous thread operations could corrupt the shared state. In the example of a counter shared by two threads, a mutex around increment operations ensures that each update is atomic, maintaining the correct final count after all operations complete .
Shell scripting is a powerful tool for performing arithmetic operations and managing arrays, as demonstrated in the examples. Arithmetic operations in shell scripts are executed using the '$((expression))' construct, which supports basic operations like addition, multiplication, subtraction, and division. For instance, the computation of sums and products of variables is straightforward and doesn't require external tools. Shell scripts also support array manipulation, where arrays are defined using parentheses, and their elements can be accessed or modified using index notation. Operations on arrays include retrieving all elements, accessing specific indices, getting array length, and substituting values with alternative text. These capabilities make shell scripting a versatile solution for small-scale data processing and task automation .