linux内核,三十个相关问题

linux内核,三十个相关问题
...

1) Linux中主要有哪几种内核锁?

说明:在多核处理器下,会存在多个进程处于内核态的情况,在内核态下,进程是可以访问所有的内核数据,因此需要对共享数据做保护,即互斥处理。

原子性:一个操作是不可中断的,即使在多个线程一起执行的时候,一个操作一旦开始,就不会被其它线程所干扰。

Linux的内核锁主要是自旋锁和信号量。

自旋锁最多只能被一个可执行线程持有,如果一个执行线程试图请求一个已被征用(已经被持有)的自旋锁,那么这个线程就会一直进行慢循环——旋转——等待锁重新可用。要是锁未被征用,请求它的执行线程便能立刻得到它并且继续进行。自旋锁可以在任何时刻防止多于一个的执行线程同时进入临界区。

Linux中的信号量是一种睡眠锁。如果有一个任务试图获得一个已被持有的信号量时,信号量会将其推入等待队列,然后让其睡眠。这时处理器获得自由去执行其它代码。当持有信号量的进程将信号量释放后,在等待队列中的一个任务将被唤醒,从而便可以获得这个信号量。

信号量的睡眠特性,使得信号量适用于锁会被长时间持有的情况;只能在进程上下文中使用,因为中断上下文中是不能被调度的;另外当代码持有信号量时,不可以再持有自旋锁。

2) Linux中的用户模式和内核模式是什么含意?


内核态:进程陷入内核代码中执行,在内核态将会使用内核栈,每个进程都有自己的内核栈。

用户态:进程在执行自身的应用代码。当正在执行用户程序而突然被中断程序中断时,此时用户程序也可以象征性地称为处于进程的内核态。因为中断处理程序将使用当前进程的内核栈。这与处于内核态的进程的状态有些类似。

当用户程序调用系统的API时,会产生中断,进入内核态的API,处理完成后,用中断再退出,返回用户态的调用函数:

user api --> interrupt --> kernel api --> interrupt

虽然用户态下和内核态下工作的程序有很多差别,但最重要的差别就在于特权级的不同,即权力的不同。

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

——>:系统调用:而系统调用的机制其核心还是使用了操作系统为用户特别开放的一个中断来实现。

——>:异常:当出现事先不可知的异常,会切到异常内核程序,比如缺页异常。

——>:外围设备的中断:由用户态切到内核态去执行中断程序。

内核模式的代码可以无限制地访问所有处理器指令集以及全部内存和I/O空间。如果用户模式的进程要享有此特权,它必须通过系统调用向设备驱动程序或其他内核模式的代码发出请求。另外,用户模式的代码允许发生缺页,而内核模式的代码则不允许。

3) 怎样申请大块内核内存?

在linux内核环境下,申请大块内存的成功率随着系统运行时间的增加而减少,虽然可以通过vmalloc系列调用申请物理不连续但虚拟地址连续的内存,但毕竟其使用效率不高且在32位系统上vmalloc的内存地址空间有限。所以,一般的建议是在系统启动阶段申请大块内存,但是其成功的概率也只是比较高而已,而不是100%。如果程序真的比较在意这个申请的成功与否,只能退用“启动内存”(Boot Memory)

4) 用户进程间通信主要哪几种方式?

这里给大家分享一个Linux内核进程通信实现的视频:点击这里

c/c++Linux服务器开发高阶知识点视频学习资料的朋友后台私信【架构】获取

...

(1)管道(Pipe):管道可用于具有亲缘关系进程间的通信,允许一个进程和另一个与它有共同祖先的进程之间进行通信。

(2)命名管道(named pipe):命名管道克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允许无亲缘关系进程间的通信。命名管道在文件系统中有对应的文件名。命名管道通过命令mkfifo或系统调用mkfifo来创建。

