The PIPEX project from 42 aims to recreate the behavior of the shell pipeline operator (|). In other words, you will create a program that links the output of one command to the input of another, just like when using the terminal command:
ls -l | grep "txt"Create a C program that executes:
./pipex file1 "cmd1" "cmd2" file2This should replicate the following shell command:
< file1 cmd1 | cmd2 > file2In other words:
1๏ธโฃ Read data from file1
2๏ธโฃ Execute cmd1 using file1 as input
3๏ธโฃ Pass the output of cmd1 as input to cmd2
4๏ธโฃ Save the final result in file2
- Use
open()to openfile1(read) andfile2(write).
- Use
pipe()to create a communication channel between processes.
- Use
fork()to create two child processes.
- The first process redirects
file1to the command input. - The second process redirects the output of the first command to the input of the second.
- Use
execve()to replace the current process with the desired command.
- Use
close()andwaitpid()to ensure everything finishes correctly.
Opens files for reading or writing. It returns a file descriptor or -1 if an error occurs.
int fd = open("file.txt", O_RDONLY);
if (fd == -1) {
perror("open failed");
}Creates a unidirectional data channel between two processes. It returns 0 on success and -1 on failure.
int fd[2];
if (pipe(fd) == -1) {
perror("pipe failed");
}Creates a new child process. Returns 0 in the child process and the child's PID in the parent.
pid_t pid = fork();
if (pid == -1) {
perror("fork failed");
}Duplicates a file descriptor to another, redirecting standard input or output.
int fd = open("input.txt", O_RDONLY);
dup2(fd, STDIN_FILENO);
close(fd);Replaces the current process with a new program. It requires the full path of the executable and an argument list.
char *args[] = {"/bin/ls", "-l", NULL};
execve(args[0], args, envp);
perror("execve failed");Closes a file descriptor, releasing system resources.
close(fd);Waits for a specific child process to terminate, ensuring proper process synchronization.
int status;
pid_t pid = waitpid(child_pid, &status, 0);
if (pid == -1) {
perror("waitpid failed");
}# ./pipex infile cmd1 cmd2 outfile
pipe()
|
|-- fork()
|
|-- child // cmd1
: |--dup2()
: |--close end[0]
: |--execve(cmd1)
:
|-- parent // cmd2
|--dup2()
|--close end[1]
|--execve(cmd2)pipe()creates a communication channel, allowing data to be transferred from one process to another.fork()generates a new child process, so we can run two commands within a single program.- Child Process:
- Uses
dup2()to redirect input/output. - Closes the read end of the pipe.
- Executes
cmd1withexecve().
- Uses
- Parent Process:
- Uses
dup2()to set up input/output redirection. - Closes the write end of the pipe.
- Executes
cmd2withexecve().
- Uses
For the child process:
infilebecomesstdin(input)end[1]becomesstdout(writescmd1output toend[1])
For the parent process:
end[0]becomesstdin(readscmd1output fromend[1])outfilebecomesstdout(writescmd2output tooutfile)
infile outfile
as stdin for cmd1 as stdout for cmd2
| PIPE โ
| |---------------------------| |
โ | | |
cmd1 --> end[1] โ end[0] --> cmd2
| |
cmd1 |---------------------------| end[0]
output reads end[1]
is written and sends cmd1
to end[1] output to cmd2
(end[1] becomes (end[0] becomes
cmd1 stdout) cmd2 stdin)
end[1]is the child process,end[0]is the parent process.- The child writes, and the parent reads.
- Since for something to be read, it must be written first,
cmd1is executed by the child, andcmd2by the parent. pipexis run like this:./pipex infile cmd1 cmd2 outfile.- FDs
0,1, and2are by default assigned to stdin, stdout, and stderr. infile,outfile, the pipe, stdin, and stdout are all FDs.
ls -la /proc/$$/fd -----------------
0 | stdin |
-----------------
1 | stdout |
-----------------
2 | stderr |
-----------------
3 | infile | // open()
-----------------
4 | outfile | // open()
-----------------
5 | end[0] |
-----------------
6 | end[1] |
-----------------
The PIPEX project was an excellent opportunity to dive deep into how pipes, processes, and input/output redirection work in Unix-like systems. With this project, I replicated the behavior of the shell pipeline operator (|), allowing the output of one command to be connected to the input of another, similar to the following shell command:
What I learned:
๐ ๏ธ Manipulating pipes for inter-process communication.
๐ง Using fork() to create child processes and execve() to execute commands.
๐ Redirecting input and output with dup2().
๐ Finding commands on the system using environment variables.
This project solidified my understanding of processes and pipes in C, providing me with a strong foundation to work on more complex system-level implementations in C. ๐ป
The application of execve() also helped me improve command handling and better understand the importance of file structures and process management.
It was an incredible challenge that taught me a lot about how operating systems handle inter-process communication! ๐ฅ
Now, I can apply this knowledge to more advanced projects and gain a better understanding of how operating system programs work day-to-day! ๐
Foi um desafio incrรญvel que me ensinou muito sobre como os sistemas operacionais lidam com comunicaรงรฃo entre processos! ๐ฅ
Agora, posso aplicar esse conhecimento em projetos mais avanรงados e entender melhor como programas de sistemas operacionais funcionam no dia a dia! ๐
Pronto! Vocรช pode copiar isso diretamente para a seรงรฃo de conclusรฃo do seu GitHub. Se precisar de mais algum ajuste, รฉ sรณ avisar!