个人整理 - Java 后端面试题 - 操作系统篇

本贴最后更新于 1749 天前,其中的信息可能已经东海扬尘
  • 标 ★ 号为重要知识点

CentOS 和 Linux 的关系?

CentOS 是 RedHat 的一个分支,RedHat 是 Linux 的一个发行版本,RedHat 与 CentOS 的区别在于,
RedHat 收费,CentOS 免费

怎么杀死进程?

一般情况下,终止一个前台进程使用 Ctrl + C 就可以了。对于一个后台进程就须用 kill 命令来终止。
我们会先使用 ps、top 等命令获得进程的 PID,然后使用 kill 命令来杀掉该进程。

线程,进程区别?

进程是资源分配的最小单位,线程是 CPU 调度的最小单位。

系统线程数量上限是多少?

具体看内存,还有系统定义的线程数 /proc/sys/kernel/pid_max 32768

什么是页式存储?

连续存储管理不足:

  • 对空间要求高
  • 会形成很多碎片
  • 通过移动技术减少碎片会增加系统的开销

分页式存储管理的基本原理如下:

  • 页框:物理地址分成大小相等的许多区,每个区称为一块(又称页框 page frame);
  • 页面:逻辑地址分成大小相等的区, 区的大小与块的大小相等,每个区称一个页面(page)。
  • 逻辑地址形式:与此对应,分页存储器的逻辑地址由两部分组成:页号和单元号。
  • 用户进程在内存空间中的每个页框内的地址是连续的,但页框和页框之间的地址可以不连续
  • 页表和地址转换:在进行存储分配时,总是以块(页框)为单位进行分配,一个作业的信息有多少页,那么在把它
    装入主存时就给它分配多少块。但是,分配给作业的主存块是可以不连续的,即作业的信息可按页分散存放在主存
    的空闲块中,这就避免了为得到连续存储空间而进行的移动。

★ 操作系统里的内存碎片你怎么理解,有什么解决办法?

内存碎片通常分为内部碎片和外部碎片:

1. 内部碎片是由于采用固定大小的内存分区,当一个进程不能完全使用分给它的固定内存区域时就会产生内部碎片,  
通常内部碎片难以完全避免;
2. 外部碎片是由于某些未分配的连续内存区域太小,以至于不能满足任意进程的内存分配请求,从而不能被进程利用  
的内存区域。  
现在普遍采取的内存分配方式是段页式内存分配。将内存分为不同的段,再将每一段分成固定大小的页。通过页表机制,  
使段内的页可以不必连续处于同一内存区域。

★ 什么情况下会发生死锁,解决策略有哪些?

产生死锁的四个必要条件:

  1. 互斥条件:一个资源每次只能被一个进程使用。
  2. 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
  3. 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
  4. 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。

所以要根据产生死锁的条件进行预防,并且要尽量避免死锁。所以解决策略有:

(1)破坏互斥条件:

就是在系统里取消互斥。若资源不被一个进程独占使用,那么死锁是肯定不会发生的。
但一般来说在所列的四个条件中,“互斥”条件是无法破坏的。因此,在死锁预防里主要是破坏其他几个必要条件,  
而不去涉及破坏“互斥”条件

(2)破坏“占有并等待”条件:

破坏“占有并等待”条件,就是在系统中不允许进程在已获得某种资源的情况下,申请其他资源。即要想出一个办法,  
阻止进程在持有资源的同时申请其他资源。
方法一:创建进程时,要求它申请所需的全部资源,系统或满足其所有要求,或么什么也不给它。这是所谓的“一次性分配”方案。
方法二:要求每个进程提出新的资源申请前,释放它所占有的资源。这样,一个进程在需要资源S时,须先把它先前占有的资源R  
释放掉,然后才能提出对S的申请,即使它可能很快又要用到资源R。

(3)破坏“不可抢占”条件

