基础文章5:APUE chap15 进程间通信

0.序
本文主要学习了APUE chapter15的
15.1 引言
15.2 管道
   
根据本章节15.11的总结:我们可以看出要重点学习管道的使用方法。 
15.1 引言
进程之间的通信 IPC:
     进程之间通信实际上就是进程之间进行消息传递。
15.2 管道
     1.管道的局限性:
          1)半双工
          2)只能在具有共同祖先的进程之间使用。通常,一个管道由一个进程创建,然后改进程fork调用,伺候父子进程之间可以应用该管道。

           当管道的一端被关闭后,下列两条规则起作用:
               1)当读一个写端被关闭的管道时,所有数据都被读取后,read返回0,用于指示达到了文件结束处。
               2)当写一个读端被关闭的管道,则产生信号SIGPIPE。如果忽略该信号或者捕捉该信号并从其处理程序返回,则write返回-1,errno设置为EPIPE。
     
/*UNP chap 15 -1 创建一个从父进程到子进程的管道,并且父进程经由该管道向子进程传送数据*/
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdlib.h>
#define MAXLINE 200
int main(void)
{
    int n;
    int fd[2];
    pid_t pid;
    char line[MAXLINE];
    if(pipe(fd) < 0)
        printf("pipe error\n");
    if((pid = fork()) < 0)
     printf("fork error\n");
    else if(pid > 0 ){
        close(fd[0]);
        write(fd[1],"hello,world",MAXLINE);
    }else{
        close(fd[1]);
        n = read(fd[0],line,MAXLINE);
        write(STDOUT_FILENO,line,n);
    }
    exit(0);
}

/*UNP chap 15-2 将文件复制到分页程序*/
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/wait.h>
#define MAXLINE 5000
#define filename "/home/hpnl/Downloads/epoll_io1.c"
int main(void)
{
    int n;
    int fd[2];
    pid_t pid;
    FILE *fp;
    char * pager,*argv0;
    char line[MAXLINE];
    if((fp = fopen(filename,"r")) == NULL){
        printf("fopen error\n");
    }
    if(pipe(fd) < 0)
        printf("pipe error\n");
    if((pid = fork()) < 0)
     printf("fork error\n");
    else if(pid > 0 ){
        close(fd[0]);
        while( fgets(line,MAXLINE,fp) != NULL){
            n = strlen(line);
            if(write(fd[1],line,n) != n)
               printf("write error\n");
        }
        close(fd[1]);
        if(waitpid(pid,NULL,0) < 0)
           printf("waitpid error\n");
        exit(0);
    }else{
        close(fd[1]);
        if(fd[0] != STDIN_FILENO){
            if(dup2(fd[0],STDIN_FILENO) != STDIN_FILENO)
                printf("dup2 error\n");
            close(fd[0]);
        }

        if((pager = getenv("PAGER")) ==NULL )
                pager = "/bin/more";
        if((argv0 = strrchr(pager,'/')) != NULL)
                argv0++;
        else
               argv0 = pager;
        if(execl(pager,argv0,(char*)0) < 0)
               printf("execl error \n");
    }
    exit(0);
}
运行结果:
在gdb调试的过程中,进入子进程 set follow-fork-mode child 运行more过程中,(gdb)q 则会出现如下提示:这刚好符合规则2:当写一个读端被关闭的管道,则产生信号SIGPIPE。如果忽略该信号或者捕捉该信号并从其处理程序返回,则write返回-1,errno设置为EPIPE。



为了充分理解管道,现在又从网上找到部分详细用例 
示例1:
/* 如果父进程不关闭管道的写端, 即便子进程关闭了管道的写端, 还是会阻塞在read上
1)当读一个写端被关闭的管道时,所有数据都被读取后,read返回0,用于指示达到了文件结束处
这个程序可以很好地说明这一点,当注释掉parent中close(pfd[1]);后,程序就进入阻塞*/

#include <stdio.h> 
#include <unistd.h> 
#include <stdlib.h> 
#include <sys/wait.h> 
int main(int argc, const char **argv) 

    int len; 
    pid_t pid; 
    int pfd[2]; 
    char buffer[1024] = {0}; 
 
    
    if (pipe(pfd) < 0) { 
        printf("pipe error\n"); 
        exit(0); 
    } 
 
    if ((pid = fork()) < 0) { 
        printf("fork error\n"); 
        exit(0); 
    } 
 
    if (pid == 0) {      /* 子进程 */ 
        close(pfd[1]); 
        while ((len = read(pfd[0], buffer, 1023)) > 0) { 
            buffer[len] = '\0'; 
            printf("len = %d, %s", len, buffer); 
        } 
    } else {               /* 父进程 */ 
        close(pfd[0]);
        write(pfd[1], "hello\n", 6); 
//        close(pfd[1]); 
        wait(0); 
    } 
    return 0; 
}
基础文章5:APUE chap15 进程间通信_第1张图片