(3)信号(Signal):信号是比较复杂的通信方式,用于通知接受进程有某种事件发生,除了用于进程间通信外,进程还可以发送信号给进程本身;linux除了支持Unix早期信号语义函数sigal外,还支持语义符合Posix.1标准的信号函数sigaction(实际上,该函数是基于BSD的,BSD为了实现可靠信号机制,又能够统一对外接口,用sigaction函数重新实现了signal函数)。

(4)消息(Message)队列:消息队列是消息的链接表,包括Posix消息队列system V消息队列。有足够权限的进程可以向队列中添加消息,被赋予读权限的进程则可以读走队列中的消息。消息队列克服了信号承载信息量少,管道只能承载无格式字节流以及缓冲区大小受限等缺

(5)共享内存:使得多个进程可以访问同一块内存空间,是最快的可用IPC形式。是针对其他通信机制运行效率较低而设计的。往往与其它通信机制,如信号量结合使用,来达到进程间的同步及互斥

(6)信号量(semaphore):主要作为进程间以及同一进程不同线程之间的同步手段

(7)套接字(Socket):更为一般的进程间通信机制,可用于不同机器之间的进程间通信。起初是由Unix系统的BSD分支开发出来的,但现在一般可以移植到其它类Unix系统上:Linux和System V的变种都支持套接字。

5) 通过伙伴系统申请内核内存的函数有哪些?

在物理页面管理上实现了基于区的伙伴系统(zone based buddy system)。对不同区的内存使用单独的伙伴系统(buddy system)管理,而且独立地监控空闲页。相应接口alloc_pages(gfp_mask, order),_ _get_free_pages(gfp_mask, order)等。

6) 通过slab分配器申请内核内存的函数有?

kmalloc 和 kfree。

引申

slab:linux下的内存分配机制,针对一些经常分配并释放的对象,如进程描述符,对象大小一般比较小。

优点

1、内核通常依赖于对小对象的分配,它们会在系统生命周期内进行无数次分配

2、slab 缓存分配器通过对类似大小的对象进行缓存而提供这种功能,从而避免了常见的碎片问题。

3、slab 分配器还支持通用对象的初始化,从而避免了为同一目的而对一个对象重复进行初始化。

4、slab 分配器还可以支持硬件缓存对齐和着色,这允许不同缓存中的对象占用相同的缓存行,从而提高缓存的利用率并获得更好的性能。

伙伴系统:(buddy system)需要谈一谈linux的内存分配系统

linux内核采取了一种同时适用于32位和64位系统的内存分页模型,对于32位系统,两级页表足够用,而64位系统,需要四级页表。分别为:

页全局目录:包含一些页上级目录地址

页上级目录:包含页中间目录地址

页中间目录:包含一些页表地址

页表: 指向页框,linux中采用4kb大小的页框作为标准的内存分配单元。

在实际应用中,经常需要分配一组连续的页框,而频繁地申请和释放不同大小的连续页框,必然导致在已分配页框的内存块中分散了许多小块的空闲页框。这样,即使这些页框是空闲的,其他需要分配连续页框的应用也很难得到满足。

为了避免出现这种情况,Linux内核中引入了伙伴系统算法(buddy system)。把所有的空闲页框分组为11个块链表,每个块链表分别包含大小为1,2,4,8,16,32,64,128,256,512和1024个连续页框的页框块。最大可以申请1024个连续页框,对应4MB大小的连续内存。每个页框块的第一个页框的物理地址是该块大小的整数倍。

假设要申请一个256个页框的块,先从256个页框的链表中查找空闲块,如果没有,就去512个页框的链表中找,找到了则将页框块分为2个256个页框的块,一个分配给应用,另外一个移到256个页框的链表中。如果512个页框的链表中仍没有空闲块,继续向1024个页框的链表查找,如果仍然没有,则返回错误。

页框块在释放时,会主动将两个连续的页框块合并为一个较大的页框块。

7) Linux的内核空间和用户空间是如何划分的(以32位系统为例)?

Linux将4G的地址划分为用户空间和内核空间两部分。在Linux内核的低版本中(2.0.X),通常0-3G为用户空间,3G-4G为内核空间。这个分界点是可以改动的。 正是这个分界点的存在,限制了Linux可用的最大内存为2G.而且要通过重编内核,调整这个分界点才能达到。实际上还可以有更好的方法来解决这个问题。由于内核空间与用户空间互不重合,所以可以用段机制提供的保护功能来保护内核级代码。2.2.X版的内核对此进行了改动。这样内核空间扩张到了4G。从表面上看内核的基地址变为了0,但实际上,内核通常仍在虚址3G以上。 用户空间在2.2.X中从直观上变为0-4G,让人迷惑,不是可以直接访问内核了? 其实不然,通过使用页机制提供的保护,阻止了用户程序访问内核空间。

8) vmalloc()申请的内存有什么特点?

原型为:void *vmalloc(unsigned long size)。size要分配内存的大小. 以字节为单位。在设备驱动程序或者内核模块中动态开辟内存,不是用malloc,而是kmalloc ,vmalloc,或者用get_free_pages直接申请页。释放内存用的是kfree,vfree,或free_pages.

kmalloc函数返回的是虚拟地址(线性地址). kmalloc特殊之处在于它分配的内存是物理上连续的,这对于要进行DMA的设备十分重要. 而用vmalloc分配的内存只是线性地址连续,物理地址不一定连续,不能直接用于DMA。vmalloc函数的工作方式类似于kmalloc,只不过前者分配的内存虚拟地址是连续的,而物理地址则无需连续。它通过分配非连续的物理内存块,再修改页表,把内存映射到逻辑地址空间的连续区域中。通过vmalloc获得的页必须一个一个地进行映射,效率不高,因此,只在不得已(一般是为了获得大块内存)时使用。vmalloc函数返回一个指针,指向逻辑上连续的一块内存区,其大小至少为size。在发生错误时,函数返回NULL。vmalloc可能睡眠,因此,不能从中断上下文中进行调用,也不能从其它不允许阻塞的情况下调用。要释放通过vmalloc所获得的内存,应使用vfree函数。

Kmalloc 是连续的物理内存。

Vmalloc 是非连续的。

vmalloc是一页一页地去获取内存,然后把获取到的这些页映射成连续的虚拟地址;

kmalloc是一次性获取所需要的所有页,并且不需要再做映射;

vmalloc 是否效率远不如 kmalloc

9) 用户程序使用malloc()申请到的内存空间在什么范围?

0~0xBFFFFFFF

简单的说就是:内存是分散成一些小块的,malloc管理的内存是通过链表的方式把这些块串在一起,以这些内存的起始地址排序组织的,相邻的内存块如果尾首地址连续,那就把它们合并为一块,当你申请一定大小的内存时以first fit模式,在内存链中找第一个大于你需要大小的内存,返回内存指针以best fit模式,要遍历整个内存链,找刚好最接近但大于所需要大小的内存当然这是出于对内存不浪费的考虑,效率是有损失.释放的话相反,把内存放回内存管理链中,可能的话合并相邻的内存碎片。避免内存过于零散Linux下malloc函数主要用来在用户空间从heap申请内存,申请成功返回指向所分配内存的指针,申请失败返回NULL。默认情况下,Linux内核使用“乐观的”分配内存策略,首先粗略估计系统可使用的内存数,然后分配内存,但是在使用的时候才真正把这块分配的内存给你。这样一来,即使用malloc申请内存没有返回NULL,你也不一定能完全使用这块内存,特别是在一次或连续多次申请很多内存的时候。如果一直连续用malloc申请内存,而不真正使用,所申请的内存总数可以超过真正可以使用的内存数。但是当真正使用这块内存,比如用memset或bzero函数一次性把所申请到的大块内存“使用掉”,Linux系统就会Out Of Memory,这个时候OOM Killer就会kill掉用户空间的其他进程来腾出更多可使用内存。OOM Killer根据OOM score来决定kill哪个进程,OOM score可以看/proc/<PID>/oom_score,score由badness函数计算得出,根据进程运行时间长短,进程优先级,进程所使用的内存数等等。可以通过/proc/<PID>/oom_adj来干预计算socre,这个值的取值范围是-17~15,如果是-17该进程就永远不会被kill(这个可能也和内核版本有关,不见得所有内核版本都支持,得实际试试)。“默认情况”Linux是这种做的,“默认情况”是指/proc/sys/vm/overcommit_memory为0的时候。这个参数也可以调整,如果为1表示“来者不拒”,只要你malloc过来申请,我啥都不做,立马给你分配内存,这样的话性能就会有大幅度的提高;如果为2表示Linux会精确计算所有可使用的内存和所申请的内存,如果所申请的超过的可使用的内存数就返回NULL。可使用的内存值计算方法,虚拟内存(swap)+ /proc/sys/vm/overcommit_memory(百分比) × 物理内存。/proc/sys/vm/overcommit_memory默认值为50,计算起来就是50%的物理内存数。Linux自身内核会占一部分内存,还有buffer/cache所占用的内存,所以实际上能被malloc申请后使用的内存并非物理内存大小,demsg的输出里面包含了相关信息(如果看不到,可能是被别的信息冲掉了,重启系统,在系统起来后马上看):