破坏“不可抢占”条件就是允许对资源实行抢夺。
方法一:如果占有某些资源的一个进程进行进一步资源请求被拒绝,则该进程必须释放它最初占有的资源,如果有必要,可再次  
请求这些资源和另外的资源。
方法二:如果一个进程请求当前被另一个进程占有的一个资源,则操作系统可以抢占另一个进程,要求它释放资源。只有在任意两  
个进程的优先级都不相同的条件下,方法二才能预防死锁。

(4)破坏“循环等待”条件

破坏“循环等待”条件的一种方法,是将系统中的所有资源统一编号,进程可在任何时刻提出资源申请,但所有申请必须按照资源的编号  
顺序(升序)提出。这样做就能保证系统不出现死锁。
避免死锁: 死锁的预防是通过破坏产生条件来阻止死锁的产生,但这种方法破坏了系统的并行性和并发性。 死锁产生的前三个条件是  
死锁产生的必要条件,也就是说要产生死锁必须具备的条件,而不是存在这3个条件就一定产生死锁,那么只要在逻辑上回避了第四个  
条件就可以避免死锁。 避免死锁采用的是允许前三个条件存在,但通过合理的资源分配算法来确保永远不会形成环形等待的封闭进程  
链,从而避免死锁。该方法支持多个进程的并行执行,为了避免死锁,系统动态的确定是否分配一个资源给请求的进程。
总的思路来说,预防死锁就是避免死锁的最好方法!不过一旦预防不了而产生死锁就应该及时中断进程和破坏中断。最有效的两个做法  
就是一,只运行需要的进程程序而禁用其他进程程序,二,重启机器来破坏中断是最直接有效的。 

★ 常用的 linux 下的命令

1、find 查找文件或目录
find / -size +204800k //在根目录下查找大于200MB的文件
find / -user username//在根目录下查找所有者为username的文件
find / -name filename.txt //根据名称查找/目录下的filename.txt文件。
2、复制文件包括其子文件到自定目录
cp -r sourceFolder targetFolder
3、查看一个程序是否运行
ps –ef|grep tomcat //查看所有有关tomcat的进程
4、终止线程
kill -9 19979 //终止线程号位19979的线程
5、查看文件,包含隐藏文件
ls -al
6、当前工作目录
pwd
7.创建目录
mkdir newfolder
8.删除目录(此目录是空目录)
rmdir deleteEmptyFolder
9.删除文件包括其子文件
rm -rf deleteFile
10.移动文件
mv /temp/movefile /targetFolder//扩展重命名 mv oldNameFile newNameFile
11.切换用户
su -username
12.修改文件权限
chmod 777 file.java //file.java的权限-rwxrwxrwx,r表示读、w表示写、x表示可执行
13.压缩文件
tar -czf test.tar.gz /test1 /test2
14.列出压缩文件列表
tar -tzf test.tar.gz
15.解压文件
tar -xvzf test.tar.gz
16.查看文件头10行
head -n 10 example.txt
17.查看文件尾10行
tail -n 10 example.txt
18.查看日志文件
tail -f exmaple.log //这个命令会自动显示新增内容,屏幕只显示10行内容的(可设置)。
19.启动Vi编辑器
vi
20.查看系统当前时间
date

linux 中 统计多个关键字在某个文本中出现的次数,并按次数排序。

cat test.log | awk {print'$n'} | sort -nr | uniq -c | sort -k1 -nr
其中 $n 为需要按出现次数排序的那一列
sort -nr 先排序
uniq -c 去重并计算出现次数
sort -k1 -nr 按出现次数排序

Linux 系统下你关注过哪些内核参数,说说你知道的。

1、编辑文件或命令

vim /etc/sysctl.conf  或 sysctl     (参数-n 查询某个值,参数-w 设置)

2、参数及简单说明

