课程设计——LINUX系统下多进程的创建与通信

2023-10-08 15:50

本文主要是介绍课程设计——LINUX系统下多进程的创建与通信,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

##课程设计报告

( 2014 – 2015 年度第 1学期)

名 称: Unix/Linux编程课程设计
题 目:LINUX系统下多进程的创建与通信
院 系: 控制与计算机工程学院
班 级:
学 号:
学生姓名:
指导教师:
设计周数: 1

成 绩:

日期:2015 年 1月 9日

一、课程设计的目的与要求

1 目的:学习UNIX/LINUX系统下的多进程创建、控制和通信。

  1. Linux上的bash和Windows中的命令行有很大的不同。但是两者都有完成相似任务的命令,比如Linux上bash的ls命令的功能,类似于Windows命令行中的dir命令的功能。用C语言写一个简单的Linux终端软件,接收用户发出的类似于Windows命令行中的命令,转换成对应的Linux命令加以执行,并将执行的结果回显给用户。比如,用户输入“dir”,程序实际返回“ls”的内容。

  2. 软件包含前、后台两个程序,用户启动前台程序时,前台程序自行启动后台程序。前台程序提供界面,负责接收用户输入,对输入进行转换,并向后台程序发出实际要执行的指令,后台负责执行实际的指令。

2 要求:

  1. 前台程序通过fork和execl系统调用启动后台程序。
  2. 前台程序创建消息队列和命名管道,通过消息队列向后台程序发送经过转换的用户命令;通过命名管道从后台程序获取命令执行的结果,并显示在终端。后台程序可以通过popen来执行转换后的命令。
  3. 至少实现如下Windows——Linux对照命令:dir——ls,rename——mv,move——mv,del——rm,cd——cd(pwd),exit——exit。
  4. 当用户输入exit时,前台程序指示后台程序结束,在后台程序结束后,前台程序退出;在此之前,用户的输入都被作为一条命令进行处理。

二、设计正文

1 设计思路概要:

根据题目要求,需要在前台程序接收用户输入,然后将Windows命令转换成Linux可执行命令,以消息队列的形式传给后台程序,后台程序接收消息队列中的内容,并执行把结果写入管道中,前台程序读取管道内容打印到标准输出。
程序关键在于理解对管道读写,消息队列的发送与接受,以及子进程是如何在父进程中调用执行的。同时,进程间同步与异步的关系也直接影响两个进程的逻辑结构。
前期开发过程中,按照《Linux编程从入门到精通》的例子分块编写好管道的读写、消息队列的接收、进程的克隆,并调试通过。然后同样分块编写Windows与Linux命令的转换和Linux命令使用popen()管道执行。中期分析了整个程序的逻辑结构,把程序的流程大体跑通。后期测试和优化了一些输出和功能。

2 程序介绍

  1. 子进程的创建
    用fork函数创建子进程,当返回值pid为0时代表子进程,pid大于0时代表父进程。然后子进程调用函数execl执行background后台程序,父进程继续执行。

  2. 命令的转换
    用两个数组分别保存Windows命令和Linux命令,将输入的命令分割成两部分,一部分是命令,另一部分是所带的参数。将命令的部分与Windows命令的数组比较,找到对应的Linux命令,然后将其和带参数的部分拼接,就完成了命令的转换。

  3. 消息队列的收发
    消息队列直接封装成两个函数,发送和接收消息队列都要用到一个结构体保存消息队列的类型和传输的内容。消息队列的收发是异步的,但是要保证先发送消息队列然后接收,才能够让程序正常运行。为了解决这个问题,在收发之前加入一个同步的管道syn_pipe。

  4. 管道的读写
    管道的使用要先在初始化过程中创建管道文件,然后才能够读写管道。关键的一点是管道是同步通讯的,也就是读取管道的时候必须要有进程写管道,否则一直等待直到其他进程写管道。同样写管道的一方也到等到其他进程读管道,才能执行之后的程序。这在编程调试的时候是很重要的。

  5. 命令的执行
    命令的执行使用管道函数popen()执行shell命令,代码是从网上摘取,详情见附录。

3 功能特色

除了课程设计的六个命令的实现外,左边会输出当前的工作目录,用dir查看文件时会按文件名长度对齐输出,仿Linux的ls -a命令。

三、课程设计总结或结论

