Translate

Chủ Nhật, 4 tháng 12, 2016

Pipe and FIFO

Created by Thomas

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 
?
1
2
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

Below example illustrates the process data transfer from child to parent. 
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
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(). 
?
1
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.

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. 
?
1
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
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#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
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
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
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
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

Không có nhận xét nào:

Đăng nhận xét