西北工业大学操作系统实验_OS3(9)
西北工业大学操作系统实验报告实验四

实验四进程与线程一、实验目的(1)理解进程的独立空间;(2)理解线程的相关概念。
二、实验内容与要求1、查阅资料,掌握进程创建和构造的相关知识和线程创建和构造的相关知识,了解C语言程序编写的相关知识;2、理解进程的独立空间的实验内容及步骤(1)编写一个程序,在其 main()函数中定义一个变量 shared,对其进行循环加/减操作,并输出每次操作后的结果;(2)使用系统调用 fork()创建子进程,观察该变量的变化;(3)修改程序把 shared变量定义到 main()函数之外,重复第(2)步操作,观察该变量的变化。
3、理解线程的实验步骤(1)编写一个程序,在其 main()函数中创建一个(或多个)线程,观察该线程是如何与主线程并发运行的。
输出每次操作后的结果;(2)在 main()函数外定义一个变量shared(全局变量),在main()中创建一个线程,在 main()中和新线程shared进行循环加/减操作,观察该变量的变化;(3)修改程序把shared变量定义到 main()函数之内,重复第(2)步操作,观察该变量的变化。
4、对整个实验过程进行分析总结,给出详细步骤;(1) 观察上述进程执行结果,并分析原因;(2) 提交源程序清单,并附加流程图与注释。
三、实验过程1、进程的与线程的创建和构造(1).进程的创建和构造进程简单来说就是在操作系统中运行的程序,它是操作系统资源管理的最小单位。
但是进程是一个动态的实体,它是程序的一次执行过程。
进程和程序的区别在于:进程是动态的,程序是静态的,进程是运行中的程序,而程序是一些保存在硬盘上的可执行代码。
新的进程通过克隆旧的程序(当前进程)而建立。
fork()和clone()(对于线程)系统调用可用来建立新的进程。
(2)线程的创建和构造线程也称做轻量级进程。
就像进程一样,线程在程序中是独立的、并发的执行路径,每个线程有它自己的堆栈、自己的程序计数器和自己的局部变量。
实验报告模板

西北工业大学操作系统实验实验报告班号:1001040×姓名: ******学号: *********/*文件名请使用“bh”+班号后两位+“xh”+学号+姓名+项目号命名,如10010401班同学张三,学号041234,则其项目0实验报告的文件名是bh01xh041234张三0.doc。
请大家严格遵守。
实验报告提交时请删除本注释*/实验日期: 2007.12.31 实验名称: GeekOS字符回显功能实现一、实验目的1、熟悉GeekOs的项目编译、调试和运行环境,掌握GeekOs运行工作过程三、实验内容1、搭建GeekOS的编译和调试平台,掌握GeekOS的内核进程工作原理完成系统的配置,调试project0并添加输入字符并回显功能2、熟悉键盘操作函数,编程实现新建一个内核进程。
该进程的功能是:接受键盘输入的字符并显示到屏幕,当输入ctrl+d时,结束进程。
四、项目要求及分析熟悉键盘操作函数,编程实现新建一个内核进程。
该进程的功能是:接受键盘输入的字符并显示到屏幕,当输入ctrl+d时,结束进程。
此项目要求掌握如何新建一个内核进程并了解键盘处理函数。
需要了解Start_Kernel_Thread函数struct Kernel_Thread* Start_Kernel_Thread(Thread_Start_Func startFunc,ulong_t arg,int priority,bool detached){struct Kernel_Thread* kthread = Create_Thread(priority, detached);if (kthread != 0) {Setup_Kernel_Thread(kthread, startFunc, arg);Make_Runnable_Atomic(kthread);}return kthread;}该函数的主要功能是以参数startFunc指向的代码为进程体生成一个内核进程。
2022年西北工业大学计算机科学与技术专业《操作系统》科目期末试卷A(有答案)

