Linux复习汇总

更新时间:2023-12-01 23:36:01 阅读量: 教育文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

第一章 概述

3、Linux系统由哪些部分组成?Linux内核

处于什么位置?

答:(1)符合POSIX标准的操作系统内核、

Shell和外围工具。(2)C语言编译器和其他

开发工 具及函数库。(3)X Window 窗口系统。(4)各种应用软件,包括字处理软 件、图象处理软件等。

Linux内核是所有Linux 发布版本的核

心。

第二章 内存寻址

2、在保护模式下,MMU如何把一个虚地

址转换为物理地址?

答:

分段机制把一个虚拟地址转换为线性地址;接着,分页机制把一个线性地址转换为物理地址。

5.为什么在设计两级页表的线性地址结构时,给页目录和页表各分配10位?如果不是这 样,举例说明会产生什么样的结果?

答:所谓两级页表就是对页表再进行分页。第一级称为页目录,其中存放的是关于页表的信息。4MB的页表再次分页(4MB/4K)可以分为1K个页,同样对每个页的描述需要4个字节,于是可以算出页目录最多占用4KB个字节,正好是一个页,其示意图如2.9所示。 页目录共有1K个表项, 于是,线性地址的最高10位(即22位~ 31位)用来产生第一级的索引。两级表结构的第二级称为页表,每个页表也刚好存放在一个4K字节的页中,包含1K个字节的表项。第二级页表由线性地址的中间10位(即21位~ 12位)进行索引,最低12位表示页内偏量。

举例:如果页目录占用31~24位,页表项占用23~12位,偏移地址占11~0位,则有2^8个页表项,一个页面有2^12(4k)个表项,每个表项大小为4字节,2^12的范围为4页面,而不是以一个页面为准。

6、深入理解图2.12,并结合图叙述线性地址到物理地址的转换。

答:第一步,用32位线性地址的最高10位第31~22位作为页目录项的索引,将它乘以4,

与CR3中的页目录的起始地址相加,获得相应目录项在内存的地址。

第二步,从这个地址开始读取32位页目录项,取出其高20位,再给低12位补0,形成的32位就是页表在内存的起始地址。

第三步,用32位线性地址中的第21~12位作为页表中页表项的索引,将它乘以4,与页表的起始地址相加,获得相应页表项在内存的地址。

第四步,从这个地址开始读取32位页表项,取出其高20位,再将线性地址的第11~0位放在低12位,形成最终32位页面物理地址。

第三章 进程

1、什么是进程控制块?它包含哪些基本信息?

答:对进程进行全面描述的数据结构,Linux中把对进程的描述结构叫做task_struct: struct task_struct {

}传统上这样的数据结构被叫做进程控制块PCB(process control blaock) 系统为了管理进程设置的一个专门的数据结构,用它来记录进程的外部特征,描述进程的运动变化过程。系统利用PCB来控制和管理进程,所以PCB是系统感知进程存在的唯一标志。进程与PCB是一一对应的。

包含的基本信息:

(1)状态信息-描述进程动态的变化。(2)链接信息-描述进程的父/子关系。(3)各种标识符-用简单数字对进程进行标识。(4)进程间通信信息-描述多个进程在同一任务上协作工作。(5)时间和定时器信息-描述进程在生存周期内使用CPU时间的统计、计费等信息。(6)调度信息-描述进程优先级、调度策略等信息。(7)文件系统信息-对进程使用文件情况进行记录。(8)虚拟内存信息-描述每个进程拥有的地址空间。(9)处理器环境信息-描述进程的执行环境(处理器的寄存器及堆栈等)

2、Linux内核的状态有哪些?请画出状态转换图,查看最新源代码,以确认有哪些状态。 答:就绪态(TASK_RUNNING);睡眠(或等待)态:浅度睡眠态(TASK_INTERRUPTIBLE),深度睡眠态(TASK_UNINTERRUPTIBLE);暂停状态(TASK_STOPPED);僵死状态(TASK_ZOMBIE)

