栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 系统运维 > 运维 > Linux

Linux编程基础 5.1:进程间通信-1

Linux 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Linux编程基础 5.1:进程间通信-1

1 简介

Linux进程通信机制:

  • 管道
  • 信号量
  • 消息队列
  • 共享内存
  • socket通信
2 管道


管道其实质是由内核管理的一个缓冲区
形象地认为管道的两端连接着两个进程:

  • 一个进程进行信息输出,将数据写入管道;
  • 另一个进程进行信息输入,从管道中读取信息。

管道分为:

  • 匿名管道:只能用于有亲缘关系的进程间通信,进程退出后管道会被销毁。
  • 命名管道:命名管道与进程的联系较弱,相当于一个读写内存的接口,进程退出后,命名管道依然存在。
2.1 匿名管道

匿名管道的使用流程如下:
①在进程中创建匿名管道,pipe函数;
②关闭进程中不使用的管道端口,close函数;
③在待通信的进程中分别对管道的读、写端口进行操作,read/write函数;
④关闭管道,close函数。

2.1.1 pipe函数
#include 
int pipe(int pipefd[2]);

功能:创建匿名管道

参数说明

  • pipefd:传入参数,一个文件描述符数组;Linux将管道抽象为一个特殊文件。

返回值说明

  • 成功:返回0.
  • 不成功:返回-1。

【案例1】使用pipe()实现父子进程间通信,父进程作为读端,子进程作为写端。

#include 
#include 
#include 
#include 
#include 
#include 
int main(){
    int tempFd[2];//定义文件描述符数组
    int tempRet=pipe(tempFd);//创建管道
    if(tempRet == -1){   
        perror("pipe");
        exit(1);
    }   
    pid_t tempPid=fork();
    if(tempPid > 0){//父进程—读
        close(tempFd[1]);//关闭写端
        char tempBuf[64]={0};
        tempRet = read(tempFd[0], tempBuf, sizeof(tempBuf));//读数据
        close(tempFd[0]);
        write(STDOUT_FILENO, tempBuf, tempRet);//将读到的数据写到标准输出
        wait(NULL);
    } else if(tempPid == 0){//子进程—写
        close(tempFd[0]);//关闭读端
        char *tempStr="hello,pipen";
        write(tempFd[1], tempStr, strlen(tempStr)+1);//写数据
        close(tempFd[1]);
   }//of if
   return 0;
}//of main

分析如下:
pipe()创建管道后读端的文件描述符为fd[0],写端的文件描述符为fd[1];
调用fork后父子进程共享文件描述符,文件描述符与管道的关系如图所示:

父进程进行读操作,子进程进行写操作;使用close()函数关闭父进程的写端与子进程的读端。

2.1.2 dup2函数

【案例2】使用管道实现兄弟进程间通信,兄弟进程实现命令“ls | wc –l”的功能。

  • 在实现本案例时会用到重定向函数dup2:
#include 
int dup2(int oldfd, int newfd);
  • 其功能是将参数oldfd的文件描述符复制给newfd
  • 若函数调用成功则返回newfd
  • 否则返回-1,并设置errno。
#include 
#include 
#include 
int main(){
    int tempFd[2];
    int tempRet = pipe(tempFd);
    if(tempRet == -1){
        perror("pipe err");
        exit(1);
    }//of if   
    int i;
    pid_t tempPid, tempWpid;
    for(i=0; i<2; i++){//2个子
        if((tempPid = fork()) == 0){
            break;
        }//of if
    }//of if
	if(2 == i){//父进程,回收子进程
        close(tempFd[0]); //关闭读
        close(tempFd[1]); //关闭写
        tempWpid = wait(NULL);
        printf("wait child 1 success,pid=%dn", tempWpid );
        tempPid = wait(NULL);
        printf("wait child 2 success,pid=%dn", tempPid);
    } else if(0 == i){//子进程1—写
        close(tempFd[0]);
        dup2(tempFd[1], STDOUT_FILENO);//定向到标准输出
        execlp("ls", "ls", NULL);
    } else if(1 == i){//子进程2—读
        close(tempFd[1]);
        dup2(tempFd[0], STDIN_FILENO);
        execlp("wc", "wc", "-l", NULL);
    }//of if
    return 0;
}//of main

兄弟进程间通信,进程文件描述符与管道的关系如图所示:

实线所示的箭头为编程中需要保留的文件描述符

