Linux进程间通信

    进程是程序运行资源分配的最小单位。每个进程各自有不同的用户地址空间,任何一个进程的全局变量在另一个进程中都看不到,所以进程之间要交换数据必须通过内核,在内核中开辟一块缓冲区,进程1把数据从用户空间拷到内核缓冲区,进程2再从内核缓冲区把数据读走,内核提供的这种机制称为进程间通信(IPC,Inter-Process Communication)。

匿名管道pipe

    pipe只能用于有血缘关系的进程进行单向通信。调用 pipe 函数时在内核中开辟一块缓冲区(称为管道)用于通信,它有一个读端一个写端,然后通过 fd 参数传出给用户程序两个文件描述符, fd[0] 指向管道的读端, fd[1] 指向管道的写端。支持多端读或多端写,但不支持一端同时读写。也就是说,管道是半双工通信(即双方都可以发送信息,但是双方不能同时发送信息)

文件描述符

文件描述符在形式上是一个非负整数。实际上,它是一个索引值,指向内核为每一个进程所维护的该进程打开的文件的记录表。当程序打开一个文件时,内核向进程返回一个文件描述符。
file descriptors: 由用户程序维护的记录表,记录的是该用户程序打开的所有的文件的fd。每个进程会预留三个默认的fd:stdin(0)、stdout(1)、stderr(2)。
file table:该表是全局唯一的,由系统内核维护,记录了所有进程打开的文件的状态、偏移量、访问模式(可读写)、文件类型、该文件对应的inode对象引用等。
Inode table: 全局唯一的表,是硬盘存储的文件的元数据的集合。

三个表的关系如下图所示:

简而言之,fd就是系统维护的file table表的某一项entry的指针,应用程序通过它能读写硬盘里文件。应用程序用它来跟内核打交道,让内核以fd定位应用程序所需访问的文件并帮忙读写数据

原文链接:

下图可以较为清楚地表明Pipe的功能:

pipe原型

#include <unistd.h>
      int pipe(int pipefd[2]);

传入的参数是一个大小为2的数组,然后就得到了两个文件描述符pipefd[0]和pipefd[1]

pipe举例

(1)举一个简单的栗子:
这里我们用一个父子进程来举例,如果要实现父子进程间的通信,在fork前就需要创建一个pipe管道

#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <unistd.h>
#define MAXLINE 80
int main(void)
{
	int n;
	int fd[2];
	pid_t pid;
	char line[MAXLINE];

	if (pipe(fd) < 0)//如果管道的文件描述符小于0
	{
		perror("pipe");
		exit(1);
	}
	if ((pid = fork()) < 0)//子进程如果创建成功了,返回的pid值一定大于0
	{
		perror("fork");
		exit(1);
	}
	if (pid > 0)
	{ /* parent */
		close(fd[0]);
		write(fd[1], "hello world\n", 12);
		wait(NULL);
	}
	else
	{ /* child */
		close(fd[1]);
		n = read(fd[0], line, MAXLINE);
		write(STDOUT_FILENO, line, n);
	}
	return 0;
}

运行结果如下:

可见这是父进程把字符串“hello world”写入到管道,子进程再从管道里面读取出来并且打印到标准输出上面来。

(2)运行博客园老师所给的pipedemo.c
代码如下:

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

int main()
{
	int	len, i, apipe[2];//两个文件描述符	
	char	buf[BUFSIZ];//长度为BUFSIZ		
	
	if ( pipe ( apipe ) == -1 ){
		perror("could not make pipe");
		exit(1);
	}
	printf("Got a pipe! It is file descriptors: { %d %d }\n", 
							apipe[0], apipe[1]);


	while ( fgets(buf, BUFSIZ, stdin) ){//从输入端获取字符,存入buf数组中
		len = strlen( buf );
		if (  write( apipe[1], buf, len) != len ){//apipe[1]是写入端,这里write()函数将buf指针指向的内存的len长个字节写入到apipe[1]所指向的管道缓冲区中。
			perror("writing to pipe");		
			break;					
		}
		for ( i = 0 ; i<len ; i++ )                     
			buf[i] = 'X' ;
		len = read( apipe[0], buf, BUFSIZ ) ;		
		if ( len == -1 ){				
			perror("reading from pipe");		
			break;
		}
		if ( write( 1, buf,len ) != len ){		
			perror("writing to stdout");		
			break;					
		}
	}
}

运行结果如下:

运行云班课里所给的代码pipedemo2.c

结果如下:

命名管道fifo

    FIFO(First In First Out)文件在磁盘上没有数据块,仅仅是内核中一条通道,各进程可以读写从而实现的进程间通信。支持多端读或多端写。
命令:mkfifo 管道名
库函数:int mkfifo(const char *pathname, mode_t mode);
我们首先需要用mkfifo myfifo生成一个fifo文件用于两个进程之间的通信

然后在同一个目录下创建两个程序文件:
wr.c:

// file: wr.c
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>

int main()
{
	int fd, ret, i = 0;
	char buf[256];

	fd = open("myfifo", O_WRONLY);
	if(fd < 0)
	{
		perror("open error");
	}
	
	printf("write start!\n");
	while(i < 100)
	{
		snprintf(buf, 256, "hello %d\n", i);
		ret = write(fd, buf, strlen(buf));
		if(ret < 0)
		{
			perror("write error");
		}
		printf("write ok: %d\n", i);
		i++;
		sleep(1);
	}

	return 0;
}

rd.c:

// file: rd.c
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main()
{
	int fd, ret;
	char buf[4096];

	fd = open("myfifo", O_RDONLY);
	if(fd < 0)
	{
		perror("open error");
	}

	printf("read start!\n");
	while(1)
	{
		ret = read(fd, buf, 4096);
		write(STDOUT_FILENO, buf, ret);
		sleep(1);
	}

	return 0;
}

运行结果如下:

进入myfifo文件夹下,首先编译运行testmf.c,创建一个fifo文件,并命名为myyfifo,该文件也处在该目录下,再编译老师所给的consumer.c和producer.c代码,运行结果如下:

可见消费者端读取出了当时在生产者端写入的hahahah,fifo管道建立成功。

原文地址:http://www.cnblogs.com/ssssspm/p/16863574.html

1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长! 2. 分享目的仅供大家学习和交流,请务用于商业用途! 3. 如果你也有好源码或者教程,可以到用户中心发布,分享有积分奖励和额外收入! 4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解! 5. 如有链接无法下载、失效或广告,请联系管理员处理! 6. 本站资源售价只是赞助,收取费用仅维持本站的日常运营所需! 7. 如遇到加密压缩包,默认解压密码为"gltf",如遇到无法解压的请联系管理员! 8. 因为资源和程序源码均为可复制品,所以不支持任何理由的退款兑现,请斟酌后支付下载 声明:如果标题没有注明"已测试"或者"测试可用"等字样的资源源码均未经过站长测试.特别注意没有标注的源码不保证任何可用性