zoukankan      html  css  js  c++  java
  • 匿名通信管道进程

    另外还有一种方法,在这一部分进行通信的学习过程中:管道。

    管道是一个过程,过程流连接的数据信道,它通常被连接到一个处理的输出以及通过管道到过程输入。于shell命令经常会看到应用程序管道。有"test"的文件:ls -l | grep test。当中"|"就代表我们在使用管道,它会把"ls -l"的查询结果通过管道,发送给grep,然后运行"grep test"命令后把结构输出到终端。总之中的一个句话:管道会把一个进程的输出数据,发送给还有一进程,作为还有一个进程的输入数据。

    http://blog.csdn.net/xiaoliangsky/article/details/40112729

    一 无名管道
    1 无名管道的特点
    1)仅仅能用于具有亲缘关系的进程之间。父子进程,兄弟进程之间通信。由于仅仅有子进程才干继续父进程的文件描写叙述符。


    2)半双共通信(同一时刻仅仅能对管道进行一种操作(读操作或者写操作)),具有读port和写port。


    3)管道是一种特殊的文件,能够使用文件io函数(read,write...)来操作。但不能使用lseek函数来定位操作。
    4)管道是在内存中,不用我们主动区删除。


    5)管道是基于队列实现的。有限制大小。

    2 pipe函数
    int pipe(int pipefd[2]);

    pipe函数创建了一个单向数据通道,这个通道能够用来在进程之间通信。


    pipefd: pipefd数组用来返回两个文件描写叙述符,这两个文件描写叙述符代表了通道的两端。pipefd[0]代表通道的读取端。pipefd[1]代表了通道的写入段。在读取端读取数据,在输写入port写入数据。



    返回值
    函数调用成功返回0
    调用失败返回-1。这时errno存储错误码。

    错误码
    EFAULT pipefd数组地址不合法
    EMFILE 进程使用了太多的文件描写叙述符(文件描写叙述符使用达到上限)
    ENFILE 系统已无文件描写叙述符可用
    注意:
    1)不要使用pipefd[0]来写数据,也不要使用pipefd[1]来读取数据,否则其行为是没有定义的。
    2)在用pipefd[0]来读数据时,要先关闭pipefd[1];在用pipefd[1]来写入数据时,要关闭pipefd[0]。

    以下来看父子进程通过管道通信的一个样例:

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <sys/wait.h>
    
    #define  MAXLINE  100
    
    void err_sys(const char *str);
    
    int main()
    {
    	int      n;
    	int      status;
    	int      fd[2];
    	pid_t    pid;
    	char     line[MAXLINE];
    	
    	if (pipe(fd) < 0)
    	{
    		err_sys("pipe error
    ");
    	}
    	
    	if ((pid = fork()) < 0)
    	{
    		err_sys("fork error
    ");
    	}
    	else if (pid == 0)
    	{
    		close(fd[0]);
    		if (write(fd[1], "hello world
    ", sizeof("hello world
    ")) == -1)
    		{
    			err_sys("write error
    ");
    			
    			exit(-1);
    		}
    	}
    	else 
    	{
    		if (waitpid(pid, &status, 0) != pid)
    		{
    			err_sys("waitpid error
    ");
    		}
    
    		if (WIFEXITED(status) == 0)
    		{
    			err_sys("child process exit failed
    ");
    		}
    
    		close(fd[1]);
    		n = read(fd[0], line, MAXLINE);
    		write(STDOUT_FILENO, line, n);
    	}
    	
    	
    	return 0;
    }
    
    void err_sys(const char *str)
    {
    	printf("%s", str);
    }
    执行结果例如以下:


    3 popen函数
    FILE *popen(const char *command, const char *type)
    int   pclose(FILE *stream);

    popen函数用创建管道的方式启动一个进程,并调用shell来运行command命令。因为管道是单向的,所以type的方式仅仅能是仅仅读或者仅仅写,不能同一时候读写。返回结果流也对应的是仅仅写或者仅仅读。
    command 參数是一个字符串指针,指向一个以null结束的字符串,这个字符串包括一个shell命令. 这个命令被送到/bin/sh以 -c 參数运行, 即由shell来运行。
    type 參数也是一个指向以null结束符结尾的字符串的指针, 这个字符串必须是'r'或者'w’来指明是读还是写。


    popen()函数的返回值是一个普通的标准I/O流, 它仅仅能用pclose()函数来关闭, 而不是fclose()函数。向这个流的写入被转化为对command命令的标准输入; 而command命令的标准输出则是和调用popen(), 函数的进程同样,除非这个被command命令自己改变;相反的, 读取一个“被popen了的” 流, 就相当于读取command命令的标准输出, 而command的标准输入则是和调用popen函数的进程同样。能够概括为:向这个流写入数据就是command的输入。从这个流读取就是command的输出。注意, popen函数的输出流默认是被全缓冲的。
    pclose函数用于关闭由popen创建出的关联文件流。

    pclose仅仅在popen启动的进程结束后才返回。假设调用pclose时被调用进程仍在执行,pclose调用将等待该进程结束,并返回一个command命令的退出状态。
    stream是popen函数返回的文件流。

    返回值
    popen 假设fork或pipe函数调用失败,或者popen函数不能申请没存,则函数返回NULL。


    pclose 函数调用失败会返回-1。

    错误码
    popen函数由于内存错误就不会设置errno,fork或pipe错误就会对应的设置errno;type參数无效,errno会被设置为EINVAL。
    pclose无法获得子进程状态。errno会被设置为ECHILD。

    popen函数的优缺点
    长处:在Linux中所有的參数扩展都是由shell来完毕的。所以在启动程序(command中的命令程序)之前先启动shell来分析命令字符串。也就能够使各种shell扩展(如通配符)在程序启动之前就所有完毕,这样我们就能够通过popen启动很复杂的shell命令。


    缺点:对于每个popen调用,不仅要启动一个被请求的程序,还要启动一个shell,即每个popen调用将启动两个进程。从效率和资源的角度看。popen函数的调用比正常方式要慢一些。

    以下来看两个列子。以便跟深入的了解popen函数。

    第一个列子

    用popen来实现一个程序。在管道中让用户输入数据,然后在从管道中取出用户输出的数据,显示在终端。首先实现一个输入程序input.c,然后在testinput.c中用popen调用这个程序,把input的输出作为testinput的输入。

    input.c的代码:

    #include <string.h>
    #include <stdio.h>
    #include <ctype.h>
    
    int main()
    {
    	int  c;
    	//用户输入数据
    	while ((c = getchar()) != EOF)
    	{
    		if (isupper(c))
    		{
    			c = tolower(c);
    		}
    		//输出数据作为testinput的输入数据
    		if (putchar(c) == EOF)
    		{
    			fputs("output error
    ", stdout);
    		}
    		
    		if (c == '
    ')
    		{
    			fflush(stdout);
    		}
    	}
    	
    	return 0;
    }
    testinput.c的代码:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <sys/wait.h>
    
    #define MAXLINE 100
    
    int main()
    {
    	char   line[MAXLINE];
    	FILE  *fpin;
    	
    	//popen的第一个參数是命令,这里运行一个可运行文件
    	if ((fpin = popen("./input", "r")) == NULL)
    	{
    		fputs("popen error
    ", stdout);
    	}
    	
    	for ( ; ; )
    	{
    		fputs("prompt> ", stdout);
    		fflush(stdout);
    		
    		if (fgets(line, MAXLINE, fpin) == NULL)
    		{
    			break;
    		}
    		
    		if (fputs(line, stdout) == EOF)
    		{
    			fputs("fputs error to pipe", stdout);
    		}
    	}
    	
    	if (pclose(fpin) == -1)
    	{
    		fputs("pclose error
    ", stdout);
    	}
    	
    	puts("");
    	
    	exit(0);
    }
    运行结果例如以下:

    第二个列子非常easy,就是调用两次popen函数,第一次运行“ls -l”命名。它的输出作为第二次命令”grep .c“的输入。

    代码:

    #include <stdio.h>
    #include <string.h>
    #include <unistd.h>
    
    #define MAXSIZE 200
    
    void err_sys(const char *str);
    
    int main()
    {
    	FILE    *fpin;
    	FILE    *fpout;
    	char     line[MAXSIZE+1];
    	
    	memset(line, '', sizeof(line));
    	
    	fpin  = popen("ls -l", "r");
    	if (fpin == NULL)
    	{
    		err_sys("popen fpin error
    ");
    
    		return -1;
    	}
    
    	fpout = popen("grep .c", "w");
    	if (fpout == NULL)
    	{
    		err_sys("popen fpout error
    ");
    		pclose(fpin);
    
            return -1;
    	}
    	//fgets失败或者读到文件未返回NULL
    	while (fgets(line, MAXSIZE, fpin) != NULL)//从第一次中取出数据
    	{	//fputs成功返回非负,失败返回EOF
    		if (fputs(line, fpout) == EOF)//将取出的数据作为第二次命令的输入
    		{
    			err_sys("fputs error
    ");
    		}
    	}
    	
    	pclose(fpin);
        pclose(fpout);
    
    	return 0;
    }
    
    void err_sys(const char *str)
    {
    	printf("%s", str);
    }
    执行结果:


    http://blog.csdn.net/xiaoliangsky/article/details/40112729

    版权声明:本文博主原创文章,博客,未经同意不得转载。

  • 相关阅读:
    LINUX 环境变量总结
    make的自动变量和预定义变量
    函数调用约定和堆栈
    如何查看linux命令源代码
    shell脚本中特定符合变量的含义
    【转载】Redhat5和6 YUM源配置的区别
    用路径分析法来编写测试用例
    linux ip 设置
    Mysql 的存储引擎,myisam和innodb的区别。
    一些编译php时的configure 参数
  • 原文地址:https://www.cnblogs.com/zfyouxi/p/4865776.html
Copyright © 2011-2022 走看看