Skip to content

g0nca/pipex

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

13 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

๐Ÿš€ PIPEX Project

๐Ÿ“Œ What is PIPEX?

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"

๐Ÿ” Objective

Create a C program that executes:

./pipex file1 "cmd1" "cmd2" file2

This should replicate the following shell command:

< file1 cmd1 | cmd2 > file2

In 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


๐Ÿ› ๏ธ How to Implement - Step by Step

1๏ธโƒฃ Open the files

  • Use open() to open file1 (read) and file2 (write).

2๏ธโƒฃ Create a pipe

  • Use pipe() to create a communication channel between processes.

3๏ธโƒฃ Create processes

  • Use fork() to create two child processes.

4๏ธโƒฃ Redirect input and output

  • The first process redirects file1 to the command input.
  • The second process redirects the output of the first command to the input of the second.

5๏ธโƒฃ Execute the commands

  • Use execve() to replace the current process with the desired command.

6๏ธโƒฃ Close files and wait for processes

  • Use close() and waitpid() to ensure everything finishes correctly.

๐Ÿ”ง Essential Functions

๐Ÿ“‚ open()

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");
}

๐Ÿ”— pipe()

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");
}

๐Ÿ‘ถ fork()

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");
}

๐Ÿ”„ dup2()

Duplicates a file descriptor to another, redirecting standard input or output.

int fd = open("input.txt", O_RDONLY);
dup2(fd, STDIN_FILENO);
close(fd);

๐Ÿš€ execve()

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");

โŒ close()

Closes a file descriptor, releasing system resources.

close(fd);

๐Ÿ•’ waitpid()

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 Execution Flow

# ./pipex infile cmd1 cmd2 outfile
pipe()
 |
 |-- fork()
      |
      |-- child // cmd1
      :     |--dup2()
      :     |--close end[0]
      :     |--execve(cmd1)
      :
      |-- parent // cmd2
            |--dup2()
            |--close end[1]
            |--execve(cmd2)

Explanation:

  • 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 cmd1 with execve().
  • Parent Process:
    • Uses dup2() to set up input/output redirection.
    • Closes the write end of the pipe.
    • Executes cmd2 with execve().

๐Ÿ“Œ Swapping FDs with dup2()

For the child process:

  • infile becomes stdin (input)
  • end[1] becomes stdout (writes cmd1 output to end[1])

For the parent process:

  • end[0] becomes stdin (reads cmd1 output from end[1])
  • outfile becomes stdout (writes cmd2 output to outfile)

๐Ÿ” Visual Representation:

    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)

๐Ÿ“Œ File Descriptors (FDs)

  • 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, cmd1 is executed by the child, and cmd2 by the parent.
  • pipex is run like this: ./pipex infile cmd1 cmd2 outfile.
  • FDs 0, 1, and 2 are by default assigned to stdin, stdout, and stderr.
  • infile, outfile, the pipe, stdin, and stdout are all FDs.

๐Ÿ” Checking Open File Descriptors on Linux

ls -la /proc/$$/fd

๐Ÿ“Œ FD Table Representation

                           -----------------    
                 0         |     stdin     |  
                           -----------------    
                 1         |     stdout    |    
                           -----------------    
                 2         |     stderr    |  
                           -----------------
                 3         |     infile    |  // open()
                           -----------------
                 4         |     outfile   |  // open()
                           -----------------
                 5         |     end[0]    | 
                           -----------------
                 6         |     end[1]    |  
                           -----------------

๐Ÿš€ Conclusion

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!

About

๐Ÿ“Œ | pipex replicates the behavior of the Unix shell pipeline (|), allowing the output of one command to be used as the input for another. It demonstrates process creation, inter-process communication (IPC), and file handling using system calls.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors