Unit6 - Subjective Questions
CSE325 • Practice Questions with Detailed Answers
Define Inter-Process Communication (IPC). List and briefly explain three common IPC mechanisms supported by UNIX/Linux systems.
Inter-Process Communication (IPC) refers to a set of mechanisms that allow processes to manage shared data or synchronize their actions. The operating system provides these facilities to allow processes to communicate with one another.\n\nCommon IPC Mechanisms:\n1. Pipes: A unidirectional data channel that can be used for communication between related processes (e.g., parent and child). Data is written to the write-end and read from the read-end.\n2. Named FIFOs (First In, First Out): Similar to pipes but exist as a device file in the file system. This allows unrelated processes to communicate.\n3. Shared Memory: A region of memory that is mapped into the address space of more than one process. This is the fastest form of IPC as no kernel intervention is required for data passing once the memory is established.
Explain the pipe() system call with its syntax and return values. Describe the role of the file descriptors generated.
The pipe() system call creates a unidirectional data channel that can be used for inter-process communication.\n\nSyntax:\n\n\nParameters:\n fd[2]: An array of two integers where the file descriptors are stored.\n\nFile Descriptors:\n fd[0]: Represents the read end of the pipe.\n fd[1]: Represents the write end of the pipe.\n\nReturn Values:\n 0: Success. The pipe is created.\n* -1: Error. The pipe creation failed (errno is set appropriately).
Differentiate between Unnamed Pipes and Named FIFOs.
Differences between Unnamed Pipes and Named FIFOs:\n\n| Feature | Unnamed Pipe | Named FIFO |\n| :--- | :--- | :--- |\n| Existence | Exists only in memory (kernel buffer). | Exists as a special file in the file system. |\n| Scope | Can only be used between related processes (e.g., Parent-Child). | Can be used between unrelated processes (different terminals/applications). |\n| Creation | Created using the pipe() system call. | Created using mkfifo() or mknod(). |\n| Persistence | Ceases to exist when the process terminates. | Persists in the file system until explicitly deleted (e.g., via rm). |\n| Identification | Identified by file descriptors. | Identified by a filename/path. |
Describe the implementation of a Parent-Child communication system using an unnamed pipe where the parent writes a message and the child reads it. Include the necessary system call logic.
To implement communication between a Parent and Child using a pipe:\n\n1. Create Pipe: Call pipe(fd) before forking. fd is an integer array of size 2.\n2. Fork: Call fork() to create a child process. Both parent and child now have copies of the file descriptors.\n3. Parent Process (Writer):\n Close the read end: close(fd[0]).\n Write data: write(fd[1], message, length).\n Close the write end after finishing: close(fd[1]).\n4. Child Process (Reader):\n Close the write end: close(fd[1]).\n Read data: read(fd[0], buffer, size).\n Process/Print the data.\n * Close the read end: close(fd[0]).\n\nLogic:\nc\nint fd[2];\npipe(fd);\nif (fork() > 0) { // Parent\n close(fd[0]);\n write(fd[1], "Hello", 5);\n close(fd[1]);\n} else { // Child\n close(fd[1]);\n read(fd[0], buffer, sizeof(buffer));\n close(fd[0]);\n}\n
Why is Shared Memory considered the fastest IPC mechanism? Explain the concept.
Shared Memory is considered the fastest IPC mechanism because:\n\n1. Zero-Copying: Once the memory segment is mapped into the address space of the sharing processes, data transfer does not require the kernel to copy data between the process memory and the kernel buffer (unlike pipes or message queues).\n2. Direct Access: Processes read and write to the shared region as if it were standard local memory.\n\nConcept:\n A segment of physical memory is reserved.\n Multiple processes attach this segment to their virtual address space.\n Changes made by one process are instantly visible to others attached to the same segment.\n Note: Synchronization mechanisms (like Semaphores) are usually required to prevent race conditions during concurrent access.
Explain the mkfifo() system call. How is it different from the standard open() call?
mkfifo() System Call:\nIt is used to create a FIFO (First In, First Out) special file, also known as a named pipe.\n\nSyntax:\n\n\n pathname: The file system path where the FIFO is created.\n mode: Specifies the permissions (e.g., 0666) for the new FIFO, modified by the process's umask.\n\nDifference from open():\n mkfifo() creates the special device file in the filesystem but does not open it for reading or writing. It establishes the node in the directory structure.\n open() is subsequently used to access the existing FIFO for actual I/O operations. mkfifo will fail if the file already exists (unless handled), whereas open with O_CREAT creates standard files.
Detailed the syntax and usage of the shmget() system call. What is the significance of the IPC_CREAT flag?
shmget() (Shared Memory Get):\nThis system call is used to create a new shared memory segment or locate an existing one based on a key.\n\nSyntax:\n\n\nParameters:\n key: Unique identifier for the shared memory segment (often generated using ftok()).\n size: The size of the shared memory segment in bytes (rounded up to page size).\n* shmflg: Permission flags and control options.\n\nReturn Value:\nReturns the Shared Memory Identifier (shmid) on success, or -1 on failure.\n\nSignificance of IPC_CREAT:\nWhen passed in the shmflg argument (e.g., IPC_CREAT | 0666), this flag instructs the kernel to create a new shared memory segment associated with the given key if it does not already exist. If the segment exists, IPC_CREAT (without IPC_EXCL) simply returns the identifier of the existing segment.
Explain how processes attach and detach shared memory segments using shmat() and shmdt(). Provide their syntax.
1. shmat() (Shared Memory Attach):\nAttaches the shared memory segment identified by shmid to the address space of the calling process.\n\nSyntax:\n\n shmid: Identifier returned by shmget.\n shmaddr: Usually set to NULL (lets the system choose a suitable address).\n shmflg: Usually 0, or SHM_RDONLY for read-only access.\n Returns: A pointer to the shared memory segment.\n\n2. shmdt() (Shared Memory Detach):\nDetaches the shared memory segment from the address space of the calling process. It does not destroy the segment.\n\nSyntax:\n\n shmaddr: The pointer returned by shmat.\n Returns: 0 on success, -1 on failure.
Derive the logic to implement full-duplex (two-way) communication between a parent and a child process using pipes.
Standard pipes are half-duplex (unidirectional). To achieve full-duplex communication between a Parent (P) and Child (C), two pipes are required.\n\nLogic:\n1. Create Two Pipes:\n Pipe1: Parent writes, Child reads.\n Pipe2: Child writes, Parent reads.\n Code: int pipe1[2], pipe2[2]; pipe(pipe1); pipe(pipe2);\n2. Fork: Create the child process.\n3. Parent Process:\n Close unused ends: Close pipe1[0] (read) and pipe2[1] (write).\n Write to pipe1[1] to send data to Child.\n Read from pipe2[0] to receive data from Child.\n4. Child Process:\n Close unused ends: Close pipe1[1] (write) and pipe2[0] (read).\n Read from pipe1[0] to receive data from Parent.\n * Write to pipe2[1] to send data to Parent.\n\nThis setup ensures distinct channels for upstream and downstream data flow, preventing data collision or reading one's own output.
Discuss the shmctl() system call. How is it used to remove a shared memory segment?
shmctl() (Shared Memory Control):\nThis system call performs control operations on a shared memory segment.\n\nSyntax:\n\n\nParameters:\n shmid: Shared memory identifier.\n cmd: Command to perform (e.g., IPC_STAT, IPC_SET, IPC_RMID).\n* buf: Pointer to a shmid_ds structure (can be NULL for removal).\n\nRemoving a Segment:\nTo remove a segment, the command IPC_RMID is used.\nc\nshmctl(shmid, IPC_RMID, NULL);\n\nNote: The segment is marked for destruction. It is actually destroyed by the OS only after the last process attached to it has detached (reference count becomes 0).
What happens if a process attempts to read from a pipe that is empty? What happens if it writes to a pipe that is full?
Reading from an Empty Pipe:\n If the write-end of the pipe is still open by at least one process, the read() call will block (wait) until data is written to the pipe.\n If the write-end has been closed by all potential writers, the read() call returns 0 (indicating End-of-File/EOF).\n\nWriting to a Full Pipe:\n Pipes have a limited capacity (buffer size). If a process attempts to write to a full pipe, the write() call will block until sufficient space is cleared by a reader process reading data from the pipe.\n If the read-end is closed by all readers, a SIGPIPE signal is generated, typically terminating the writer process unless handled.
Compare Shared Memory and Message Passing (Pipes/Queues) in terms of implementation complexity and performance.
Comparison: Shared Memory vs. Message Passing\n\n1. Performance:\n Shared Memory: Faster. Communication occurs at memory speeds. System calls are only needed to set up the memory; data access incurs no kernel overhead.\n Message Passing (Pipes): Slower. Requires system calls (read/write) for every data exchange, involving data copying from user space to kernel buffer and back.\n\n2. Implementation/Complexity:\n Shared Memory: More complex. Since multiple processes access the same memory address, the programmer must explicitly handle synchronization (using semaphores or mutexes) to prevent race conditions.\n Message Passing: Simpler. The OS kernel handles the synchronization (e.g., blocking on read/write), ensuring atomic data transfer without explicit locking code from the user.
Describe the standard workflow for two unrelated processes to communicate using a Named FIFO.
To enable communication between two unrelated processes (e.g., Process A and Process B) using a Named FIFO:\n\n1. Creation: One process (or an external setup script) must create the FIFO using mkfifo("my_fifo", 0666). This creates a file in the filesystem.\n2. Process A (Writer):\n Opens the FIFO file using open("my_fifo", O_WRONLY).\n The open call blocks until Process B opens the FIFO for reading.\n Writes data using write().\n Closes the FIFO using close().\n3. Process B (Reader):\n Opens the FIFO file using open("my_fifo", O_RDONLY).\n The open call blocks until Process A opens the FIFO for writing.\n Reads data using read().\n Closes the FIFO using close().\n4. Cleanup: The FIFO file persists and should be removed using unlink("my_fifo") when no longer needed.
What is a Race Condition in the context of Shared Memory? How can it be prevented?
Race Condition:\nIn Shared Memory IPC, a race condition occurs when two or more processes attempt to access and manipulate the shared data concurrently. The final outcome of the data depends on the unpredictable timing or order of execution of the processes. For example, if two processes try to increment a shared counter simultaneously, one update might be lost.\n\nPrevention:\nTo prevent race conditions, access to the shared memory (specifically the Critical Section) must be synchronized. This is typically achieved using:\n1. Semaphores: Signaling mechanisms to control access.\n2. Mutexes (Mutual Exclusion Locks): Ensures only one process can access the shared resource at a time.
Explain the significance of ftok() in System V IPC (Shared Memory).
ftok() (File to Key):\nIn System V IPC (which includes Shared Memory), resources are identified by a unique integer key (key_t). ftok() is a library function used to generate this unique key systematically.\n\nSyntax:\n\n\nSignificance:\n It converts a file path (of an existing file) and a project identifier (an integer/character) into a unique System V IPC key.\n This allows unrelated processes to generate the same key independently, provided they agree on the pathname and proj_id.\n* This key is then passed to shmget() (or msgget, semget) to access the specific shared resource.
Develop a C program snippet to read a string from the user in a parent process and send it to the child process using a pipe. The child should print the received string.
Program Snippet:\n\nc\n#include <stdio.h>\n#include <unistd.h>\n#include <string.h>\n\nint main() {\n int fd[2];\n char buffer[100];\n pid_t p;\n\n if (pipe(fd) == -1) return 1; // Create Pipe\n\n p = fork();\n\n if (p > 0) { // Parent\n close(fd[0]); // Close read end\n printf("Enter string: ");\n scanf("%s", buffer);\n write(fd[1], buffer, strlen(buffer) + 1);\n close(fd[1]);\n } \n else { // Child\n close(fd[1]); // Close write end\n read(fd[0], buffer, 100);\n printf("Child received: %s\n", buffer);\n close(fd[0]);\n }\n return 0;\n}\n\nExplanation: The parent closes the read descriptor, gets input, and writes to fd[1]. The child closes the write descriptor, reads from fd[0], and prints the output.
Why is it important to close unused file descriptors in a pipe based implementation? What happens if the write end is not closed by the reader process?
Importance of Closing Unused Descriptors:\n1. EOF Detection: A process reading from a pipe will only receive an End-of-File (EOF) (return 0) when all open file descriptors referring to the write end of the pipe are closed. If a reader keeps its own write-end open (inherited via fork), read() will block indefinitely waiting for data, causing a deadlock.\n2. Resource Management: File descriptors are a limited resource in the OS. Keeping unused descriptors open can lead to resource exhaustion.\n\nScenario:\nIf a child process is the reader but fails to close fd[1] (the write end), the pipe's reference count for writers never drops to zero. Even after the parent closes its write end, the child's read() will wait forever, thinking a writer (itself) still exists.
Discuss the persistence of IPC objects. How does the persistence of a Pipe differ from that of a Shared Memory segment?
Persistence defines how long an IPC object exists in the system.\n\n1. Process Persistence (Pipes):\n Unnamed pipes exist only as long as the processes that created/opened them are running.\n Once the last file descriptor referring to the pipe is closed, the kernel destroys the pipe and reclaims the resources.\n\n2. Kernel Persistence (Shared Memory / Named FIFOs):\n Shared Memory: System V Shared Memory segments are kernel-persistent. They exist until they are explicitly removed (via shmctl with IPC_RMID or command ipcrm) or the system is rebooted. Even if the creating process dies, the segment remains in memory.\n Named FIFOs: Filesystem persistent. They exist as file entries until deleted (e.g., rm command), though the data inside is lost if no processes have it open.
Describe the limitations of Pipes as an IPC mechanism.
Limitations of Pipes:\n\n1. Unidirectional: Standard pipes are half-duplex. Data flows in only one direction. Full-duplex requires two pipes or specialized implementation.\n2. Related Processes Only (Unnamed): Unnamed pipes can only be used between processes that share a common ancestor (e.g., Parent-Child or siblings via fork()). Unrelated processes cannot share the file descriptors.\n3. Byte Stream: Pipes treat data as a continuous stream of bytes. There are no message boundaries (unlike Message Queues), so the application must implement its own protocol to distinguish messages.\n4. Buffer Size: Pipes have a limited buffer capacity (e.g., 64KB on Linux). If the buffer fills up, the writer blocks.
What are the command-line utilities used to list and remove IPC facilities (specifically Shared Memory) in Linux? Give examples.
In Linux, the ipcs and ipcrm utilities are used to manage System V IPC facilities.\n\n1. Listing IPCs (ipcs):\n To view information about active IPC facilities.\n Command: ipcs -m\n Effect: Lists active Shared Memory segments (Key, shmid, Owner, Perms, Bytes).\n\n2. Removing IPCs (ipcrm):\n To manually remove an IPC object from the system.\n Command: ipcrm -m <shmid>\n Example: If ipcs -m shows a segment with ID 12345: ipcrm -m 12345.\n* This is useful for cleaning up shared memory if a program crashes before calling shmctl(..., IPC_RMID, ...).