# 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;
}
```