3、Linux的进程控制块如何存放?为什么?假设ESP中存放的是栈顶指针,请用三句汇编语句描述如何获得current的PCB的地址。

答:当进程一进入内核态,CPU就自动设置进程的内核栈。这个栈位于内核的数据段上,为了节省空间,Linux把内核栈和一个紧挨近的PCB的小数据结构,thread_info放在一起,占用8kb的内存区。因为这样可以节省空间,内核很容易从ESP寄存器的值获得,当前在CPU上正在运行的thread_info结构的地址。 movl $0xfffe000, êx andl %esp, ìx movl ìx, p

4、PCB的组织方式有哪几种?为什么要采取这些组织方式? 答:(1)进程链表(2)哈希表(3)就绪队列(4)等待队列 在一个系统中,通常可以拥有数十个、数百个乃至数千个进程,相应的就有这么多PCB。为了能有效的对它们加以管理,应该用适当的方式将这些PCB组织起来。

5、请编写内核模块,打印系统中各进程的名字以及PID,同时统计系统中进程的个数。 答:

static int print_pid(void) { count++; { struct task_struct *task,*p; struct list_head *pos; printk(\ int count = 0; } printk(\ printk(\ task = &init_task; is:%d\\n\ list_for_each(pos,struct return 0; task_struct,tasks); }

7、什么是写时复制技术,这种技术在什么情况下最能发挥其优势? 答:父进程和子进程共享页面而不是复制页面。然而,只要页面被共享,它们就不能被修改。无论父进程和子进程何时试图写一个共享的页面,就产生一个错误,这时内核就把这个页复制到一个新的页面中并标记为可写。原来的页面仍然是写保护的:当其它进程试图写入时,内核检查写进程是否是这个页面的唯一属主;如果是,它把这个页面标记为对这个进程是可写的。采用这种技术,显然只有预测到将要修改的页才会被复制,而且必须被复制,不然的话,就会破坏父进程的程序执行。

9、init内核线程与init进程是一回事吗?它们有什么本质的区别? 答:(1)init()函数是内核代码的一部分,在内核态运行,是独立的可执行代码的一部分。 (2)init进程在Linux操作系统中是一个具有特殊意义的进程,它是由内核启动并运行的第一个用户进程,因此它不是运行在内核态,而是运行在用户态。它的代码不是内核本身的一部分,而是存放在硬盘上可执行文件的映象中,和其他用户进程没有什么两样。

10、用fork写一个简单的测试程序,从父进程和子进程中打印信息。信息应该包括父进程和子进程的PID。执行程序若干次,看两个信息是否以同样的次序打印。 答:

#include { int main(void) printf(\{ of %d\\n\ pid_t pid; } pid = fork(); else if(pid < 0) { { printf(\ printf(\pid of %d\\n\ return 0; } } return 0; else if(pid == 0) }

第四章 内存管理

1、内核空间存放什么内容?如何把其中的一个虚地址转换成物理地址? 答:内核空间存放的是内核代码和数据。

2、什么是内核映像?它存放在物理空间和内核空间的什么地方?

答:内核空间由所有进程共享,其中存放的是内核代码和数据,即“内核映象”

Linux内核映像被装入在物理地址0x00100000开始的地方 内核映像在内核空间的起始地址就为0xC0100000。

3、用户空间划分为哪几部分?用户程序调用malloc()分配的内存属于那一部分?

答:分为堆栈段、BSS(未初始化的数据段)、数据段和代码段。从数据段的顶部到堆栈段地址的下沿这个区间是一个巨大的空洞,这就是进程在运行时调用malloc()可以动态分配的空间,也叫动态内存或堆。

4、什么是虚存映射?有哪几种类型?

答:随着进程的运行,被引用的程序部分会由操作系统装入到物理内存,这种将映像链接到进程用户空间的方法被称为“虚存映射”,也就是把文件从磁盘映射到进程的用户空间,这样把对文件的访问转化为对虚存区的访问。有两种类型的虚存映射:

(1)共享的:有几个进程共享这一映射,也就是说,如果一个进程对共享的虚存区进行写,其它进程都能感觉到,而且会修改磁盘上对应的文件。

(2)私有的:进程创建的这种映射只是为了读文件,而不是写文件,因此,对虚存区的写

操作不会修改磁盘上的文件,由此可以看出,私有映射的效率要比共享映射的高。 除了这两种映射外,如果映射与文件无关,就叫匿名映射。

6、说明mmap()系统调用的功能?利用mmap()写一个拷贝文件的程序。

答:mmap()系统调用时通过调用do_mmap内核函数来实现建立文件到显存的映射。 int i,fd1,fd2; perror(\char *buf1,buf2; exit(-1); fd1 = open(\ } if(fd1&&fd2&&(fstar(fd);&star buf1()=0) if((map_FAFCE) == buf2) { { if(lseek(fd) statbuf,st_size-1,seek_set)<0 perror(\ { exit(-1); perror\ } exit(-1); mempy(buf2,buf1,(size - } f)startbuf,st_size); if(MAP_FAILED == buf()) } {

7、Linux是如何实现“请求调页”的?

答:1)如果被访问的页不在内存,也就是说,这个页还没有被存放在任何一个物理页面中,那么,内核分配一个新的页面并将其适当地初始化,这种技术称为“请求调页”; 2)“请求调页”是一种动态内存分配技术,它将页面的分配推迟到不能再推迟为止,也就是说,一直推迟到进程要访问的页不在物理内存时为止,由此引起一个缺页异常;该技术的引入主要是因为进程开始运行时并不访问其地址空间中的全部地址。

8、试叙述伙伴算法的工作原理,并说明为什么伙伴算法可以消除外碎片? 答:假设要求分配的块其大小为128个页面。该算法先在块大小为128个页面的链表中查找,看是否有这样一个空闲块。如果有,就直接分配;如果没有,该算法会查找下一个更大的块,具体地说,就是在块大小为256个页面的链表中查找一个空闲块。如果存在这样的空闲块,内核就把这256个页面分为两等份,一份分配出去,另一份插入到块大小为128个页面的链表中。如果在块大小为256个页面的链表中也没有找到空闲页块,就继续找更大的块,即512个页面的块。如果存在这样的块,内核就从512个页面的块中分出128个页面满足请求,然后从384个页面中取出256个页面插入到块大小为256个页面的链表中。然后把剩余的128个页面插入到块大小为128个页面的链表中。如果512个页面的链表中还没有空闲块,该算法就放弃分配,并发出出错信号。

9、vmalloc()和kmalloc()有何区别?编写内核模块程序,调用这两个函数以观察二者所分配空间位于不同的区域。

答:在内核空间中调用kmalloc()分配连续物理空间,而调用vmalloc()分配非物理连续空间。

我们把kmalloc()所分配内核空间中的地址称为内核逻辑地址 把vmalloc()分配的内核空间中的地址称为内核虚拟地址 vmalloc()在分配过程中须更新内核页表 #include #include #include #include

#include Kfree(kmallocmen); unsigned char * kmalloc men; fail 2: unsigned char * vmalloc men; free_page(pagemon); MODULL_LICENSE(\ fail 3: static int_init init_mmshow(void) return -1; { } pagemem = static void_exit cleanup_mmshow(void) _get_free_page(GFP_KERNEL); { if(!kmallocmen) Vfree(vmallocmen); goto fail2; Kfree(kmallocmen); printk(KERN_INFO\ free_page(pagemen); 0x%p\\n\} return 0; module_init(imit_mmshow); failB module_exit(cleanup_mmshow); fail 1:

第五章 中断和异常

1、什么是中断向量?Linux是如何分配中断向量的?

答:每个中断源都被分配一个8位无符号整数作为类型码,即中断向量

Linux对256个向量的分配如下:(1)从0~31的向量对应于异常和非屏蔽中断。(2)从32~47的向量(即由I/O设备引起的中断)分配给屏蔽中断。(3)剩余的从48~255的向量用来标识软中断。Linux只用了其中的一个(即128或0x80向量)用来实现系统调用。

2、什么是中断描述符表?什么是门描述符?请描述其格式。

答:在保护模式下,中断向量表中的表项由8个字节组成,中断向量表也改叫做中断描述符表IDT(Interrupt Descriptor Table)。其中的每个表项叫做一个门描述符(gate descriptor),

3、如何对中断描述符表进行初始化?

答:Linux内核在系统的初始化阶段要初始化可编程控制器8259A;将中断描述符表的起始

地址装入IDTR寄存器,并初始化表中的每一项;当计算机运行在实模式时,中断描述符表被初始化,并由BIOS使用;真正进入了Linux内核,中断描述符表就被移到内存的另一个区域,并为进入保护模式进行预初始化。

4、在中断描述符表中如何插入一个中断门、陷阱门和系统门?

答:IDT表项的设置通过_set_gaet()函数实现。调用该函数在IDT表中插入一个中断门:set_intr_gate(unsigned int n, void *addr);调用该函数在IDT表中插入一个陷阱门:set_trap_gate(unsigned int n, void *addr);调用该函数在IDT表中插入一个系统门:

set_system_gate(unsigned int n, void *addr)

5、CPU为什么要进行有效性检查?如何检查?CPU是如何跳到中断或异常处理程序的? 答:有效性检查避免用户应用程序访问特殊的陷阱门或中断门。

当从用户态堆栈切换到内核态堆栈时,先把用户态堆栈的值压入中断程序的内核态堆栈中,同时把 EFLAGS寄存器自动压栈,然后把被中断进程的返回地址压入堆栈。如果异常产生了一个硬错误码,则将它也保存在堆栈中。如果特权级没有发生变化,则压入栈中的内容如图5.4中?。此时,CS:EIP的值就是IDT表中第i项门描述符的段选择符和偏移量的值,于是,CPU就跳转到了中断或异常处理程序。

分两步进行有效性检查:首先是“段”级检查,将CPU的当前特权级CPL(存放在CS寄存器的最低两位)与IDT中第i项段选择符中的DPL相比较,如果DPL(3)大于CPL(0),就产生一个“通用保护”异常,因为中断处理程序的特权级不能低于引起中断的进程的特权级。这种情况发生的可能性不大,因为中断处理程序一般运行在内核态,其特权级为0。然后是“门”级检查,把CPL与IDT中第i个门的DPL相比较,如果CPL大于DPL,也就是当前特权级(3)小于这个门的特权级(0),CPU就不能“穿过”这个门,于是产生一个“通用保护”异常,这是为了避免用户应用程序访问特殊的陷阱门或中断门。但是请注意,这种“门”级检查是针对一般的用户程序,而不包括外部I/O产生的中断或因CPU内部异常而产生的异常,也就是说,如果产生了中断或异常,就免去了“门”级检查。

6、中断处理程序和中断服务程序有何区别?Linux如何描述一条共享的中断线?

答:中断服务程序ISR与中断处理程序是两个不同的概念。在Linux中,15条中断线对应15个中断处理程序。具体来说,中断处理程序相当于某个中断向量的总处理程序,例如IRQ0x05_interrupt()是中断号为5(向量为37)的总处理程序,如果这个5号中断由网卡和图形卡共享,则网卡和图形卡分别有其相应的中断服务程序。 typedef irqreturn_t(* irq_handler_t) (int, void const char *name; *); void *dev_id; struct irqaction{ struct irqaction *next; irq_handler_t handler; int irq; unsigned long flags; ... cpumask_t mask; };

7、叙述中断处理程序的执行过程,并给出几个主要函数的调用关系和功能。

答:CPU从中断控制器的一个端口取得中断向量I;根据I从中断描述符表IDT中找到相应的中断门;从中断门获得中断处理程序的入口地址;判断是否要进行堆栈切换;调用do_IRQ()对所接收的中断进行应答,并禁止这条中断线;调用handle_IRQ_event()来运行对应的中断服务例程。

8、为什么把中断分为两部分来处理?

答:中断服务例程一般都是在中断请求关闭的条件下执行的,以避免嵌套而使中断控制复杂化。但是,中断是一个随机事件,它随时会到来,如果关中断的时间太长,CPU就不能及时响应其他的中断请求,从而造成中断的丢失。因此,内核的目标就是尽可能快的处理完中断请求,尽其所能把更多的处理向后推迟。例如,假设一个数据块已经达到了网线,当中断控制器接受到这个中断请求信号时,Linux内核只是简单地标志数据到来了,然后让处理器恢复到它以前运行的状态,其余的处理稍后再进行(如把数据移入一个缓冲区,接受数据的进程就可以在缓冲区找到数据)。因此,内核把中断处理分为两部分:前半部分(top half)和后半部分(bottom half),前半部分内核立即执行,而后半部分留着稍后处理。

9、实时时钟和操作系统时钟有何不同?

答:大部分PC机中有两个时钟源,分别是实时时钟(RTC)和 操作系统(OS)时钟

实时时钟也叫硬件时钟,它靠电池供电,即使系统断电,也可以维持日期和时间。 RTC是OS时钟的时间基准,操作系统通过读取RTC来初始化OS时钟,此后二者保持同步运行,共同维持着系统时间。所谓同步,是指操作系统在运行过程中,每隔一个固定时间会刷新或校正RTC中的信息

10、jiffies表示什么?什么时候对其增加?

答:jiffies是Linux内核中的一个全局变量,用它来表示系统自启动以来的时钟节拍总数。启动时,内核将该变量初始化为0,此后,每次时钟中断处理程序都会增加该变量的值。

11、时钟中断的下半部分主要做什么?

答:run_lock_timers()函数去处理所有到期的定时器,定时器作为软中断在下半部分中执行。Scheduler_tick()函数负责减少当前运行进程的时间片计数值并且在需要时设置need_resched标志。当update_process_timer()函数返回后,do_timer()函数接着会调用update_times()函数更新墙上时钟。 第六章 系统调用

1、系统调用与库函数、系统命令及内核函数有什么区别和联系?

答:应用编程接口(API)其实是一个函数定义, 比如常见的read()、malloc()、free()、abs()函数等,这些函数说明了如何获得一个给定的服务;而系统调用是通过软中断向内核发出一个明确的请求。从编程者的观点看,API和系统调用之间没有什么差别,二者关注的都是函

数名、参数类型及返回代码的含义。然而,从设计者的观点看,这是有差别的,因为系统调用的实现是在内核完成的,而用户态的函数是在函数库中实现的。系统命令相对应用编程接口更高一层,每个系统命令都是一个可执行程序,比如常用的系统命令ls、hostname等,这些命令的实现调用了系统调用。内核函数与普通函数形式上没有什么区别,只不过前者在内核实现,因此要满足一些内核编程的要求。系统调用是用户进程进入内核的接口层,它本身并非内核函数,但它是由内核函数实现的,进入内核后,不同的系统调用会找到各自对应的内核函数。

2、内核为什么要设置系统调用处理程序,它与服务例程有什么区别?

答:用户空间的程序无法直接执行内核代码,内核驻留在受保护的地址空间上,不允许用户进程内核地址空间上读写。当用户态的进程调用一个系统调用时,CPU切换到内核态并开始执行一个内核函数。系统调用处理程序执行下列操作: 在内核栈保存大多数寄存器的内容;

调用所谓系统调用服务例程的相应的C函数来处理系统调用; 通过ret_from_sys_call( )函数从系统调用返回;

不同的系统调用会找到各自对应的内核函数,这些内核函数被称为系统调用的“服务例程”。

3、说明系统调用号的作用。 答:因为内核实现了很多不同的系统调用,因此进程必须传递一个叫做系统调用号的参数来识别所需的系统调用;

本文来源:https://www.bwwdw.com/article/nsvt.html

Top