2022年西北工业大学计算机科学与技术专业《操作系统》科目期末试卷A(有答案)一、选择题1、下面关于文件系统的说法正确的是()。
A.文件系统负责文件存储空间的管理,但不能实现文件名到物理地址的转换B.在多级目录结构中,对文件的访问是通过路径名和用户目录名进行的C.文件可以被划分成大小相等的若干物理块,且物理块大小也可以任意指定D.逻辑记录是对文件进行存取操作的基本单位2、下列关厂索引表的叙述中,正确的是()。
A.索引表中每个记录的索引项可以有多个B.对索引文件存取时,必须先查找索引表C.索引表中含有索引文件的数据及其物理地址D.建立索引表的i1的之,是减少存储空间,3、通常用户进程被建立后()A.使一直存在于系统中,直到被操作人员撤销B.随着作业运行正常或不正常结束而撤销C.随着时间片轮转而撤销与建立D.随着进程的阻塞或唤醒而撤销与建立4、下列选项中,导致创建新进程的操作是()。
I.用户登录成功 II.设备分配 III.启动程序执行A.仅I和IIB.仅II和IIIC. 仅I和IIID. I,II,III5、一个正在访问临界资源的进程由于申请等待1/0操作而被中断时,它()。
A.允许其他进程进入与该进程相关的临界区B.不允许其他进程进入临界区C.允许其他进程抢占处理器,但不能进入该进程的临界区D.不允许任何进程抢占处理器6、总体上说,“按需调页”(Demand-Paging)是个很好的虚拟内存管理策略。
但是,有些程序设计技术并不适合于这种环境,例如()A.堆栈B.线性搜索C.矢量运算D.分法搜索7、采用分段存储管理的系统中,若段地址用24位表示,其中8位表示段号,则允许每段的最大长度是()。
A.224BB.216BC.28BD.232 B8、下列选项中,在用户态执行的是()。
A.命令解释程序B.缺页处理程序C.进程调度程序D.时钟中断处理程序9、下列指令中,不能在用户态执行的是()A.trap指令B.跳转指令C.压栈指令D.关中断指令10、下列关于SPOOLing技术的叙述中,错误的是()A.需要外存的文持B.需要多道程序设计技术的支持C.可以让多个作业共享一台独占设备D.由用户作业控制设备与输入/输出之间的数据传送11、操作系统的I/O子系统通常由4个层次组成,每-层明确定义了与邻近层次的接口,其合理的层次组织排列顺序是()。
西北工业大学计算机网络实验报告3

题目:1、修改udp实验程序完成两台电脑通讯2、修改tcp实验程序完成两台电脑通讯3、修改IP源代码实现只调用一个函数**: ***学号:**********班号:10011303时间:2015-12-25计算机学院目录摘要1 目的 .................................................... 错误!未定义书签。
2 要求 (1)3 相关知识 (1)4 实验内容及过程................................. 错误!未定义书签。
5参考文献 .. (4)1、实验目的1.学习UDP和TCP及IP的通讯原理。
2.掌握Socket的编程方法。
3.培养学生自己的创新实验的能力。
4、训练修改实验代码能力。
2、实验要求1、熟悉UDP和TCP通讯的原理及socket编程。
2、自己修改UDP和TCP协议代码中的错误部分,完成两台电脑之间通讯。
3、修改IP源代码使所有外部调用函数都放在一个主函数里面。
3、相关知识1、UDP协议UDP协议[2]的全称是用户数据包协议[3],在网络中它与TCP协议一样用于处理数据包,是一种无连接的协议。
在OSI模型中,在第四层——传输层,处于IP协议的上一层。
UDP有不提供数据包分组、组装和不能对数据包进行排序的缺点,也就是说,当报文发送之后,是无法得知其是否安全完整到达的。
UDP用来支持那些需要在计算机之间传输数据的网络应用。
包括网络视频会议系统在内的众多的客户/服务器模式的网络应用都需要使用UDP协议。
UDP协议从问世至今已经被使用了很多年,虽然其最初的光彩已经被一些类似协议所掩盖,但是即使是在现在,UDP仍然不失为一项非常实用和可行的网络传输层协议。
与所熟知的TCP(传输控制协议)协议一样,UDP协议直接位于IP(网际协议)协议的顶层。
根据OSI(开放系统互连)参考模型,UDP和TCP都属于传输层协议。
UDP协议的主要作用是将网络数据流量压缩成数据包的形式。
BUAAOS——Lab3实验报告

