操作系统-面试

操作系统

  • 并发:同时运行多个程序—进程和线程
  • 并行:同时运行多个指令

进程状态:

在这里插入图片描述
在这里插入图片描述
由于进程的不断创建,系统资源特别是主存资源已不能满足所有进程运行的要求。这时,就必须将某些进程挂起,放到磁盘对换区,暂时不参加调度,以平衡系统负载;进程挂起的原因可能是系统故障,或者是用户调试程序,也可能是需要检查问题。

线程状态:

状态转换
在这里插入图片描述
在这里插入图片描述

New:新创建的线程,尚未执行;
Runnable:运行中的线程,正在执行run()方法的Java代码;
Blocked:运行中的线程,因为某些操作被阻塞而挂起;
Waiting:运行中的线程,因为某些操作在等待中;
Timed Waiting:运行中的线程,因为执行sleep()方法正在计时等待;
Terminated:线程已终止,因为run()方法执行完毕。

在这里插入图片描述
在这里插入图片描述

  • wait与notify
    • wait():持有锁的线程调用后一直等待
    • notify(): 唤醒一个等待线程
    • notifyAll(): 唤醒所有等待队列的线程
  • 同步队列和等待队列
    • 同步队列:所有尝试获取该对象Monitor失败的线程,都加入同步队列排队获取锁
    • 等待队列:已经拿到锁的线程在等待其他资源时,主动释放锁,置入该对象等待队列中,等待被唤醒,当调用notify()会在等待队列中任意唤醒一个线程,将其置入同步队列的尾部,排队获取锁
  • sleep方法和wait方法的区别是什么?
    sleep 方法是Thread类的一个静态方法,其作用是使运行中的线程暂时停止指定的毫秒数,从而该线程进入阻塞状态并让出处理器,将执行的机会让给其他线程。但是这个过程中监控状态始终保持,当sleep的时间到了之后线程会自动恢复。
    wait 方法是Object类的方法,它是用来实现线程同步的。当调用某个对象的wait方法后,当前线程会被阻塞并释放同步锁,直到其他线程调用了该对象的 notify 方法或者 notifyAll 方法来唤醒该线程。所以 wait 方法和 notify(或notifyAll)应当成对出现以保证线程间的协调运行。

创建线程的方法:

  • 继承extends Thread
  • 该类有父类时,实现Runnable接口,覆盖run()
  • 实现Callable接口
  • 使用线程池

线程池

如果提交给线程的任务是执行时间较短,而且执行次数极其频繁,那么服务器将处于不停的创建线程,销毁线程的状态。

线程池的出现正是着眼于减少线程池本身带来的开销。线程池采用预创建的技术,在应用程序启动之后,将立即创建一定数量的线程(N1),放入空闲队列中。这些线程都是处于阻塞(Suspended)状态,不消耗CPU,但占用较小的内存空间。当任务到来后,缓冲池选择一个空闲线程,把任务传入此线程中运行。当N1个线程都在处理任务后,缓冲池自动创建一定数量的新线程,用于处理更多的任务。在任务执行完毕后线程也不退出,而是继续保持在池中等待下一次的任务。当系统比较空闲时,大部分线程都一直处于暂停状态,线程池自动销毁一部分线程,回收系统资源。

基于这种预创建技术,线程池将线程创建和销毁本身所带来的开销分摊到了各个具体的任务上,执行次数越多,每个任务所分担到的线程本身开销则越小,不过我们另外可能需要考虑进去线程之间同步所带来的开销。

进程同步的方法:

  • 使用synchronized关键字:在需要访问的类中的方法声明(共享资源)前加上synchronized关键字
  • synchronized(object){…},object就是我们需要访问的资源,谁访问了object,谁就拥有控制权

死锁

java死锁面试
条件:
互斥
持有等待
环路等待
不可剥夺
解决死锁问题的四种方法:
1、破坏不剥夺条件:让对面的司机放弃了自己已有的资源。
2、破坏请求与保持条件:在自己需要的材料缺少时,主动放弃自己持有的资源,防止出现互相等待。
3、破坏循环等待条件:由于筷子指定了编号和获取规则,所以每个锁定状态都将按照顺序执行,于是便杜绝了环路等待条件。
4、破坏互斥条件:由于每次使用时都拷贝一份,所以一个资源可以被多个进程使用。

加锁顺序:当多个线程需要相同的一些锁,但是按照不同的顺序加锁,死锁就很容易发生。如果能确保所有的线程都是按照相同的顺序获得锁,那么死锁就不会发生。当然这种方式需要你事先知道所有可能会用到的锁,然而总有些时候是无法预知的。

进程和线程的区别

区别

进程间的通信方式

进程通信

  • 无名管道pipe:只能用于亲缘间。只存在于内存不存在于文件系统。半双工,单向流动(linux:|)。简单,不适用于频繁的通信。
  • 有名管道fifo:可用于非亲缘间。存在于文件系统。半双工,单向流动
  • 共享内存:最快。映射同一块内存空间。和信号量结合使用。
  • 消息队列:克服了信号量承载信息量少,管道只能以无格式字节流通信的缺点。消息的链表,存放在内核中并由消息队列标识符标识。
  • 信号量:访问临界区。不同进程间或者同一进程不同线程间。
  • 信号:通知进程某件事发生。kill。异步。
  • socket:可以用于不同设备间

线程间的通信方式

  • 锁机制
  • 信号量机制
  • 信号机制

进程调度算法

CPU调度算法

linux下查看某一端口被哪个进程占用

同步非同步/阻塞非阻塞

1.同步与异步

