c++面经整理(操作系统 协程、协程与线程、系统调用、fork实例、微内核与宏内核、僵尸进程、IO模型、异步编程的事件循环、page cache)

说一说协程

顾名思义,协程就是协助主城的运行,协程,又称微线程,虽然协程看上去也是子程序,但执行过程中,在子程序内部可以中断,然后转而实现执行别的子程序,在适当的时候再返回执行;

协程和线程的区别:
  1. 和多线程相比,协程最大的优势就是协程的执行效率,由于子程序切换不是线程切换,而是由程序自身的控制,因此,协程并没有线程切换时的开销,和多线程相比,线程数越多,协程的性能优势就越明显
  2. 第二大优势就是不需要多线程的锁机制,因为只有一个线程,也不存在同时写变量冲突,在协程中控制的资源不加锁,只需要判断状态就好了,所以执行效率比多线程高很多
系统调用是什么,你用过那些系统调用

在计算机中,系统调用又称之为系统呼叫,是指运行在使用者空间的程序向系统请求要求更高权限的服务,系统调用提供了用户程序与操作系统之间的接口,因此,系统调用就是用户的程序去调用系统调用提供的接口;

为什么需要分为两种状态,用户态和内核态呢?是因为应用程序有的时候会做一些危险的,权限很高的操作,如果把这些操作放心的交给用户程序来做是非常危险的,而通过接口来令系统调用,实际上是把工作交给了操作系统,它是已经被完美构建后的,对于一些危险的操作都采取了一些措施;

系统调用举例:
对文件进行读写,创建进程等都是系统调用

请你来手写一下fork调用实例
int main(void)
{
	pid_t pid;
	signal(SIGCHLD, SIG_IGN);
	printf("before fork pid:%d\n", getpid());
	int abc = 10;
	pid = fork();
	if (pid == -1) {           //错误返回
		perror("tile");
		return -1;
	}
	if (pid > 0) {              //父进程空间
		abc++;
		printf("parent:pid:%d \n", getpid());
		printf("abc:%d \n", abc);
		sleep(20);
	}
	else if (pid == 0) {       //子进程空间
		abc++;
		printf("child:%d,parent: %d\n", getpid(), getppid());
		printf("abc:%d", abc);
	}
	printf("fork after...\n");
 }

signal(SIGCHLD,SIG_IGN);
参数一:我们将要进行处理的信号
参数二:我们处理的方式(是系统默认还是忽略还是捕获)

SIGCHLD信号:
子进程结束的时候,父进程会受到这个信号,如果父进程没有处理这个信号,也没有等待子进程,子进程虽然终止,但是还会在内核进程表中占有表项,这时的子进程称之为僵尸进程,这种情况我们应该避免;通过令父进程忽略SIGHLD信号或者捕捉它,或者wait它派生的子进程,或者父进程先终止,这时,子进程的终止后自动由init进程来接管

请你来说一下微内核与宏内核

宏内核:储了最基本的进程、线程管理、内存管理外,将文件系统,驱动,网络协议等等都继承在内核中
优点:效率高
缺点:稳定性差,开发过程中的bug经常会导致整个系统挂掉

微内核:内核中只有最基本的调度、内存管理。驱动、文件系统都是用户态的守护进程去实现的;
优点:稳定,驱动等的错误只会导致相应进程死掉,不会导致真个系统都崩溃
缺点:效率低
两者最大的区别就是,文件系统、驱动和网络协议集成的方式不同,一个在内核中,一个由用户态的守护进程时间;

请你说一下僵尸进程
  1. 正常进程:
    正常情况下,由于子进程是通过父进程创建的,子进程再创建新的进程,子进程的结束和父进程的运行是一个一部过程,即父进程永远无法预知子进程到底什么时候结束,当一个进程完成他的工作终止之后,他的父进程需要调用wait或者waitPid系统调用取得子进程的终止状态,如果父进程没有去经过系统调用来取得子进程的终止状态,那么系统为子进程保存的消息不会释放,
    保存的消息有:
    1. 进程号
    2. 退出状态
    3. 运行时间
  2. 孤儿进程
    当父进程开辟了新的子进程之后,如果父进程先于子进程结束,那么这些子进程被称之为孤儿进程,最终由init进程对他们完成状态收集工作
  3. 僵尸进程
    一个进程shiyongfork创建子进程,如果子进程退出,但是父进程没有调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述任然保存在系统中,这种进程称之为僵尸进程