这次课程设计加深了我对书本知识的理解,Makefile文件的编写、vi编辑器的使用、以及课设中使用的函数的用途等。
课程设计让我对C语言的语法、C语言指针、内存等问题有了深刻的认识,也付出了一些的代价。
这次课程设计编写用时3天,查找错误陷入各种Bugg用时3天。所有的代码都是自己独立完成,即使是书上的代码用例和网上借鉴的小程序段都在参考文献一一注明。有些功能实现的细节并没有写在文档上。
期间对进程之间以同步异步方式通讯、子进程的调用执行、sscanf和printf函数的使用、动态申请内存有了进一步的理解。
总之,这次课程设计有一些收获,同时也能够在要求的时间完成任务。、
学完Linux这么课程,以后有时间会试着写简单的Shell脚本。

四、参考文献
[1] Linux下使用popen()执行shell命令
http://www.cnblogs.com/caosiyang/archive/2012/06/25/2560976.html
[2] 管道读写:《Linux编程》例子15-6,7 9-11
[3] 进程创建子进程:《Linux编程》例子13-11
[4] 消息队列的发送接收:例子16-4,5
[5] 用sscanf解析字符串 http://www.cnblogs.com/lyq105/archive/2009/11/28/1612677.html
[6] 用sprintf格式化输出字符串

附录

1 设计流程图

流程图

2 程序代码

main.c

#include <stdio.h>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>static int order_num = 6;
char **windows_orders[6] = {"dir","rename","move","del","cd","exit"};
char **linux_orders[6] = {"ls","mv","mv","rm","cd","exit"};char *order_change(char *windows_order)
{int i;char *temp;char *order_pre;char *order_para;if((temp = (char*)malloc(20*sizeof(char))) ==0)return 0;if((order_pre = (char*)malloc(20*sizeof(char))) ==0)return 0;if((order_para = (char*)malloc(20*sizeof(char))) ==0)return 0;sscanf(windows_order,"%s%[^'\n']",order_pre,order_para);//printf("order_pre   :%s\n",order_pre);//printf("order_para   :%s\n",order_para);if(!strcmp(order_pre,"cd") && strlen(order_para) == 0)return "pwd";else if(!strcmp(order_pre,"cd") && strlen(order_para) != 0){chdir("home");}for(i=0;i<order_num;i++) {if(strstr(order_pre,windows_orders[i]) != 0){strcpy(temp,linux_orders[i]);strcat(temp,order_para);return temp;}}return "";
}char *loadpath(char *pwd, char *filename)
{char path[1024];strcpy(path,pwd);strcat(path,"/");strcat(path,filename);return path;
}
main()
{pid_t pid;char *win_order;char *linux_order;
//	Why ?	Segmentation fault (core dumped)if((win_order = (char*)malloc(20*sizeof(char))) == 0)return 0;if((linux_order = (char*)malloc(20*sizeof(char))) ==0)return 0;char result[1001];char pwd[1024];char pro_path[1024];getcwd(pro_path,1024);create_pipe(loadpath(pro_path,"lx_pipe"));create_pipe(loadpath(pro_path,"syn_pipe"));create_pipe(loadpath(pro_path,"pwd_pipe"));// 最新的signal.h已经没有SIGCLD了,可能得用 signal(SIGCHLD, SIG_IGN);signal(SIGCLD,SIG_IGN);switch(pid=fork()){case -1:perror("fork");break;case 0:			/* 子进程 */{if(execl(loadpath(pro_path,"background"),NULL)== -1){perror("execl");exit(0);}break;}default:		/* 父进程 */{while(1){read_pipe(loadpath(pro_path,"pwd_pipe"),&pwd);sscanf(pwd,"%[^ ]",pwd);printf("%s:",pwd);gets(win_order);//if(strcmp(win_order,"exit") == 0){kill(pid,SIGKILL);exit(0);}linux_order = order_change(win_order);write_pipe(loadpath(pro_path,"syn_pipe"),"Start Send");//	发送msg//if(strlen(linux_order) == 0)//{//	printf("Couldn't found the order,please check your order\n");//	continue;//}send_msg(linux_order,pro_path);//	读取piperead_pipe(loadpath(pro_path,"lx_pipe"),&result);printf("%s\n",result);}break;}}
}

background.c

