免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 942 | 回复: 0
打印 上一主题 下一主题

Beginning Linux Programming (pipes) [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2009-05-24 08:36 |只看该作者 |倒序浏览
Pipes which allow more useful data to be exchanged between processes.

-------------------------------------------------------------------------------
★process pipes

#include
FILE* popen (const char *command, const char *open_mode) ;
int pclose (FILE *stream_to_close ) ;

open_mode must be either r or w ; This means that we cannt invoke another program and both read and write to it. If you want bidrectional communication using pipes, then the normal solution is to use two pipe, one for data flow in each direction.

"r" is open mode: output from the invoked program is made available to the invoking program and can be read from the file stream FILE* returned by popen, using the usual stdio library functions for reading.

"w" is open mode: the program can send the daa to invoked command with calls to fwrite.The invoked program can then read the data on its standard input.

pclose call will only return when the process started with popen finishes. If it's still running when pclose is called, the pclose callwill wait for the process to finish .

The pclose call normally returns the exit code of the process whoes file stream it is closing. If the invoking process has executed a wait statement before calling pclose, the exit code will be lost and pclose call will wait for the process to finish.

Simple example:
----------------------------------------
#include
#include
#include
#include
int main()
{
    FILE *read_fp;
    char buffer[BUFSIZ + 1];

    int chars_read;
    memset(buffer, '\0', sizeof(buffer));
    read_fp = popen("ps -ax", "r");
    if (read_fp != NULL) {
        chars_read = fread(buffer, sizeof(char), BUFSIZ, read_fp);
        while (chars_read > 0) {
            buffer[chars_read - 1] = '\0';
            printf("Reading:-\n %s\n", buffer);
            chars_read = fread(buffer, sizeof(char), BUFSIZ, read_fp);
        }
        pclose(read_fp);
        exit(EXIT_SUCCESS);
    }
    exit(EXIT_FAILURE);
}



-------------------------------------

#include
#include
#include
int main()
{
    FILE *write_fp;
    char buffer[BUFSIZ + 1];
    sprintf(buffer, "Once upon a time, there was...\n");
    write_fp = popen("od -c", "w");
    if (write_fp != NULL) {
       fwrite(buffer, sizeof(char), strlen(buffer), write_fp);
       pclose(write_fp);
        exit(EXIT_SUCCESS);
    }
    exit(EXIT_FAILURE);
}
the same effect : echo Once upon a time, there was... | od -c
------------------------------------
How popen is implemented
popen 的运行 首先要调用 shell sh. 然后将命令作为传递给shell的参数。 然后再开始运行命令。
this has two effects.

In Linux, all parameter expansion is done by the shell , so invoking the shell to parse the command string before the program is invoked allows any shell expansion, such as determining what files '*.c' actually refers to, to be done before the program starts. --> 对于可以执行复杂的shell命令非常有用。 就想是其他的函数 execl 等等。

不利于的一面是:a shell is invoked along with the requested program. Each call to popen then results in two extra processes being started , which makes the popen function a little more expensive in terms of system resources and invocation of the target command slower than it might otherwise have been .

--------------------------------------
★The Pipe Call
We now more on to look at the lower-level pipe function.

the pipe function has the following prototype:
#include
int pipe( int file_descriptor[2] );

pipe is passed (a pointer to) an array of two integer file descriptors. It fills the array with two new file descriptor and returns a zero. On failure, it returns
-1 and set errno.
errors defined: (you can find it on Linux manual pages)
EMFILE Too many file descriptors are in use by the process.
ENFILE The system file table is full.
EFAULT The file descriptor is not valid

Two file descripters returned are connected in a special way. Any dara written to file_descriptor[1] can be read back from file_descriptor[0]. The data is processed in a fist in , first out basis, usually abbreviated to FIFO.

Note That: it's important to realize that these are file descriptors, not file streams , so we must use the lower-level read and write calls to access the data, rather than fread and fwrite.

#include
#include
#include
#include
int main()
{
    int data_processed;
    int file_pipes[2];
    const char some_data[] = "123";
    char buffer[BUFSIZ + 1];
    memset(buffer, '\0', sizeof(buffer));
    if (pipe(file_pipes) == 0) {
        data_processed = write(file_pipes[1], some_data, strlen(some_data));
        printf("Wrote %d bytes\n", data_processed);
        data_processed = read(file_pipes[0], buffer, BUFSIZ);
        printf("Read %d bytes: %s\n", data_processed, buffer);
        exit(EXIT_SUCCESS);
    }
    exit(EXIT_FAILURE);
}

Note that the pipe has some internal buffering that stores the data in between the calls to write and read. file_descriptor[0] using to write and [1] using to read is undefined.

--------------------------------------------------
The read advantage of pipes comes when you wish to pass data between two processes. When a program creates a new process using fork call, file descriptors that were previously open remain open. By creating a pipe in the original process and then forking to create a new process, we can pass data from one process to the other down the pipe.

#include
#include
#include
#include
int main()
{
    int data_processed;
    int file_pipes[2];
    const char some_data[] = "123";
    char buffer[BUFSIZ + 1];
    pid_t fork_result;
    memset(buffer, '\0', sizeof(buffer));
    if (pipe(file_pipes) == 0) {
        fork_result = fork();
        if (fork_result == -1) {
            fprintf(stderr, "Fork failure");
            exit(EXIT_FAILURE);
        }

// We've made sure the fork worked, so if fork_result equals zero, we're in the child process.
       if (fork_result == 0) {
            data_processed = read(file_pipes[0], buffer, BUFSIZ);
            printf("Read %d bytes: %s\n", data_processed, buffer);
            exit(EXIT_SUCCESS);
        }

// Otherwise, we must be the parent process.
        else {
            data_processed = write(file_pipes[1], some_data,
                                   strlen(some_data));
            printf("Wrote %d bytes\n", data_processed);
        }

    }
    exit(EXIT_SUCCESS);
}

-----------------------------------------------------
★The parent and child process
The next step in our investigation of the pipe call is to allow the child process to be a different program from its parent , rather than just a different process running the same program.

#include
#include
#include
#include
int main()
{
    int data_processed;
    int file_pipes[2];
    const char some_data[] = "123";
    char buffer[BUFSIZ + 1];
    pid_t fork_result;
    memset(buffer, '\0', sizeof(buffer));
    if (pipe(file_pipes) == 0) {
        fork_result = fork();
        if (fork_result == (pid_t)-1) {
            fprintf(stderr, "Fork failure");
            exit(EXIT_FAILURE);
        }
        if (fork_result == 0) {
            sprintf(buffer, "%d", file_pipes[0]);
            (void)execl("pipe4", "pipe4", buffer, (char *)0);
            exit(EXIT_FAILURE);
        }
        else {
            data_processed = write(file_pipes[1], some_data,
                                   strlen(some_data));
            printf("%d - wrote %d bytes\n", getpid(), data_processed);
        }
    }
    exit(EXIT_SUCCESS);
}


/*pipe4.c*/
#include
#include
#include
#include
int main(int argc, char *argv[])
{
    int data_processed;
    char buffer[BUFSIZ + 1];
    int file_descriptor;
    memset(buffer, '\0', sizeof(buffer));
    sscanf(argv[1], "%d", &file_descriptor);
    data_processed = read(file_descriptor, buffer, BUFSIZ);
    printf("%d - read %d bytes: %s\n", getpid(), data_processed, buffer);
    exit(EXIT_SUCCESS);
}

(char* ) 0, which termninates the parameters.

----------------------------------------------------------
★Reading Closed Pipes
A read call will normally block; that is, it will cause the process to wait until data becomes available. If the other end of the pipe has been closed , then no process has the pipe open for writing, and the read blocks. Since this is not helpful, a reda on a pipe that is not open for writing returns zero rather than block. This allows the reading process to detect the pipe equivalent of end of file and act appropriately. Note that this isn't the same as reading an invalid file descriptor, which read considers an error and indicates by returning -1.

If we use a pipe across a fork call, there are two different file descriptors that we can use to wirte to the pipe: one in parent and one in child process . we must close the write file descriptors of pipe in both parent and child processes before the pipe is considered closed and a read call on the pipe will fail.


★Pipes Used as Standard Input and Output

Now that we know how to make a read on an empty pipe fail, we can look at a much cleaner method connecting two processes with a pipe.

Two closely related versions of dup :
#include
int dup(int  file_descriptor) ;
int dup2(int file_descriptor_one, int file_descriptor_two )

The purpose of dup call is to open a new file descriptor. a little like the open call, the difference is that the new file descriptor created by dup refers o the same file(or pipe) as an existing file descriptor.

Note: We can get the same effect as dup and dup2 by using the more general fcntl call, with a command F_DUPFD. Having said that, the dup call is easier to use because it's tailored specfically to the needs of creating duplicate file descriptors. It's also very commonly used, so you will find it more frequently in existing programs than fcntl and F_DUPFD.

The standard output file descriptor is always 0aand dup always returns a new descriptor 0 and then calling dup, the new file descriptor will have the number 0.

To look at how the state of the first four file descriptors changes during the sequence:
File descriptor number Initially       After close           After dup
0               Standard input            Pipe             file descriptor
1               Standard output        Standard output     Standard output
2               Standard error         Standard error      Standard error
3              Pipe file descriptor  Pipe file descriptor  Pipe file descriptor

#include
#include
#include
#include
int main()
{
    int data_processed;
    int file_pipes[2];
    const char some_data[] = "123";
    pid_t fork_result;
    if (pipe(file_pipes) == 0) {
        fork_result = fork();
        if (fork_result == (pid_t)-1) {
            fprintf(stderr, "Fork failure");
            exit(EXIT_FAILURE);
        }
        if (fork_result == (pid_t)0) {
            close(0);
            dup(file_pipes[0]);
            close(file_pipes[0]);
            close(file_pipes[1]);

            execlp("od", "od", "-c", (char *)0);
            exit(EXIT_FAILURE);
        }
        else {
            close(file_pipes[0]);
            data_processed = write(file_pipes[1], some_data,
                                   strlen(some_data));
            close(file_pipes[1]);
            printf("%d - wrote %d bytes\n", (int)getpid(), data_processed);
        }
    }
    exit(EXIT_SUCCESS);
}
___________   File_pipes[0]                File_pipes[0]            __________
|           |     read                            read               |         |
|  Parent   |                      |  Pipe  |                        | child   |
|___________|     write                           write              |_________|
               File_pipes[1]                 File_pipes[1]





___________                                 standard input          __________
|           |                      |              read               |         |
|  Parent   |                      |  Pipe  |                        | child   |
|___________|     write                     |                        |_________|
                    
               standard output
                     ------------------>


----------------------------------------------------------------------------------
★Named Pipes: FIFOs
To exchange data between unrelated processe. We do this with FIFOs, often referred to as named pipes.

We create pipe files: mknod filename p, However, the mknod command is not in the X/Open command list, so it may not be available on all UNIX-like systems. The preferred command line method is to use mkfifo filename

two different calls :
#include
#include

int mkfifo( const char *filename , mode_t mode ) ;
int mknod ( const char *filename , mode_t mode |S_IFIFO, (dev_t) 0 ) ;
mknod 可以创建很多类型的文件, 用(dev_t) 0 和 S_IFIFO 就是为了创建pipe 文件

Note that: Unlike a pipe created with the pipe call, a FIFO exists as a named file, not as an open file descriptor, and it mist be opened before it can be read from and written to.

open( const char *path, O_RDONLY ) ;
In this case, the open call will block, i.e. not return until a process opens the same FIFO for writing. This is like the first cat example above.
open(const char *path, O_RDONLY | O_NONBLOCK);
The open call will now succeed and return immediately, even if the FIFO had not been opened for writing by any process.
open(const char *path, O_WRONLY);
In this case, the open call will block until a process opens the same FIFO for reading.

open(const char *path, O_WRONLY | O_NONBLOCK);
This will always return immediately, but if no process has the FIFO open for reading, open will return an error, −1, and the FIFO won't be opened. If a process does have the FIFO open for reading, the file descriptor returned can be used for writing to the FIFO.


#include
#include
#include
#include
#include
#include
#include
#define FIFO_NAME "/tmp/my_fifo"
int main(int argc, char *argv[])
{
    int res;
    int open_mode = 0;
    int i;
    if (argc \n", *argv);
        exit(EXIT_FAILURE);
    }
// Assuming that the program passed the test, we now set the value of open_mode
// from those arguments.
    for(i = 1; i
// We now check whether the FIFO exists and create it if necessary.
// Then the FIFO is opened and output given to that effect while the program
// catches forty winks. Last of all, the FIFO is closed.
    if (access(FIFO_NAME, F_OK) == -1) {
        res = mkfifo(FIFO_NAME, 0777);
        if (res != 0) {
            fprintf(stderr, "Could not create fifo %s\n", FIFO_NAME);
            exit(EXIT_FAILURE);
        }

    }
    printf("Process %d opening FIFO\n", getpid());
    res = open(FIFO_NAME, open_mode);
    printf("Process %d result %d\n", getpid(), res);
    sleep(5);
    if (res != -1) (void)close(res);
    printf("Process %d finished\n", getpid());
    exit(EXIT_SUCCESS);
}
-------------------------------------------------------------------------------

进程间通信 使用管道:
#include
#include
#include
#include
#include
#include
#include
#include
#define FIFO_NAME "/tmp/my_fifo"
#define BUFFER_SIZE PIPE_BUF
#define TEN_MEG (1024 * 1024 * 10)
int main()
{
    int pipe_fd;
    int res;
    int open_mode = O_WRONLY;
    int bytes_sent = 0;
    char buffer[BUFFER_SIZE + 1];
    if (access(FIFO_NAME, F_OK) == -1) {
        res = mkfifo(FIFO_NAME, 0777);
        if (res != 0) {
            fprintf(stderr, "Could not create fifo %s\n", FIFO_NAME);
            exit(EXIT_FAILURE);
        }
    }
    printf("Process %d opening FIFO O_WRONLY\n", getpid());
    pipe_fd = open(FIFO_NAME, open_mode);
    printf("Process %d result %d\n", getpid(), pipe_fd);
    if (pipe_fd != -1) {
        while(bytes_sent
    printf("Process %d finished\n", getpid());
    exit(EXIT_SUCCESS);
}

----------------------------------------------
#include
#include
#include
#include
#include
#include
#include
#include
#define FIFO_NAME "/tmp/my_fifo"
#define BUFFER_SIZE PIPE_BUF
int main()
{
    int pipe_fd;
    int res;
    int open_mode = O_RDONLY;
    char buffer[BUFFER_SIZE + 1];
    int bytes_read = 0;
    memset(buffer, '\0', sizeof(buffer));
   
    printf("Process %d opening FIFO O_RDONLY\n", getpid());
    pipe_fd = open(FIFO_NAME, open_mode);
    printf("Process %d result %d\n", getpid(), pipe_fd);
    if (pipe_fd != -1) {
        do {
            res = read(pipe_fd, buffer, BUFFER_SIZE);
            bytes_read += res;
        } while (res > 0);
        (void)close(pipe_fd);
    }
    else {
        exit(EXIT_FAILURE);
    }
    printf("Process %d finished, %d bytes read\n", getpid(), bytes_read);
    exit(EXIT_SUCCESS);
}


---------------------------------------------------
★Advanced Topic: Client/Server Using FIFOs

多个用户发送数据给服务器
By opening the FIFO in blocking mode, the server and the clients will be blocked automatically as required.

/*client.h*/
#include
#include
#include
#include
#include
#include
#include
#include
#define SERVER_FIFO_NAME "/tmp/serv_fifo"
#define CLIENT_FIFO_NAME "/tmp/cli_%d_fifo"
#define BUFFER_SIZE 20
struct data_to_pass_st {
    pid_t  client_pid;
    char   some_data[BUFFER_SIZE - 1];
};

/*client.c*/

// Here's the client, client.c. The first part of this program opens the server FIFO,
// if it already exists, as a file. It then gets its own process ID, which forms some
// of the data that will be sent to the server. The client FIFO is created, ready for
// the next section.
#include "client.h"
#include
int main()
{
    int server_fifo_fd, client_fifo_fd;
    struct data_to_pass_st my_data;
    int times_to_send;
    char client_fifo[256];
    server_fifo_fd = open(SERVER_FIFO_NAME, O_WRONLY);
    if (server_fifo_fd == -1) {
        fprintf(stderr, "Sorry, no server\n");
        exit(EXIT_FAILURE);
    }
    my_data.client_pid = getpid();
    sprintf(client_fifo, CLIENT_FIFO_NAME, my_data.client_pid);
    if (mkfifo(client_fifo, 0777) == -1) {
        fprintf(stderr, "Sorry, can't make %s\n", client_fifo);
        exit(EXIT_FAILURE);
    }
// For each of the five loops, the client data is sent to the server.
// Then the client FIFO is opened (read-only, blocking mode) and the data read back.
// Finally, the server FIFO is closed and the client FIFO removed from memory.
    for (times_to_send = 0; times_to_send  0) {
                printf("received: %s\n", my_data.some_data);
            }
            close(client_fifo_fd);
        }
    }
    close(server_fifo_fd);
    unlink(client_fifo);
    exit(EXIT_SUCCESS);
}
------------------------------------------------------
/*server.c*/
#include "client.h"
#include
int main()
{
    int server_fifo_fd, client_fifo_fd;
    struct data_to_pass_st my_data;
    int read_res;
    char client_fifo[256];
    char *tmp_char_ptr;
    mkfifo(SERVER_FIFO_NAME, 0777);
    server_fifo_fd = open(SERVER_FIFO_NAME, O_RDONLY);
    if (server_fifo_fd == -1) {
        fprintf(stderr, "Server fifo failure\n");
        exit(EXIT_FAILURE);
    }
    sleep(10); /* lets clients queue for demo purposes */
    do {
       read_res = read(server_fifo_fd, &my_data, sizeof(my_data));
        if (read_res > 0) {
// In this next stage, we perform some processing on the data just read from the client.
// We convert all the characters in some_data to uppercase and combine the CLIENT_FIFO_NAME
// with the received client_pid.
            tmp_char_ptr = my_data.some_data;
            while (*tmp_char_ptr) {
                *tmp_char_ptr = toupper(*tmp_char_ptr);
                tmp_char_ptr++;
            }
            sprintf(client_fifo, CLIENT_FIFO_NAME, my_data.client_pid);
// Then we send the processed data back, opening the client pipe in write-only, blocking mode.
// Finally, we shut down the server FIFO by closing the file and then unlinking the FIFO.
            client_fifo_fd = open(client_fifo, O_WRONLY);
            if (client_fifo_fd != -1) {
                write(client_fifo_fd, &my_data, sizeof(my_data));
                close(client_fifo_fd);

            }
        }
    } while (read_res > 0);
    close(server_fifo_fd);
    unlink(SERVER_FIFO_NAME);
    exit(EXIT_SUCCESS);
}


本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u3/94518/showart_1941385.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP