操作系统课程实践5_进程通信

2023-11-04

一.实验目的

       (1)初步了解Linux环境下进程创建和进程间通信的机制

       (2)掌握如何利用消息和共享内存进行通信的原理

       (3)利用POSIX API函数编写实例程序

       (4)实现父子进程间通过消息传递和共享内存方式进行通信。

 

二.实验内容

编制两个程序P1、P2。当这程序P1运行后,它创建一个子进程P2(也就是在P1中调用P2),其中P1是父进程,P2由P1创建,是P1的子进程。

P1和P2利用两种机制进行通信:消息和共享内存。P1输入4个数字并指定通讯模式,通过消息或共享内存方式传送给P2;P2接收到后计算这四个数字和加减乘除四个运算符结合后是否可以算出24;把结果(包括所有可能的计算方式)通过消息机制返回P1并显示。

 

三.实验步骤和结果

1、编写进程通信代码shmmsg.c。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/msg.h>
#include <fcntl.h>
#include <signal.h>
#include <unistd.h>
#include <errno.h>
#include <time.h>

#define size 32	//共享内存大小
#define MSGKEY 75	//消息队列键值

char op[5] = { '#', '+', '-', '*', '/', };
int flag = 0;	//标志位,记录是否能算出24点
int shmid;	//共享内存id
char buff[8000];

//消息队列结构体
struct msgform{
    long mtype;	//消息类型
    char mtext[8192];	//消息内容
};

float cal(float x, float y, int op)
{
	switch (op)	//将运算符初始化为1,2,3,4四个数字
	{
	case 1:return x + y;
	case 2:return x - y;
	case 3:return x * y;
	case 4:return x / y;
	}
}
//对应表达式((AoB)oC)oD
float m1(float i, float j, float k, float t, int op1, int op2, int op3)
{
	float r1, r2, r3;
	r1 = cal(i, j, op1);
	r2 = cal(r1, k, op2);
	r3 = cal(r2, t, op3);
	return r3;
}
//对应表达式(Ao(BoC))oD
float m2(float i, float j, float k, float t, int op1, int op2, int op3)
{
	float r1, r2, r3;
	r1 = cal(j, k, op2);
	r2 = cal(i, r1, op1);
	r3 = cal(r2, t, op3);
	return r3;
}
//对应表达式Ao(Bo(CoD))
float m3(float i, float j, float k, float t, int op1, int op2, int op3)
{
	float r1, r2, r3;
	r1 = cal(k, t, op3);
	r2 = cal(j, r1, op2);
	r3 = cal(i, r2, op1);
	return r3;
}
//对应表达式Ao((BoC)oD)
float m4(float i, float j, float k, float t, int op1, int op2, int op3)
{
	float r1, r2, r3;
	r1 = cal(j, k, op2);
	r2 = cal(r1, t, op3);
	r3 = cal(i, r2, op1);
	return r3;
}
//对应表达式(AoB)o(CoD)
float m5(float i, float j, float k, float t, int op1, int op2, int op3)
{
	float r1, r2, r3;
	r1 = cal(i, j, op1);
	r2 = cal(k, t, op3);
	r3 = cal(r1, r2, op2);
	return r3;
}
void get24(int i, int j, int k, int t)
{
	int op1, op2, op3;
	for (op1 = 1; op1 <= 4; op1++) {						//通过一个三重循坏列举了符号的变化情况
		for (op2 = 1; op2 <= 4; op2++) {
			for (op3 = 1; op3 <= 4; op3++) {
				if (m1(i, j, k, t, op1, op2, op3) == 24)
				{
                    char mes[24];
					sprintf(mes,"((%d%c%d)%c%d)%c%d=24\n", i, op[op1], j, op[op2], k, op[op3], t);
                    strcat(buff,mes);
					flag = 1;
				}
				if (m2(i, j, k, t, op1, op2, op3) == 24)
				{
                    char mes[24];
					sprintf(mes,"(%d%c(%d%c%d))%c%d=24\n", i, op[op1], j, op[op2], k, op[op3], t);
                    strcat(buff,mes);
					flag = 1;
				}
				if (m3(i, j, k, t, op1, op2, op3) == 24)
				{
                    char mes[24];
					sprintf(mes,"%d%c(%d%c(%d%c%d))=24\n", i, op[op1], j, op[op2], k, op[op3], t);
                    strcat(buff,mes);
					flag = 1;
				}
				if (m4(i, j, k, t, op1, op2, op3) == 24)
				{
                    char mes[24];
					sprintf(mes,"%d%c((%d%c%d)%c%d)=24\n", i, op[op1], j, op[op2], k, op[op3], t);
                    strcat(buff,mes);
					flag = 1;
				}
				if (m5(i, j, k, t, op1, op2, op3) == 24)
				{
                    char mes[24];
					sprintf(mes,"(%d%c%d)%c(%d%c%d)=24\n", i, op[op1], j, op[op2], k, op[op3], t);
                    strcat(buff,mes);
					flag = 1;
				}
			}
		}
	}
}


