Pipe
- A type of IPC
- Only used for related processes
How to transfer data between parent and child using pipe.
Fig 1. Example of exchanging data
A pipe is created by pipe() function and provides a one-way flow of data
int fd[2]; pipe(fd);
Return two file descriptors:
fd[0]: open for reading
fd[1]: open for writing
Pipe is used to communicate between related processes, which have a common parent.
Typically, it is used between a parent and a child.
Parent create a pipe first, then fork() to create child.
Fig 2. a pipe in single process
Fig 3. Pipe, immediately after fork
Next, to transfer one-way data from parent to child.
parent closes read end of pipe - fd(0)
child closes write end of pipe - fd(1).
Fig 4. Transfer data from parent to child
Otherwise, to transfer one-way data from child to parent.
parent closes write end of pipe - fd(1)
child closes read end of pipe - fd(0).
Fig 4. Transfer data from child to parent
int main(void) { int fd[2], nbytes; pid_t childpid; char string[] = "Hello, world!\n"; char readbuffer[80]; // create two descriptor fd[0], fd[1] // fd[0]: input side (read) // fd[1]: output side (write) // create 1 pipe -> one-drirection flow of data // create 2 pipe -> two-drirection flow of data pipe(fd); if((childpid = fork()) == -1) { perror("fork"); exit(1); } if(childpid == 0) { /* Child process closes up input side of pipe */ close(fd[0]); /* Send "string" through the output side of pipe */ write(fd[1], string, (strlen(string)+1)); exit(0); } else { /* Parent process closes up output side of pipe */ close(fd[1]); /* Read in a string from the pipe */ nbytes = read(fd[0], readbuffer, sizeof(readbuffer)); printf("Received string: %s", readbuffer); } return(0); }
Download above code.
Two transfer data in both ways, two pipes need to create. Process of exchanging data is similar to Fig. 1. We don't discuss about it here because we commonly handle this case using FIFO.
There is another way of pipe using popen(). We discuss it later.
FIFOs
- For Pipe, it have no name and can be used only between related processes.
- FIFO (fist in, first out) supports data transfer between unrelated processes
- It has pathname, and unrelated processes communicate via pathname.
- FIFO is called named pipe.
Illustrate data transfer between parent-child (compared it to Fig.1 in pipe part).
and model of one server-multiple clients using FIFO.
Create a fifo using mkfifo().
int mkfifo(const char *pathname, mode-t mode) ;
mode specifies the file permission bits.
Once a FIFO is created, it must be opened for reading or writing by open() function.
A FIFO can only opened either read-only or write-only, not be opened for read-write, because a FIFO is half-duplex.
a write() to a FIFO or pipe always appends the data. A read() returns beginning of the pipe or FIFO.
- Some properties of FIFO with regard to opening, reading, writing.
We can set O_NONBLOCK when open a FIFO.
writefd = open(FIFO1, 0-WRONLY I 0-NONBLOCK, 0);
Effect of O_NONBLOCK flag on FIFO is given on below table.
Below code illustrates implementation of a server and client using FIFO.
Detailed explanation will be updated soon.
Define header file. Download
#define SERVER_FIFO "/tmp/seqnum_sv" /* Well-known name for server's FIFO */ #define CLIENT_FIFO_TEMPLATE "/tmp/seqnum_cl.%ld" /* Template for building client FIFO name */ #define CLIENT_FIFO_NAME_LEN (sizeof(CLIENT_FIFO_TEMPLATE) + 20) /* Space required for client FIFO pathname (+20 as a generous allowance for the PID) */ struct request { /* Request (client --> server) */ pid_t pid; /* PID of client */ int seqLen; /* Length of desired sequence */ }; struct response { /* Response (server --> client) */ int seqNum; /* Start of sequence */ };
Client program. Download
static char clientFifo[CLIENT_FIFO_NAME_LEN]; static void /* Invoked on exit to delete client FIFO */ removeFifo(void) { unlink(clientFifo); } int main(int argc, char *argv[]) { int serverFd, clientFd; struct request req; struct response resp; if (argc > 1 && strcmp(argv[1], "--help") == 0) usageErr("%s [seq-len...]\n", argv[0]); /* Create our FIFO (before sending request, to avoid a race) */ umask(0); /* So we get the permissions we want */ snprintf(clientFifo, CLIENT_FIFO_NAME_LEN, CLIENT_FIFO_TEMPLATE, (long) getpid()); if (mkfifo(clientFifo, S_IRUSR | S_IWUSR | S_IWGRP) == -1 && errno != EEXIST) errExit("mkfifo %s", clientFifo); if (atexit(removeFifo) != 0) errExit("atexit"); /* Construct request message, open server FIFO, and send message */ req.pid = getpid(); req.seqLen = (argc > 1) ? getInt(argv[1], GN_GT_0, "seq-len") : 1; serverFd = open(SERVER_FIFO, O_WRONLY); if (serverFd == -1) errExit("open %s", SERVER_FIFO); if (write(serverFd, &req, sizeof(struct request)) != sizeof(struct request)) fatal("Can't write to server"); /* Open our FIFO, read and display response */ clientFd = open(clientFifo, O_RDONLY); if (clientFd == -1) errExit("open %s", clientFifo); if (read(clientFd, &resp, sizeof(struct response)) != sizeof(struct response)) fatal("Can't read response from server"); printf("%d\n", resp.seqNum); exit(EXIT_SUCCESS); }
Server program. Download
int main(int argc, char *argv[]) { int serverFd, dummyFd, clientFd; char clientFifo[CLIENT_FIFO_NAME_LEN]; struct request req; struct response resp; int seqNum = 0; /* This is our "service" */ /* Create well-known FIFO, and open it for reading */ umask(0); /* So we get the permissions we want */ if (mkfifo(SERVER_FIFO, S_IRUSR | S_IWUSR | S_IWGRP) == -1 && errno != EEXIST) errExit("mkfifo %s", SERVER_FIFO); serverFd = open(SERVER_FIFO, O_RDONLY); if (serverFd == -1) errExit("open %s", SERVER_FIFO); /* Open an extra write descriptor, so that we never see EOF */ dummyFd = open(SERVER_FIFO, O_WRONLY); if (dummyFd == -1) errExit("open %s", SERVER_FIFO); /* Let's find out about broken client pipe via failed write() */ if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) errExit("signal"); for (;;) { /* Read requests and send responses */ if (read(serverFd, &req, sizeof(struct request)) != sizeof(struct request)) { fprintf(stderr, "Error reading request; discarding\n"); continue; /* Either partial read or error */ } /* Open client FIFO (previously created by client) */ snprintf(clientFifo, CLIENT_FIFO_NAME_LEN, CLIENT_FIFO_TEMPLATE, (long) req.pid); clientFd = open(clientFifo, O_WRONLY); if (clientFd == -1) { /* Open failed, give up on client */ errMsg("open %s", clientFifo); continue; } /* Send response and close FIFO */ resp.seqNum = seqNum; if (write(clientFd, &resp, sizeof(struct response)) != sizeof(struct response)) fprintf(stderr, "Error writing to FIFO %s\n", clientFifo); if (close(clientFd) == -1) errMsg("close"); seqNum += req.seqLen; /* Update our sequence number */ } }
References
1. http://man7.org/linux/man-pages/man7/fifo.7.html
2. http://www.tldp.org/LDP/lpg/node15.html
3. Unix network programming, Vol.2
4. http://www.linuxjournal.com/article/2156?page=0,0
No comments:
Post a Comment