BUAAOS——Lab3实验报告lab3实验报告思考题3.1 为什么我们在构造空闲进程链表时必须使⽤特定的插⼊的顺序?(顺序或者逆序)完成空闲链表的插⼊后,envs数组下标正好对应链表中的由前到后的顺序,因此调⽤空闲进程数组时优先调⽤下标最⼩的。
3.2 思考env.c/mkenvid 函数和envid2env 函数:请你谈谈对mkenvid函数中⽣成id 的运算的理解,为什么这么做?根据进程数组下标给每个进程⼀个独⼀⽆⼆的进程ID,同时还可以通过进程号获取数组偏移量找到进程块进⽽获得进程的全部信息。
为什么envid2env中需要判断e->env_id != envid 的情况?如果没有这步判断会发⽣什么情况?进程块也许会被替换,⽽ID只与⼀个进程块对应,如果没有这⼀步,可能会执⾏错误的进程块。
3.3 结合include/mmu.h中的地址空间布局,思考env_setup_vm 函数:我们在初始化新进程的地址空间时为什么不把整个地址空间的 pgdir都清零,⽽是复制内核的boot_pgdir作为⼀部分模板?(提⽰:mips 虚拟空间布局)因为每个进程都需要拥有内核的页表信息,这样才能够在陷⼊内核时正确执⾏,因此需要把内核拥有的虚拟地址对应的页表进⾏拷贝。
UTOP 和 ULIM 的含义分别是什么,在 UTOP 到 ULIM 的区域与其他⽤户区相⽐有什么最⼤的区别?UTOP是⽤户进程读写部分的最⾼地址,ULIM是⽤户进程的最⾼地址,UTOP到ULIM的区域为⽤户进程的进程块还有页表,不能被⽤户⾃⼰更改只能读取。
在 step4 中我们为什么要让pgdir[PDX(UVPT)]=env_cr3?(提⽰: 结合系统⾃映射机制)env_cr3是页⽬录的物理地址,pgdir[PDX(UVPT)]对应页⽬录的页⽬录项。
谈谈⾃⼰对进程中物理地址和虚拟地址的理解。
进程对应的地址都是虚拟地址,所有的物理地址只能从页表中查询所得到。
设计项目3

4、信号量定义 GveekOS定义了信号量的结构体: struct Semaphore{ int semaphoreID; /*信号量的ID*/ char *semaphoreName; /*信号量的名字 */ int value; /*信号量的值*/ int registeredThreadCount; /*注册该信号量 的线程数量*/ struct Kernel_Thread *registeredThreads[MAX_REGISTERED_THREADS]; /*注册的线程*/ struct Thread_Queue waitingThreads; /*等待该 信号的线程队列*/ DEFINE_LINK(Semaphore_List,Semaphore); /*连接 信号链表的域*/ } 西北工业大学计算机学院
西北工业大学计算机学院
5、信号量PV操作 信号量操作: Semaphore_Create( ) Semaphore_Acquire(P操作) Semaphore_Release(V操作) Semaphore_Destroy( ) Create_Semaphore()函数首先检查请求创建的这个信号量 的名字是否存在,如果存在,那么就把这个线程加入到 这个信号量所注册的线程链表上;如果不存在,则分配 内存给新的信号量,清空它的线程队列,把当前的这个 线程加入到它的线程队列中,设置注册线程数量为1,初 始化信号量的名字,值和信号量的ID,并把这个信号量 添加到信号量链表上,最后返回信号量的ID。
西北工业大学计算机学院
项目设计提示
1、多级学院
2、多级反馈队列与分时调度进程队列的转换
西北工业大学计算机学院
3、函数设计提示 (1)添加函数Chang_Scheduling_Policy(int policy, int quantum),policy是设置的调度策略,quantum是设置的时间 片。例如policy为1说明设置的是多级反馈队列调度算法, 此时若g_SchedPolicy(为系统添加的标识算法的变量,初 始化为0)为0,说明当前的调度算法为轮转调度,要变成 MLF就必须把空闲线程放入3队列,若g_SchedPolicy为1,说 明当前是多级反馈队列调度算法,则返回。如果policy为0, 则说明设置的是轮转调度,此时若g_SchedPolicy为1,则必 须把4个队列变成一个队列,即所有的线程都在队列0上了。 若g_SchedPolicy为0,则返回。
计算机操作系统实验-运行用户态程序

