操作系统-课堂笔记-进程通信(南航)

进程通信

引言

进程之间为何需要通信?

  • 概述中第六个问题提到,如果一个C/Java程序想要使用matlab来分析数据,那么程序之间怎么传输数据?—正是进程通信
  • 其他应用:多进程排序,排序还需要多进程?如果1万条数据,那么直接一个函数排序就OK。如果10亿条呢?—使用多进程排序算法,需要使用进程间通信。
  • 网络服务器大多都是多进程的,多个进程之间是需要传递数据的。

思考如何实现进程通信

很容易想到:使用文件,文件大多都保存在磁盘中,磁盘读取速度太慢了,有没有快一点的实现方案?

既然磁盘慢,那么内存总比磁盘块吧,所以能不能在内存中引入进程间通信机制?

1. 消息传递

在这里插入图片描述

简介

上图为内存的抽象,我们在内存中开辟一部分空间,该部分空间用于保存消息。

何为消息?可以理解成进程的交流语言。

该方案:在内核空间中开辟一段地址空间,用来保存进程之间通信的消息

实现

操作系统提供两个系统调用:

  • send(P, msg), 发送msg到进程P
  • receive(Q,msg), 从进程Q接收消息存到msg

提供了两种实现方式:

  • 阻塞式(同步):发出系统调用的进程变为阻塞进程,直到消息被对方收到或接受到对方的消息。
  • 非阻塞式(异步):消息发送或接受成功与否均立即返回。

消息定长与变长:

  • 定长容易实现,不易使用
  • 变长容易使用,不易实现

上文提到在内核中开辟一段空间用来保存消息,那么这里的数据结构是怎样的?

  • 消息队列:其是一个队列。
  • 对消息队列有写权限的进程可以向其中按照规则添加新消息。
  • 对消息队列有读权限的进程可以从中按照规则读走消息。
消息队列编程

头文件:

#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>

相关函数:
int msgget(key_t key, int msgflg); 返回消息队列ID
int msgrcv(int msqid, struct msgbuf* msgp, int msgsz, long msgtyp, int msgflg); 从队列接受消息
int msgsnd(int msqid, struct msgbuf *msgp, int msgsz, int msgflg); 向队列发送消息
int msgctl(int msgqid, int cmd, struct msqid_ds *buf); 向队列发送控制命令,cmd:IPC_STAT, IPC_SET, IPC_RMID。

对于每个消息队列,内核维护一个如下的结构:

struct msqid_ds {
    struct ipc_perm msg_perm; /* operation permission struct */
    struct msg *msg_first; /* ptr to first message on q */
    struct msg *msg_last; /* ptr to last message on q */
    unsigned short msg_cbytes; /* current # bytes on q */
    msgqnum_t msg_qnum; /* # of messages on q */
    msglen_t msg_qbytes; /* max # of bytes on q */
    pid_t msg_lspid; /* pid of last msgsnd */
    pid_t msg_lrpid; /* pid of last msgrcv */
    time_t msg_stime; /* last msgsnd time */
    time_t msg_rtime; /* last msgrcv time */
    time_t msg_ctime; /* last change time */
};
函数讲解

这部分比较无聊,忽略,有兴趣的读者自行查资料。

2. 共享内存

在这里插入图片描述
上图为共享内存的示意图,从图中可以看出,其与消息传递类似,也是在内核空间开辟一段地址空间,称为共享内存。

其实现原理:
在这里插入图片描述

  • 需要通信时直接读写共享存储区即可
  • 但是同步问题需要程序员自行处理
Linux中的共享内存:

int shmget(key, size, flags)创建或获取共享内存

int shmat(shmid, addr, flag) 将shmid对应的共享内存绑定到本进程的地址空间,addr为指向共享内存的指针

int shmdt(shmid) 将共享内存解绑

3.管道

在这里插入图片描述
管道:

  • 简单、高效、被大量操作系统采用
  • 特殊的文件或缓冲区,大小一般固定
  • 先入先出(FIFO)
  • 被大量操作系统用于将一个程序的输出重定向为另一个程序的输入

如:ls -l /etc | less
该命令将ls程序的输出重定向为less程序的输入,从而实现一页一页的显示ls数据,这是一个管道的实例,也是一个很简单的进程通信实例。

管道的读写特征:

  • 如管道已满,则试图写的进程被阻塞,直到有进程将部分数据取走
  • 如管道为空,则试图读的进程被阻塞,直到有进程写入新的数据
  • 如无人使用管道的另一侧,则阻塞的进程被唤醒

下面看一个实例:

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

int main(){
	int pipdf[2];
	int pid;
	char recv[32];

	pip(pipfd); //创建一个管道,管道的两端分别时pipfd[0],pipfd[1]
	switch(pip=fork()){
		case -1:
			perror("fork");
			exit(1);
		case 0:
			close(pipfd[0]);	//将用不到的fd关掉
			FILE *out=fdopen(pipfd[1], "w"); //打开pipfd[1]作为子进程的输出端
			fprintf(out, "Hello World\n");	//向管道中输出数据
			break;
		case 1:
			cloes(pipfd[1]);
			FILE *in=fdopen(pipfd[0], "r"); //打开管道的另一侧
			fscanf(in, "%s", recv);	//从管道的另一侧将刚才的数据读进来
			printf("%s", revv);	//打印到标准输出
			break;
	}
}

小结

本节内容不多,除了管道以外其他的可以暂时当作了解内容。

主要记住:

  • 实现进程通信的三种方式:消息传递、共享内存、管道
  • 管道的使用方法以及原理(利用fd,即文件描述符)

如果觉得写的不错,对读者有帮助,可以给笔者点个赞,鼓励一下哦~

该系列博客目录
下一篇-CPU调度

  • 5
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值