示例2:
/*1 只有管道写端的引用计数变成0,才会关闭管道得写端.
2 进程结束后,自动关闭打开的文件
3 如果父进程先退出,而且没有调用wait等待子进程, 那么子进程就会变成僵死进程*/

#include <sys/wait.h>
#include <stdio.h> 
#include <unistd.h> 
#include <stdlib.h> 
 
int main(int argc, const char **argv) 

    int len; 
    pid_t pid; 
    int pfd[2]; 
    char buffer[1024] = {0}; 
 
    if (pipe(pfd) < 0) { 
        printf("pipe error\n"); 
        exit(0); 
    } 
 
    if ((pid = fork()) < 0) { 
        printf("fork error\n"); 
        exit(0); 
    } 
 
    if (pid == 0) {      /* 子进程 */ 
        write(pfd[1], "hello\n", 6); 
//        close(pfd[1]); 
        sleep(2); 
    } else {               /* 父进程 */ 
        close(pfd[1]); 
        while ((len = read(pfd[0], buffer, 1023)) > 0) { 
            buffer[len] = '\0'; 
            printf("len = %d, %s", len, buffer); 
        } 
        printf("read done\n"); 
        wait(0);      /* 等待子进程退出才能看到效果 */ 
    } 
    return 0; 
}

15.3 popen和pclose函数
     常见的操作是创建一个管道连接到另一个进程,然后读其输出或向其输入端发送数据,为此标准I/O库提供了两个函数popen和pclose函数。

15.4 协同进程
15.5 FIFO
FIFO又称为命名管道,通过FIFO,不相关的进程也能交换数据。
15.6 XSI IPC
15.7消息队列
消息队列是消息的链接表,存放在内核中并由消息队列标识符标识。
15.8 信号量
     信号量semaphore 是一个计数器,用于多进程对共享数据对象的访问。
    信号量实际上是同步原语,而不是IPC,常用于共享资源的同步访问。
     内核为每个信号量集设置了一个semid_ds结构
     
struct semid_ds{
   struct ipc_perm sem_perm
   unsigned short   sem_nsems;
   time_t   sem_otime;
  time_t    sem_ctime;
 .........


}

15.9 共享存储
      共享存储允许两个或更多进程共享一给定的存储区。因为数据不需要在客户进程和服务器进程之间复制,所以这是最快的一种IPC。
     使用共享存储要掌握的唯一窍门是多个进程之间对一给定存储区的 同步访问。通常, 信号量被用来实现对共享存储访问的同步。
 /* One shmid data structure for each shared memory segment in the system. */
        struct shmid_ds {
                struct ipc_perm shm_perm;        /* operation perms */
                int     shm_segsz;               /* size of segment (bytes) */
                time_t  shm_atime;               /* last attach time */
                time_t  shm_dtime;               /* last detach time */
                time_t  shm_ctime;               /* last change time */
                unsigned short  shm_cpid;        /* pid of creator */
                unsigned short  shm_lpid;        /* pid of last operator */
                short   shm_nattch;              /* no. of current attaches */

                                                 /* the following are private */

                unsigned short   shm_npages;     /* size of segment (pages) */
                unsigned long   *shm_pages;      /* array of ptrs to frames -> SHMMAX */ 
                struct vm_area_struct *attaches; /* descriptors for attaches */
        };

15.10 客户进程-服务器进程属性
15.11 小结
     本章详细说明了进程间通信的多种形式:管道,命名管道(FIFO),以及另外三种IPC形式(通常称为XSI IPC),即消息队列、信号量、共享存储。
     提出下列建议:
     1) 要学会使用管道和FIFO,因为在大量应用程序中仍可有效地使用这两种基本技术。
     2)在新的应用程序中,要尽可能 避免使用消息队列和信号量,而应当考虑 全双工管道和记录锁,他们使用起来会简单很多。

你可能感兴趣的:(基础文章5:APUE chap15 进程间通信)