int main()
{
	//共享内存
	key_t key = ftok("./shm", 'b');

	shmid = shmget(key, size, 0644 | IPC_CREAT);
	if (shmid == -1)
	{
		perror("shm get error!\n");
		exit(1);
	}
    else
    {
        char *ptr = (char *)shmat(shmid, NULL, 0);
        ptr[0]=1;
        printf("start\n");
    }
	//消息队列
	int id;
    struct msgform msg;
    id=msgget(MSGKEY,0777|IPC_CREAT);
    if (id==-1)
    {
    	perror("msg get error!\n");
    	exit(1);
    }
    //创建子进程
	pid_t pid2 = fork();
	if (pid2 < 0)
	{
		perror("fork error!");
		exit(1);
	}
//-----------------------------子进程代码--------------------------------
	if (pid2 == 0)
	{
        int i,j,k,t=0;
		char *p2 = (char *)shmat(shmid, NULL, 0);
		while (p2[0]!=0)
        {
            sleep(1);
        }
		printf("this is the child process P2!\n");
		strcpy(buff,"the result is:\n");
		if (p2[2]==0)
		{
			i=p2[3];
		    j=p2[4];
		    k=p2[5];
		    t=p2[6];
		}
        else if (p2[2]==1)
		{
			msgrcv(id,&msg,sizeof(msg),1,0);	//接收父进程消息
			sscanf(msg.mtext,"%d %d %d %d",&i,&j,&k,&t);
		}
        int a[4] = { i,j,k,t };
		for (int b = 0; b < 4; b++)
		{
			for (int c = 0; c < 4; c++)
			{
				if (c != b)
				{
					for (int d = 0; d < 4; d++)
					{
						if (d != b && d != c)
						{
							for (int e = 0; e < 4; e++)
							{
								if (e != b && e != c && e != d)
								{
									get24(a[b], a[c], a[d], a[e]);
								}
							}
						}
					}
				}
			}
		}
		printf("I have count the 4 numbers if they can get 24,the result is sent by msg\n");
		if (flag == 0)
		{
			msg.mtype=2;
			strcpy(msg.mtext,"这四个数算不出来24\n");
			msgsnd(id,&msg,sizeof(char)*8192,0);
		}
        else
        {
			msg.mtype=2;
			strcpy(msg.mtext,buff);
			msgsnd(id,&msg,sizeof(char)*8192,0);
            
        }
        p2[0]=2;	//信号量控制
	}

//----------------------------父进程代码-----------------------------
	else if (pid2 > 0)
	{
        int num1,num2,num3,num4=0;
		int mode=0;
		char *p1 = (char *)shmat(shmid, NULL, 0);
		//------------父进程向子进程通信---------------
        while (p1[0]!=1)
        {
			sleep(1);
        }
		printf("this is the parent process P1!\n");
		printf("please select a communication mode(0 represent shm,1 represent msg):\n");
		scanf("%d",&mode);
        printf("please input 4 numbers:\n");
        scanf("%d %d %d %d",&num1,&num2,&num3,&num4);
		p1[2]=mode;	//指定通讯模式
		if (p1[2]==0)	//共享内存
		{
			p1[3]=num1;
			p1[4]=num2;
			p1[5]=num3;
			p1[6]=num4;
			printf("4 numbers are sent by shm\n");
			p1[0]=0;	//信号量控制
		}
		else if (p1[2]==1)	//消息队列
		{
			msg.mtype=1;
			sprintf(msg.mtext,"%d %d %d %d",num1,num2,num3,num4);
			msgsnd(id,&msg,sizeof(char)*8192,0);
			printf("4 numbers are sent by msg\n");
			p1[0]=0;
		}
   		//------------------父进程接收子进程消息-------------------
        while (p1[0]!=2)
        {
            sleep(1);
        }
		
		if (msgrcv(id,&msg,sizeof(char)*8192,2,0) == -1) {
			printf("father process failed to receive!\n");
		}
		printf("this is the parent process P1!\n");
		printf("%s",msg.mtext);
		//删除消息队列和共享内存
		msgctl(id,IPC_RMID,0);
		if (shmctl(shmid, IPC_RMID, 0) == -1)
		{
			fprintf(stderr, "shmctl(IPC_RMID) failed\n");
			exit(EXIT_FAILURE);
		}
	}
	
	return 0;
}

