上午工作效率高甚至到了每周二上午电视没有信号是为什么?


面试的过程中,为了考察面试者的基础功力,除了算法以外,操作系统将会占比很大的权重,本文给大家分享我在面试过程中出现的非常高频的面试题,我基本上会从两个角度来阐述,一个是"官话",一个是“大白话”。希望对即将面试的你有所帮助。


为什么有了进程,还要有线程呢?

为了提高系统资源的利用率和系统的吞吐量,通常进程可让多个程序并发的执行,但是也会带来一些问题

  • 进程如果在执行的过程被阻塞,那这个进程将被挂起,这时候进程中有些等待的资源得不到执行;

  • 进程在同一时间只能做一件事儿

基于以上的缺点,操作系统引入了比进程粒度更小的线程,作为并发执行的基本单位,从而减少程序在并发执行时所付出的时间和空间开销,提高并发性能。

小Q当年开发了一个聊天软件,给女朋友说:咋们以后不用什么qq,微信了,我写个聊天工具,咱两正儿八经的两人世界可好。

说干就干,小Q很快就完成了开发,然后开始测试,打电话让女朋友让她发个信息,小Q就等着,等啊等啊,怎么还没有发信息过来,没显示啊,一脸懵逼,单步调试吧,发现程序卡在了wati for user input不动了。

厉害,程序不输入就没办法执行后面的任务。咋搞?要不设置个1s,用户1s不输入直接跳过进行后面的接收阶段和显示阶段,牛皮牛皮,果然好使,好使个锤锤,这样用户输入信息不就很可能丢失,咋搞?

能不能将输入和显示这两个动作给分开,一个负责输入,发送消息,一个负责读信息和显示。不夸夸你自己吗,直接开干呈现两个窗口。

回来回来,这就是我们的多进程。不过多进程也还是有些问题需要注意,开多个窗口没问题,无脑开窗口撩骚直接被榨干(内存耗尽),而且想要几个窗口交换个数据也是贼麻烦,这是为啥呢?

多进程的程序,每个进程都有自己的独立内存空间,都穿了衣服,不能相互乱看,要想通信就要接触系统层面来通信,所以肯定就会造成较多的资源消耗和时间浪费。怎么整?

几个进程为了方便,干脆商量一波,能不能开辟一块内存空间,共乐其中,这就是线程非常重要的意义,不过共享了不代表我们就是"裸"的,个人保密还是要做到,也不要吵架,可不可以通过锁的方式保密呢,这就涉及到了线程的同步。这样我猜测你应该了解进程和线程了吧。

简单说下你对并发和并行的理解?

在一个时间段中多个程序都启动运行在同一个处理机中
假设目前A,B两个进程,两个进程分别由不同的 CPU 管理执行,两个进程不抢占 CPU 资源且可以同时运行,这叫做并行。

并发是指多个任务在一段时间内发生。比如今日成都某家老火锅店做活动,全场5折(这还是比较狠),但是只有200个位置,但是来的人太多了,来了250个人,此时多出的50个人只好等待着或者去另外家火锅店。

那么火锅店老板对这250人的安排不是同一时刻安排而是一段时间去处理,其实这就是并发。这个例子好像整的不算生动,我们再来一个:

到了周末就是我们"开黑"的时光,奈何到了周一不迟到怎么对得起自己,但是迟到了被逮住就要被"BB"。好嘛,我们作为学生娃儿只好认怂,常规操作,两脚一登,起床,刷牙,上厕所,拿包包冲。

就是这样类似复读机的习惯操作是怎么回事?莫非我们就能同时干这么多事?其实不是的,我们大脑下达指令,起床,刷牙这些操作早形成了肌肉记忆,所以我们在这小段时间完成了这么多事儿,还可以多几分钟出来看看美女不香?


平时玩儿电脑的时候,边写代码边听音乐,计算机同时处理了这么多任务。如果是单核 CPU,在我们看来这些事儿是同时发生的,其实那是因为底层 CPU 切换的速度太快以致于我们完全感受不到它的切换,仅此为错觉而已。

但是如果是多核 CPU,各个 CPU 负责不同的进程,各个进程不抢占 CPU,这样同时进行,这就是真正意义上的并行。


说了这么多,那并行和并发到底啥区别?