Memory: 2071220k/2097152k available (2122k kernel code, 24584k reserved, 884k data, 228k init, 1179584k highmem)

10) 在支持并使用MMU的系统中,Linux内核和用户程序分别运行在物理地址模式还是虚拟地址模式?

虚拟地址模式

在ARM存储系统中,使用内存管理单元(MMU)实现虚拟地址到实际物理地址的映射。利用MMU,可把SDRAM的地址完全映射到0x0起始的一片连续地址空间,而把原来占据这片空间的FLASH或者ROM映射到其他不相冲突的存储空间位置。例如,FLASH的地址从0x0000 0000~0x00ff ffff,而SDRAM的地址范围是0x3000 0000~Ox3lff ffff,则可把SDRAM地址映射为0x0000 0000~Oxlfff ffff而FLASH的地址可以映射到Ox9000 0000~Ox90ff ffff(此处地址空间为空闲,未被占用)。映射完成后,如果处理器发生异常,假设依然为IRQ中断,PC指针指向Oxl8处的地址,而这个时候PC实际上是从位于物理地址的Ox3000 0018处读取指令。通过MMU的映射,则可实现程序完全运行在SDRAM之中。在实际的应用中.可能会把两片不连续的物理地址空间分配给SDRAM。而在操作系统中,习惯于把SDRAM的空间连续起来,方便内存管理,且应用程序申请大块的内存时,操作系统内核也可方便地分配。通过MMU可实现不连续的物理地址空间映射为连续的虚拟地址空间。操作系统内核或者一些比较关键的代码,一般是不希望被用户应用程序访问。通过MMU可以控制地址空间的访问权限,从而保护这些代码不被破坏。

MMU的实现过程,实际上就是一个查表映射的过程。创建页表(translate table)是实现MMU功能不可缺少的一步。页表是位于系统的内存中,页表的每一项对应于一个虚拟地址到物理地址的映射。每一项的长度即是一个字的长度(在ARM中,一个字的长度被定义为4B)。页表项除完成虚拟地址到物理地址的映射功能之外,还定义了访问权限和缓冲特性等。

MMU的映射分为两种,一级页表的变换和二级页表变换。两者的不同之处就是实现的变换地址空间大小不同。一级页表变换支持1 M大小的存储空间的映射,而二级可以支持64 kB,4 kB和1 kB大小地址空间的映射

11).ARM处理器是通过几级页表进行存储空间映射的?

虚拟存储空间到物理存储空间的映射是以内存块为单位进行的,虚拟存储空间中的一块连续存储空间被映射成物理存储空间中同样大小的一块连续存储空间。每一个地址变换条目(页表项)记录了一个虚拟存储空间的存储块的基地址与物理存储空间相应的一个存储块的基地址的对应关系。根据存储块大小不同,可以有多种地址变换。

