APUE学习笔记——第十五章 进程间通信

进程间通信IPC(InterProcess Communication)是指能在两个进程间进行数据交换的机制。

1、管道

管道是UNIX系统IPC的最古老形式,并且所有UNIX系统都提供此通信机制。管道有下面两种局限性:
(1)历史上,它们是半双工的,现在某些系统提供全双工管道
(2)它们只能在具有公共祖先的进程之间使用。通常,一个管道由一个进程创建,然后该进程调用fork,此后父子进程之间就可以应用该管道。
管道是由调用pipe函数创建
#include <unistd.h>
int pipe(int filedes[2])
经由参数filedes返回两个文件描述符:filedes[0]为读而打开,filedes[1]为写而打开,filedes[1]的输出是filedes[0]的输入

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(){
    int fd[2];
    pid_t pid;
    char line[32];
    pipe(fd);
    pid = fork();
    if(pid > 0) {
        close(fd[0]);
        dup2(fd[1],STDOUT_FILENO);
        close(fd[1]);
        write(1,"hello linux\n",12);//由于父子进程执行顺序不定,下面最好加上waitpid(pid,NULL,0)
    }else{
        close(fd[1]);
        dup2(fd[0],STDIN_FILENO);
        close(fd[0]);
        int n = read(0,line,32);
        write(1,line,n);
    }
    exit(0);
}
将文件复制到分页程序

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>

#define PAGER "/bin/more"

int main(int argc,char*argv[]){
    int fd[2];
    pid_t pid;
    char line[64];
    FILE *fp;
    fp = fopen(argv[1],"r");
    pipe(fd);
    pid = fork();
    if(pid > 0){
        close(fd[0]);
        while(fgets(line,63,fp) != NULL){//父进程先将文件内容通过fd[1]写端,写到缓冲区中
            int n = strlen(line);
            write(fd[1],line,n);
        }
        close(fd[1]);
        waitpid(pid,NULL,0);//等待子进程读取
    }else{
        close(fd[1]);
        if(fd[0] != 0){
            dup2(fd[0],0);
            close(fd[0]);
        }
        char* argv0;
        if((argv0 = strrchr(PAGER,'/')) != NULL)
             argv0 ++;
        else argv0 = PAGER;
        execl(PAGER,argv0,(char*)0);
    }
    exit(0);
}
2、popen和pclose函数
常见的操作是创建一个管道连接到另一个进程,然后读取输出或向其输入端发送数据,为此,标准IO库提供两个函数popen和pclose。
这两个函数实现的操作是:创建一个管道,调用fork产生一个子进程,关闭管道的不使用端,执行一个shell以运行命令,然后等待命令终止。
#include <stdio.h>
FILE *popen(const char* cmdstring,const char* type)
int pclose(FILE* fp)
函数popen先执行fork,然后调用exec以执行cmdstring,并且返回一个标准IO文件指针。
如果type是"r",则文件指针连接到cmdstring的标准输出。
如果type是"w",则文件指针连接到cmdstring的标准输入。

将文件复制到分页程序:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

int main(int argc, char* argv[]){
    FILE *fpin,*fpout;
    fpin = fopen(argv[1],"r");//以读方式打开文件
    fpout = popen("more","w");
    char line[64];
    while(fgets(line,63,fpin) != NULL){
        fputs(line,fpout);
    }
    pclose(fpin);
    pclose(fpout);
    exit(0);
}
小写换大写程序

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main(){
    int c;
    char line[164],out[164];
    FILE* fp;
    fp = fopen("upper.c","r");
    while(fgets(line,163,fp) != NULL){
       memset(out,0,sizeof(out));
       for(c = 0 ; c < strlen(line) ; c ++)
            if(line[c] >= 'a' && line[c] <= 'z') out[c] = toupper(line[c]);
            else out[c] = line[c];
       fputs(out,stdout);
       fflush(stdout);
    }
    exit(0);
}
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

int main(int argc, char* argv[]){
    FILE *fop;
    fop = popen("./upper","r");//连接到cmdstring的标准输出
    char line[64];
    while(1){
        fflush(stdout);
        if(fgets(line,63,fop) == NULL) break;
        fputs(line,stdout);
    }
    pclose(fop);
}

3、协同进程

UNIX系统过滤程序从标准输入读取数据(popen函数),对其进行适当处理后写到标准输出。几个过滤程序通常在shell管道命令行中线性地连接。当一个程序产生某个过滤程序的输入,同时又读取该过滤程序的输出时,则该过滤程序就成为协同进程(coprocess)。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>

int main(){
    int n,a,b;
    char line[64];
    while((n = read(0,line,63)) > 0){
        line[n] = 0;
        sscanf(line,"%d %d",&a,&b);
        sprintf(line,"%d + %d = %d\n",a,b,a+b);
        write(1,line,strlen(line));
    }
    exit(0);
}

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>

int main(){//代码没有任何容错处理啊
    int n,fd1[2],fd2[2];
    char line[64];
    pipe(fd1),pipe(fd2);
    pid_t pid;
    pid = fork();
    if(pid > 0){
        close(fd1[0]);
        close(fd2[1]);
        while(fgets(line,63,stdin) != NULL){
            write(fd1[1],line,strlen(line));
            n = read(fd2[0],line,63);
            line[n] = 0;
            fputs(line,stdout);
        }
    }else{
        close(fd1[1]);
        close(fd2[0]);
        dup2(fd1[0],0);
        dup2(fd2[1],1);
        execl("./add","add",(char*)0);
    }
    exit(0);
}
4、FIFO
FIFO也被成为命名管道,通过FIFO,不相关的进程也能交换数据。创建FIFO类似于创建文件。
#include <sys/stat.h>
int mkfifo(const char* pathname,mode_t mode);
一旦用mkfido创建一个FIFO,就可以open打开,一般的文件IO函数(close,read,write,unlink等)都可以用于FIFO
命名管道的打开:命名管道比管道多了一个打开操作:open ,在open时,用O_NONBLOCK 标志表示非阻塞模式,如fd=open(“/tmp/fifo”,O_RDONLY|O_NONBLOCK,0)。
命名管道的读入:read 读取管道数据,读取分为阻塞和非阻塞模式,阻塞模式下,如果没有数据被入,进程会在read处停下来.直到有新数据被写入,或管道被关闭,才会继续。
命名管道的写入:write 写入管道数据,PIPE_BUF表示一次触发管道读操作最大长度.如果每次写入数据长于PIPE_BUF ,write将会多次触发read 操作。
命名管道的关闭:管道文件也是一种文件,因此用close关闭即可。
FIFO的两种用途:
(1)FIFO有shell命令使用以便将数据从一条管道线传送到另一条,为此无需创建中间临时文件。
(2)FIFO用于客户进程—服务器进程应用程序中,以在客户进程和服务器进程之间传递数据。

5、XSI IPC

有三种IPC称为XSI IPC:消息队列,信号量,共享存储器。

下面单独分章节分析XSI IPC 和 FIFO





发布了101 篇原创文章 · 获赞 26 · 访问量 46万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览