2.1.3 popen/pclose函数

Linux标准I/O库两个函数popen()和pclose(),可完成管道通信的流程。

#include 
FILE *popen(const char *command, const char *type);
int pclose(FILE *stream);

功能
popen函数的功能是:

  • 调用pipe()函数创建管道;
  • 调用fork()函数创建子进程;
  • 之后在子进程中通过execve()函数调用shell命令执行相应功能。

pclose函数的功能:

  • 关闭popen()打开的I/O流;
  • 通过调用wait()函数等待子进程命令执行结束;
  • 返回shell的终止状态,防止产生僵尸进程。

参数说明
popen函数的参数:

  • command:命令;
  • type:指定命令类型(输入w/输出r);

pclose函数的参数:

  • stream:I/O流。

返回值说明
popen函数的返回值:

  • 成功:管道文件的描述符;
  • 不成功:返回-1。

pclose函数的返回值:

  • 成功:0;
  • 不成功:-1。

【案例3】使用popen与pclose函数实现管道通信。

#include 
#include 
#include 
int main(){
    FILE *tempRfp,*tempWfp;
    char temBuf[100];
    tempRfp = popen("ls","r");		//读取命令执行结果
    tempWfp = popen("wc -l","w");	//将管道中的数据传递给进程
    while(fgets(temBuf, sizeof(temBuf), tempRfp)!=NULL){
    	fputs(temBuf, tempWfp);
    }//of while   
    pclose(tempRfp);
    pclose(tempWfp);
    return 0;
}//of main
2.2 命名管道
#include 
#include 

int mkfifo(const char *pathname, mode_t mode);

功能:创建命名管道(FIFO文件),命名管道与系统中的一个路径名关联,以文件的形式存在于文件系统中,通过FIFO的路径名访问FIFO文件,实现进程间通信。

参数说明

  • pathname:管道文件的路径名,通过FIFO路径名访问FIFO文件;
  • mode:指定FIFO的权限;

返回值说明

  • 成功:0;
  • 不成功:-1,并设置errno。

【案例4】使用FIFO实现没有亲缘关系进程间的通信。没有亲缘关系的进程间通信,需要两段程序来实现:

  • fifo_write.c实现FIFO的写操作;
  • fifo_read.c实现FIFO的读操作。
fifo_write.c
#include 
#include 
#include 
#include 
#include 
#include 
#include 
int main(int paraArgc,char *paraArgv[]){
    if(paraArgc < 2){							//判断是否传入文件名
        printf("./a.out fifonamen");
        exit(1);
    }//of if
    int tempRet = access(paraArgv[1], F_OK);	//判断fifo文件是否存在
    if(tempRet == -1){							//若fifo不存在就创建fifo 
        int tempFIFO = mkfifo(argv[1], 0664);
        if(tempFIFO == -1){						//判断文件是否创建成功
            perror("mkfifo");
            exit(1);
        } else{
            printf("fifo creat success!n");
        }//of if
    }//of if
   int tempFd = open(argv[1], O_WRONLY);		//读写方式打开
   while(1){									//循环写入数据
        char *tempStrp="hello,world!";
        write(tempFd, tempStrp, strlen(tempStrp)+1);
        sleep(1);
    }//of while
    close(tempFd);
    return 0;
}//of main

fifo_read.c
#include 
#include 
#include 
#include 
#include 
#include 
#include 
int main(int paraArgc,char *paraArgv[]){
    if(paraArgc < 2){						//判断是否传入文件名
        printf("./a.out fifonamen");
        exit(1);
    }//of if
    int tempRet = access(argv[1], F_OK);	//判断fifo文件是否存在
    if(tempRet == -1){						//若fifo不存在就创建fifo 
        int tempFIFO = mkfifo(argv[1], 0664);
        if(tempFIFO == -1){					//判断文件是否创建成功
            perror("mkfifo");
            exit(1);
        } else{
            printf("fifo creat success!n");
        }//of if
    }//of if
    int tempFd = open(argv[1], O_RDONLY);	//只读方式打开
    if(tempFd == -1){
        perror("open");
        exit(1);
    }//of if
    while(1){								//不断读取fifo中的数据并打印
        char temBuf[1024]={0};
        read(tempFd, temBuf, sizeof(temBuf));
        printf("buf=%sn", temBuf);
    }//of while
    close(tempFd);							//关闭文件
    return 0;
}//of main
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/858858.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号