进程间通信
4.1 pipe管道
什么是管道? 可以理解为内存中的一个缓冲区,用于将某个进程的数据流导入,由某一个进程导出,实现通信。 再通俗的说,看图:
晓得了吧
这个pipe管道可以理解为匿名管道,是基于文件描述符的通信方式,使用时两个进程必须有血缘关系,父子进程之间的通信。
放码出来:
#include<unistd.h>
int pipe(int filedes[2]);
//成功返回0,失败返回-1
pipe函数在内存中开辟一块缓冲区,由filedes参数传出给用户程序使用的两个文件描述符。 【0】为读端使用,【1】为写端使用。
行了,还是来段代码吧
主要就是三部曲,看我标注出来
#include <stdio.h>
#include <unistd.h>
#include <string.h>
int main()
{
int fds[2];
if(pipe(fds) < 0) //父进程调用pipe创建管道,得到两个文件描述符指向管道的两端(1)
{
perror("pipe");
return 1;
}
char buf[1024];
printf("Please enter:");
fflush(stdout);
ssize_t s = read(0,buf,sizeof(buf)-1);
if(s > 0)
{
buf[s] = 0;
}
pid_t pid = fork(); //父进程克隆出子进程,子进程同样有两个文件描述符指向同一管道(2)
if(pid == 0)
{
//子进程只写,关闭读端(3.1)
close(fds[0]);
while(1)
{
sleep(1);
write(fds[1],buf,strlen(buf));//将buf的内容写入管道(3.3)
}
}
else
{
//父进程只读,关闭写端(3.2)
close(fds[1]);
char buf1[1024];
while(1)
{
ssize_t s = read(fds[0],buf1,sizeof(buf1)-1);//从管道里读数据,放入buf(3.3)
//形成一个消息循环
if(s > 0)
{
buf1[s-1] = 0;
printf("client->farther:%sn",buf1);
}
}
}
}
使用匿名管道有一些限制:
1、只能够进行单向通信 2、只能够用于有血缘关系(父子,兄弟,爷孙)的进程之间,多常用于父子之间 3、管道内部自带同步机制:子进程写一条,父进程读一条 4、管道的生命周期为随进程,进程结束管道就没了 5、管道内没有数据时,读端(read)发生阻塞,等待有效数据进行读取 6、管道容量被数据填满时,写端(write)发生阻塞,等待进程将数据读走再进行写入
4.2FIFO有名管道
创建一个有名管道,解决无血缘关系的进程之间的通信 :FIFO
这个有两个办法来创建:
//方法1:
mkfifo 管道名
方法2:
#include<sys/types.h>
#include<sys/stat.h>
int mkfifo(const char *pathname,mode_t mode);
//参数释义:
/*
filename:创建的有名管道的全路径名
mode:创建的命名管道的模式,指明其存取权限
*/
还是先上个实例:(以下为伪代码,主要为管道部分)
write.c
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
//读取文件,将文件内容写入管道
int main()
{
mkfifo("tp",0644);//创建一个管道文件
int infd = open("123",O_RDONLY);//打开一个文件
int outfd = open("tp",O_WRONLY);//打开管道文件,将123文件的内容写入管道文件
char buf[1024];//用于存放文件内容
ssize_t s;
while( (s = read(infd,buf,sizeof(buf))) >0)
{
write(outfd,buf,s);//将123文件的内容写入管道文件
}
close(infd);
close(outfd);//关闭有名管道文件描述符
}
read.c
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
//从管道文件里面读取内容,并将内容写入另一个文件中
int main()
{
int infd = open("abc.bak",O_CREAT | O_WRONLY | O_TRUNC,0644);//创建一个新的文件
//将从管道读取的内容写入到新的文件中
int outfd = open("tp",O_RDONLY);//打开管道文件
char buf[1024];//临时数组
ssize_t s;
while( (s = read(outfd,buf,sizeof(buf))) > 0) //从管道中读取数据
{
write(infd,buf,s);
}
close(infd);
close(outfd); //关闭有名管道
return 0;
}
有名管道也有·自己的独特之处:
- 可以进行不相干进程间的通信
- 命名管道是一个文件,对于文件的相关操作对其同样适用
- 对于管道文件,当前进程操作为只读时,则进行阻塞,直至有进程对其写入数据
- 对于管道文件,当前进程操作为只写时,则进行阻塞,直至有进程从管道中读取数据
5.FIFO可以一个读端,多个写端。也可以一个写端,多个读端。 6.FIFO支持双向通信
4.3 内存共享映射
起的一个好名字,mmap/munmap
mmap可以把磁盘文件的一部分直接映射到内存,这样文件中的位置直接就有对应的内存地址,对文件的读写可以直接用指针来做而不需要read/write
共享内存可以说是最有用的进程间通信方式,也是最快的IPC形式。
哎,手指都要肿了,放码吧
//mmap()系统调用形式如下:
void* mmap ( void * addr , size_t len , int prot , int flags , int fd , off_t offset )
参数释义 addr指定文件应被映射到进程空间的起始地址,一般被指定一个空指针,反正你也找不到,此时选择起始地址的任务留给内核来完成。 len是映射到调用进程地址空间的字节数,它从被映射文件开头offset个字节开始算起。 prot 参数指定共享内存的访问权限。可取如下几个值的或: PROT_READ(可读) , PROT_WRITE (可写), PROT_EXEC (可执行), PROT_NONE(不可访问)。 flags由以下几个常值指定:MAP_SHARED , MAP_PRIVATE , MAP_FIXED, 其中,MAP_SHARED , MAP_PRIVATE必选其一,而MAP_FIXED则不推荐使用。 fd为即将映射到进程空间的文件描述字,一般由open()返回 offset参数一般设为0,表示从文件头开始映射。(必须是页大小的整数倍(4K)) 函数的返回值为最后文件映射到进程空间的地址,进程可直接操作起始地址为该值的有效地址。
//系统调用munmap()
int munmap( void * addr, size_t len )
/*该调用在进程地址空间中解除一个映射关系,addr是调用mmap()时返回的地址,len是映射区的大小。当映射关系解除后,对原来映射地址的访问将导致段错误发生。*/
//系统调用msync()
int msync ( void * addr , size_t len, int flags)
/*一般说来,进程在映射空间的对共享内容的改变并不直接写回到磁盘文件中,往往在调用munmap()后才执行该操作。可以通过调用msync()实现磁盘上文件内容与共享内存区的内容一致。*/
好,看范例
#include <sys/mman.h>;
#include <sys/types.h>;
#include <fcntl.h>;
#include <unistd.h>;
typedef struct
{
char name[4];
int age;
}people;
main(int argc, char** argv) // map a normal file as shared mem:
{
int fd,i;
people *p_map;
char temp;
fd=open(argv[1],O_CREAT|O_RDWR|O_TRUNC,0777);
lseek(fd,sizeof(people)*5-1,SEEK_SET);
write(fd,"",1);
p_map = (people*) mmap( NULL,sizeof(people)*10,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0 );
close( fd );
temp = 'a';
//直接使用这种写入方式
for(i=0; i<10; i++)
{
temp += 1;
memcpy( ( *(p_map+i) ).name, &temp,2 );
( *(p_map+i) ).age = 20+i;
}
printf(" initialize over /n ");
sleep(10);
munmap( p_map, sizeof(people)*10 );
printf( "umap ok /n" );
}
- CSS 命名之Dialog, Modal, Popup, Popover, Lightbox 等的区别
- Eclipse JAVA文件注释乱码
- 2018年小程序的红利趋势预测,懂的来……或许你将成为下个富翁
- VUE 入门基础(6)
- 五年换4高管,6000员工裁95%剩300人,王健林为何抛弃万达网科?
- Android Permission中英对照
- 你知道人脸识别技术是如何实现的吗?
- WordPress REST API 定制化输出
- ASP.NET MVC的Action Filter
- Android LayoutInflater详解
- 在Android中实现service动态更新UI界面
- VUE 入门基础(5)
- Android的UI设计与后台线程交互
- 更强悍的Silverlight: WCF RIA Services
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法