# Recitation 5 ## Topics Today * fork(), exec() * dup(), pipe() ## fork(), exec() ### fork() `fork()` create a new process by duplicating the existing (parent) process. ```c= #include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <sys/wait.h> int main() { pid_t pid = fork(); if (pid == 0) { // Child process printf("Child process\n"); sleep(3000); } else if (pid > 0) { wait(NULL); // Parent process printf("Parent process\n"); } else { // Forking error perror("Fork failed"); } return 0; } ``` We can use `pgrep -a "fork"` to view the process. ### exec() `exec()` is used to replace the current process image with a new one, the child process often calls one of the exec() functions to run a different program. ```c= #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/wait.h> int main() { pid_t pid = fork(); if (pid == 0) { // Child process execl("/bin/ls", "ls", "-l", NULL); // The following code will not be reached if execl is successful printf("Child process\n"); sleep(3); } else if (pid > 0) { // Parent process wait(NULL); // Wait for the child to finish printf("Parent process\n"); } else { // Forking error perror("Fork failed"); } return 0; } ``` ## dup(), pipe() `pipe()` creates an interprocess communication (IPC) channel between two processes. It establishes a unidirectional communication channel, allowing one process to write to the pipe, and the other process to read from it. Pipes are often used for communication between parent and child processes. ```c= #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/wait.h> int main() { int pipe_fd[2]; //creation failed if (pipe(pipe_fd) == -1) { perror("Pipe creation failed"); exit(EXIT_FAILURE); } pid_t pid = fork(); if (pid == -1) { perror("Fork failed"); exit(EXIT_FAILURE); } else if (pid == 0) { // child process close(pipe_fd[0]); // close read // write to pipe char message[] = "Hello from child!"; write(pipe_fd[1], message, strlen(message) + 1); close(pipe_fd[1]); // close write exit(EXIT_SUCCESS); } else { wait(NULL); // wait for child // father process close(pipe_fd[1]); // close write // read from pipe char buffer[100]; read(pipe_fd[0], buffer, sizeof(buffer)); printf("Parent received: %s\n", buffer); close(pipe_fd[0]); // close read } return 0; } ``` - `pipe_fd[0]` is the file descriptor for the read end of the pipe. - `pipe_fd[1]` is the file descriptor for the write end of the pipe. `dup()` duplicates file descriptors, takes an old file descriptor and returns a new file descriptor. ```c= #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <sys/types.h> #include <sys/wait.h> int main() { int pipe_fd[2]; // File descriptors for the pipe int file_fd; // File descriptor for the text file // Create a pipe if (pipe(pipe_fd) == -1) { perror("Pipe creation failed"); exit(EXIT_FAILURE); } // Open a text file for reading in the child process file_fd = open("input.txt", O_RDONLY); if (file_fd == -1) { perror("Error opening input file"); exit(EXIT_FAILURE); } pid_t pid = fork(); if (pid == 0) { // Child process // Close the read end of the pipe since the child is writing close(pipe_fd[0]); // Duplicate the write end of the pipe to stdout dup2(pipe_fd[1], STDOUT_FILENO); // Now, stdout is redirected to the write end of the pipe // Read from the file and write to stdout (which is redirected to the pipe) char buffer[256]; ssize_t bytesRead; while ((bytesRead = read(file_fd, buffer, sizeof(buffer))) > 0) { write(STDOUT_FILENO, buffer, bytesRead); } // Close the write end of the pipe close(pipe_fd[1]); // Close the file descriptor for the input file close(file_fd); exit(EXIT_SUCCESS); } else if (pid > 0) { // Wait for the child to finish wait(NULL); // Parent process // Close the write end of the pipe since the parent is reading close(pipe_fd[1]); // Open a text file for writing in the parent process file_fd = open("output.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644); if (file_fd == -1) { perror("Error opening output file"); exit(EXIT_FAILURE); } // Duplicate the read end of the pipe to stdin dup2(pipe_fd[0], STDIN_FILENO); // Now, stdin is redirected to the read end of the pipe // Read from stdin (which is redirected to the pipe) and write to the file char buffer[256]; ssize_t bytesRead; while ((bytesRead = read(STDIN_FILENO, buffer, sizeof(buffer))) > 0) { write(file_fd, buffer, bytesRead); } // Close the file descriptor for the output file close(file_fd); // Close the read end of the pipe close(pipe_fd[0]); exit(EXIT_SUCCESS); } else { perror("Fork failed"); exit(EXIT_FAILURE); } return 0; } ``` `dup2()` vs `dup()` - `dup()` assigns the lowest available file descriptor to the new file descriptor. - `dup2()` allows you to specify the desired file descriptor number (newfd). If newfd is already open, it is closed before the duplication. ```c= #include <stdio.h> #include <unistd.h> int main() { int old_fd = 2; // File descriptor for stdout int new_fd; // Using dup() new_fd = dup(old_fd); printf("dup: new_fd = %d\n", new_fd); // Using dup2() int target_fd = 100; new_fd = dup2(old_fd, target_fd); printf("dup2: new_fd = %d\n", new_fd); return 0; } ```