2、使用命令cc -o shmmsg shmmsg.c编译源文件,使用命令./shmmsg执行文件。

(1)P1通过共享内存向P2发送四个数字1 9 10 11,P2返回“这四个数算不出来24”。

 

(2)P1通过消息队列向P2发送数字2 5 6 9,P2返回所有可能情况:

 

四.实验问题分析

1、本次实验稍有难度,主要在于创建子进程的方法上,我使用的是fork来创建子进程,它会将父进程之前的代码都复制和执行一遍,导致一些全局变量并没有起到全局的作用,所以在前期的时候,一直在不停的测试fork子进程的执行顺序,花了大量时间。后来转念一想,直接就用共享内存来存放全局变量不是就可以了吗,单独分配一块区域用来进行子进程和父进程之间通信顺序的控制。

 

2、一开始的时候,我只写了共享内存的通信方式,还一切顺利,后来加了一个消息队列进来后,发现只要是涉及到消息队列的语句都执行不了,光标就停在了消息队列发送接收执行语句之前,但是消息队列已经创建成功了。

我找了好久之后才发现是msgsnd、msgrcv这两个消息队列函数使用的有问题,这两个函数都有一个传入参数msgsz,他表示要发送或接收消息的大小,不含消息类型占用的4个字节,即mtext的长度,而我在写的时候传的参数是sizeof(msgform),是整个消息结构体的大小,没有去掉消息类型占用的四个字节。

 

3、在解决了上面的问题之后发现,子进程可以接收到父进程发来的消息了,但是父进程却收不到子进程发回来的消息,这个就是之前第一个问题里讲到的父子进程之间的执行顺序问题。

我的销毁共享内存和消息队列的语句一开始是写在main函数的最末尾,独立于子进程和父进程的分支,我一开始以为这样就可以在父子进程执行完毕后释放内存了。但其实,在子进程处理完24点,将相应的消息再发送给父进程后,他就去执行后面的销毁语句了,并不会等待父进程接收到相应的消息,所以父进程就会接收不到消息。

解决办法就是将销毁语句放在父进程代码的最末尾处,因为按照顺序父进程应该是最后执行的,等到父进程接收完相应的消息后,再释放相应的内存空间就可以了。

 

五.实验总结

本次实验我通过利用POSIX API函数编写实例程序,实现了父子进程间通过消息传递和共享内存方式进行通信。初步了解了Linux环境下进程创建和进程间通信的机制,掌握了如何利用消息和共享内存进行通信的原理。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