计算机操作系统实验-运行用户态程序(总13页)--本页仅作为文档封面,使用时请直接删除即可----内页可以根据需求调整合适字体及大小--西北工业大学操作系统实验实验报告一、实验目的掌握在GeekOS系统用户态模式下加载并运行可执行程序的方法。
二、实验要求1. 按照实验讲义P127页中的设计要求,实现在用户态模式下加载并运行可执行程序的代码,给出关键函数的代码以及实验结果。
三、实验过程及结果答:核心函数代码如下:================== ===============*/Set_Kernel_Stack_Pointer(esp0);ffsetInFile=proHeader->offset;exeFormat->segmentList[i].lengthInFile=proHeader->fileSize;exeFormat->segmentList[i].startAddress=proHeader->vaddr;exeFormat->segmentList[i].sizeInMemory=proHeader->memSize;exeFormat->segmentList[i].protFlags=proHeader->flags;proHeader++;}return 0;}=================== ===================//需在此文件各函数前增加一个函数,此函数的功能是按给定的大小创建一个用户级进程上下文,具体实现如下://函数功能:按给定的大小创建一个用户级进程上下文static struct User_Context* Create_User_Context(ulong_t size){struct User_Context * UserContext;size = Round_Up_To_Page(size);UserContext = (struct User_Context *)Malloc(sizeof(struct User_Context)); if (UserContext != 0)UserContext->memory = Malloc(size);//为核心态进程elsegoto fail;//内存为空if (0 == UserContext->memory)goto fail;memset(UserContext->memory, '\0', size);UserContext->size = size;//以下为用户态进程创建LDT(段描述符表)//新建一个LDT描述符UserContext->ldtDescriptor = Allocate_Segment_Descriptor();if (0 == UserContext->ldtDescriptor)goto fail;//初始化段描述符Init_LDT_Descriptor(UserContext->ldtDescriptor, UserContext->ldt, NUM_USER_LDT_ENTRIES);//新建一个LDT选择子UserContext->ldtSelector = Selector(KERNEL_PRIVILEGE, true,Get_Descriptor_Index(UserContext->ldtDescriptor));//新建一个文本段描述符Init_Code_Segment_Descriptor(&UserContext->ldt[0],(ulong_t) UserContext->memory,size / PAGE_SIZE,USER_PRIVILEGE);//新建一个数据段Init_Data_Segment_Descriptor(&UserContext->ldt[1],(ulong_t) UserContext->memory,size / PAGE_SIZE,USER_PRIVILEGE);//新建数据段和文本段选择子UserContext->csSelector = Selector(USER_PRIVILEGE, false, 0);UserContext->dsSelector = Selector(USER_PRIVILEGE, false, 1);//将引用数清0UserContext->refCount = 0;return UserContext;fail:if (UserContext != 0){if (UserContext->memory != 0){Free(UserContext->memory);}Free(UserContext);}return 0;}--------------------------------------------//摧毁用户上下文void Destroy_User_Context(struct User_Context* userContext) {//TODO("Destroy a User_Context");//释放占用的LDTFree_Segment_Descriptor(userContext->ldtDescriptor);userContext->ldtDescriptor=0;//释放内存空间Free(userContext->memory);userContext->memory=0;//释放userContext本身占用的内存Free(userContext);userContext=0;}----------------------------------------------int Load_User_Program(char *exeFileData,ulong_t exeFileLength,struct Exe_Format *exeFormat,const char *command,struct User_Context**pUserContext){//TODO("Load a user executable into a user memory space using segmentation");int i;ulong_t maxva = 0;//要分配的最大内存空间unsigned numArgs;//进程数目ulong_t argBlockSize;//参数块的大小ulong_t size, argBlockAddr;//参数块地址struct User_Context *userContext = 0;//计算用户态进程所需的最大内存空间for (i = 0; i < exeFormat->numSegments; ++i) {//struct Exe_Segment *segment = &exeFormat->segmentList[i];ulong_t topva = segment->startAddress + segment->sizeInMemory; /* FIXME: range check */if (topva > maxva)maxva = topva;}Get_Argument_Block_Size(command, &numArgs, &argBlockSize);//获取参数块信息size = Round_Up_To_Page(maxva) + DEFAULT_USER_STACK_SIZE;argBlockAddr = size;size += argBlockSize;userContext = Create_User_Context(size);//按相应大小创建一个进程if (userContext == 0)//如果为核心态进程return -1;for (i = 0; i < exeFormat->numSegments; ++i) {struct Exe_Segment *segment = &exeFormat->segmentList[i];//根据段信息将用户程序中的各段内容复制到分配的用户内存空间memcpy(userContext->memory + segment->startAddress, exeFileData + segment->offsetInFile,segment->lengthInFile);}//格式化参数块Format_Argument_Block(userContext->memory + argBlockAddr, numArgs, argBlockAddr, command);//初始化数据段,堆栈段及代码段信息userContext->entryAddr = exeFormat->entryAddr;userContext->argBlockAddr = argBlockAddr;userContext->stackPointerAddr = argBlockAddr;//将初始化完毕的User_Context赋给*pUserContext*pUserContext = userContext;return 0;//成功}----------------------------------------------//将用户态的进程复制到内核缓冲区bool Copy_From_User(void* destInKernel, ulong_t srcInUser, ulong_t bufSize) {struct User_Context * UserContext = g_currentThread->userContext;//--: check if memory if validatedif (!Validate_User_Memory(UserContext,srcInUser, bufSize))return false;memcpy(destInKernel, UserContext->memory + srcInUser, bufSize);return true;}-----------------------------------------//将内核态的进程复制到用户态bool Copy_To_User(ulong_t destInUser, void* srcInKernel, ulong_t bufSize) {struct User_Context * UserContext = g_currentThread->userContext;if (!Validate_User_Memory(UserContext, destInUser, bufSize))return false;memcpy(UserContext->memory + destInUser, srcInKernel, bufSize);return true;}----------------------------------------//切换到用户地址空间void Switch_To_Address_Space(struct User_Context *userContext){ushort_t ldtSelector= userContext->ldtSelector;/* Switch to the LDT of the new user context */__asm__ __volatile__ ("lldt %0"::"a"(ldtSelector));}================= ===============添加头文件 #include <geekos/>----------------------------------//创建一个用户进程/*static*/ void Setup_User_Thread(struct Kernel_Thread* kthread, struct User_Context* userContext){ulong_t eflags = EFLAGS_IF;unsigned csSelector=userContext->csSelector;//CS选择子unsigned dsSelector=userContext->dsSelector;//DS选择子Attach_User_Context(kthread, userContext);//初始化用户态进程堆栈,使之看上去像刚被中断运行一样//分别调用Push函数将以下数据压入堆栈Push(kthread, dsSelector); //数据选择子Push(kthread, userContext->stackPointerAddr); //堆栈指针Push(kthread, eflags); //EflagsPush(kthread, csSelector); //文本选择子Push(kthread, userContext->entryAddr); //程序计数器Push(kthread, 0); //错误代码(0)Push(kthread, 0); //中断号(0)//初始化通用寄存单元,将ESI用户传递参数块地址Push(kthread, 0); /* eax */Push(kthread, 0); /* ebx */Push(kthread, 0); /* edx */Push(kthread, 0); /* edx */Push(kthread, userContext->argBlockAddr); /* esi */Push(kthread, 0); /* edi */Push(kthread, 0); /* ebp *///初始化数据段寄存单元Push(kthread, dsSelector); /* ds */Push(kthread, dsSelector); /* es */Push(kthread, dsSelector); /* fs */Push(kthread, dsSelector); /* gs */}//开始用户进程struct Kernel_Thread* Start_User_Thread(struct User_Context* userContext, bool detached){struct Kernel_Thread* kthread = Create_Thread(PRIORITY_USER, detached);if (kthread != 0){Setup_User_Thread(kthread, userContext);Make_Runnable_Atomic(kthread);}return kthread;}================ =================//需在此文件别的函数前增加一个函数,函数名为Copy_User_String,它被函数Sys_PrintString调用,具体实现如下:static int Copy_User_String(ulong_t uaddr, ulong_t len, ulong_t maxLen, char**pStr){ int rc = 0;char *str;if (len > maxLen){ //超过最大长度return EINVALID;}str = (char*) Malloc(len+1); //为字符串分配空间if (0 == str){rc = ENOMEM;goto fail;}if (!Copy_From_User(str, uaddr, len)){ //从用户空间中复制数据rc = EINVALID;Free(str);goto fail;}str[len] = '\0';//成功*pStr = str;fail:return rc;}-----------------------------------------static int Sys_Exit(struct Interrupt_State* state){Exit(state->ebx);}-----------------------------------------static int Sys_PrintString(struct Interrupt_State* state){int rc = 0;//返回值uint_t length = state->ecx;//字符串长度uchar_t* buf = 0;if (length > 0) {if ((rc = Copy_User_String(state->ebx, length, 1023, (char**) &buf)) != 0)goto done;Put_Buf(buf, length);}done:if (buf != 0)Free(buf);return rc;}----------------------------------------------static int Sys_GetKey(struct Interrupt_State* state){return Wait_For_Key(); //返回按键码Wait_For_Key()}---------------------------------------------static int Sys_SetAttr(struct Interrupt_State* state){Set_Current_Attr((uchar_t) state->ebx);return 0;}---------------------------------------------static int Sys_GetCursor(struct Interrupt_State* state){int row, col;Get_Cursor(&row, &col);if (!Copy_To_User(state->ebx, &row, sizeof(int)) ||!Copy_To_User(state->ecx, &col, sizeof(int)))return -1;return 0;}-----------------------------------------------static int Sys_PutCursor(struct Interrupt_State* state){return Put_Cursor(state->ebx, state->ecx) 0 : -1;}-----------------------------------------------static int Sys_Spawn(struct Interrupt_State* state){int rc; //函数返回值char *program = 0; //进程名称char *command = 0; //用户命令struct Kernel_Thread *process;if ((rc = Copy_User_String(state->ebx, state->ecx, VFS_MAX_PATH_LEN, &program)) != 0){goto fail;}if(rc = Copy_User_String(state->edx, state->esi, 1023, &command)) != 0) {//从用户空间复制用户命令goto fail;}Enable_Interrupts(); //开中断rc = Spawn(program, command, &process);//得到进程名称和用户命令后便可生成一个新进程if (rc == 0) {//若成功则返回新进程ID号KASSERT(process != 0);rc = process->pid;}Disable_Interrupts();//关中断if (program != 0)Free(program);if (command != 0)Free(command);return rc;}-----------------------------------------static int Sys_Wait(struct Interrupt_State* state){ //TODO("Wait system call");int exitCode;struct Kernel_Thread *kthread = Lookup_Thread(state->ebx);if (kthread == 0)return -12;Enable_Interrupts();exitCode = Join(kthread);Disable_Interrupts();return exitCode;}---------------------------------------static int Sys_GetPID(struct Interrupt_State* state) { //TODO("GetPID system call");return g_currentThread->pid;}================= ================== static void Spawn_Init_Process(void){ //TODO("Spawn the init process");struct Kernel_Thread *pThread;Spawn("/c/","/c/",&pThread);}实验结果如图所示:原理:Geekos 提供了一个简单的shell,保存在PFAT文件系统内,所以geekos系统启动后,启动shell程序/c/运行,将/c/作为可执行文件传递给spawn函数的program参数,创建第一个用户态进程,然后由它来创建其他进程。
操作系统实验三实验报告