#include <stdio.h>
#include <string.h>
#include <unistd.h> 
//execute shell command
//执行一个shell命令,输出结果逐行存储在resvec中,并返回行数
void myexec(const char *cmd, char *resvec) {int line_num = 0;char path[1024];strcpy(resvec,"");if(strstr(cmd,"cd") != 0){sscanf(cmd,"%*s%s",path);int error = chdir(path);if(error == -1)strcpy(resvec,"error: couldn't find the filepath");return;}FILE *pp = popen(cmd, "r"); //建立管道if (!pp) {return;}char tmp[1024]; //设置一个合适的长度,以存储每一行输出while (fgets(tmp, sizeof(tmp), pp) != NULL) {if (tmp[strlen(tmp) - 1] == '\n') {tmp[strlen(tmp) - 1] = '\0'; //去除换行符}if(strlen(tmp) > 12){sprintf(tmp, "%-24s", tmp);line_num++;}elsesprintf(tmp, "%-12s", tmp);	//标准格式strcat(resvec,tmp);if(++line_num % 6 == 0)strcat(resvec,"\n");}pclose(pp); //关闭管道return;
}
char *loadpath(char *pwd, char *filename)
{char path[1024];strcpy(path,pwd);strcat(path,"/");strcat(path,filename);return path;
}
main()
{char resvec[1000];char rev_msg[256];char rcv_p[20];char pro_path[1024];getcwd(pro_path,1024);while(1){myexec("pwd",&resvec);write_pipe(loadpath(pro_path,"pwd_pipe"),resvec);read_pipe(loadpath(pro_path,"syn_pipe"),&rcv_p);receive_msg(&rev_msg,pro_path);myexec(rev_msg,&resvec);//printf("%s\n",resvec);//打印容器的内容write_pipe(loadpath(pro_path,"lx_pipe"),resvec);}
}

message.c

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <errno.h>
typedef struct
{long int nType;char szText[256];
}MSG;char *load(char *pwd, char *filename)
{char path[1024];strcpy(path,pwd);strcat(path,"/");strcat(path,filename);return path;
}
void send_msg(char *content, char *pro_path)
{key_t IKey;int nMsgId;MSG msg;if((IKey = ftok (load(pro_path,"profile"),2)) == -1){printf("send\n");perror("ftok");exit(1);}// 队列存在则退出,否则创建新的队列if((nMsgId = msgget(IKey,IPC_CREAT|IPC_EXCL|0666)) == -1){if(errno != EEXIST)		/* 消息队列创建失败 */{perror("msgget");exit(2);}if((nMsgId = msgget(IKey,0)) == -1)  /* 消息队列已存在 */{perror("msgget");exit(3);}}memset(&msg,0x00,sizeof(MSG));		/* 清空队列结构 */msg.nType = 2;memcpy(msg.szText,content,strlen(content));if(msgsnd(nMsgId,(const void *)&msg,strlen(msg.szText),IPC_NOWAIT) <0){printf("error\n");perror("msgsnd");}
}
void receive_msg(char *rcv_msg, char *pro_path)
{key_t IKey;int n,nMsgId;MSG msg;if((IKey = ftok (load(pro_path,"profile"),2)) == -1){printf("receive\n");perror("ftok");exit(1);}if((nMsgId = msgget(IKey,0)) == -1)  /* 消息队列已存在 */{printf("receive\n");perror("ftok");exit(2);}memset(&msg,0x00,sizeof(MSG));		/* 清空队列结构 */if((n = msgrcv(nMsgId,(const void *)&msg,sizeof(msg.szText),2L,0)) <0){perror("msgrcv");}else{	strcpy(rcv_msg,msg.szText);
//		return msg.szText;}
}

pipe.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>void create_pipe(char *pipe_name)
{if(access(pipe_name, R_OK|W_OK) != -1)return;if(mkfifo(pipe_name,0644) < 0){perror("mkfifo");exit(-1);}
}
void read_pipe(char *pipe_name,char *result)
{int fd;if((fd = open(pipe_name,O_RDONLY,0)) < 0){perror("open");exit(-1);}
//	printf("start read pipe\n");if(fd != -1){if(read(fd,result,1000) != -1){return;}}close(fd);return;
}
void write_pipe(char *pipe_name,char *content)
{int fd;if((fd = open(pipe_name,O_WRONLY,0)) < 0){perror("open");exit(-1);}
//	printf("start write pipe\n");if(fd != -1){if(write(fd,content,1000) == -1)return;}close(fd);return;
}

Makefile

all :demo background profile pipe
profile:touch profile
pipe:rm -f *_pipe
background:background.o message.o pipe.ogcc background.o message.o pipe.o -o background
demo:main.o message.o pipe.ogcc main.o message.o pipe.o -o demo
clean:rm *.o demo background

3 开始界面

dir命令
rename命令
move命令
del命令
cd命令
exit命令

4 命令介绍

dir 只可查询当前目录或者用户主目录。可以使用dir :显示文件的信息。
rename 格式:rename 旧文件 新文件。建立文件时要写文件后缀(可改变文件的后缀),但是旧的文件必须有后缀名,新的可没有。
move 格式:move 1.txt ./1(文件夹) 注:数字就必须加./,字母的名字可不用
move ./a ./b 文件夹,移动的文件可以没有后缀名。可从文件夹下移出,move ./a/d.c ./。
del 格式:del 文件名。不能删文件夹。可以删文件夹下的某个文件a/a.c。可以递归删除文件夹内容,如:del –r ./a将a文件夹及其文件夹下的内容全删掉。可删上一级目录,如:del …/1.c
pwd 显示工作目录的路径名称
exit 退出

这篇关于课程设计——LINUX系统下多进程的创建与通信的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/166374

相关文章

防止Linux rm命令误操作的多场景防护方案与实践

《防止Linuxrm命令误操作的多场景防护方案与实践》在Linux系统中,rm命令是删除文件和目录的高效工具,但一旦误操作,如执行rm-rf/或rm-rf/*,极易导致系统数据灾难,本文针对不同场景... 目录引言理解 rm 命令及误操作风险rm 命令基础常见误操作案例防护方案使用 rm编程 别名及安全删除

Linux下MySQL数据库定时备份脚本与Crontab配置教学

《Linux下MySQL数据库定时备份脚本与Crontab配置教学》在生产环境中,数据库是核心资产之一,定期备份数据库可以有效防止意外数据丢失,本文将分享一份MySQL定时备份脚本,并讲解如何通过cr... 目录备份脚本详解脚本功能说明授权与可执行权限使用 Crontab 定时执行编辑 Crontab添加定

使用docker搭建嵌入式Linux开发环境

《使用docker搭建嵌入式Linux开发环境》本文主要介绍了使用docker搭建嵌入式Linux开发环境,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面... 目录1、前言2、安装docker3、编写容器管理脚本4、创建容器1、前言在日常开发全志、rk等不同

JWT + 拦截器实现无状态登录系统

《JWT+拦截器实现无状态登录系统》JWT(JSONWebToken)提供了一种无状态的解决方案:用户登录后,服务器返回一个Token,后续请求携带该Token即可完成身份验证,无需服务器存储会话... 目录✅ 引言 一、JWT 是什么? 二、技术选型 三、项目结构 四、核心代码实现4.1 添加依赖(pom

基于Python实现自动化邮件发送系统的完整指南

《基于Python实现自动化邮件发送系统的完整指南》在现代软件开发和自动化流程中,邮件通知是一个常见且实用的功能,无论是用于发送报告、告警信息还是用户提醒,通过Python实现自动化的邮件发送功能都能... 目录一、前言:二、项目概述三、配置文件 `.env` 解析四、代码结构解析1. 导入模块2. 加载环

linux系统上安装JDK8全过程

《linux系统上安装JDK8全过程》文章介绍安装JDK的必要性及Linux下JDK8的安装步骤,包括卸载旧版本、下载解压、配置环境变量等,强调开发需JDK,运行可选JRE,现JDK已集成JRE... 目录为什么要安装jdk?1.查看linux系统是否有自带的jdk:2.下载jdk压缩包2.解压3.配置环境

Linux搭建ftp服务器的步骤

《Linux搭建ftp服务器的步骤》本文给大家分享Linux搭建ftp服务器的步骤,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录ftp搭建1:下载vsftpd工具2:下载客户端工具3:进入配置文件目录vsftpd.conf配置文件4:

Spring创建Bean的八种主要方式详解

《Spring创建Bean的八种主要方式详解》Spring(尤其是SpringBoot)提供了多种方式来让容器创建和管理Bean,@Component、@Configuration+@Bean、@En... 目录引言一、Spring 创建 Bean 的 8 种主要方式1. @Component 及其衍生注解

Linux实现查看某一端口是否开放

《Linux实现查看某一端口是否开放》文章介绍了三种检查端口6379是否开放的方法:通过lsof查看进程占用,用netstat区分TCP/UDP监听状态,以及用telnet测试远程连接可达性... 目录1、使用lsof 命令来查看端口是否开放2、使用netstat 命令来查看端口是否开放3、使用telnet

Linux系统管理与进程任务管理方式

《Linux系统管理与进程任务管理方式》本文系统讲解Linux管理核心技能,涵盖引导流程、服务控制(Systemd与GRUB2)、进程管理(前台/后台运行、工具使用)、计划任务(at/cron)及常用... 目录引言一、linux系统引导过程与服务控制1.1 系统引导的五个关键阶段1.2 GRUB2的进化优