进程通信是指进程间的信息交换。在进程的互斥与同步中,也在进程之间交换了一些信息,因此不少学者也将他们归为低级进程通信。他们低级的原因在与:① 效率低 ② 通信对与用户不透明.
在进程间要传递大量数据是,应当利用OS提供的的高级通信工具,目前,常见的高级通信机制可归为四类:共享存储器,管道通信系统,消息传递系统,客户机-服务机系统.
相互通信的进程间共享某些数据结构或者共享存储去,进程之间通过他们进行通信,据此把他们分为两种类型:基于共享数据结构,基于共享存储区。
在这种通信方式下,要就诸进程公用某些数据结构,借此来实现诸进程间的信息交换。数据结构的设置和同步处理都是由用户完成的吗,不透明,效率低,仅用于传递少量数据.
这周通信方式下,进程向系统申请共享存储区的一个分区,并指定该分区的关键字,并将该分区连接到本进程对其进行写。另一进程可根据关键字将该分区连接到自己之上对其进行读写,通过同一分区的读写,实现两进程间的信息交换。
管道也称共享文件方式,基于文件系统,利用打开的共享文件连接两个相互通信的进程,文件作为缓冲传输介质。
管道就是用于连接读进程和写进程的共享文件,写进程想管道发送字符流。读进程从管道中接受字符流。
a.特点:
b.原型:
c.例子
#include
#include
int main()
{
int fd[2]; // 两个文件描述符
pid_t pid;
char buff[20];
if(pipe(fd) < 0) // 创建管道
printf("Create Pipe Error!\n");
if((pid = fork()) < 0) // 创建子进程
printf("Fork Error!\n");
else if(pid > 0) // 父进程
{
close(fd[0]); // 关闭读端
write(fd[1], "hello world\n", 12);
}
else
{
close(fd[1]); // 关闭写端
read(fd[0], buff, 20);
printf("%s", buff);
}
return 0;
}
a.特点:
b.原型:
c.例子
FIFO的通信方式类似于在进程中使用文件来传输数据,只不过FIFO类型文件同时具有管道的特性。在数据读出时,FIFO管道中同时清除数据,并且“先进先出”。下面的例子演示了使用 FIFO 进行 IPC 的过程:
write_fifo.c
#include
#include // exit
#include // O_WRONLY
#include
#include // time
int main()
{
int fd;
int n, i;
char buf[1024];
time_t tp;
printf("I am %d process.\n", getpid()); // 说明进程ID
if((fd = open("fifo1", O_WRONLY)) < 0) // 以写打开一个FIFO
{
perror("Open FIFO Failed");
exit(1);
}
for(i=0; i<10; ++i)
{
time(&tp); // 取系统当前时间
n=sprintf(buf,"Process %d's time is %s",getpid(),ctime(&tp));
printf("Send message: %s", buf); // 打印
if(write(fd, buf, n+1) < 0) // 写入到FIFO中
{
perror("Write FIFO Failed");
close(fd);
exit(1);
}
sleep(1); // 休眠1秒
}
close(fd); // 关闭FIFO文件
return 0;
}
read_fifo.c
#include
#include
#include
#include
#include
int main()
{
int fd;
int len;
char buf[1024];
if(mkfifo("fifo1", 0666) < 0 && errno!=EEXIST) // 创建FIFO管道
perror("Create FIFO Failed");
if((fd = open("fifo1", O_RDONLY)) < 0) // 以读打开FIFO
{
perror("Open FIFO Failed");
exit(1);
}
while((len = read(fd, buf, 1024)) > 0) // 读取FIFO管道
printf("Read message: %s", buf);
close(fd); // 关闭FIFO文件
return 0;
}
在该机制中,以格式化的消息(message)为通信单位;利用系统为进程提供的两个高级通信原语send和received进行通信,隐藏看通讯的实现细节,对用户是透明的,使用非常方便,是使用最广泛的进程通信机制。
send: 但要进行消息传递时,执行send
receive:当接收者要接收消息是执行receive。
根据实现方式不同,可进一步将他们分为两种:
a.直接通信原语
直接信息传递系统分为两种。
对称寻址方式,这种方式下,发送进程和进程都要求以显式方式提供对方的的标识符,通常,系统提供的原语如下
send(receiver, message);发送一个消息给接受进程
receive(sender,message);接受发送进程的消息
非对称寻址方式,这种方式中,接受进程的原语中,并不需要命名发送进程。只填写表示源进程参数。原语如下:
send(P,message);
recrive(id,message);
b.消息的格式
消息的格式常用的就是较短的定长消息格式,和较长的可变的消息格式。定长的消息格式处理简单,存储空间开销少。可变的消息格式方便用户的使用,但是处理和存储空间需要更多的更大的开销.
c.通路链路
为了在发送进程和接受进程自己能进行通信,必须在两者之间建立一条通信链路。
有两种方式建立通信链路:
第一种:由发送进程在通信之前用显示的"通信连接"命令请求系统为之建立一条通信链路。在链路使用完后拆除链路。(主要用于计算机网络)
第二种:发送进程无需明确提出建立链路的请求,只需要利用系统提供的发送命令原因,系统会自动为之建立一条链路。
信箱通信属于间接的通信方式,即进程之间的通信,通过某种实体来完成。接受进程可以从该实体中取出发送进程的消息,通常我们把这份实体成为信箱(邮箱),每一个信箱都有唯一一个标识符。
a.信箱的结构
信箱定义为一种数据结构,通常从逻辑上分为两部分。
信息头:存放有关信箱的描述信息,如信箱标识符,信箱的拥有者,信箱的口令,信箱的空格数。
信箱体:由若干个可以存放消息(或者消息头)的信箱个。
b.信箱通信原语
系统为信箱通信提供了若干个原语:
信箱的创建和取消。进程可以利用信箱创建原语来建立一个新信箱,创建者进程应该给出信箱的名字,信箱的属性;对于共享信箱还应该给出共享者。当进程不再需要信箱时,可以利用撤销原语撤销。
消息的发送和接受
send(mainbox,message); //将一个信息发送到指定的信箱中
receive(mainbox,message);//从指定的信箱中取出信息
c.信箱的类型
信箱可由操作系统创建,也可以由用户进程创建,创建者是信箱的拥有者。据此,可以将信箱分为以下三类:
a.数据结构
在消息缓冲队列通信方式中,主要利用的数据结构是消息缓冲区
描述如下:
typedef struct message_buffer{
int sender; //发送者进程标识符
int size; //消息长度
char *text; /消息正文
struct message_buffer *next; //指向下一个消息缓冲区的指针
}
在PCB有关通信的数据项中。加入以下项
typedef struct processcontrol_block{
···
struct message_buffer *mq;//消息队列首指针.
semaphore muter; //消息队列互斥量
semaohore sm; //消息队列资源信号量
···
}PCB
b.发送原语
发送原语描述如下:
void send(receiver,a){ //receiver为接受进程的标识符,a为发送区间首地址
getbuf(a.size,i); //根据a.size申请缓冲区。
i.sender = a.sender;
i.size = a.size;
copy(i.text,a.text);//将发送区a的信息复制到消息缓冲区i中
i.next=0;
getid(PCBset,receiver.j);//获得接受进程内部的标识符;
wait(j.mutex);
insert(&j.mq,i);//将小修缓冲区插入消息队列中
signal(j.mutex);
a=signal(j.sm);
}
c.接收原语
接受原语描述如下:
void receive(b){
j = internal name;//j为接收进程的标识符。
wait(j.sm);
wait(j.mutex);
removce(j.mq,i); //将消息队列中第一个消息移出。
signel(j.mutex);
b.sender = i.sender;
b.size = i.size;
cope(b.tezt,i.text); //将消息缓存区i中的信息复制到b中
releasebuf(i);
}
前面所述的各种技术,虽然也实现了不同计算机间进程的通信,但客户机——服务器的通信机制,在网络环境的各种应用已成为当前主流,其主要的实现方式分为三类:套接字(Scoket),远程过程调用和远程方法调用。
一个套接字就是有关通信标识类型的数据结构,包含了通信目的地,通信使用的端口号,通信网络的传输层协议,进程所在的网络地址,以及针对客户或者服务器所提供的不同的系统调用。通常套接字包括两类:
套接字的优势在于,它不仅适用于同一台计算机内部的进程通信,也适用于网络环境中不同计算机间的进程通信。
远程过程调用,是一个通信协议,用于通过网络连接的系统。该协议允许运行一台主机系统上的进程调用另一台主机系统上的进程,而对程序员表现为常规的进程调用,无需为此额外编程。
《操作系统 第四版》
进程间通信(IPC)介绍