(一)进程创建
编写程序实现创建多个进程,并观察进程的执行情况。通过调用Windows API函数`CreateProcess`来创建新的进程。在创建进程时,设置不同的参数,如进程的优先级、命令行参数等,观察这些参数对进程执行的影响。
(二)进程控制
实现对进程的暂停、恢复和终止操作。使用`SuspendThread`和`ResumeThread`函数来暂停和恢复进程中的线程,使用`TerminateProcess`函数来终止进程。通过控制进程的执行状态,观察系统的资源使用情况和进程的响应。
(一)进程创建实验结果与分析
创建多个进程后,通过任务管理器观察到新创建的进程在系统中运行。不同的进程优先级设置对进程的CPU占用和响应时间产生了明显的影响。高优先级的进程能够更快地获得CPU资源,执行速度相对较快;而低优先级的进程则在CPU资源竞争中处于劣势,可能会出现短暂的卡顿或计一个多进程同步的程序,使用信号量、互斥量等同步机制来协调多个进程的执行。例如,实现一个生产者消费者问题,多个生产者进程和消费者进程通过共享缓冲区进行数据交换,使用同步机制来保证数据的一致性和正确性。
四、实验步骤
(一)进程创建实验步骤
1、打开Visual Studio 2019,创建一个新的C++控制台应用程序项目。
六、实验中遇到的问题及解决方法
(一)进程创建失败
在创建进程时,可能会由于参数设置不正确或系统资源不足等原因导致创建失败。通过仔细检查参数的设置,确保命令行参数、环境变量等的正确性,并释放不必要的系统资源,解决了创建失败的问题。
(二)线程控制异常
在暂停和恢复线程时,可能会出现线程状态不一致或死锁等异常情况。通过合理的线程同步和错误处理机制,避免了这些异常的发生。在代码中添加了对线程状态的判断和异常处理的代码,保证了线程控制的稳定性和可靠性。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
评语: 课中检查完成的题号及题数:课后完成的题号与题数:成绩: 指导教师:实验报告三实验名称:七、八日期:2013.05.23 班级:10011007 学号:2010302557 姓名:张彬彬实验七理解线程的相关概念1. 实验目的理解当操作系统引入线程的概念后,进程是操作系统独立分配资源的单位,线程成为系统调度的单位,与同一个进程中的其他线程共享程序空间。
2. 实验预习内容预习线程创建和构造的相关知识,了解C语言程序编写的相关知识。
3. 实验内容及步骤(1)编写一个程序,在其main()函数中创建一个(或多个)线程,观察该线程是如何与主线程并发运行的。
输出每次操作后的结果;(2)在main()函数外定义一个变量shared(全局变量),在main()中创建一个线程,在main()中和新线程shared 进行循环加/减操作,观察该变量的变化;(3)修改程序把shared 变量定义到main()函数之内,重复第(2)步操作,观察该变量的变化。
4. 实验总结(1) 观察上述程序执行结果,并分析原因;(2) 提交源程序清单,并附加流程图与注释。
思考:分析进程和线程的不同之处。
5. 具体实现1)观察线程并发性:#include <stdio.h>#include <unistd.h>#include <pthread.h>void * func(void *params){while(1){printf("i am the thread 2\n");sleep(1);}return NULL;}int main(){pthread_t tid;int res=pthread_create(&tid, NULL,func,NULL);while(1){printf("i am main thread\n");sleep(1);}return 0;}2)全局变量共享#include <stdio.h>#include <unistd.h>#include <pthread.h>int shared=0;void * func(void *params){while(1){shared--;printf("i am thread2 shared=%d\n",shared);sleep(1);}return NULL;}int main(){pthread_t tid;int res=pthread_create(&tid, NULL,func,NULL);while(1){shared++;printf("i am main thread shared=%d\n",shared);sleep(1);}return 0;}3)局部变量,指针传参#include <stdio.h>#include <unistd.h>#include <pthread.h>void * func(void *params){while(1){printf("i am thread2 shared=%d\n",*(int *)params);(*(int *)params)--;sleep(1);}return NULL;}int main(){pthread_t tid;int shared=0;int res=pthread_create(&tid, NULL,func,(void *)(&shared));while(1){shared++;printf("i am main thread shared=%d\n",shared);sleep(1);}return 0;}实验八请求分页存储管理设计1. 实验目的模拟存储管理常用的请求分页存储管理技术,通过本实验使学生更加深入的理解虚拟内存的思想和主要的页面淘汰算法。
2. 实验预习内容学习虚拟存储器的相关基础知识,了解请求分页存储管理系统的原理和具体实现过程,熟悉各种主要的页面调度算法。
3. 实验内容及步骤(1) 通过随机数产生一个指令行列,共320条指令,指令中的地址按下述原则生成:50%的指令是顺序执行;25%的指令均匀分布在前地址部分;25%的指令均匀分布在后地址部分。
(2) 具体实验办法是:在[0,319]之间选一起始点M;顺序执行一条指令,即第M+1条;向前地址[0,M-1]中执行一条指令M;顺序执行一条指令,即第M+1条;向后地址[M+2,319]中执行一条指令M。
如此继续,直至产生320条指令。
使用产生随机数的函数之前,首先要初始化设置RAN()产生序列的开始点,SRAND(400);然后计算随机数,产生指令序列。
例如:a[0]=1.0*rand()/32767*319+1;a[1]=a[0]+1;a[2]=1.0*rand()/32767*(a[1]-1)+1;a[3]=a[2]+1;a[4]=319-1.0*rand()/32767*(a[3]-1);其中rand()和srand()为Linux操作系统提供的函数分别进行初始化和产生随机数,多次重复使用这5条指令,产生以后的指令序列。
(3) 将指令序列变换成页面地址流:假设,页面大小为1KB;用户实存容量(内存区容量)为4页或32页;用户虚存容量(逻辑地址空间容量)为32KB;用户虚存容量32KB,每1KB中放10条指令,共320条指令序列,按其地址0~9在0页,10~19在1页,…….,310~319在31页。
(4) 使用不同的页面调度算法处理缺页中断,并计算不同实存容量下的命中率:先进先出(FIFO)算法;最近最少使用(LRU)算法;命中率的算法为:命中率= 1 - (缺页中断次数/页地址流长度)。
本实验中,页地址流长度为320,缺页中断次数为每次访问相应指令时,该指令所对应的页不在内存的次数。
4. 实验总结(1) 编制的各程序采用的数据结构及符号说明,提交源程序清单,并附加流程图与注释;(2) 打印页表,对不同算法打印每次调出和装入的页面号,执行最后一条指令后在主存中页面号;(3) 根据实验结果分析并比较不同淘汰算法对不同实存容量的命中率,进而作出评价。
5.具体实现Fifo使用循环队列实现#include<stdio.h>#include<time.h>#include<stdlib.h>#define HARD_NUM 4#define INSTRUCT 320#define VIRTUAL_NUM 32int instruct[INSTRUCT]={0}; typedef struct{int id;//虚存idbool flag;//是否装入int count;//最近使用次数}HM;typedef struct{HM hm[HARD_NUM+1];int head;int tail;}FIFO;typedef struct{HM hm[HARD_NUM];}LRU;FIFO fifo;LRU lru;void init();void fifoTest();bool isInFifo(int page);void lruTest();int main(){init();printf("先进先出fifo...\n");fifoTest();printf("\n\n");printf("最近最少使用fifo...\n");lruTest();system("pause");return 0;}void init(){int i,j;srand(time(0));for(i=0; i<INSTRUCT; i+=5){instruct[i] = 1.0 * rand() / RAND_MAX * 320;instruct[i+1] = instruct[i] + 1;instruct[i+2] = 1.0 *rand() / RAND_MAX * instruct[i];instruct[i+3] = instruct[i+2] + 1;instruct[i+4] = 319 - 1.0 *rand() / RAND_MAX * instruct[i+3];}}void fifoTest(){int i,j;int lack=0;//缺页次数//内存初始化for(i=0; i<HARD_NUM; i++){fifo.hm[i].id=0;fifo.hm[i].flag=false;fifo.hm[i].count=0;}//模拟执行320条指令for(i=0; i<INSTRUCT; i++){//计算该指令在那一页,判断是否在FIFO中int page = instruct[i] % VIRTUAL_NUM;bool isIn =isInFifo(page);if(isIn)//在实存中{printf("%d alread in fifo\n",instruct[i]);}else//缺页,插入队列{lack ++;if((fifo.tail + 1)%(HARD_NUM+1) == fifo.head)//队列已满{printf("page %d in,page %dout\n",page,fifo.hm[fifo.head].id);fifo.hm[fifo.head].id = page;fifo.tail =fifo.head;fifo.head = (fifo.head + 1) %(HARD_NUM+1);}else{printf("page %d in\n",page);fifo.hm[fifo.tail].id = page;fifo.hm[fifo.tail].flag = true;fifo.tail = (fifo.tail+1) %(HARD_NUM+1);}}}printf("缺页次数:%d\n",lack);printf("命中:%lf\n",1.0 - 1.0 * lack / INSTRUCT);}bool isInFifo(int page){int cur=fifo.head;if( cur ==fifo.tail)//队列为空return false;while(1){if( cur % (HARD_NUM+1) ==fifo.tail)//已经到队列尾break;if(page == fifo.hm[cur].id && fifo.hm[cur].flag == true) return true;cur++;}return false;}void lruTest(){int i,j;int lack=0;//缺页次数//内存初始化for(i=0; i<HARD_NUM; i++){lru.hm[i].id=0;lru.hm[i].flag=false;lru.hm[i].count=0;}//模拟执行320条指令for(i=0; i<INSTRUCT; i++){//计算该指令在那一页,判断是否在实存中int page = instruct[i] % VIRTUAL_NUM;bool isIn=false;for(j=0; j < HARD_NUM; j++)if(lru.hm[j].id==page && lru.hm[j].flag ==true){isIn=true;lru.hm[j].count ++;}if(isIn)printf("%d alread in...\n",page);else{lack++;//查找最近最少使用的项int cur=0,count=INT_MAX;for(j=0; j < HARD_NUM; j++){if(lru.hm[j].count < count){cur = j;count = lru.hm[j].count;}}if(lru.hm[cur].flag ==false){printf("page %d in\n",page);lru.hm[cur].flag =true;}elseprintf("page %d in,page %dout\n",page,lru.hm[cur].id);lru.hm[cur].id=page;lru.hm[cur].count ++;}}printf("缺页次数:%d\n",lack);printf("命中:%lf\n",1.0 - 1.0 * lack / INSTRUCT);}实验结果:实存为4kb,4个页面结果:(仅打印结果)实存为10Kb,10个页面结果:由以上结果可以看出,最近最少使用算法lru效果比先进先出fifo要好的多,平均超过2倍的性能。