zoukankan      html  css  js  c++  java
  • 【基础复习】十一:操作系统


    ps:
    挺好的一篇博客
    操作系统常考知识点总结
    操作系统原理笔/面试题目总结
    操作系统笔/面试题整理


    【补充】

    1.试解释操作系统原理中的作业,进程,线程,管程各自的定义。进程间的通信如何实现?

    2.在Linux平台下运行C程序。如果fork()函数不失败,下面哪个描述是正确的?

    #include<stdio.h>
    #include<unistd.h>
    
    int main() {
        int i = 1;
        if (!fork())
            i++;
        printf("%d
    ", i);
        
        if (!fork())
            i++;
        printf("%d
    ", i);
    
        return 0;
    }
    

    A.输出中有2次"1"
    B.输出中有2次"2"
    C.最后输出的是"3
    D.最后的输出中没有3,因为"i++"不是一个原子操作

    解析:
    答案:A
    fork的意思是进程从这里开始分叉,分成了两个进程:一个是父进程,一个是子进程。子进程复制了父进程的绝大部分:栈、缓冲区等等。系统为子进程创建了一个新的进程表项,其中进程id与父进程是不相同的,这也就是说父子进程是两个独立的进程,虽然父子进程共享代码空间,但是涉及写数据时子进程有自己的数据空间,在有数据修改时,系统会为子进程申请新的页面。
    在本题中,因为if(!fork()) i++; 所以只有子进程才会执行i++。执行顺序如下:

    A-+---1-+---1
     |    |-C-2
     |
     |-B-2-+---2
         |-D-3
    
    0. 假设开始是进程为A
    1. 第一次fork
    
    2.第一个printf("%d
    ", i);
    2.1 进程A输出1
    2.2 进程B输出2
    
    3. 第二次fork
    3.1 A fork 产生子进程C
    3.2 B fork 产生子进程D
    
    4.第二个printf("%d
    ", i);
    4.1 进程A输出1
    4.2 进程B输出2
    4.3 进程C输出2
    4.4 进程D输出3
    

    觉得有必要了解一下fork这个函数:linux中fork()函数详解 ...嗯....其实我的运行结果和这个博客里面写的不一样?但是可以根据自己的运行结果大概的了解这个思想和fork函数的过程。记住调用fork之后,是将当前进程”剩下的代码周期“+”缓冲里的东西“+”内存的一些数据“克隆出一个子进程。这里还有一个fork总结

    3.进程的3种基本状态

    4.面试题:哲学家就餐问题
    重温:什么是哲学家问题

    5.PE文件被称为可移植的执行体是Portable Execute的全称,常见的EXE、DLL、OCX、SYS、COM都是PE文件,PE文件是微软Windows操作系统上的程序文件(可能是间接被执行,如DLL)

    6.一道多线程的计算题

    7.创建两个线程模拟火车站两个窗口售票程序,窗口售票时间为1秒,两个窗口不能同时售票。
    解析:
    进程是由两个部分构成的,一个是进程内核对象,另一个是地址空间。同样,线程也是由两个部分组成的:一个是 线程的内核对象,操作系统用它来对线程实施管理。内核对象也是系统用来存放线程统计信息的地方。另一个是 线程堆栈,它用于维护线程在执行代码时需要的所有函数参数和局部变量。

    进程是不活泼的。进程从来不执行任何东西,它只是 线程的容器。线程总是在某个进程环境中创建的,而且它的整个寿命期都在该进程中。这意味着线程在它的 进程地址空间中执行代码,并且在进程的地址空间中对数据进行操作 。因此,如果在单进程环境中,你有两个或多个线程正在运行,那么这两个线程将共享单个地址空间。这些线程能够执行相同的代码,对相同的数据进行操作。这些线程还能共享内核对象句柄,因为句柄表依赖于每个进程而不是每个线程的存在。

    进程使用的系统资源比线程多得多,原因是它需要更多的地址空间。为进程创建一个虚拟地址空间需要许多系统资源。系统中要保留大量的记录,这要占用大量的内存。由于线程需要的开销比进程少,因此一般用增加线程来解决编程问题,而要避免创建新的进程。

    每当进程被初始化是,系统就要创建一个主线程。该线程与C/C++运行期库的启动代码一道开始运行,启动代码则调用进入点函数,并且继续运行直到进入点函数返回并且C/C++运行期库的启动代码调用退出位置。对于许多应用程序来时候,这个主线程是应用程序需要的唯一线程。不过,进程能够创建更多的线程来帮助执行它们的操作。

    每个线程必须拥有一个进入点函数,线程从这个进入点开始运行。即main、wmain、WinMain或wWinMain。如果想要在你的进程中创建一个辅助线程,他必定也是一个进入点函数,类似下面的样子:

    DWORD WINAPI ThreadFunc(PVOID pvParam){
        DWORD dwResult = 0;
        ...
        return (dwResult);
    }
    

    线程函数可以执行你想要它做的任何任务。最终,线程函数到达它的结尾出并且返回。这时,线程终止运行,该堆栈的内存被释放,线程的内核对象的使用计数被递减。如果使用计数降为0,线程的内核对象就被撤销。与进程内核对象的情况相同,线程内核对象的寿命至少可以达到他们相关联的线程那样长,不过,该对象的寿命可以远远超过线程本身的寿命。

    代码如下:(是win下的)

    #include<iostream>
    #include<windows.h>
    using namespace std;
    
    //这是2个线程模拟卖火车票的小程序
    DWORD WINAPI Fun1Proc(LPVOID lpParameter); //thread data
    DWORD WINAPI Fun2Proc(LPVOID lpParameter); //thread data
    
    int index = 0;
    int tickets = 10;
    HANDLE hMutex;
    
    int main() {
        HANDLE hThread1;
        HANDLE hThread2;
        
        //创建线程
        hThread1 = CreateThread(NULL, 0, Fun1Proc, NULL, 0, NULL);
        hThread2 = CreateThread(NULL, 0, Fun2Proc, NULL, 0, NULL);
        CloseHandle(hThread1);
        CloseHandle(hThread2);
    
        //创建互斥对象
        hMutex = CreateMutex(NULL, TRUE, "tickets");
        if (hMutex) {
            if (ERROR ALREADY EXISTS == GetLastError()) {
                cout << "only one instance can run!" << endl;
                return 0;
            } 
        }
    
        WaitForSingleObject(hMutex, INFINITE);
        ReleaseMutex(hMutex);
        ReleaseMutex(hMutex);
    
        Sleep(4000);
        return 0;
    }
    
    //线程1的入口函数
    DWORD WINAPI Fun1Proc(LPVOID lpParameter) {
        while(true) {
            ReleaseMutex(hMutex);
            WaitForSingleObject(hMutex, INFINITE);
            if (tickets>0) {
                Sleep(1);
                cout << "thread1 sell ticket:" << tickets-- << endl;
            } else {
                break;
            }
    
            ReleaseMutex(hMutex);
        }
        return 0;
    }
    
    //线程1的入口函数
    DWORD WINAPI Fun2Proc(LPVOID lpParameter) {
        while(true) {
            ReleaseMutex(hMutex);
            WaitForSingleObject(hMutex, INFINITE);
            if (tickets>0) {
                Sleep(1);
                cout << "thread2 sell ticket:" << tickets-- << endl;
            } else {
                break;
            }
    
            ReleaseMutex(hMutex);
        }
        return 0;
    }
    

    8.Belady's Anomaly 出现在哪?
    A. 内存管理算法
    B. 内存换页算法
    C. 预防死锁算法
    D. 磁盘调度算法
    解析:
    所谓Belady现象是指:采用FIFO算法时,如果对一个进程未分配它所要求的全部页面,有时就会出现分配的页面数增多但缺页率反而增高的异常现象。Belady现象的原因是FIFO算法的置换特征与进程访问内存的动态特征是矛盾的,即被置换的页面并不是进程不会访问的。这些页在FIFO算法下被反复调入和调出,并且有Belady现象。答案是:B

    9.什么是Thrashing?
    A.非常频繁的换页活动
    B.非常高的CPU执行活动
    C.一个极长的执行过程
    D.一个极大的虚拟内存法
    解析:
    内存抖动(Thrashing)一般是内存分配算法不好,内存太小或者程序的算法不佳引起的页面频繁从内存调入调出的行为。答案是:A

    10.某主机安装了2GB内存,在其上运行的某支持MMU的32位Linux发行版中,一共运行了X,Y,Z三个进程,下面关于三个内存使用程序的方式,哪个是可行的?
    A.X,Y,Z的虚拟地址空间都映射到0~4G虚拟地址上
    B.X在堆上分配总大小为1GB的空间,Y在堆上分配200MB,Z在堆上分配500MB,并且内存映射访问一个1GB的磁盘文件
    C.X在堆上分配1GB,Y在堆上分配800MB,Z在堆上分配400MB
    D.以上的访问方式都是可行的
    解析:
    虚拟存储器的基本思想是程序、数据、堆栈的总的大小可以超过物理存储器的大小,操作系统把当前使用的部分保留在内存中,而把其他未被使用的部分保存在磁盘上。答案是D。

    11.某虚拟内存系统采用页式内存管理,使用LRU页面管理算法。考虑下面的页面访问地址流(每次访问在一个时间单位内完成)中一共有几次页面失败:
    1,8,1,7,8,2,7,2,1,8,3,8,2,1,3,1,7,1,3,7?
    A.4
    B.5
    C.6
    D.7

    解析:LRU算法的提出,是基于这样一个事实:在前面几条指令中使用频繁的页面,可能在后面的几条指令中频繁使用。反过来说,已经很久没有使用的页面,很可能在未来较长的一段时间内不会被用到。答案:C(这里应该是假设内存容量为4页了...)




    from《程序员面试宝典》



  • 相关阅读:
    unity3d优化-代码篇(不定期更新)
    Activity的生命周期
    继承了AppCompatActivity的全屏设置
    shaderlab UV动画所需的变量声明
    加载Assetbundle需要注意的地方
    VisualStudio中的编辑后期生成事件
    unity3D射线检测敌人是否在前方
    Unity3D 定时发射子弹
    Unity3D使用NGUI做个弹窗
    Unity3D TouchScript 插件教程一
  • 原文地址:https://www.cnblogs.com/zengyh-1900/p/5267768.html
Copyright © 2011-2022 走看看