两者区别在于是否"同时"发生。是在一段时间同时发生还是多个事情在同一个时间点同时发生。

同步、异步、阻塞、非阻塞的概念

首先大家应该知道同步异步,阻塞非阻塞是两个不同层面的问题,一个是operation 层面,一个是 kernal 层面。同步异步最大的区别在于是否需要底层的响应再执行。阻塞非阻塞最大的区别在于是否立即给出响应。

同步:当一个同步调用发出后,调用者要一直等待返回结果。通知后,才能进行后续的执行。

异步:当一个异步过程调用发出后,调用者不能立刻得到返回结果。实际处理这个调用的部件在完成后,通过状态、通知和回调来通知调用者。

阻塞:是指调用结果返回前,当前线程会被挂起,即阻塞。

非阻塞:是指即使调用结果没返回,也不会阻塞当前线程。

  • 小Q去钓鱼,抛完线后就傻傻的看着有没有动静,有则拉杆(同步阻塞);

  • 小Q去钓鱼,拿鱼网捞一下,有没有鱼立即知道,不用等,直接就捞(同步非阻塞);

  • 小Q去钓鱼,这个鱼缸比较牛皮,扔了后自己就打王者荣耀去了,因为鱼上钩了这个鱼缸带的报警器会通知我。这样实现异步(异步非阻塞)。

进程:进程是系统进行资源分配和调度的一个独立单位,是系统中的并发执行的单位。

  • 线程:线程是进程的一个实体,也是 CPU 调度和分派的基本单位,它是比进程更小的能独立运行的基本单位,有时又被称为轻量级进程。

  • 进程是资源分配的最小单位,而线程是 ** CPU 调度**的最小单位;

  • 创建进程或撤销进程,系统都要为之分配或回收资源,操作系统开销远大于创建或撤销线程时的开销;

  • 不同进程地址空间相互独立,同一进程内的线程共享同一地址空间。一个进程的线程在另一个进程内是不可见的;

  • 进程间不会相互影响,而一个线程挂掉将可能导致整个进程挂掉;

别背了,我知道你们都会背,举个例子:

计算机中的核心是 CPU,说它是我们的大脑一点不为过。在此用一个连锁火锅店举例,因为疫情的影响,今天到目前营业额一般,只好开一家店,其它疫情较为严重的店只好先关闭,这里涉及的含义:单个 CPU 一次只运行一个任务。

进程就类似这家火锅店,它代表 CPU 所能处理的单个任务,其他地方火锅店只能处于非运行状态。

一个火锅店有很多服务员,一起协同工作,将火锅店做的红红火火,那么线程就好比这些服务员,一个进程可有多个线程。

火锅店各个工作房间是共享的,所有服务员都可以进出,这意味着进程的内存空间共享,每个线程都可以使用这些共享内存。但是不是每个房间都可以容纳相同的人数,比如卫生间就只有一个人,其他人不能同时进去。

这意味着一个线程在使用共享内存的时候,其它线程需要等待结束才能使用这块内存。那怎么防止别人进入呢?很直接的办法就是进去之后记得关门(上锁),上了锁,其他人想进来发现是上锁了,那就等锁打开后再进去,这就叫做互斥锁,防止多个线程同时读写某一块内存区域。

ok到这里,我们总结一波:

  • 如果以多进程的方式运行,那么允许多个任务同时运行;

  • 如果以多线程的方式运行,那么允许将单个任务分成不同的部分运行;

  • 为了防止进程/线程之间产生冲突和允许进程之间的共享资源,需要提供一种协调机制。

多线程与多进程的基本概念

不知道大家经历过食堂打菜的场景没有,如果学校食堂就开设一个窗口,打菜的阿姨也没办法,只好一个个给大家依次打菜,这就好比"单线程",效率非常低(此处可以考虑为什么redis使用单线程却这么牛逼)。

为了提高效率,在食堂多加了几个窗口,这就类似"多线程"形式。

那么又想起一个问题,“多线程一定就比单线程效率高麦?”(ps Memcache是多线程模型而Redis是单线程模型)

貌似我们一提到高并发,分布式,就不得不想起多线程,那么多线程一定比单线程效率高?

上面说了采用多线程多核效果更好,但是多线程对 CPU,内存等都要求比较高,如果存在的上下文切换过于耗时,互斥时间太久,效率反而会低。