同步和异步关注的是消息通信机制 (synchronous communication/ asynchronous communication)所谓同步,就是在发出一个调用时,在没有得到结果之前,该调用就不返回。但是一旦调用返回,就得到返回值了。换句话说,就是由调用者主动等待这个调用的结果。而异步则是相反,调用在发出之后,这个调用就直接返回了,所以没有返回结果。换句话说,当一个异步过程调用发出后,调用者不会立刻得到结果。而是在调用发出后,被调用者通过状态、通知来通知调用者,或通过回调函数处理这个调用。典型的异步编程模型比如Node.js

举个通俗的例子:你打电话问书店老板有没有《分布式系统》这本书,如果是同步通信机制,书店老板会说,你稍等,”我查一下",然后开始查啊查,等查好了(可能是5秒,也可能是一天)告诉你结果(返回结果)。而异步通信机制,书店老板直接告诉你我查一下啊,查好了打电话给你,然后直接挂电话了(不返回结果)。然后查好了,他会主动打电话给你。在这里老板通过“回电”这种方式来回调。

  1. 阻塞与非阻塞
    阻塞和非阻塞关注的是程序在等待调用结果(消息,返回值)时的状态.阻塞调用是指调用结果返回之前,当前线程会被挂起。调用线程只有在得到结果之后才会返回。非阻塞调用指在不能立刻得到结果之前,该调用不会阻塞当前线程。

还是上面的例子,你打电话问书店老板有没有《分布式系统》这本书,你如果是阻塞式调用,你会一直把自己“挂起”,直到得到这本书有没有的结果,如果是非阻塞式调用,你不管老板有没有告诉你,你自己先一边去玩了, 当然你也要偶尔过几分钟check一下老板有没有返回结果。在这里阻塞与非阻塞与是否同步异步无关。跟老板通过什么方式回答你结果无关。

作者:卢毅luis
链接:https://www.zhihu.com/question/19732473/answer/20851256
来源:知乎

用户态/核心态

操作系统为什么分内核态和用户态,这两者之间如何切换

因为在CPU的指令中,有一些是非常危险的,比如清理内存、设置时钟等,如果所有的程序都能使用,就可能造成系统的崩溃,所以,CPU 将指令分为特权指令和非特权指令,对于那些危险的指令,只允许操作系统使用。CPU 的特权级别有四级,从 Ring0 到 Ring3,正常使用时一般只有两级,即用户态的 Ring3 和内核态的 Ring0。Ring3 状态不能访问 Ring0 的地址空间,包括代码和数据。

用户态切换到内核态的三种方式

系统调用(系统调用是通过软中断实现的)
中断(硬)
异常

用户态访问内核态资源的方式

用户态的应用程序可以通过三种方式来访问内核态的资源:

1)系统调用
2)库函数
3)Shell脚本

系统调用

系统调用是操作系统的最小功能单位,根据不同的应用场景,不同的Linux发行版本提供的系统调用数量也不尽相同,大致在240-350之间。系统调用组成了用户态跟内核态交互的基本接口。
例如:用户态想要申请一块20K大小的动态内存,就需要brk系统调用,将数据段指针向下偏移,如果用户态多处申请20K动态内存,同时又释放呢?这个内存的管理就变得非常的复杂。
我们可以把系统调用看成是一种不能再化简的操作(类似于原子操作,但是不同概念),有人把它比作一个汉字的一个“笔画”,而一个“汉字”就代表一个上层应用,因此,有时候如果要实现一个完整的汉字(给某个变量分配内存空间),就必须调用很多的系统调用。

库函数

库函数就是屏蔽这些复杂的底层实现细节,减轻程序员的负担,从而更加关注上层的逻辑实现。它对系统调用进行封装,提供简单的基本接口给用户,这样增强了程序的灵活性,当然对于简单的接口,也可以直接使用系统调用访问资源,例如:open(),write(),read()等等。库函数根据不同的标准也有不同的版本,例如:glibc库,posix库等。

接着上面的系统调用继续说:

系统调用过多,这势必会加重程序员的负担,良好的程序设计方法是:重视上层的业务逻辑操作,而尽可能避免底层复杂的实现细节。
库函数正是为了将程序员从复杂的细节中解脱出来而提出的一种有效方法。它实现对系统调用的封装,将简单的业务逻辑接口呈现给用户,方便用户调用,从这个角度上看,库函数就像是组成汉字的“偏旁”。
如“人”,对于复杂操作,我们借助于库函数来实现,如“仁”。显然,这样的库函数依据不同的标准也可以有不同的实现版本,如ISO C标准库,POSIX标准库等。

Shell脚本

Shell是一个特殊的应用程序,俗称命令行,本质上是一个命令解释器,它下通系统调用,上通各种应用,通常充当着一种“胶水”的角色,来连接各个小功能程序,让不同程序能够以一个清晰的接口协同工作,从而增强各个程序的功能。

select, poll,epoll区别

select,poll,epoll
io多路复用

BIO,NIO

bio,nio

内存管理

为什么虚拟地址

  • 程序可以使用一系列相邻的虚拟地址来访问物理内存中不相邻的大内存缓冲区。
  • 程序可以使用一系列虚拟地址来访问大于可用物理内存的内存缓冲区。当物理内存的供应量变小时,内存管理器会将物理内存页(通常大小为 4 KB)保存到磁盘文件。数据或代码页会根据需要在物理内存与磁盘之间移动。
  • 不同进程使用的虚拟地址彼此隔离。 一个进程中的代码无法更改正在由另一进程使用的物理内存。

分页 分段

悲观锁

中断

中断和异常

区别

中断处理

在这里插入图片描述
中断请求–》中断响应–》关中断–》保护断电–》中断源识别–》保护现场(断点处各寄存器的内容)–》处理中断–》恢复现场–》中断返回

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值