ARM处理器支持的存储块大小有以下几种:

1)段(section):大小为1MB的存储块。

2)大页(Large Page):大小为64KB的存储块。

3)小页(Small Page):大小为4KB的存储块。

4)极小页(Tiny Page):大小为1KB的存储块。

通过采用适当的访问控制机制,还可以将大页分成大小为16KB的子页,也可将小页分成大小为1KB的子页,但极小页不能再细分,只能以1KB大小的整页为单位。

ARM处理器采用两级页表实现地址映射:

1)一级页表中包含以段为单位的地址变换条目或者指向二级页表的指针,一级页表实现的地址映射力度较大。

2)二级页表中包含以大页、小页和极小页为单位的地址变换条目。

当以二级分页管理某段存储空间时,要同时设置一级页表项和二级页表项,一个一级页表项对应一段(1section=1MB)虚拟存储空间的映射关系,一个一级页表项对应一张二级页表,这张二级页表中的所有页表项合在一起对应了前面一级页表项所对应的一段虚拟存储空间的映射关系。ARM处理器的二级页表分为粗粒度二级页表和细粒度二级页表,一张粗粒度二级页表的最大容量为1KB,一张细粒度二级页表的最大容量为4KB,不管是粗粒度还是细粒度页表,每张页表都对应一段(1M)虚拟存储空间的映射关系。所以粗粒度二级页表的页表项(页描述符)最小只能描述4KB大小(小页)虚拟存储空间的映射关系,如果再小就没有足够的页表空间来存放一段虚拟存储空间的全部页表项(因为粗粒度二级页表的最大容量为1KB),因为粗粒度二级页表容量最大容量为1K,一个页表项为4个字节,所以1K容量最多有1024/4=256个页表项,所以最小只能描述1M/256=4K大小(小页)虚拟空间的存储映射关系,而细粒度二级页表的页描述符最小可以描述1KB大小(极小页)的虚拟存储空间的映射关系。

综上所述,我们可以归纳出以下6种ARM处理器的地址变换方法:

1)分段地址变换,这种变换只需要一级地址变换,而下面5种都需要两级地址变换;

2)粗粒度大页(64K)地址变换;

3)粗粒度小页(4K)地址变换(Linux通常使用的地址变换方法);

4)细粒度大页(64K)地址变换;

5)细粒度小页(4K)地址变换;

6)细粒度极小页(1K)地址变换。

在讲解以上各种地址变换方法之前我们先介绍一下一级映射描述符和二级映射描述符的定义。

12) Linux是通过什么组件来实现支持多种文件系统的?

VFS

13) Linux虚拟文件系统的关键数据结构有哪些?(至少写出四个)

VFS超级块:struct super_block

VFS的i节点:struct inode

i节点的操作函数:struct inode_operations

VFS的dentry结构:struct dentry

14) 对文件或设备的操作函数保存在哪个数据结构中?

file_operations

file_operations结构体,用来存储驱动内核模块提供的对设备进行各种操作的函数的指针。

常用的操作包括:read、write、poll、ioctl、mmap、open、close、flush、llseek等方法。

file 每个打开的文件在内核空间都有一个对应的file结构

file结构体,打开的文件描述

通用的描述有,f_mode(读写属性)、f_ops(当前文件位移)、f_rdev(设备)、f_flags(文件标志)、f_count(打开文件的数目)、f_readaf_inode(指向inode的结构指针)、file_operations

inode

inode 索引节点,和存储有关。

inode内容:

除了文件名以外的所有文件信息,都存在inode之中。


* 文件的字节数


* 文件拥有者的User ID

* 文件的Group ID

* 文件的读、写、执行权限

* 文件的时间戳,共有三个:ctime指inode上一次变动的时间,mtime指文件内容上一次变动的时间,atime指文件上一次打开的时间。

