《操作系统原理》实验报告
一、实验目的
(1)理解操作系统生成的概念和过程;
(2)理解操作系统两类用户界面(操作界面,系统调用)概念;
二、实验内容
(1)在Unbantu或Fedora环境下裁剪和编译Linux内核,并启用新的内核。
(2)在Unbantu或Fedora环境下为Linux内核增加1-3个新的系统调用,并启用新的内核,编写一个应用程序测试新增加的系统调用是否能正确工作。
(3)在windows环境下,利用高级语言编程环境(限定为VS环境或VC环境)调用相关的系统调用(即系统API)实现一个包括“进程创建,文件读写”的应用程序。(特别注意:自己在寝室先做,老师在机房现场检查结果!):有一个文本文件CommandList.txt,第一行是说明性文字:本文件最后一次打开和运行日期是0325。第二行开始每行是-一个可执行程序的名称(含路径)。编写一个应用程序能打开该文件,并顺序执行其中的每个程序,并更新文件第一行中的日期。
(4)在windows环境下,编写一个批处理程序(算命大师.bat),程序运行后,输入:出生年月日(例如20000731)。系统输出你的属相和星座,例如:你属兔,狮子座。(特别注意:自己在寝室先做,老师在机房现场检查结果!)
三、实验过程
此处主要粘贴核心的关键源代码和程序流程,并分析。把你做完了的全部编程题按1),2)…方式顺序编号,每个编程题目自己给取一个5-10字的名字作为标题。
实验过程要给出环境配置过程的说明,开发过程的文字说明,开发环境中关键代码的典型截图和文字说明。
1)Ubuntu下编译新内核
环境配置:下载安装VMware,并创建Ubuntu系统的新虚拟机。
逐次执行以下命令:
sudo apt-get install libncurses5-dev libssl-dev
sudo apt-get install build-essential openssl
sudo apt-get install zlibc minizip
sudo apt-get install libidn11-dev libidn11
从这里下载linux内核
/pub/linux/kernel/v4.x/下载后解压即可。
然后依次执行如下命令:
sudo make mrproper
sudo make clean
sudo make menuconfig
其中mrproper为清除编译过程中产生的所有中间文件,clean为清除上一次产生的编译中间文件,在menuconfig中出现选择的图形化界面后,直接按右方向键选择到exit退出,退出提示中选择保存,实现内核的默认配置。
• 设置的四核八线程,故使用指令make –j8
2)Ubuntu中为linux添加系统调用
*下载linux内核包并解压,在/kernel/sys.c文件末尾加入系统调用函数。
*添加函数声明,添加系统调用号
在linux-4.16.10/arch/x86/include/asm/vim syscalls.h
(插入asmlinkage long sys_mycall(int number);)
*添加函数调用id
在linux-4.16.10/arch/x86/syscalls的syscall_64.tbl文件中做如下添加。
配置编译安装内核
依次执行make oldconfig
make –j8
sudo makemodules_install
sudomake install
重启虚拟机,选择最新的linux内核运行。
验证系统调用是否成功
a、编写测试代码
b、打开终端
c、输入下列指令:
3)创建进程程序
利用vc编写源代码
获取时间写入文本,
读取文本的第二到最后一行,每行存入字符数组
依次读取每行地址,创建相应进程。
部分代码截图
Windows进程创建函数解读
BOOL CreateProcess
(
LPCTSTR lpApplicationName,
LPTSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes。
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCTSTR lpCurrentDirectory,
LPSTARTUPINFO lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation
);
lpApplicationName:指向一个NULL结尾的、用来指定可执行模块的字符串。
这个字符串可以使可执行模块的绝对路径,也可以是相对路径,在后一种情况下,函数使用当前驱动器和目录建立可执行模块的路径。
这个参数可以被设为NULL,在这种情况下,可执行模块的名字必须处于 lpCommandLine 参数的最前面并由空格符与后面的字符分开。
lpCommandLine:指向一个NULL结尾的、用来指定要运行的命令行。
这个参数可以为空,那么函数将使用参数指定的字符串当作要运行的程序的命令行。
如果lpApplicationName和lpCommandLine参数都不为空,那么lpApplicationName参数指定将要被运行的模块,lpCommandLine参数指定将被运行的模块的命令行
。新运行的进程可以使用GetCommandLine函数获得整个命令行。
lpProcessAttributes:指向一个SECURITY_ATTRIBUTES结构体,这个结构体决定是否返回的句柄可以被子进程继承。如果lpProcessAttributes参数为空(NULL)
,那么句柄不能被继承。
lpThreadAttributes:指向一个SECURITY_ATTRIBUTES结构体,这个结构体决定是否返回的句柄可以被子进程继承。如果lpThreadAttributes参数为空(NULL),
那么句柄不能被继承。
bInheritHandles:指示新进程是否从调用进程处继承了句柄。如果参数的值为真,调用进程中的每一个可继承的打开句柄都将被子进程继承。被继承的句柄与原
进程拥有完全相同的值和访问权限。
dwCreationFlags:指定附加的、用来控制优先类和进程的创建的标志。
lpEnvironment:指向一个新进程的环境块。如果此参数为空,新进程使用调用进程的环境。
一个环境块存在于一个由以NULL结尾的字符串组成的块中,这个块也是以NULL结尾的。每个字符串都是name=value的形式。
lpCurrentDirectory:指向一个以NULL结尾的字符串,这个字符串用来指定子进程的工作路径。这个字符串必须是一个包含驱动器名的绝对路径。如果这个参数
为空,新进程将使用与调用进程相同的驱动器和目录。这个选项是一个需要启动启动应用程序并指定它们的驱动器和工作目录的外壳程序的主要条件。
lpStartupInfo:指向一个用于决定新进程的主窗体如何显示的STARTUPINFO结构体。
lpProcessInformation:指向一个用来接收新进程的识别信息的PROCESS_INFORMATION结构体。
4)算命大师
代码如下
主要是利用脚本语言输入参数,处理参数,比较参数输出相应信息。
@Echo Off
Set /p yearmonthday=输入: 出生年月日 (例如 2000-07-31)
Set year=%yearmonthday:~0,4%
Set monthday=%yearmonthday:5,2%%yearmonthday:8,2%
Set /a mod=%year%%%12
if %mod%==0 Echo 你属猴
if %mod%==1 Echo 你属鸡
if %mod%==2 Echo 你属狗
if %mod%==3 Echo 你属猪
if %mod%==4 Echo 你属鼠
if %mod%==5 Echo 你属牛
if %mod%==6 Echo 你属虎
if %mod%==7 Echo 你属兔
if %mod%==8 Echo 你属龙
if %mod%==9 Echo 你属蛇
if %mod%==10 Echo 你属马
if %mod%==11 Echo 你属羊
if “%monthday%” LEQ “0119” echo 你的星座是魔蝎座
if “%monthday%” GEQ “0120” if “%monthday%” LEQ “0218” echo 你的星座是水瓶座
if “%monthday%” GEQ “0219” if “%monthday%” LEQ “0320” echo 你的星座是双鱼座
if “%monthday%” GEQ “0321” if “%monthday%” LEQ “0419” echo 你的星座是白羊座
if “%monthday%” GEQ “0420” if “%monthday%” LEQ “0520” echo 你的星座是金牛座
if “%monthday%” GEQ “0521” if “%monthday%” LEQ “0621” echo 你的星座是双子座
if “%monthday%” GEQ “0622” if “%monthday%” LEQ “0722” echo 你的星座是巨蟹座
if “%monthday%” GEQ “0723” if “%monthday%” LEQ “0822” echo 你的星座是狮子座
if “%monthday%” GEQ “0823” if “%monthday%” LEQ “0922” echo 你的星座是处女座
if “%monthday%” GEQ “0923” if “%monthday%” LEQ “1023” echo 你的星座是天秤座
if “%monthday%” GEQ “1024” if “%monthday%” LEQ “1122” echo 你的星座是天蝎座
if “%monthday%” GEQ “0321” if “%monthday%” LEQ “0419” echo 你的星座是白羊座
if “%monthday%” GEQ “1222” echo 你的星座是魔蝎座
四、实验结果
此处主要粘贴运行截图和分析。把你做完了的全部编程题按1),2)…方式顺序编号,每个编程题目自己给取一个5-10字的名字作为标题。
1)重启长按shift键,进入选择
选择新内核运行之,编译安装新内核成功!!
2)重启虚拟机,选择最新的linux内核运行。
验证系统调用是否成功
a、编写测试代码
b、打开终端
c、输入下列指令:
3)成功启动了两个程序(护眼包和酷狗音乐)
4)运行结果如下
/*进程创建代码(第三个实验代码)---------------------------------author JaryJin*/#include <windows.h>#include <string>#pragma warning(disable:4996)int main(){char timeContent[100] = {0};char readBuffer[1000] = {0};char command[10][100] = {0}; int commandNum = 0; DWORD dwRead = 0;DWORD dwWrite = 0;HANDLE hFile = CreateFile("./CommandList.txt", GENERIC_WRITE | GENERIC_READ, 0,NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);if (hFile == INVALID_HANDLE_VALUE){printf("CreateFile failed!");CloseHandle(hFile);return -1;}//写入时间FILETIME createTime,lastAccessTime,lastWriteTime;GetFileTime(hFile,&createTime,&lastAccessTime,&lastWriteTime);SYSTEMTIME st;memset(&st,0x0,sizeof(st));FileTimeToSystemTime(&lastWriteTime,&st);//将文件时间格式转换为系统时间格式(UTC格式),可以看到小时数比真实的小了8TIME_ZONE_INFORMATION tz;GetTimeZoneInformation(&tz);//获取当地时区信息SYSTEMTIME localST;SystemTimeToTzSpecificLocalTime(&tz,&st,&localST);//将UTC时间格式转换为当地时间格式,因为中国是东8区,所以转换时在小时上加了8sprintf(timeContent,"本文件最后一次打开和运行日期是%4d年%02d月%02d日%02d时%02d分%02d秒\r\n",localST.wYear,localST.wMonth,localST.wDay,localST.wHour,localST.wMinute,localST.wSecond);WriteFile(hFile, timeContent, strlen(timeContent), &dwWrite, NULL);//读取文本DWORD fileSize = GetFileSize(hFile, NULL);ReadFile(hFile, readBuffer, fileSize, &dwRead, NULL);readBuffer[fileSize] = '\0';//printf("%s",readBuffer);CloseHandle(hFile);//循环将读取的命令分隔开来char tmpBuffer[1000] = {0};for(int i=0;i<fileSize;i++){if('\r' == readBuffer[i]){strncpy(command[commandNum],readBuffer,i);i=i+2;commandNum++;strcpy(tmpBuffer,&readBuffer[i]);strcpy(readBuffer,tmpBuffer);i=0;}else if('\0' == readBuffer[i]){strncpy(command[commandNum],readBuffer,i);commandNum++;break;}}//循环创建进程for(int j=0;j<commandNum;j++){STARTUPINFO si = { sizeof(si) };PROCESS_INFORMATION pi;si.dwFlags = STARTF_USESHOWWINDOW; si.wShowWindow = TRUE; BOOL bRet = CreateProcess ( NULL,command[j],NULL,NULL,FALSE,CREATE_NEW_CONSOLE,NULL,NULL,&si, &pi);if(bRet){ // 不使用的句柄最好关掉CloseHandle(pi.hThread);CloseHandle(pi.hProcess);printf("新进程的ID号:%d\n",pi.dwProcessId);printf("新进程的主线程ID号:%d\n",pi.dwThreadId);}Sleep(1000);}system("pause");return 0;}
五、体会
作为一个linux小白实现上述过程是很痛苦的,但是真的很想去把这件事做好,一步一步,从什么都不知道到好像懂了一点点,再到可以看懂每行代码的含义,到最后做出成果,刚开始做的时候比较浮躁,到了后面就冷静了。看了20多篇博客,解决各种问题;之前做的时候犯的错误多的数不胜数,后面弄的时候一点点规避和解决,功夫不负有心人,虽然为了这个作业花了我不少精力(本人理解能力较差,周围有的大佬很快就弄完了),这次的成功相信可以给我后面进一步学好操作系统的信心,也培养了自己的耐心和攻坚精神。相信自己会继续加油的!