Linux 面试题
1. Linux 的体系结构
从大的方面讲,Linux 体系结构可以分为两块:
用户空间(User Space) :用户空间又包括用户的应用程序(User Applications)、C 库(C Library) 。
内核空间(Kernel Space) :内核空间又包括系统调用接口(System Call Interface)、内核(Kernel)、平台架构相关的代码(Architecture-Dependent Kernel Code) 。
Linux内核主要包括由5个子系统组成:进程调度(SCHED),内存管理(MM),虚拟文件系统(VFS),网络接口(NET) ,进程间通信(IPC)。
2. Linux Inode和Dentry
dentry 保存文件和目录的名称和相互之间的包含关系, inode 节点表将文件的逻辑结构和物理结构进行转换。
inode 节点是一个 64 字节长的表,表中包含了文件的相关信息,其中有文件的大小、文件所有者、文件的存取许可方式以及文件的类型等重要信息。在 inode 节点表中最重要的内容是磁盘地址表。在磁盘地址表中有 13 个块号,文件将以块号在磁盘地址表中出现的顺序依次读取相应的块。
Linux 文件系统通过把 inode 节点和文件名进行连接,当需要读取该文件时,文件系统在当前目录表中查找该文件名对应的项,由此得到该文件相对应的 inode 节点号,通过该 inode 节点的磁盘地址表把分散存放的文件物理块连接成文件的逻辑结构。
3. 什么是硬链接和软链接?
硬链接:本质是一个 dentry 资源,但是其所对应的 inode 复用原始文件的 inode 资源。
软链接:在文件系统中新建一个链接文件,并将其内容设置为原始文件绝对路径或者相对路径,当链接文件被访问时会请求会被重定向到原始文件。
4. Linux 主要有哪几种内核锁?Linux 内核的同步机制是什么?
自旋锁:自旋锁的主要特征是使用者在想要获得临界区执行权限时,如果临界区已经被加锁,那么自旋锁并不会阻塞睡眠,等待系统来主动唤醒,而是原地忙轮询资源是否被释放加锁。(spin_lock)
信号量:semxxx down/up write/read
互斥锁:加锁后,任何其他试图再次加锁的线程会被阻塞,直到当前进程解锁。
读写锁:读写锁也叫共享互斥锁:读模式共享和写模式互斥,本质上这种非常合理,因为在数据没有被写的前提下,多个使用者读取时完全不需要加锁的。
RCU(read-copy update): 读写锁的扩展版本,简单来说就是支持多读多写同时加锁,多读没什么好说的,但是对于多写同时加锁,还是存在一些技术挑战的。
5. malloc、vmalloc 和 kmalloc 有什么区别?
malloc 用户空间下的内存管理接口,保证的是在虚拟地址空间上的连续。stack 和 heap 中间。小于128M的通过brk申请,大于的通过 mmap 申请。
vmalloc 用于申请大块内存,虚拟地址连续,物理地址不一定连续,不能直接用于DMA,在进程地址空间有专门的一块。对应释放函数 vfree()。
kmalloc 用于申请小内存,由 slab 管理实现,一般至少小于4KB(page)。不能申请大于128K的数据。物理地址和虚拟地址都连续,可用于DMA操作。
6. Linux 内核空间布局
x86架构中将内核地址空间划分三部分:ZONE_DMA、ZONE_NORMAL和 ZONE_HIGHMEM。ZONE_HIGHMEM即为高端内存,这就是内存高端内存概念的由来。
ZONE_DMA 内存开始的16MB
ZONE_NORMAL 16MB~896MB
ZONE_HIGHMEM 896MB ~ 结束(1G)
当内核想访问高于896MB物理地址内存时,从0xF8000000 ~ 0xFFFFFFFF地址空间范围内找一段相应大小空闲的逻辑地址空间,借用一会。借用这段逻辑地址空间,建立映射到想访问的那段物理内存(即填充内核PTE页面表),临时用一会,用完后归还。这样别人也可以借用这段地址空间访问其他物理内存,实现了使用有限的地址空间,访问所有所有物理内存。
用户进程没有高端内存概念。只有在内核空间才存在高端内存。用户进程最多只可以访问3G物理内存,而内核进程可以访问所有物理内存。
目前现实中,64位Linux内核不存在高端内存,因为64位内核可以支持超过512GB内存。若机器安装的物理内存超过内核地址空间范围,就会存在高端内存。
8. Linux 用户内存空间布局
text段:就是放程序代码的,编译时确定,只读。
data段:存放在编译阶段(而非运行时)就能确定的数据,可读可写就是通常所说的静态存储区,赋了初值的全局变量和静态变量存放在这个域,常量也存放在这个区域。
rdata段:rdata是用来存放只读初始化变量的,当我们在源程序中的变量前面加了const后,编译器知道个字符串是永远不会改变的,或说是只读的,所以将其分配到.rdata段中。
bss段:定义而没有赋初值的全局变量和静态变量,放在这个区域
栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
堆区(heap) — 般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收
程序内存段和进程地址空间中的内存区域是种模糊对应,也就是说,堆、bss、数据段(初始化过的)都在进程空间中由数据段内存区域表示。
9. 进程内存的分配与回收
创建进程fork()、程序载入execve()、映射文件mmap()、动态内存分配malloc()/brk()等进程相关操作都需要分配内存给进程。不过这时进程申请和获得的还不是实际内存,而是虚拟内存,准确的说是“内存区域”。进程对内存区域的分配最终都会归结到do_mmap()函数上来(brk调用被单独以系统调用实现,不用do_mmap()), 内核使用do_mmap()函数创建一个新的线性地址区间。但是说该函数创建了一个新VMA并不非常准确,因为如果创建的地址区间和一个已经存在的地址区间相邻,并且它们具有相同的访问权限的话,那么两个区间将合并为一个。如果不能合并,那么就确实需要创建一个新的VMA了。但无论哪种情况, do_mmap()函数都会将一个地址区间加入到进程的地址空间中--无论是扩展已存在的内存区域还是创建一个新的区域。 同样,释放一个内存区域应使用函数do_ummap(),它会销毁对应的内存区域。
10. 进程间通信主要有哪几种方式?
管道:两个进程需要有共同的祖先,pipe/popen
命名管道:两个进程可以无关
信号
共享内存
信号量
套接字
8. 伙伴系统申请内存的函数有哪些?
alloc_page(gfp_mask, order)
__get_free_pages(gfp_mask, order)
9. 通过 slab 分配器申请内存的函数有哪些?
自己构造对象:kmem_cache_create/kmem_cache_alloc
普通匿名内存申请:kmalloc
10. Linux 的内核空间和用户空间如何划分的?进程地址空间布局图?
32位可配置3G/1G, 2G/2G,一般是两级页表
64位可配置几级页表,一般可选3级/4级页表,256G/256G,或512T/512T
11. 在支持并使能 MMU 的系统中,Linux 内核和用于程序分别运行在物理地址模式还是虚拟地址模式?
都运行在虚拟地址模式,页表转换对应由硬件单元MMU完成。
12. Linux虚拟文件系统结构
super_block超级块
inode索引节点
dentry目录项
file文件
13. Linux 中的文件包括哪些?
可执行文件,普通文件,目录文件,链接文件,设备文件,管道文件
14. 创建进程的系统调用有哪些?
clone, fork, vfork
15. 调用 schedule() 进行进程切换的方式有几种?
do_fork/do_timer/wake_up_process/setscheduler/sys_sched_yield
16. Linux 调度程序是根据进程的动态优先级还是静态优先级来调度进程的?
cfs 会计算虚拟时间,还有一个计算出来的优先级。
17. TLB 中缓存的是什么内容
translation lookaside buffer, 也叫快表,用作页表缓冲。记录虚拟地址和物理地址的对应关系,用于加快地址转换。
18. Linux 中有哪几种设备?
字符设备和块设备
19. 设备驱动程序包括哪些功能函数?
open/read/write/ioctl/release/llseek
20. 如何唯一标识一个设备?
主设备号和次设备号。dev_t,12位表示主设备号,20位表示次设备号。
MKDEV(int major, int minor)用于生产一个 dev_t 类型的对象。
21. Linux 通过什么方式实现系统调用?
软件中断。系统调用编号,异常处理程序
22. Linux 软中断和工作队列的作用是什么?
软中断:不可睡眠阻塞,处于中断上下文,不能进程切换,不能被自己打断。
工作队列:处理进程上下文中,可以睡眠阻塞。
23. Linux 磁盘I/O的三种方式对比
标准I/O、直接 I/O、mmap