* 链接数,即有多少文件名指向这个inode

* 文件数据block的位置

可以用stat命令,查看某个文件的inode信息:stat xxx.txt

struct file 结构体中包含有struct file_operations结构体,struct file_operations是struct file的一个域;我们在使用系统调用open()打开一个设备节点struct inode时,我们会得到一个文件struct file,同时返回一个文件描述符,该文件描述符是一个整数,我们称之为句柄,通过访问句柄我们能够访问设备文件struct file,描述符是一个有着特殊含义的整数,特定位都有一定的意义或属性。

文件描述符就是句柄,是一个整数,通过句柄来访问设备文件 file。文件file里有相关的文件属性,并且包含相关的文件操作。

15) Linux中的文件包括哪些?

普通文件 : 通常是流式文件

目录文件 : 用于表示和管理系统中的全部文件

连接文件 : 用于不同目录下文件的共享

设备文件 : 包括块设备文件和字符设备文件,块设备文件表示磁盘文件、光盘等,字符设备文件按照字符操作终端、键盘等设备。

管道(FIFO)文件 : 提供进程建通信的一种方式

套接字(socket) 文件: 该文件类型与网络通信有关

16) 创建进程的系统调用有哪些?

fork();创建子进程,其实是复制了父进程,但是父进程的程序代码和全局变量没有被复制。

父进程调用fork会返回子进程的ID号,调用失败返回-1,子进程调用返回0。

execve();子进程调用execve(),是为了运行另一个属于自己的程序。

exit();进程调用exit()就会立即退出,系统的跟进程会接替被中止进程的地位。

wait();进程调用,阻塞自己,等到自己的某个子进程退出再继续运行。

vfork();也是创建一个子进程,但它并不把父进程的图像全部复制到子进程中,而是只是用复制指针的方法使子进程与父进程的资源共享。它与父进程共享一个内存空间。

17) 调用schedule()进行进程切换的方式有几种?

进程的切换与系统的一般执行过程 - 20135222 - 博客园

直接调用 延迟调用

切换用户空间

切换内存堆栈

引申

进程的调度时机

中断处理过程(包括时钟中断、I/O中断、系统调用和异常)中,直接调用schedule(),或者返回用户态时根据need_resched标记调用schedule();

内核线程可以直接调用schedule()进行进程切换,也可以在中断处理过程中进行调度,也就是说内核线程作为一类的特殊的进程可以主动调度,也可以被动调度;

用户态进程无法实现主动调度,仅能通过陷入内核态后的某个时机点进行调度,即在中断处理过程中进行调度。

进程的切换

——为了控制进程的执行,内核必须有能力挂起正在CPU上执行的进程,并恢复以前挂起的某个进程的执行,这叫做进程切换、任务切换、上下文切换;

——挂起正在CPU上执行的进程,与中断时保存现场是不同的,中断前后是在同一个进程上下文中,只是由用户态转向内核态执行;

——进程上下文包含了进程执行需要的所有信息

| 用户地址空间:包括程序代码,数据,用户堆栈等

| 控制信息:进程描述符,内核堆栈等

| 硬件上下文(注意中断也要保存硬件上下文只是保存的方法不同)

——schedule()函数选择一个新的进程来运行,并调用context_switch进行上下文的切换,这个宏调用switch_to来进行关键上下文切换

| next = pick_next_task(rq, prev); //进程调度算法都封装这个函数内部

| context_switch(rq, prev, next); //进程上下文切换

| switch_to利用了prev和next两个参数:prev指向当前进程,next指向被调度的进程


18) Linux调度程序是根据进程的动态优先级还是静态优先级来调度进程的?

动态优先级----完全公平调度器CFS

CFS 调度器 - uefi_artisan - CSDN博客

实时进程优于普通进程,每次进行进程调度时,会计算出每一个进程占用处理器权利的权重参数weight,优先运行weight值大的进程。

19) 进程调度的核心数据结构是哪个?

进程控制块 task_struct