net.ipv4.tcp_fin_timeout = 2           #保持在FIN-WAIT-2状态的时间,使系统可以处理更多的连接。此参数值为整数,单位为秒。
net.ipv4.tcp_tw_reuse = 1              #开启重用,允许将TIME_WAIT socket用于新的TCP连接。默认为0,表示关闭。
net.ipv4.tcp_tw_recycle = 1            #开启TCP连接中TIME_WAIT socket的快速回收。默认值为0,表示关闭。
net.ipv4.tcp_syncookies = 1            #开启SYN cookie,出现SYN等待队列溢出时启用cookie处理,防范少量的SYN攻击。默认为0,表示关闭。
net.ipv4.tcp_keepalive_time = 600      #keepalived启用时TCP发送keepalived消息的拼度。默认位2小时。
net.ipv4.tcp_keepalive_probes = 5      #TCP发送keepalive探测以确定该连接已经断开的次数。根据情形也可以适当地缩短此值。
net.ipv4.tcp_keepalive_intvl = 15      #探测消息发送的频率,乘以tcp_keepalive_probes就得到对于从开始探测以来没有响应的连接杀除的时间。
默认值为75秒,也就是没有活动的连接将在大约11分钟以后将被丢弃。对于普通应用来说,这个值有一些偏大,可以根据需要改小.特别是web类服务器需要改小该
值。
net.ipv4.ip_local_port_range = 1024 65000 #指定外部连接的端口范围。默认值为32768 61000。
net.ipv4.tcp_max_syn_backlog = 262144  #表示SYN队列的长度,预设为1024,这里设置队列长度为262 144,以容纳更多的等待连接。
net.ipv4.tcp_max_tw_buckets =5000      #系统同时保持TIME_WAIT套接字的最大数量,如果超过这个数值将立刻被清楚并输出警告信息。默认值为
180000。对于squid来说效果不是很大,但可以控制TIME_WAIT套接字最大值,避免squid服务器被拖死。 
net.ipv4.tcp_syn_retries = 1           #表示在内核放弃建立连接之前发送SYN包的数量。
net.ipv4.tcp_synack_retries = 1        #设置内核放弃连接之前发送SYN+ACK包的数量。
net.core.somaxconn = 16384             #定义了系统中每一个端口最大的监听队列的长度, 对于一个经常处理新连接的高负载 web服务环境来说,
默认值为128,偏小。
net.core.netdev_max_backlog = 16384    #表示当在每个网络接口接收数据包的速率比内核处理这些包的速率快时,允许发送到队列的数据包的最大数量。
net.ipv4.tcp_max_orphans = 16384       #表示系统中最多有多少TCP套接字不被关联到任何一个用户文件句柄上。如果超过这里设置的数字,连接就
会复位并输出警告信息。这个限制仅仅是为了防止简单的DoS攻击。此值不能太小。 

3、生效

sysctl -p

★Linux 下 IO 模型有几种,各自的含义是什么。

  1. 阻塞 IO:

简介:进程会一直阻塞,直到数据拷贝完成。应用程序调用一个 IO 函数,导致应用程序阻塞,等待数据准备好。 如果数据没有准备好,
一直等待….数据准备好了,从内核拷贝到用户空间,IO 函数返回成功指示。

  1. 非阻塞 I/O 模型:

创建 socket 套接字并返回对应的文件描述符。
简介:非阻塞 IO 通过进程反复调用 IO 函数(轮询,多次系统调用,并马上返回);在数据拷贝的过程中,进程是阻塞的;
我们把一个 SOCKET 接口设置为非阻塞就是告诉内核,当所请求的 I/O 操作无法完成时,不要将进程睡眠,而是返回一个错误(EWOULDBLOCK)。
这样我们的 I/O 操作函数将不断的测试数据是否已经准备好,如果没有准备好,继续测试,直到数据准备好为止。在这个不断测试的过程中,
会大量的占用 CPU 的时间。

  1. I/O 复用模型:

