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; } |

示例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)在新的应用程序中,要尽可能
避免使用消息队列和信号量,而应当考虑
全双工管道和记录锁,他们使用起来会简单很多。