浅析Linux下的task_struct结构体 - qq_29503203的博客 - CSDN博客

20) 如何加载、卸载一个模块?

使用insmod 命令加载模块,使用rmmod命令卸载模块。

insmod xxx.ko —— rmmod xxx.ko —— lsmod

21) 模块和应用程序分别运行在什么空间?

模块运行在内核空间、应用程序运行在用户空间

22) Linux中的浮点运算由应用程序实现还是内核实现?

由内核实现

23) 模块程序能否使用可连接的库函数?

不能,模块是内核函数,不能使用C库函数

24) TLB中缓存的是什么内容?

TLB(Translation Lookaside Buffer)传输后备缓冲器是一个内存管理单元用于改进虚拟地址到物理地址转换速度的缓存。TLB是一个小的,虚拟寻址的缓存,其中每一行都保存着一个由单个PTE组成的块。如果没有TLB,则每次取数据都需要两次访问内存,即查页表获得物理地址和取数据。

25) Linux中有哪几种设备?

字符设备 块设备 网络设备

26) 字符设备驱动程序的关键数据结构是哪个?

file_operations

字符设备驱动的开始,我们必须了解的是三个很重要的数据结构,他们分别是file_operations、inode、file

27) 设备驱动程序包括哪些功能函数?

模块的注册与注销

设备的打开、关闭、读、写及其他操作函数

设备的终端服务程序

28) 如何唯一标识一个设备?

主设备号和从设备号

通常,为了使应用程序区分所控制设备的类型,内核使用主设备号。而存在多台同类设备时,为了选择其中的一种,设备驱动程序就使用此设备号。

29) Linux通过什么方式实现系统调用?

SWI(软中断)

linux内核中设置了一组用于实现系统功能的子程序,称为系统调用。系统调用和普通库函数调用非常相似,只是系统调用由操作系统核心提供,运行于核心态,而普通的函数调用由函数库或用户自己提供,运行于用户态。

用户空间的程序无法直接执行内核代码。它们不能直接调用内核空间中的函数,因为内核驻留在受保护的地址空间上。如果进程可以直接在内核的地址空间上读写的话,系统安全就会失去控制。所以,应用程序应该以某种方式通知系统,告诉内核自己需要执行一个系统调用,希望系统切换到内核态,这样内核就可以代表应用程序来执行该系统调用了。

通知内核的机制是靠软件中断实现的。首先,用户程序为系统调用设置参数。其中一个参数是系统调用编号。参数设置完成后,程序执行“系统调用”指令。x86系统上的软中断由int产生。这个指令会导致一个异常:产生一个事件,这个事件会致使处理器切换到内核态并跳转到一个新的地址,并开始执行那里的异常处理程序。此时的异常处理程序实际上就是系统调用处理程序。它与硬件体系结构紧密相关。

新地址的指令会保存程序的状态,计算出应该调用哪个系统调用,调用内核中实现哪个系统调用的函数,恢复用户程序状态,然后将控制权返还给用户程序。系统调用是设备驱动程序中定义的函数最终被调用的一种方式。

在Linux中,每个系统调用被赋予一个系统调用号。这样,通过这个独一无二的号就可以关联系统调用。当用户空间的进程执行一个系统调用的时候,这个系统调用号就被用来指明到底是要执行哪个系统调用。进程不会提及系统调用的名称。

30) Linux软中断和工作队列的作用是什么?

软中断(softirq):Linux系统把中断分为两部分,前半部分是中断立即执行的,后半部分可以在稍后的某个时候执行。由于后半部分的紧急程度不那么“硬”,也由于后半部分的执行是由软件来启动的,所以后半部分的中断机制也叫做“软中断”。

工作队列:Linux总是在已经进入就绪状态的进程中来选择一个合适的进程来运行的。为了加快寻找速度,Linux就把所有已就绪的进程集中起来形成一个就绪进程队列。这个队列就叫工作队列。

0人点赞

6. linux内核和驱动相关