不一定。我不从专业术语来将,举个例子,假设目前接水房有四个水管可以接水,我如果用4个桶分别对应4个水管,那么就比较完美,如果少一个则闲置一个,多一个则会出现抢占。

如果此时我的水桶个数大于水管数,为了每个桶都有水,我们就需要切换水桶,这个过程实际上就是线程的上下文切换,代价一样不小。

多线程与多进程的应用场景
  • 更加高效的内存共享。多进程下内存共享不便;

  • 较轻的上下文切换。因为不用切换地址空间,CR3寄存器和清空TLB。

  • 各个进程有自己内存空间,所以具有更强的容错性,不至于一个集成crash导致系统崩溃;

  • 具有更好的多核可伸缩性,因为进程将地址空间,页表等进行了隔离,在多核的系统上可伸缩性更强。

如何提升多线程的效率?

  • 尽量使用池化技术,也就是线程池,从而不用频繁的创建,销毁线程;

  • 减少线程之间的同步和通信;

  • 通过Huge Page的方式避免产生大量的缺页异常;

  • 避免需要频繁共享写的数据。

在Linux中,进程的状态有七种:

英文名词为TASK_RUNNING,其实这个状态虽然是RUNING,实际上并不一定会占有 CPU ,可能修改TASK_RUNABLE会更妥当。TASK_RUNGING根据是否在在 CPU 上运行分为RUNGING和READY两种状态。处于READY状态的进程随时可以运行,只不过因为此时 CPU 资源受限,调度器没选中运行。
  • 可中断睡眠状态与不可中断睡眠状态

我们知道进程不可能一直处于可运行的状态。假设A进程需要读取磁盘中的文件,这样的系统调用消耗时间较长,进程需要等待较长的时间才能执行后面的命令,而且等待的时间还是不可估算的,这样的话进程还占用 CPU 就不友好了,因此内核就会将其更改为其他的状态并从 CPU 可运行的队列移除。

Linux中存在两种睡眠状态,分别为:可中断的睡眠状态和不可中断的状态。两者最大的区别为是否响应收到的信号,那么从可中断的睡眠的进程是如何返回到可运行的状态呢?

  • 等待的事情发生且继续运行的条件满足

  • 收到了没有被屏蔽的信号

处于此状态的进程,收到信号会返回EINTR给用户空间。开发者通过检测返回值的方式进行后续逻辑处理

但是对于不可中断的睡眠状态,就只有一种方式返回到可运行状态,即等待事情发生了继续运行。


上图中为什么出现个 TASK_UNINTERRUPTIBLE 状态,主要是因为内核中的进程不是什么进程都可以被打断,假设响应的是异步信号,程序在执行的过程中插入一段用于处理异步信号的而流程,原来的流程就会被中断。

所以当进程在和硬件打交道的时候,需要使用 TASK_UNINTERRUPTIBLE 状态将进程保护起来,从而避免进程和设备打交道的过程中被打断导致设备处于不可控的状态。

其实 TASK_UNINTERRUPTIBLE 状态是很危险的状态,因为它刀枪不入,你无法通过信号杀死这样一个不可中断的休眠状态,正常情况,TASK_UNINTERRUPTIBLE状态存在时间很短,但是不排除存在此状态进程比较持久的情况,真的刀枪不入了?可不可以进行提前的预防?

可以的,早就考虑了。内核提供了hung task机制,它会启动一个khungtaskd内核线程对TASK_UNINTERRUPTIBLE状态进行检测,不能让它失控了。khungtaskd会定期的唤醒,如果超过120s都还没有调度,内核就会通过打印警告和堆栈信息。当然,不一定就是120s,可以通过下面选项进行定制:

关于操作系统的内容细节非常的多,也将伴随你的大学四年,考研多半会考,找工作对你底层功力的考察也跑不掉,更重要的在了解其他技术的时候你会发现其实在操作系统基本原理中都有类似思想。

另外说说工作上的事儿,最近刚工作两周,咋们小组牛批了,八个人六个小姐姐,相关故事后续慢慢道来,一定带劲儿。

最后,如果你能在这篇文章中有点点收获,请不要吝啬你的在看和点赞,这将给与小蓝更多写作的动力,fighting。

我要回帖

更多关于 为什么电视突然不能看点播了 的文章

 

随机推荐