危害
如果进程不调用wait或者waitpid的话,那么保留的那段信息就不会释放,其进程号就会一直被占用,但是系统所能使用的进程号是有限的,如果大量产生僵尸进程, 将因为没有可用的进程号而导致系统不能产生新的进程
解决办法

  1. 通过kill发送SIGTERM或者SIGKILL信号消灭产生僵尸进程的进程(也就是僵尸进程的父进程),这样的话直接令僵尸进程变成孤儿进程,通过init进行回收
#include <sys/wait.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
int main(void)
{
    pid_t childpid;
    int status;
    int retval;
    
    childpid = fork();  // 创建子进程
    if (-1 == childpid)  //判断是否创建失败
    {
        perror("fork()");
        exit(EXIT_FAILURE);
    }
    else if (0 == childpid)
    {
        puts("In child process");
        sleep(1000); //让子进程睡眠,以便父进程查看其行为
        exit(EXIT_SUCCESS);
    }
    else
    {
        if (0 == (waitpid(childpid, &status, WNOHANG))) //判断子进程是否已经退出
        {
            retval = kill(childpid, SIGKILL);  //发送SIGKILL给子进程,要求其停止运行
            
            if (retval)   //判断是否发生信号
            {
                puts("kill failed.");
                perror("kill");
                waitpid(childpid, &status, 0);
            }
            else
            {
                printf("%d killed\n", childpid);
            }
            
        }
    }
    
    exit(EXIT_SUCCESS);
}
  1. 子进程推出的时候向父进程发送SIGCHILD信号,父进程处理信号,再信号处理函数中调用wait进程处理僵尸进程
  2. 将真正想要执行的进程变成孙进程,那么由于其父进程只是个形式,那么孙进程将变成孤儿进程,通过init进程直接处理
请你来介绍一下5种IO模型

五种IO模型详解

异步编程的事件循环

事件循环就是不停循环等待事件的发生,然后将这个事件的所有处理器,以及按照订阅这个事件的时间顺序依次执行,当这个事件的所有处理器都被执行完毕之后,事件循环就会继续的等待下一个事件的触发,不断往复,当同时并发地处理多个请求的时候,也是可以的,比如,在单个线程中,事件处理器是一个一个按照顺序执行的,即如果某个事件绑定了两个处理器,那么第二个处理器就会在第一个处理器执行结束后,才开始执行,当这个事件的所有处理器都执行完毕之后,事件循环才回去检查是否由新的事件触发
事件循环与跨线程调用

操作系统为什么要分内核态和用户态

根本上来讲是为了安全性,如果把一些安全级别高的指令权限都放给了用户的程序,那么导致系统奔溃的可能性就会大很多,但是如果当用户想要使用安全性高的指令时,令系统调用去执行,而内核提供的一些安全性的API就可以保证系统的安全了;

请你回答一下为什么要有page cache,操作系统是怎样设计的page cache

基本知识:为什么要有pagecache?
在现代计算机系统中,cpu,RAM(RAM(Random Access Memory)又称作“随机存储器”,是与CPU直接交换数据的内部存储器,也叫主存(内存)。它可以随时读写,而且速度很快,通常作为操作系统或其他正在运行中的程序的临时数据存储媒介。),DISK(硬盘)的速度是不同的,其速度关系是CPU>RAM>DISK 在CPU与RAM之间,RAM和DISK之间的速度差异都是指数级别的,与此同是,他们的处理容量也是不一样的,其差异也是指数级别的,因此为了匹配他们之间的速度和容量差别,在CPU与RAM之间使用CPUcache提高访存速度,在RAM与磁盘之间使用page cache提高访存速度(这就是page cache的由来)
page cache 是指的页面缓存,其作用就是为了加快从磁盘读取文件的速度,page cache中有一部分磁盘文件的缓存,由于从磁盘文件中读取文件速度很慢,所以读取文件先去页面缓存中查找,如果在其中找到,就不需要去读盘中找了,如果找不到就启用磁盘的IO,将磁盘文件中的数据块加载到page cache中的一个空闲块,然后再copy到用户缓冲区,当当前的文件使用结束以后,并不马上释放当前page cache中相应的文件,而是一直保留,等到系统物理内存不足的时候,才会去清理,这样的话,提高了系统整体性能(提高page cache 命中率)

请介绍一下操作系统中的中断

中断是指CPU对系统发生的某个事件做出的一种反应,CPU暂停正在执行的程序,保存现场后自动去执行相应的处理程序,处理完该事件后再返回中断处继续执行原来的程序。中断一般三类,一种是由CPU外部引起的,如I/O中断、时钟中断,一种是来自CPU内部事件或程序执行中引起的中断,例如程序非法操作,地址越界、浮点溢出),最后一种是在程序中使用了系统调用引起的。而中断处理一般分为中断响应和中断处理两个步骤,中断响应由硬件实施,中断处理主要由软件实施。

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值