操作系统课程实践5_进程通信 的相关文章

  • C:如果文件描述符被删除,阻塞读取应该返回

    我正在以阻塞的方式从设备 文件描述符中读取 可能会发生这样的情况 在不同的线程中 设备被关闭并且文件描述符被删除 不幸的是 读取没有返回或注意到并且一直阻塞 作为一种解决方法 我可以使用 select 作为超时来执行 while 循环 如果
  • 无法在 64 位 Linux 上从汇编 (yasm) 代码调用 C 标准库函数

    我有一个函数foo以汇编语言编写 并在 Linux Ubuntu 64 位上使用 yasm 和 GCC 编译 它只是使用以下命令将消息打印到标准输出puts 如下所示 bits 64 extern puts global foo secti
  • 使用 inotify 的正确方法是什么?

    我想使用inotifyLinux 上的机制 我希望我的应用程序知道文件何时aaa被改变了 您能给我提供一个如何做到这一点的示例吗 文档 来自监视文件系统活动 inotify https developer ibm com tutorials
  • 推荐用于小型站点的 IRC 服务器 (ircd)? [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 情况 我想使用 IRC 机器人作为我正在研究的其他代码的通用通信接口 服务器硬件陈旧且内存不足 但运行在相对最新的 Debian GNU
  • dlopen 或 dlclose 未调用信号处理程序

    我在随机时间内收到分段错误 我注册了信号 但发生分段错误时未调用信号处理程序 include
  • 从 php/linux 获取 pdf 的布局模式(横向或纵向)

    给定一个 PDF 如何使用 PHP lib 或 Linux 命令行工具获取 PDF 的布局模式 或相对宽度 高度 Using http www tecnick com public code cp dpage php aiocp dp tc
  • 未找到 Gem 命令

    我已经在 Ubuntu 10 10 32 位上安装了 gem apt get install gem y 但当我尝试跑步时 gem install something gem 我收到未找到命令的错误 bash gem command not
  • 错误:命令“c++”失败,退出状态为 1

    所以我尝试按照以下说明安装 Pyv8https andrewwilkinson wordpress com 2012 01 23 integrating python and javascript with pyv8 https andre
  • 使用 gcc 理解共享库

    我试图理解 C 中共享库的以下行为 机器一 cat one c include
  • 在 Linux 上创建线程与进程的开销

    我试图回答在 python 中创建线程与进程有多少开销的问题 我修改了类似问题的代码 该问题基本上运行一个带有两个线程的函数 然后运行带有两个进程的相同函数并报告时间 import time sys NUM RANGE 100000000
  • 如何从远程 ssh 连接上运行的 tmux(复制模式)复制到本地剪贴板

    我通过 OS X 上的 VirtualBox 运行 Linux 我通过在无头状态下运行虚拟机 然后使用端口转发 sshing 到 Linux 机器来实现这一点 现在 无论复制到我的虚拟机上的剪贴板 我都可以粘贴到我的远程 ssh 会话上 但
  • XAMPP Windows 上的 Php Cron 作业

    嗯 我是这个词的新手CRON 据我所知 这是一个Unix安排特定操作在定义的时间间隔后执行的概念 我需要运行一个php文件 每小时更新一次数据库 但我的困惑在于安排执行 我在用XAMPP用于 Windows 7 上的本地开发测试 我发现了什
  • 在 Linux 控制台中返回一行?

    我知道我可以返回该行并用以下内容覆盖其内容 r 现在我怎样才能进入上一行来改变它呢 或者有没有办法打印到控制台窗口中的特定光标位置 我的目标是使用 PHP 创建一些自刷新的多行控制台应用程序 Use ANSI 转义码 http en wik
  • 为什么docker容器提示“权限被拒绝”?

    我使用以下命令来运行 docker 容器 并从主机映射目录 root database 到容器 tmp install database docker run it name oracle install v root database t
  • SMP 上如何处理中断?

    SMP 对称多处理器 多核 机器上如何处理中断 内存管理单元是只有一个还是多个 假设两个线程 A 和 B 运行在不同的内核上 同时 访问页表中不存在的内存页面 在这种情况下 将会出现页面错误 并从内存中引入新页面 将会发生的事件的顺序是什么
  • 运行 shell 命令并将输出发送到文件?

    我需要能够通过 php 脚本修改我的 openvpn 身份验证文件 我已将我的 http 用户设置为免通 sudoer 因为这台机器仅在我的家庭网络中可用 我目前有以下命令 echo shell exec sudo echo usernam
  • 用于时间线数据的类似 gnuplot 的程序

    我正在寻找一个类似 gnuplot用于在时间轴中绘制数据图表的程序 类似 gnuplot 在 Linux 上运行 命令行功能 GUI 对我帮助不大 可编写脚本的语法 输出为 jpg png svg 或 gif 输出应该是这样的 set5 s
  • php_network_getaddresses: getaddrinfo 失败: 名称或服务未知 (0) 连接失败..!

    我正在使用 php 邮件程序功能 但出现以下错误 如何修复它 2016 01 22 06 15 48 SMTP 错误 无法连接到服务器 php network getaddresses getaddrinfo失败 名称或服务未知 0 连接失
  • aarch64 Linux 硬浮点或软浮点

    linux系统有arm64 有arm架构armv8 a 如何知道 Debian 运行的是硬浮动还是软浮动 符合 AAPCS64 GNU GCC for armv8仅提供硬浮动aarch64工具链 这与 armv7 a 的 GCC 不同 后者
  • 如何从程序内部获取指向程序的特定可执行文件部分的指针? (也许是诽谤)

    我在 Linux 环境中 需要编写一个程序来检索放置在其可执行文件的某个部分中的一些数据 那么 如何从程序内部获取指向程序某个部分 通过其名称 的指针呢 我知道可以使用elf getdata 将节的索引作为参数传递给 get 和Elf Da

随机推荐

  • Qt程序的打包和发布(使用windeployqt)

    将编写完成的Qt程序进行打包 使得没有安装Qt环境的电脑也可以运行编写完成的应用 1 Release文件生成 在Qt Creator中 将构建方式改为Release 点击运行 或ctrl r快捷键运行 2 找到exe文件 复制到一个空目录中
  • java单元测试之Mock静态方法

    用例子说明 例如有下面静态方法 public final class AmountUtil public static String CustomFormatWith2Digits int amount return 1 单元测试代码 需要
  • 服务器端虚拟化安卓,安卓服务器端实例

    安卓服务器端实例 内容精选 换一换 本章节以Linux操作系统为例 指导您通过内网IP的方式连接GaussDB for Redis 实例 目标实例必须与弹性云服务器在同一个虚拟私有云和子网内才能访问 弹性云服务器必须处于目标实例所属安全组允
  • vscode和vs有什么区别?

    vscode是微软新推出的一款代码编辑器 内置了一些编译器 免费开源跨平台的工具 VS是微软的商业企业级开发环境IDE 在这之前 我们写代码的工具可以分为三个层次 最轻量级的叫做代码编辑器 例如notepad notepad subline
  • OD查看字符串

    在反汇编窗口中右击 出来一个菜单 我们在 查找 gt 所有参考文本字串 上左键点击 在text string窗口后 再右击这个窗口里面随便一处 选 search for text 输入要查找的内容 把Case sensitive 区分大小写
  • 【RoCE】拥塞控制机制(ECN, DC-QCN)

    1 网络拥塞问题 在网络交换机中 当入口流量大于出口流量的带宽时会发生网络拥塞 典型的例子是多个发送方同时向同一个目的地发送网络数据 交换机的缓存可以处理暂时的拥塞 但是当拥塞太久时 交换机的缓存就会过载 当交换机缓存过载时 下一个收到的新
  • mysql ipk 编译_OpenWrt的ipk包安装

    在 make menuconfig 进行裁减 OpenWrt 时 为了让系统更精小一点 我们会把部分功能以 模块 的方式编译 即不编入内核 只是在后期用户可以进行安装与卸载 包安装示例 如下关于Lua的配置项 其中 json4lua lua
  • 头文件库文件

    问题 不知道 Visual Studio 的头文件库文件如何配置 方法 C C gt 常规 gt 附加包含目录 添加头文件 链接器 gt 常规 gt 附加库目录 添加库目录 链接器 gt 输入 gt 附加依赖项 添加库文件
  • zip文件解压详解

    文章目录 1 起因 2 详解 3 实践 4 参考 1 起因 自己之前在linux系统解压zip文件 一直用 unzip zip 我们期望解压后的文件都是统一放到以 命名的文件夹下 但是自己有一次解压后发现所有的文件并没有放到上述文件夹下 而
  • 使用 @RequestMapping 注解,需要导入的包:spring-webmvc

    在Controller 层使用 RequestMapping注解 需要导入的包 spring webmvc 在类中需要添加 import org springframework web bind annotation RequestMapp
  • Pycharm激活方法使用的是(license server)Activate new license with: License server

    pycharm所有版本 http www jetbrains com pycharm download previous html 打开激活窗口 选择 Activate new license with License server 用li
  • 【JVM调优】JDK11-JVM基本参数调优以及日志打印

    Parm Xmx2g Xms1g Xss256k XX MaxDirectMemorySize 256m XX UseG1GC XX UseCompressedOops XX UseCompressedClassPointers XX Se
  • Python Interview Questions: A Review

    本文转载至 https www udemy com blog python interview questions Python is an elegant and versatile language used for a wide va
  • java获取当前时间戳的方法

    https www cnblogs com zhujiabin p 6168671 html utm source itdadao utm medium referral 获取当前时间戳 方法 一System currentTimeMill
  • Qt中的位置和尺寸

    在QT中我们常见的 点 线 尺寸 矩形 都被进行了封装 下边依次为大家介绍相关的类 目录 QPoint QLine QSize QRect QPoint QPoint是C 编程语言中Qt框架中的一个类 它表示2D坐标系中的一个点 它用于定义
  • python如何判断一个数是整数,浮点数,复数还是字符?

    遇到判断字符类型 上网搜索了一下 整理下来 1 判断字符串 python字符串常用的判断函数很多 有如下8种 1 str isalnum 所有字符都是数字或者字母 2 str isdecimal 所有字符都是十进制数字 3 str isdi
  • 字符串格式化:% 运算符

    1 课题导入 任务 用字符串拼接法原样输出 圆周率是3 1415926 声明变量pi 用于存储圆周率 pi 3 1415926 type函数查看变量pi 的数据类型 print type pi 用字符串拼接的方法输出 str函数将浮点数类型
  • 一个简单音乐播放器的java实现(一)

    写在前面 这几天正在读head first系列的书籍 现在正好读的是java 这本书讲的深入浅出 环环相扣 非常精彩 不妨安利给大家 顺便把我学习过程中的一些心得体会已经实例分享出来 1 一个最简单音乐播放器的需求 我们需要四样东西 1 播
  • 【AlexNet论文精读以及代码复现以及训练结果】

    AlexNet论文精读以及代码复现 三遍读一篇论文的方法 李沐 1 标题 gt 摘要 gt 结论 gt 关键的图表 2 从标题开始读到最后 太过于细节部分可以先放一下 搞清楚重要的图标的细节 3 第三遍要弄清楚每句话在干嘛 搞清楚细节 并且
  • 操作系统课程实践5_进程通信

    一 实验目的 1 初步了解Linux环境下进程创建和进程间通信的机制 2 掌握如何利用消息和共享内存进行通信的原理 3 利用POSIX API函数编写实例程序 4 实现父子进程间通过消息传递和共享内存方式进行通信 二 实验内容 编制两个程序