• 进程间通信第一课--管道


    一个进程连接数据流到另一个进程--管道--pipe

    进程管道

    1 #include <stdio.h>
    2 FILE * popen(const char * command, const char * open_mode)
    3 int pclose(FILE * stream_to_close);

    popen函数允许一个程序将另一个程序作为新进程来启动
    并可以传递数据给它或者通过它接收数据
    command是要运行的程序名和相应的参数
    open_mode必须是r或者是w
    r的情况是:被调用程序的输出可以由调用程序使用,调用程序可以利用流指针通过库函数读取被调用程序的输出
    w的情况是:调用程序可以用fwrite向被调用程序发送命令,被调用程序可以在自己的标准输入上读取这些数据,
        不会意识到自己正在从另外一个进程读取数据
    没有其他的open_mode


    函数失败时候返回的是空指针
    要是想实现双向管道,通常的解决方法是使用两个管道,每个管道负责一个方向的数据流


    pclose调用只在popen启动的进程结束才返回,要是仍在运行的话,就等待进程的结束
    返回的结果是关闭的文件流所在的进程的退出码
    如果调用进程在调用pclose之前就执行一个wait语句,被调用的进程的退出状态就会丢失。
    pclose将返回-1并设置为ECHILD

    读取外部程序的输出例程:

     1 #include <unistd.h>
     2 #include <stdlib.h>
     3 #include <stdio.h>
     4 #include <string.h>
     5 
     6 int main()
     7 {
     8     FILE *read_fp;
     9     char buffer[BUFSIZ + 1];
    10     int chars_read;
    11     memset(buffer, '', sizeof(buffer));
    12     read_fp = popen("uname -a", "r");//调用popen打开新的进程,返回流
    13     if (read_fp != NULL) {
    14         chars_read = fread(buffer, sizeof(char), BUFSIZ, read_fp);//从流中读取缓冲到buffer
    15         if (chars_read > 0) {
    16             printf("Output was:-
    %s
    ", buffer);//打印buffer
    17         }
    18         pclose(read_fp);//关闭打开的流
    19         exit(EXIT_SUCCESS);//成功退出
    20     }
    21     exit(EXIT_FAILURE);
    22 }

    执行效果:

    1 jason@t61:~/c_program/544977-blp3e/chapter13$ gcc popen1.c -o popen1
    2 jason@t61:~/c_program/544977-blp3e/chapter13$ ./popen1
    3 Output was:-
    4 Linux t61 3.19.0-21-generic #21-Ubuntu SMP Sun Jun 14 18:34:06 UTC 2015 i686 i686 i686 GNU/Linux
    5 
    6 jason@t61:~/c_program/544977-blp3e/chapter13$ 


    将输出送往popen:

     1 #include <unistd.h>
     2 #include <stdlib.h>
     3 #include <stdio.h>
     4 
     5 int main()
     6 {
     7     FILE *write_fp;
     8     char buffer[BUFSIZ + 1];
     9 
    10     sprintf(buffer, "Once upon a time, there was...
    ");
    11     write_fp = popen("od -c", "w");
    12     if (write_fp != NULL) {
    13         fwrite(buffer, sizeof(char), strlen(buffer), write_fp);//buffer写到文件描述符
    14         pclose(write_fp);
    15         exit(EXIT_SUCCESS);
    16     }
    17     exit(EXIT_FAILURE);
    18 }

    程序的效果等价于:

    1 echo "Once upon a time, there was ..."|od -c
    2 程序的执行效果是:
    3 jason@t61:~/c_program/544977-blp3e/chapter13$ ./popen2
    4 0000000   O   n   c   e       u   p   o   n       a       t   i   m   e
    5 0000020   ,       t   h   e   r   e       w   a   s   .   .   .  
    
    6 0000037

    使用ASCII码进行格式化输出,注意其中包括转义字符
    左侧的默认地址格式为八字节



    传递更多的数据


    我们上面实现的都是一次fread和fwrite调用来发送和接收
    有时我们希望能以块的方式发送数据,或者根本不知道输出数据的长度
    为了避免定义一个非常大的缓冲区,我们可以用多个fread和fwrite调用来按部分处理数据

    通过管道读取大量数据:

     1 #include <unistd.h>
     2 #include <stdlib.h>
     3 #include <stdio.h>
     4 #include <string.h>
     5 
     6 int main()
     7 {
     8     FILE *read_fp;
     9     char buffer[BUFSIZ + 1];
    10     int chars_read;
    11 
    12     memset(buffer, '', sizeof(buffer));
    13     read_fp = popen("ps -ax", "r");
    14     if (read_fp != NULL) {
    15         chars_read = fread(buffer, sizeof(char), BUFSIZ, read_fp);
    16         while (chars_read > 0) {
    17             buffer[chars_read - 1] = '';
    18             printf("Reading:-
     %s
    ", buffer);
    19             chars_read = fread(buffer, sizeof(char), BUFSIZ, read_fp);
    20         }
    21         pclose(read_fp);
    22         exit(EXIT_SUCCESS);
    23     }
    24     exit(EXIT_FAILURE);
    25 }

    函数的执行的结果:

     1 Reading:-
     2    PID TTY      STAT   TIME COMMAND
     3     1 ?        Ss     0:02 /sbin/init splash
     4     2 ?        S      0:00 [kthreadd]
     5     3 ?        S      0:01 [ksoftirqd/0]
     6 .
     7 .
     8 .
     9  2662 ?        Sl     0:01 nm-applet
    10  2663 ?        Sl     0:00 /usr/lib/policykit-1-gnome/polkit-gnome-authentication-agent-1
    11  2670 ?        S      0:00 /usr/lib/i386-linux-gnu/gconf/gconfd-2
    12  2682 ?        Sl     0:03 /usr/bin/chinese-calendar
    13  2688 ?        Sl     0:00 /usr/lib/gvfs/gvfs-udisks2-volume-monitor
    14  2690 ?        Ssl    0:03 /
    15 Reading:-
    16  sr/lib/udisks2/udisksd --no-debug
    17  2701 ?        Sl     0:00 /usr/lib/gvfs/gvfs-gphoto2-volume-monitor
    18  2705 ?        Sl     0:00 /usr/lib/gvfs/gvfs-mtp-volume-monitor
    19  2713 ?        Sl     0:00 /usr/lib/gvfs/gvfs-afc-volume-monitor
    20  2728 ?        Sl     0:00 /usr/lib/i386-linux-gnu/notify-osd
    21  2743 ?        Sl     1:55 sogou-qimpanel
    22  .
    23  .
    24  .
    25  //唯一要注意的是,linux会安排ps和popen3的读取,互相等待。。


     
     pipe调用

    1  #include <unistd.h>
    2  int pipe(int file_descriptor[2]);

     函数的参数是一个由两个整数类型的文件描述符的组成的数组的指针
     该函数在数组中填上两个新的文件描述符后返回0
     失败的话返回-1设置errno
         EMFILE->进程使用的文件描述符过多
         ENPFILE->系统的文件表已满
         EFAULT->文件描述符无效
    写到file_descriptor[1]的所有的数据都可以通过file_descriptor[0]读出
    加入写的是1,2,3那么读取的顺序也会是1,2,3遵循先进先出的FIFO原则
    这里使用的是文件描述符而不是文件流
    所以必须用底层的read和write调用来访问数据,而不是fread和fwrite
    pipe函数:

     1 #include <unistd.h>
     2 #include <stdlib.h>
     3 #include <stdio.h>
     4 #include <string.h>
     5 
     6 int main()
     7 {
     8     int data_processed;
     9     int file_pipes[2];
    10     const char some_data[] = "123";
    11     char buffer[BUFSIZ + 1];
    12 
    13     memset(buffer, '', sizeof(buffer));
    14 
    15     if (pipe(file_pipes) == 0) {
    16         data_processed = write(file_pipes[1], some_data, strlen(some_data));
    17         printf("Wrote %d bytes
    ", data_processed);
    18         data_processed = read(file_pipes[0], buffer, BUFSIZ);
    19         printf("Read %d bytes: %s
    ", data_processed, buffer);
    20         exit(EXIT_SUCCESS);
    21     }
    22     exit(EXIT_FAILURE);
    23 }//程序用两个文件描述符创建一个管道,用第二个文件描述符写数据,从第一个文件描述符读取数据
    24 //管道中有一些缓存区,在write和read调用之间保存数据

    程序的执行效果:

    1 jason@t61:~/c_program/544977-blp3e/chapter13$ ./pipe1
    2 Wrote 3 bytes
    3 Read 3 bytes: 123


    跨越fork调用的管道:

     1 #include <unistd.h>
     2 #include <stdlib.h>
     3 #include <stdio.h>
     4 #include <string.h>
     5 
     6 int main()
     7 {
     8     int data_processed;
     9     int file_pipes[2];
    10     const char some_data[] = "123";
    11     char buffer[BUFSIZ + 1];
    12     pid_t fork_result;
    13 
    14     memset(buffer, '', sizeof(buffer));
    15 
    16     if (pipe(file_pipes) == 0) {//创建管道成功
    17         fork_result = fork();//创建子进程
    18         if (fork_result == -1) {
    19             fprintf(stderr, "Fork failure");
    20             exit(EXIT_FAILURE);
    21         }//说明子进程创建成功
    22 
    23         if (fork_result == 0) {//说明在子进程中执行读取
    24             data_processed = read(file_pipes[0], buffer, BUFSIZ);
    25             printf("Read %d bytes: %s
    ", data_processed, buffer);
    26             exit(EXIT_SUCCESS);
    27         }
    28         else {//在父进程中执行写入
    29             data_processed = write(file_pipes[1], some_data,
    30                                    strlen(some_data));
    31             printf("Wrote %d bytes
    ", data_processed);
    32         }
    33     }
    34     exit(EXIT_SUCCESS);
    35 }//函数中,首先创建一个管道,接着创建新进程,父进程给管道写数据,子进程从管道读取数据
    36 //要是父进程在子进程之前退出的话,会在两部分内容之间看到shell提示符


    管道和exec函数的例程:


    //pipe3.c

     1 #include <unistd.h>
     2 #include <stdlib.h>
     3 #include <stdio.h>
     4 #include <string.h>
     5 
     6 int main()
     7 {
     8     int data_processed;
     9     int file_pipes[2];
    10     const char some_data[] = "123";
    11     char buffer[BUFSIZ + 1];
    12     pid_t fork_result;
    13 
    14     memset(buffer, '', sizeof(buffer));
    15 
    16     if (pipe(file_pipes) == 0) {//创建管道成功
    17         fork_result = fork();
    18         if (fork_result == (pid_t)-1) {
    19             fprintf(stderr, "Fork failure");
    20             exit(EXIT_FAILURE);
    21         }//创建进程成功
    22 
    23         if (fork_result == 0) {
    24             sprintf(buffer, "%d", file_pipes[0]);//子进程从pipe的输出读取数据到buffer中
    25             (void)execl("pipe4", "pipe4", buffer, (char *)0);//读到的数据换成由path=pipe4指定的路径新进程
    26             exit(EXIT_FAILURE);
    27         }
    28         else {//父进程写数据到pipe的输入
    29             data_processed = write(file_pipes[1], some_data,
    30                                    strlen(some_data));
    31             printf("%d - wrote %d bytes
    ", getpid(), data_processed);
    32         }
    33     }
    34     exit(EXIT_SUCCESS);
    35 }

    //pipe4.c

     1 #include <unistd.h>
     2 #include <stdlib.h>
     3 #include <stdio.h>
     4 #include <string.h>
     5 
     6 int main(int argc, char *argv[])//参数[0]是程序名。参数[1]是文件描述符
     7 {
     8     int data_processed;
     9     char buffer[BUFSIZ + 1];
    10     int file_descriptor;
    11 
    12     memset(buffer, '', sizeof(buffer));//分配内存
    13     sscanf(argv[1], "%d", &file_descriptor);//将[1]也就是buffer中的数据按照这个格式传递给这个文件描述符指针
    14     data_processed = read(file_descriptor, buffer, BUFSIZ);//将这个指针的数据读取到buffer中
    15 
    16     printf("%d - read %d bytes: %s
    ", getpid(), data_processed, buffer);//打印buffer数据
    17     exit(EXIT_SUCCESS);
    18 }

    执行的效果:

    1 jason@t61:~/c_program/544977-blp3e/chapter13$ gcc pipe3.c -o pipe3
    2 jason@t61:~/c_program/544977-blp3e/chapter13$ gcc pipe4.c -o pipe4
    3 jason@t61:~/c_program/544977-blp3e/chapter13$ ./pipe3
    4 4585 - wrote 3 bytes
    5 4586 - read 3 bytes: 123

    管道关闭后的操作:
    通常我们并不知道有多少数据需要读取,所以往往采取循环的方式,读取数据-处理数据-读取更多的数据
    直到没有数据可读为止

    read把读取一个无效的文件描述符看做是一个错误并返回-1
    但是会把对一个关闭写数据的管道的read调用不当成错误而是返回0也就是不阻塞


    把管道用作标准输入和标准输出

    1 #include <unistd.h>
    2 int dup(int file_descriptor);
    3 int dup2(int file_descriptor_one, int file_descriptor_two);

    dup调用的目的是打开一个新的文件描述符这和open调用有些类似
    dup调用创建的新的文件描述符与作为它的参数的那个已有的文件描述符指向同一个文件(或者管道)
    对于dup函数来说,新的文件描述符总是去最小的可用值
    对于dup2函数来说,它所创建的新的文件描述符或者与参数file_descriptor_two相同或者是第一个大于该参数的可用值

    要是我们关闭文件描述符0然后调用dup那么新的文件描述符就是0
    所以标准输入就会指向我们传递给dup函数的文件描述符所对应的文件或者管道
    管道和dup函数例程:

     1 #include <unistd.h>
     2 #include <stdlib.h>
     3 #include <stdio.h>
     4 #include <string.h>
     5 
     6 int main()
     7 {
     8     int data_processed;
     9     int file_pipes[2];
    10     const char some_data[] = "123";
    11     pid_t fork_result;
    12 
    13     if (pipe(file_pipes) == 0) {//创建管道成功
    14         fork_result = fork();//fork出一个子进程
    15         if (fork_result == (pid_t)-1) {
    16             fprintf(stderr, "Fork failure");
    17             exit(EXIT_FAILURE);
    18         }
    19 
    20         if (fork_result == (pid_t)0) {//fork成功
    21             close(0);//关闭子进程文件描述符0
    22             dup(file_pipes[0]);//打开一个新的文件描述符,标准输入会指向这个管道
    23             close(file_pipes[0]);//关闭管道输出
    24             close(file_pipes[1]);//关闭管道输入
    25 
    26             execlp("od", "od", "-c", (char *)0);//从path中找到文件,后面两个是参数,第一个是程序,第二个是输入
    27             exit(EXIT_FAILURE);//会认为从标准输入读取数据,其实数据是父进程给管道写入,子进程从标换输入读取
    28         }    //按照字符串打印
    29         else {
    30             close(file_pipes[0]);//关闭管道的输出
    31             data_processed = write(file_pipes[1], some_data, strlen(some_data));//向管道输入端写数据
    32             close(file_pipes[1]);//关闭管道的输入
    33             printf("%d - wrote %d bytes
    ", (int)getpid(), data_processed);//打印写入的数据
    34         }
    35     }
    36     exit(EXIT_SUCCESS);
    37 }


    程序的执行效果:

    1 jason@t61:~/c_program/544977-blp3e/chapter13$ 0000000   1   2   3
    2 0000003



    命名管道FIFO


    命名管道是一种特殊类型的文件,在文件系统中以文件名的形式存在

    1 mknod filename -p//创建特殊的文件filename,-p保证是管道
    2 mkfifo filename//实现在命令行上创建命名管道
    1 #include <sys/types.h>
    2 #include <sys/stat.h>
    3 int mkfifo(const char * filename, node_t mode);
    4 int mknod(const char * filename, node_t mode|S_IFIFO, (dev_t) 0);


    创建命名管道:

     1 #include <unistd.h>
     2 #include <stdlib.h>
     3 #include <stdio.h>
     4 #include <sys/types.h>
     5 #include <sys/stat.h>
     6 int main()
     7 {
     8     int res=mkfifo("/tmp/wy_fifo",0777);
     9     if(res==0) printf("FIFO created
    ");
    10     exit(EXIT_SUCCESS);
    11 }

    查看运行效果

    1 jason@t61:~/c_program/544977-blp3e/chapter13$ ls -lF /tmp/my_fifo
    2 prwxrwxr-x 1 jason jason 0  7月  1 10:21 /tmp/my_fifo|
    3 jason@t61:~/c_program/544977-blp3e/chapter13$ umask
    4 0002

    访问fifo文件

     1 open(const char * path, O_RDONLY);
     2 //此时open调用将阻塞,除非有一个进程以写的方式打开同一个FIFO否则不会返回
     3 
     4 open(const char * path, O_RDONLY|O_NONBLOCK);
     5 //即使没有其他进程以写方式打开FIFO这个open调用也将成功并立即返回
     6 
     7 open(const char * path, O_WRONLY);
     8 //此时open调用将阻塞,直到有一个进程以读方式打开一个FIFO为止
     9 
    10 open(const char * path, O_WRONLY|O_NONBLOCK);
    11 //这个调用总是立刻返回,但是如果没有进程以读方式打开FIFO文件
    12 //open调用将返回一个错误-1并且FIFO也不会被打开
    13 //如果有一个进程以读方式打开FIFO文件,那么可以通过它返回的文件描述符对这个FIFO文件进行写操作




    打开FIFO文件的程序:

     1 #include <unistd.h>
     2 #include <stdlib.h>
     3 #include <stdio.h>
     4 #include <string.h>
     5 #include <fcntl.h>
     6 #include <sys/types.h>
     7 #include <sys/stat.h>
     8 
     9 #define FIFO_NAME "/tmp/my_fifo"
    10 
    11 int main(int argc, char *argv[])
    12 {
    13     int res;
    14     int open_mode = 0;
    15     int i;
    16 
    17     if (argc < 2) {//输入参数的数目不对的时候提示一些信息
    18         fprintf(stderr, "Usage: %s <some combination of
    19                O_RDONLY O_WRONLY O_NONBLOCK>
    ", *argv);
    20         exit(EXIT_FAILURE);
    21     }
    22 
    23 
    24     for(i = 1; i < argc; i++) {
    25         if (strncmp(*++argv, "O_RDONLY", 8) == 0)
    26              open_mode |= O_RDONLY;
    27         if (strncmp(*argv, "O_WRONLY", 8) == 0)
    28              open_mode |= O_WRONLY;
    29         if (strncmp(*argv, "O_NONBLOCK", 10) == 0)
    30              open_mode |= O_NONBLOCK;
    31      }//根据命令行参数设置open_mode的值
    32 
    33     if (access(FIFO_NAME, F_OK) == -1) {//要是这个特殊的文件不存在的话
    34         res = mkfifo(FIFO_NAME, 0777);//按照给定的权限区创建这个fifo
    35         if (res != 0) {
    36             fprintf(stderr, "Could not create fifo %s
    ", FIFO_NAME);
    37             exit(EXIT_FAILURE);
    38         }
    39     }
    40 
    41     printf("Process %d opening FIFO
    ", getpid());
    42     res = open(FIFO_NAME, open_mode);//按照FIFO_MODE来打开这个FIFO
    43     printf("Process %d result %d
    ", getpid(), res);
    44     sleep(5);//休眠程序
    45     if (res != -1) (void)close(res);//关闭FIFO
    46     printf("Process %d finished
    ", getpid());
    47     exit(EXIT_SUCCESS);
    48 }


    执行的效果:

     1 jason@t61:~/c_program/544977-blp3e/chapter13$ ./fifo2 O_RDONLY &
     2 [1] 11845
     3 jason@t61:~/c_program/544977-blp3e/chapter13$ Process 11845 opening FIFO
     4 
     5 jason@t61:~/c_program/544977-blp3e/chapter13$
     6 jason@t61:~/c_program/544977-blp3e/chapter13$ ./fifo2 O_WRONLY
     7 Process 11972 opening FIFO
     8 Process 11845 result 3
     9 Process 11972 result 3
    10 Process 11845 finished
    11 Process 11972 finished
    12 [1]+  已完成               ./fifo2 O_RDONLY
    13 jason@t61:~/c_program/544977-blp3e/chapter13$
    14 //说明允许读先启动,并在open调用中等待,当第二个程序打开FIFO文件时,两个程序继续执行
    15 读进程和写进程在open调用处取得同步,当一个Linux进程被阻塞时,并不消耗CPU资源,所以这种进程
    16 同步方式对CPU来说是非常有效的
     1 jason@t61:~/c_program/544977-blp3e/chapter13$ ./fifo2 O_RDONLY O_NONBLOCK&
     2 [1] 13745
     3 jason@t61:~/c_program/544977-blp3e/chapter13$ Process 13745 opening FIFO
     4 Process 13745 result 3
     5 
     6 jason@t61:~/c_program/544977-blp3e/chapter13$ ./fifo2 O_WRONLY
     7 Process 13764 opening FIFO
     8 Process 13764 result 3
     9 Process 13745 finished
    10 Process 13764 finished
    11 [1]+  已完成               ./fifo2 O_RDONLY O_NONBLOCK
    12 jason@t61:~/c_program/544977-blp3e/chapter13$ 



    对FIFO进行读写操作:

    对于一个完全阻塞FIFO的write调用将等待直到有数据可以被写入时才继续执行

    如果FIFO不能接收所有写入的数据,它将按下面的规则执行
        如果请求写入的数据的长度小于等于PIPE_BUF字节,调用失败,数据不能写入
        如果请求写入的长度大于PIPE_BUF字节,将写入部分数据,返回实际写入的字节数,返回值也可能是0

    FIFO的数据长度是有限制的
    在一个以O_WRONLY方式打开的FIFO中,如果写入的数据长度小于等于PIPE_BUF
    那么或者写入全部字节或者一个字节都不写入

    使用FIFO实现进程间通信
    //fifo3.c

     1 #include <unistd.h>
     2 #include <stdlib.h>
     3 #include <stdio.h>
     4 #include <string.h>
     5 #include <fcntl.h>
     6 #include <limits.h>
     7 #include <sys/types.h>
     8 #include <sys/stat.h>
     9 
    10 #define FIFO_NAME "/tmp/my_fifo"
    11 #define BUFFER_SIZE PIPE_BUF
    12 #define TEN_MEG (1024 * 1024 * 10)
    13 
    14 int main()
    15 {
    16     int pipe_fd;
    17     int res;
    18     int open_mode = O_WRONLY;
    19     int bytes_sent = 0;
    20     char buffer[BUFFER_SIZE + 1];
    21 
    22     if (access(FIFO_NAME, F_OK) == -1) {//要是FIFO不存在
    23         res = mkfifo(FIFO_NAME, 0777);//就按照这个权限去创建一个FIFO
    24         if (res != 0) {
    25             fprintf(stderr, "Could not create fifo %s
    ", FIFO_NAME);
    26             exit(EXIT_FAILURE);
    27         }
    28     }
    29 
    30     printf("Process %d opening FIFO O_WRONLY
    ", getpid());
    31     pipe_fd = open(FIFO_NAME, open_mode);//按照写权限去打开这个FIFO
    32     printf("Process %d result %d
    ", getpid(), pipe_fd);
    33 
    34     if (pipe_fd != -1) {//打开成功
    35         while(bytes_sent < TEN_MEG) {//执行条件
    36             res = write(pipe_fd, buffer, BUFFER_SIZE);//将buffer处的管道长度的字节数据写到管道中
    37             if (res == -1) {
    38                 fprintf(stderr, "Write error on pipe
    ");
    39                 exit(EXIT_FAILURE);
    40             }
    41             bytes_sent += res;//增加写入的长度,区改变while循环的条件
    42         }
    43         (void)close(pipe_fd); //关闭这个管道
    44     }
    45     else {
    46         exit(EXIT_FAILURE);        
    47     }
    48 
    49     printf("Process %d finished
    ", getpid());
    50     exit(EXIT_SUCCESS);
    51 }


    //fifo4.c

     1 #include <unistd.h>
     2 #include <stdlib.h>
     3 #include <stdio.h>
     4 #include <string.h>
     5 #include <fcntl.h>
     6 #include <limits.h>
     7 #include <sys/types.h>
     8 #include <sys/stat.h>
     9 
    10 #define FIFO_NAME "/tmp/my_fifo"
    11 #define BUFFER_SIZE PIPE_BUF
    12 
    13 int main()
    14 {
    15     int pipe_fd;
    16     int res;
    17     int open_mode = O_RDONLY;
    18     char buffer[BUFFER_SIZE + 1];
    19     int bytes_read = 0;
    20 
    21     memset(buffer, '', sizeof(buffer));
    22     
    23     printf("Process %d opening FIFO O_RDONLY
    ", getpid());
    24     pipe_fd = open(FIFO_NAME, open_mode);//读取的方式打开管道
    25     printf("Process %d result %d
    ", getpid(), pipe_fd);
    26 
    27     if (pipe_fd != -1) {
    28         do {
    29             res = read(pipe_fd, buffer, BUFFER_SIZE);//从管道中读取到buffer中
    30             bytes_read += res;
    31         } while (res > 0);
    32         (void)close(pipe_fd);
    33     }
    34     else {
    35         exit(EXIT_FAILURE);
    36     }
    37 
    38     printf("Process %d finished, %d bytes read
    ", getpid(), bytes_read);
    39     exit(EXIT_SUCCESS);
    40 }


    //fifo3是数据的生产者。它将阻塞以等待读取进程打开这个FIFO
    //fifo4是数据的消费者。它将启动解除阻塞并开始向管道写数据,同时读取进程也从管道中读取数据
    Linux会安排好两个进程的调度,使他们在可以运行的时候运行,在不能运行的时候阻塞
    写进程将在管道满时阻塞,读进程将在管道空时阻塞
    //执行的效果

     1 jason@t61:~/c_program/544977-blp3e/chapter13$ ./fifo3 &
     2 [1] 22927
     3 jason@t61:~/c_program/544977-blp3e/chapter13$ Process 22927 opening FIFO O_WRONLY
     4 
     5 jason@t61:~/c_program/544977-blp3e/chapter13$ time ./fifo4
     6 Process 23009 opening FIFO O_RDONLY
     7 Process 23009 result 3
     8 Process 22927 result 3
     9 Process 22927 finished
    10 Process 23009 finished, 10485760 bytes read
    11 [1]+  已完成               ./fifo3
    12 
    13 real    0m0.016s
    14 user    0m0.004s
    15 sys    0m0.020s
    16 jason@t61:~/c_program/544977-blp3e/chapter13$
    17 //读取程序读取了10M的数据,时间很短,说明程序之间传递数据是很有效率的



    使用fifo的客服/服务器(C/S架构)程序


    //client.h

     1 #include <unistd.h>
     2 #include <stdlib.h>
     3 #include <stdio.h>
     4 #include <string.h>
     5 #include <fcntl.h>
     6 #include <limits.h>
     7 #include <sys/types.h>
     8 #include <sys/stat.h>
     9 
    10 #define SERVER_FIFO_NAME "/tmp/serv_fifo"//定义了server的fifo
    11 #define CLIENT_FIFO_NAME "/tmp/cli_%d_fifo"//定义了cli_i的fifo
    12 
    13 #define BUFFER_SIZE 20//缓冲区大小
    14 
    15 struct data_to_pass_st {//定义一个结构体包含一个pit_t结构和一个结构数组
    16     pid_t  client_pid;
    17     char   some_data[BUFFER_SIZE - 1];
    18 };


    //server.c服务器版本的程序

     1 #include "client.h"
     2 #include <ctype.h>
     3 
     4 int main()
     5 {
     6     int server_fifo_fd, client_fifo_fd;
     7     struct data_to_pass_st my_data;
     8     int read_res;
     9     char client_fifo[256];
    10     char *tmp_char_ptr;
    11 
    12     mkfifo(SERVER_FIFO_NAME, 0777);//新建fifo
    13     server_fifo_fd = open(SERVER_FIFO_NAME, O_RDONLY);//读取模式打开fifo,立即阻塞
    14     if (server_fifo_fd == -1) {
    15         fprintf(stderr, "Server fifo failure
    ");
    16         exit(EXIT_FAILURE);
    17     }
    18 
    19     sleep(10); //一旦cli传递数据,阻塞解除,休眠让cli的数据排队等候
    20 
    21     do {
    22         read_res = read(server_fifo_fd, &my_data, sizeof(my_data));//将服务器fifo的数据读取到buffer中
    23         if (read_res > 0) {
    24             tmp_char_ptr = my_data.some_data;
    25             while (*tmp_char_ptr) {
    26                 *tmp_char_ptr = toupper(*tmp_char_ptr);
    27                 tmp_char_ptr++;//将客户端传递的数据转化为大写
    28             }
    29             sprintf(client_fifo, CLIENT_FIFO_NAME, my_data.client_pid);//把格式化的数据写入到客户端fifo中
    30 
    31             client_fifo_fd = open(client_fifo, O_WRONLY);//以写的方式打开客户端fifo
    32             if (client_fifo_fd != -1) {//将处理过的数据发送到客户端fifo
    33                 write(client_fifo_fd, &my_data, sizeof(my_data));
    34                 close(client_fifo_fd);
    35             }
    36         }
    37     } while (read_res > 0);
    38     close(server_fifo_fd);
    39     unlink(SERVER_FIFO_NAME);
    40     exit(EXIT_SUCCESS);
    41 }


    //client.c

     1 #include "client.h"
     2 #include <ctype.h>
     3 
     4 int main()
     5 {
     6     int server_fifo_fd, client_fifo_fd;
     7     struct data_to_pass_st my_data;
     8     int times_to_send;
     9     char client_fifo[256];
    10 
    11     server_fifo_fd = open(SERVER_FIFO_NAME, O_WRONLY);//写的函数打开服务器的fifo
    12     if (server_fifo_fd == -1) {
    13         fprintf(stderr, "Sorry, no server
    ");
    14         exit(EXIT_FAILURE);
    15     }
    16 
    17     my_data.client_pid = getpid();
    18     sprintf(client_fifo, CLIENT_FIFO_NAME, my_data.client_pid);
    19     if (mkfifo(client_fifo, 0777) == -1) {//新建一个cli_fifo
    20         fprintf(stderr, "Sorry, can't make %s
    ", client_fifo);
    21         exit(EXIT_FAILURE);
    22     }
    23 
    24 // For each of the five loops, the client data is sent to the server.
    25 // Then the client FIFO is opened (read-only, blocking mode) and the data read back.
    26 // Finally, the server FIFO is closed and the client FIFO removed from memory.
    27 
    28     for (times_to_send = 0; times_to_send < 5; times_to_send++) {//循环五次
    29         sprintf(my_data.some_data, "Hello from %d", my_data.client_pid); //打印这个pid的招呼到自定义结构体
    30         printf("%d sent %s, ", my_data.client_pid, my_data.some_data);
    31         write(server_fifo_fd, &my_data, sizeof(my_data));//将自定义结构体内容写到服务器fifo中
    32         client_fifo_fd = open(client_fifo, O_RDONLY);//以读方式打开cli的fifo这时会阻塞等待服务器返回数据
    33         if (client_fifo_fd != -1) {//将cli的fifo读到的数据写到自定义的数据结构中
    34             if (read(client_fifo_fd, &my_data, sizeof(my_data)) > 0) {
    35                 printf("received: %s
    ", my_data.some_data);
    36             }
    37             close(client_fifo_fd);
    38         }
    39     }
    40     close(server_fifo_fd);
    41     unlink(client_fifo);
    42     exit(EXIT_SUCCESS);
    43 }


    //执行效果:

     1 jason@t61:~/c_program/544977-blp3e/chapter13$ ./server &
     2 [1] 32168
     3 jason@t61:~/c_program/544977-blp3e/chapter13$ for i in 1 2 3 4 5
     4 > do
     5 > ./client &
     6 > done
     7 [2] 32268
     8 [3] 32269
     9 [4] 32270
    10 [5] 32271
    11 [6] 32272
    12 jason@t61:~/c_program/544977-blp3e/chapter13$ 32268 sent Hello from 32268, received: HELLO FROM 32268
    13 32271 sent Hello from 32271, received: HELLO FROM 32271
    14 32271 sent Hello from 32271, received: HELLO FROM 32271
    15 32271 sent Hello from 32271, received: HELLO FROM 32271
    16 32271 sent Hello from 32271, received: HELLO FROM 32271
    17 32271 sent Hello from 32271, received: HELLO FROM 32271
    18 32270 sent Hello from 32270, received: HELLO FROM 32270
    19 32268 sent Hello from 32268, received: HELLO FROM 32268
    20 32272 sent Hello from 32272, received: HELLO FROM 32272
    21 32269 sent Hello from 32269, received: HELLO FROM 32269
    22 32270 sent Hello from 32270, received: HELLO FROM 32270
    23 32272 sent Hello from 32272, received: HELLO FROM 32272
    24 32269 sent Hello from 32269, received: HELLO FROM 32269
    25 32268 sent Hello from 32268, received: HELLO FROM 32268
    26 32269 sent Hello from 32269, received: HELLO FROM 32269
    27 32272 sent Hello from 32272, received: HELLO FROM 32272
    28 32269 sent Hello from 32269, received: HELLO FROM 32269
    29 32272 sent Hello from 32272, received: HELLO FROM 32272
    30 32269 sent Hello from 32269, received: HELLO FROM 32269
    31 32272 sent Hello from 32272, received: HELLO FROM 32272
    32 32270 sent Hello from 32270, received: HELLO FROM 32270
    33 32268 sent Hello from 32268, received: HELLO FROM 32268
    34 32270 sent Hello from 32270, received: HELLO FROM 32270
    35 32268 sent Hello from 32268, received: HELLO FROM 32268
    36 32270 sent Hello from 32270, received: HELLO FROM 32270
    37 
    38 [1]   已完成               ./server
    39 [2]   已完成               ./client
    40 [3]   已完成               ./client
    41 [4]   已完成               ./client
    42 [5]-  已完成               ./client
    43 [6]+  已完成               ./client
    44 jason@t61:~/c_program/544977-blp3e/chapter13$ 










    参考文献:Linux 程序设计 Neil Matthew
    date:2015年 07月 01日 星期三 17:06:13 CST



    万事走心 精益求美


  • 相关阅读:
    [Bzoj2152]聪聪可可
    [2019杭电多校第七场][hdu6655]Just Repeat
    [2019杭电多校第七场][hdu6651]Final Exam
    [2019杭电多校第七场][hdu6646]A + B = C(hash)
    [2019杭电多校第六场][hdu6641]TDL
    [2019杭电多校第六场][hdu6638]Snowy Smile(维护区间最大子段和)
    abc179f
    Codeforces Round #680A
    Codeforces Round #680B
    Codeforces Round #681 D
  • 原文地址:https://www.cnblogs.com/kongchung/p/4613746.html
Copyright © 2020-2023  润新知