简介:主要是 select 和 epoll;对一个 IO 端口,两次调用,两次返回,比阻塞 IO 并没有什么优越性;关键是能实现同时对多个 IO 端口进行监听;
调用阻塞在系统调用 selcte 上,而不是在 recv 真正得 IO 调用上面,内核数据准备好了,通知引用程序,然后调用 recv 将数据从内核空间拷贝到用户空间处理。

  1. 信号驱动式 I/O 模型:
    使用信号,让内核在描述符准备就绪的时候发送 SIGIO 信号通知我们,然后在信号回调函数里面,将数据拷贝到用户空间。

  2. 异步 I/O 模型:
    调用 aio_read 读取,这些函数的工作机制是:告知内核启动某个操作,并让内核在整个操作(包括将数据从内核复制到我们自己的缓冲区)
    完成后通知我们。信号驱动式是有了内核缓冲区有了数据,才发出信号让我们调用 IO 函数去读。而这个就是数据有了将其已经复制到了应用程序,
    然后通知我们,我们直接处理就可以了,不需要再次调用 IO 从缓冲区读到用户空间。

select、poll 和 epoll 之间的区别

  1. select==> 时间复杂度 O(n)

它仅仅知道了,有 I/O 事件发生了,却并不知道是哪那几个流(可能有一个,多个,甚至全部),我们只能无差别轮询所有流,找出能读出数据,或者写入数据的流,对他们进行操作。所以 select 具有 O(n)的无差别轮询复杂度,同时处理的流越多,无差别轮询时间就越长。

  1. poll==> 时间复杂度 O(n)

poll 本质上和 select 没有区别,它将用户传入的数组拷贝到内核空间,然后查询每个 fd 对应的设备状态, 但是它没有最大连接数的限制,原因是它是基于链表来存储的.

  1. epoll==> 时间复杂度 O(1)

epoll 可以理解为 event poll,不同于忙轮询和无差别轮询,epoll 会把哪个流发生了怎样的 I/O 事件通知我们。
所以我们说 epoll 实际上是事件驱动(每个事件关联上 fd)的,此时我们对这些流的操作都是有意义的。(复杂度降低到了 O(1))select,poll,epoll 都是 IO 多路复用的机制。I/O 多路复用就通过一种机制,可以监视多个描述符,一旦某个描述符就绪 一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。但 select,poll,epoll 本质上都是同步 I/O,因为他们都需要 在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的,而异步 I/O 则无需自己负责进行读写,异步 I/O 的实现会负责把数据从内核拷贝到用户空间。

用一行命令查看文件的最后五行。

tail -n 5 /path/filename

用一行命令输出正在运行的 java 进程。

ps -A | grep java

top 命令之后有哪些内容,有什么作用。

https://blog.csdn.net/zhuoya_/article/details/81049967

线上 CPU 爆高,请问你如何找到问题所在。

  1. 首先根据 top 命令找出 cpu 占用高的进程 PID
  2. ps aux | grep PID 查看是什么进程
  3. ps -mp pid -o THREAD,tid,time 显示进程内的线程
  4. 其次将需要的线程 ID 转换为 16 进制格式:printf "%x\n" tid
  5. jstack pid |grep tid -A 30

★ 总结下排查 CPU 故障的方法和技巧有哪些:

  1. top 命令:Linux 命令。可以查看实时的 CPU 使用情况。也可以查看最近一段时间的 CPU 使用情况。
  2. PS 命令:Linux 命令。强大的进程状态监控命令。可以查看进程以及进程中线程的当前 CPU 使用情况。属于当前状态的采样数据。
  3. jstack:Java 提供的命令。可以查看某个进程的当前线程栈运行情况。根据这个命令的输出可以定位某个进程的所有线程的当前 运行状态、运行代码,以及是否死锁等等。
  4. pstack:Linux 命令。可以查看某个进程的当前线程栈运行情况。

★linux 利用哪些命令,查找哪里出了问题(例如 io 密集任务,cpu 过度)

  • iotop 命令可以找出 io 高负载的进程
  • top 命令可以找出 cpu 高负载的进程

转自我的 github

技术讨论群 QQ:1398880
  • 面试

    面试造航母,上班拧螺丝。多面试,少加班。

    325 引用 • 1395 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...