简体   繁体   中英

how is pipe uni-directional when we get two file descriptors by calling pipe() function in C

I am reading the documentation of pipe function and I have encountered this

pipe() creates a pipe, a unidirectional data channel that can be used for interprocess communication

But it returns two file descriptors. One for reading and one for writing. What am I missing?

You can view the two file descriptors as the ends of a pipe. You can only write into one end, and you can only read from the other. If you wanted two-way communication between two processes, you would need two pipes. Pipes are therefore unidirectional.

+----------+                   +----------+
|          |       pipe        |          |
|     fd1>=======================>fd2     |
|          |       --->        |          |
|          |                   |          |
|          |       pipe        |          |
|     fd3<=======================<fd4     |
|          |       <---        |          |
+----------+                   +----------+

In contrast, you can write to either end of a socket, and whatever you write to one end can be read by the other end. There are two streams of data, so a single pair of sockets would be sufficient for two-way communication between two processes. Sockets are therefore bidirectional.

+----------+                   +----------+
|          |      socket       |          |
|          |       --->        |          |
|     fd1=========================fd2     |
|          |       <---        |          |
|          |                   |          |
+----------+                   +----------+

A pipe is implemented as a FIFO buffer inside the Linux kernel .

Each end of the buffer corresponds to a file descriptor . See pipe(7) and read Advanced Linux Programming then syscalls(2)

For examples using pipe(2) look inside the source code of open source shells such as sash or GNU bash , and study their dynamic behavior with strace(1) and gdb(1) .

You may download then study and recompile the source code of open source software (perhaps using GCC as gcc -g -Wall ) from places like github or gitlab .

In simple cases, you might use popen(3) (whose source code is inside GNU glibc or musl-libc ). Don't forget to check for failure, and check somehow (or construct carefully) the command passed to popen

If you are more curious, study the source code of the Linux kernel and ask on kernelnewbies after having read a textbook about operating systems .

With linux pipes the child writes and the parent reads, or vice versa; meaning data can only flow one way. pipe() creates a pair of file descriptors: one pointing to the read end and one to the write end. Data written to the write end can be read by the read end, thus being unidirectional interprocess communication channel.

Sockets: socket is a duplex connection that you can send data in both directions; you can send data in the same socket you read from. You can close the socket in one direction and still send data in the other direction. Sockets configured as streams are bidirectional and control follows a client/server pattern.

Pipes provide a unidirectional interprocess communication channel . Like any other channels a pipe has a read end and a write end. Data written to the write end of a pipe can be read from the read end of the pipe, something like read and write offset in channels.

Here's the actual pipe system call: More info

/*
 * The sys-pipe entry.
 * Allocate an inode on the root device.
 * Allocate 2 file structures.
 * Put it all together with flags.
 */
pipe()
{
    register *ip, *rf, *wf;
    int r;

    ip = ialloc(rootdev);
    if(ip == NULL)
        return;
    rf = falloc();
    if(rf == NULL) {
        iput(ip);
        return;
    }
    r = u.u_ar0[R0];
    wf = falloc();
    if(wf == NULL) {
        rf->f_count = 0;
        u.u_ofile[r] = NULL;
        iput(ip);
        return;
    }
    u.u_ar0[R1] = u.u_ar0[R0]; /* wf's fd */
    u.u_ar0[R0] = r;           /* rf's fd */
    wf->f_flag = FWRITE|FPIPE;
    wf->f_inode = ip;
    rf->f_flag = FREAD|FPIPE;
    rf->f_inode = ip;
    ip->i_count = 2;
    ip->i_flag = IACC|IUPD;
    ip->i_mode = IALLOC;
}

The pipe call gives two file descriptors, pfd [0] for read and pfd [1] for write. You can write on pfd [1] and read what your wrote from pfd [0]. But there is no advantage in doing that. A pipe is meant for communication between two related processes. So, after creating the pipe, a fork is done and both file descriptors are available to parent and the child processes. One process uses pfd [1] for writing and the other process uses pfd [0] for read. And the communication takes place.

Suppose the parent process has been configured to write on pfd [1] and the child is configured to read on pfd [0]. Then, it is not possible for the parent process to read pfd [0] and the child process to write on pfd [1] and expect communication to take place, because there is only one data flow inside the pipe. This mimics the real life pipes used for watering the plants. Water flows only in one direction. As pointed out by @ikegami, if you need two way communication between processes, two pipes should be used.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM