linux内核_linux内核模块 - CSDN
linux内核 订阅
Linux是一种开源电脑操作系统内核。它是一个用C语言写成,符合POSIX标准的类Unix操作系统。 [1]  Linux最早是由芬兰 Linus Torvalds为尝试在英特尔x86架构上提供自由的类Unix操作系统而开发的。该计划开始于1991年,在计划的早期有一些Minix 黑客提供了协助,而今天全球无数程序员正在为该计划无偿提供帮助。 展开全文
Linux是一种开源电脑操作系统内核。它是一个用C语言写成,符合POSIX标准的类Unix操作系统。 [1]  Linux最早是由芬兰 Linus Torvalds为尝试在英特尔x86架构上提供自由的类Unix操作系统而开发的。该计划开始于1991年,在计划的早期有一些Minix 黑客提供了协助,而今天全球无数程序员正在为该计划无偿提供帮助。
信息
软件授权
免费
软件名称
Linux内核
发明者
Linus Torvalds
使用时间
1991年
软件语言
C语言
Linux内核内核结构
操作系统是一个用来和硬件打交道并为用户程序提供一个有限服务集的低级支撑软件。一个计算机系统是一个硬件和软件的共生体,它们互相依赖,不可分割。计算机的硬件,含有外围设备、处理器、内存、硬盘和其他的电子设备组成计算机的发动机。但是没有软件来操作和控制它,自身是不能工作的。完成这个控制工作的软件就称为操作系统,在Linux的术语中被称为“内核”,也可以称为“核心”。Linux内核的主要模块(或组件)分以下几个部分:存储管理、CPU和进程管理、文件系统、设备管理和驱动、网络通信,以及系统的初始化(引导)、系统调用等。Linux内核使用三种不同的版本编号方式。  第一种方式用于1.0版本之前(包括1.0)。第一个版本是0.01,紧接着是0.02、0.03、0.10、0.11、0.12、0.95、0.96、0.97、0.98、0.99和之后的1.0。第二种方式用于1.0之后到2.6,数字由三部分“A.B.C”,A代表主版本号,B代表次主版本号,C代表较小的末版本号。只有在内核发生很大变化时(历史上只发生过两次,1994年的1.0,1996年的2.0),A才变化。可以通过数字B来判断Linux是否稳定,偶数的B代表稳定版,奇数的B代表开发版。C代表一些bug修复,安全更新,新特性和驱动的次数。以版本2.4.0为例,2代表主版本号,4代表次版本号,0代表改动较小的末版本号。在版本号中,序号的第二位为偶数的版本表明这是一个可以使用的稳定版本,如2.2.5,而序号的第二位为奇数的版本一般有一些新的东西加入,是个不一定很稳定的测试版本,如2.3.1。这样稳定版本来源于上一个测试版升级版本号,而一个稳定版本发展到完全成熟后就不再发展。第三种方式从2004年2.6.0版本开始,使用一种“time-based”的方式。3.0版本之前,是一种“A.B.C.D”的格式。七年里,前两个数字A.B即“2.6”保持不变,C随着新版本的发布而增加,D代表一些bug修复,安全更新,添加新特性和驱动的次数。3.0版本之后是“A.B.C”格式,B随着新版本的发布而增加,C代表一些bug修复,安全更新,新特性和驱动的次数。第三种方式中不再使用偶数代表稳定版,奇数代表开发版这样的命名方式。举个例子:3.7.0代表的不是开发版,而是稳定版!Linux最早是由芬兰人Linus Torvalds设计的。当时由于UNⅨ的 商业化,Andrew Tannebaum教授开发了Minix操作系统以便于不受AT&T许可协议的约束,为教学科研提供一个操作系统。当时发布在Internet上,免费给全世界的学生使用。Minix具有较多UNⅨ的特点,但与UNⅨ不完全兼容。1991年10月5日,Linus为了给Minix用户设计一个比较有效的UNⅨ PC版本,自己动手写了一个“类Minix”的操作系统。整个故事从两个在终端上打印AAAA...和BBBB...的进程开始的,当时最初的内核版本是0.02。Linus Torvalds将它发到了Minix新闻组,很快就得到了反应。Linus Torvalds在这种简单的任务切换机制上进行扩展,并在很多热心支持者的帮助下开发和推出了Linux的第一个稳定的工作版本。1991年11月,Linux0.10版本推出,0.11版本随后在1991年12月推出,当时将它发布在Internet上,免费供人们使用。当Linux非常接近于一种可靠的/稳定的系统时,Linus决定将0.13版本称为0.95版本。1994年3月,正式的Linux 1.0出现了,这差不多是一种正式的独立宣言。截至那时为止,它的用户基数已经发展得很大,而且Linux的核心开发队伍也建立起来了。在讨论大型而复杂的系统的体系结构时,可以从很多角度来审视系统。体系结构分析的一个目标是提供一种方法更好地理解源代码。Linux 内核实现了很多重要的体系结构属性。在或高或低的层次上,内核被划分为多个子系统。Linux 也可以看作是一个整体,因为它会将所有这些基本服务都集成到内核中。这与微内核的体系结构不同,后者会提供一些基本的服务,例如通信、I/O、内存和进程管理,更具体的服务都是插入到微内核层中的。随着时间的流逝,Linux 内核在内存和 CPU 使用方面具有较高的效率,并且非常稳定。但是对于 Linux 来说,最为有趣的是在这种大小和复杂性的前提下,依然具有良好的可移植性。Linux 编译后可在大量处理器和具有不同体系结构约束和需求的平台上运行。一个例子是 Linux 可以在一个具有内存管理单元(MMU)的处理器上运行,也可以在那些不提供MMU的处理器上运行。Linux 内核的uClinux移植提供了对非 MMU 的支持。核心的开发和规范一直是由Linux社区控制着,版本也是唯一的。实际上,操作系统的内核版本指的是在Linus本人领导下的开发小组开发出的系统内核的版本号。自1994年3月14日发布了第一个正式版本Linux 1.0以来,每隔一段时间就有新的版本或其修订版公布。Linux将标准的GNU许可协议改称Copyleft,以便与Copyright相对照。通用的公共许可(GPL)允许用户销售、拷贝和改变具有Copyleft的应用程序。当然这些程序也可以是Copyright的,但是你必须允许进一步的销售、拷贝和对其代码进行改变,同时也必须使他人可以免费得到修改后的源代码。事实证明,GPL对于Linux的成功起到了极大的作用。它启动了一个十分繁荣的商用Linux阶段,还为编程人员提供了一种凝聚力,诱使大家加入这个充满了慈善精神的Linux运动。
收起全文
精华内容
参与话题
  • Linux升级内核的正确姿势

    万次阅读 热门讨论 2018-08-21 11:26:10
    Linux升级内核的正确姿势 很多童鞋在玩耍linux发行版的时候,都会遇到各种各样的问题,比如:网卡不能使用,亮度不能调节,触摸板不能识别...Linux内核最早是于1991年由芬兰黑客林纳斯·托瓦兹为自己的个人计算机开...

    Linux升级内核的正确姿势

    很多童鞋在玩耍linux发行版的时候,都会遇到各种各样的问题,比如:网卡不能使用亮度不能调节触摸板不能识别蓝牙不能使用等等,这些关系都和linux内核有关系。

    什么是linux内核?

    Linux内核(英语:Linux kernel),是一种开源的类Unix操作系统宏内核

    Linux内核最早是于1991年由芬兰黑客林纳斯·托瓦兹为自己的个人计算机开发的,他当时在Usenet新闻组comp.os.minix登载帖子,这份著名的帖子标志着Linux内核计划的正式开始。如今,该计划已经拓展到支持大量的计算机体系架构,远超其他操作系统和内核。它迅速吸引了一批开发者和用户,利用它作为其他自由软件项目的内核,如著名的 GNU 操作系统。

    内核长什么样子呢?

    这里写图片描述

    有精力的同学可以去学习学习!

    查看当前内核

    打开终端执行:

    vincent@dell-Inspiron-7559 Dir:~
    ·····$uname -r
    4.15.0-32-generic

    当前电脑内核为4.15版本,一般更换内核我们是将内核升级为更高版本。

    下载内核

    目前下载有很多地址,我主要使用以下两个:

    1. github上下载linux kernel
    2. ubuntu的仓库中下载linux kernel

    内核需要下载那些文件?

    目前ubuntu 18.04已经使用4.15版本内核,linux mint 19也使用4.15版本内核。

    4代版本中4.164.17有安装上的区别:

    下面是4.16版本的文件选择:

    这里写图片描述

    下面是4.17版本的文件选择:

    这里写图片描述

    安装顺序

    使用sudo dpkg -i ***.deb 的形式安装。

    1. 首先安装linux-headers-4.17.0-041700_4.17.0-041700.201806041953_all.deb

    2. 安装linux-headers-4.17.0-041700-generic_4.17.0-041700.201806041953_amd64.deb

    3. 安装linux-modules-4.17.0-041700-generic_4.17.0-041700.201806041953_amd64.deb

    4. 安装linux-image-unsigned-4.17.0-041700-generic_4.17.0-041700.201806041953_amd64.deb

    如果是4.16版本及以前内核,此文件是没有的,也不需要安装。

    安装完成后重启即可。

    删除多余内核

    1. 查看当前系统安装的内核

    dpkg --get-selections| grep linux
    console-setup-linux install
    libselinux1:amd64 install
    libselinux1:i386 install
    linux-base install
    linux-firmware install
    linux-generic install
    linux-headers-4.4.0-22 install
    linux-headers-4.4.0-22-generic install
    linux-headers-4.4.0-24 install
    linux-headers-4.4.0-24-generic install
    linux-headers-4.4.0-28 install
    linux-headers-4.4.0-28-generic install
    linux-headers-4.4.0-31 install
    linux-headers-4.4.0-31-generic install
    linux-headers-4.4.0-34 install
    linux-headers-4.4.0-34-generic install
    linux-headers-4.4.0-36 install
    linux-headers-4.4.0-36-generic install
    linux-headers-4.4.0-38 install
    linux-headers-4.4.0-38-generic install
    linux-headers-4.4.0-42 install
    linux-headers-4.4.0-42-generic install
    linux-headers-generic install
    linux-image-4.2.0-21-generic deinstall
    linux-image-4.2.0-22-generic deinstall
    linux-image-4.4.0-31-generic deinstall
    linux-image-4.4.0-34-generic install
    linux-image-4.4.0-36-generic install
    linux-image-4.4.0-38-generic install
    linux-image-4.4.0-42-generic install
    linux-image-extra-4.2.0-21-generic deinstall
    linux-image-extra-4.4.0-31-generic deinstall
    linux-image-extra-4.4.0-34-generic deinstall
    linux-image-extra-4.4.0-36-generic deinstall
    linux-image-extra-4.4.0-38-generic install
    linux-image-extra-4.4.0-42-generic install
    linux-image-generic install
    linux-libc-dev:amd64 install
    linux-sound-base install
    pptp-linux install
    syslinux install
    syslinux-common install
    syslinux-legacy install
    util-linux install

    2. 卸载内核

    可以看到系统已经安装了多个内核版本,包括 headers 和 image ,可以删除这些来卸载旧的内核。

    比如删除 4.4.0-22 内核,我们看到上面只有 headers,因此可以这样:

    sudo apt-get remove --purge linux-headers-4.4.0-22
    
    sudo apt-get remove --purge linux-image-4.2.0-22-generic

    以此类推。

    然后终端执行:

    sudo apt-get autoclean
    sudo apt-get autoremove

    然后重启电脑即可。

    END

    展开全文
  • 深入理解Linux内核(完整版)-笔记

    万次阅读 2018-05-17 08:32:16
    第一章、绪论 1.Unix文件可以是下列类型之一: a.正规文件(regular file) b.目录(directroy) c.符号链(symbolic link) d.块设备文件(block-oriented device file) e.字符设备文件(charactor-oriented ... f....
    第一章、绪论
    	1.Unix文件可以是下列类型之一:
    		a.正规文件(regular file)
    		b.目录(directroy)
    		c.符号链(symbolic link)
    		d.块设备文件(block-oriented device file)
    		e.字符设备文件(charactor-oriented device file)
    		f.管道(pipe)命名管道(named pipe)(即FIFO)
    		h.套接字(socket)
    
    	2.内核分配给进程的虚拟地址空间由以下内存区域组成:
    		a.程序的可执行代码
    		b.程序的初始化数据
    		c.程序的未初始化数据
    		d.初始程序栈(即用户态栈)
    		e.需要共享的库的可执行代码和数据
    		f.堆(由程序动态请求的内存)
    	
    	3.设备驱动程序
    		内核通过设备驱动程序(device driver)与I/O设备打交道。设备驱动程序包含在内核中,由控制一个或多个设备的数据结构和函数组成。
    		这些设备包括硬盘、键盘、鼠标和监视器等。通过特定的接口,每个驱动程序与内核中的其余部分(甚至与其他驱动程序)相互作用:
    		优点:
    			1.可以把特定设备的代码封装在特定的模块中。
    			2.厂商可以不懂内核代码,只知道接口规范,就能增加新的设备。
    			3.内核以统一的方式对待所有的设备,并且通过相同的接口访问这些设备。
    			4.可以把设备驱动程序写成模块,并动态装到内核中,不需要重启系统,不需要的时候可以卸载模块,
    				以减少存储在RAM中的内核映像大小。
    
    第三章、进程				
    	1.进程状态:
    		a.可运行状态
    		b.可中断状态
    		c.不可中断的等待状态
    		d.暂停状态
    		e.僵死状态
    
    第十章、进程调度		
    	1.涉及进程调度
    		a.传统上把进程分类:
    			I/O范围(I/O-bound):频繁地使用I/O设备,并花费很多时间等待I/O操作的完成。
    			CPU范围(CPU-bound):需要大量CPU时间的数值计算应用程序。
    			
    		b.非传统分类:
    			交互式进程:如命令shell、文本编辑程序及图像应用程序。
    			批处理进程:如编译程序、数据库搜索引擎及科学计算。
    			实时进程:视频和音频应用程序、机器人控制程序及从物理传感器上收集数据的程序。
    		
    第十一章、内核同步	
    	1.内核同步,内核态进程的非抢占性:
    		a.在内核态中运行的进程不会被其他进程取代,除非这个进程主动放弃CPU的控制权
    		b.中断或异常处理可以中断在内核态中运行的进程。但是,在中断处理程序结束时,该进程的内核控制路径被恢复
    		c.执行中断或异常处理的内核控制路径只能被执行中断或异常处理的其他内核控制路径所中断
    
    	2.SMP原子操作 从Intel 80286开始引入lock指令解决这中问题。
    		lock只是一个用于一条汇编指令之前的特殊字节。当控制单元检测到一个lock字节时,就锁定内存总线
    		这样其他处理器就不能存取下一条汇编语言指令的目的操作数所指定的内存单元。只有在这条指令执行完成时总线锁才会被释放。
    		因此,带有lock前缀的读-修改-写指令即使在多处理器环境中也是原子的。
    			
    	3.内核数据结构进行同步访问的常用方法是使用信号量和自旋锁。
    		
    第十二章、虚拟文件系统
    	1.通用文件模型由下列对象类型组成:
    		a.超级块对象(superblock object):存放已安装文件系统的有关信息。对于基于磁盘的文件系统,
    			这类对象通常对应于存放在磁盘上的文件系统控制块(filesystem control block)。
    		b.索引节点对象(inode object):存放关于具体文件的一般信息。对于基于磁盘的文件系统,
    		  这类对象通常对应于存放在磁盘上的文件控制块(file control block)。
    			每个索引节点对象都有一个索引节点号,这个号唯一地标识文件系统中的指定文件。
    		c.文件对象(file object):存放打开文件与进程之间交互的相关信息。这类信息仅当进程访问文件期间存在于内核内存中。
    		d.目录项对象(dentry object):存放目录项与对应文件进行连接的信息。每个基于磁盘的文件系统都以自己特有的方式将该类信息存在磁盘上。
    			slab分配高速缓存中。
    		e.进程->文件对象->目录项对象->索引节点->(超级块对象)->磁盘文件。
    		
    	2.虚拟文件系统,目录项对象属于以下四种状态之一:
    		a.空闲状态:		还没有被VFS使用,对于德内存区由slab分配器进行管理。
    		b.未使用状态:		该目录项对象当前还没有被内核使用。该对象的引用计数器d_count为NULL。
    			但其d_indoe域只想相关索引节点。为了必要时回收内存,目录项包含有效信息,它的内存可能被丢失。
    		c.正在使用状态:	该对象被内核使用。d_count的值为正数。目录项包含有效的信息,并且不能被丢弃。
    		d.负状态:			与目录项相关的索引节点不复存在,那是因为相应的磁盘索引节点已被删除,d_count域被置NULL。
    	
    第十三章、管理I/O设备
    	1.三类内存地址:
    		a.逻辑地址(CPU内部使用)
    		b.线性地址(CPU内部使用)
    		c.物理地址(CPU从物理上驱动数据总线所用的内存地址)
    		d.总线地址(bus address):除CPU之外的硬件设备驱动数据总线所用的内存地址。
    		
    	2.设备文件(mknod()系统调用来创建设备文件):用来表示Linux所支持的大部分I/O设备的,除了文件名,每个设备文件都还有三个主要属性。
    		a.类型(type):块设备或字符设备
    		b.主号(major number):从1到255之间的一个数,用以标识设备的类型,通常,具有相同主号和相同类型的所有设备文件共享相同的文件操作集合。
    			因为他们是由同一设备驱动程序处理的。
    		c.次号(minor number):在一组主号相同的设备之间唯一标识特定设备所使用的一个数字。
    	
    	3.没有对应设备文件的I/O设备
    		比如网卡:网卡把向外发送的数据放入通往远程计算机系统的一条线上,把从远程系统中接受到的报文装入内核内存。
    				 由于没有使用文件系统,所以系统管理员必须建立设备名和网络地址之间的联系。
    				 应用程序和网络接口之间的数据通信不是基于标准的有关文件的系统调用。
                                     而是基于socket()、bind()、listen()、accept()和connect()系统调用。
    				 这些系统调用对网络地址进行操作。这组系统调用由Unix BSD中首先引入,现在已经成为网络设备的标准变成模型。
    				 
    	4.VFS对设备文件的处理:设备文件也在系统的目录数中。但它们和正规文件及目录有根本的不同。当进程访问正规文件时,
               它会通过文件系统访问磁盘分区的一些
    		数据块;而在进程访问设备文件时,它只要驱动硬件设备就可以了。VFS的责任是为应用程序隐藏设备文件与正规文件之间的差异。
    		VFS改变打开的设备文件的缺省文件操作。可以把对设备文件的任一系统调用转换成对设备相关的函数的调用,而不是对主文件系统对于函数的调用。
    		设备相关的函数对硬件设备进行操作以完成进程所请求的操作。
    		
    		驱动程序:控制I/O设备的一组相关的函数称为设备驱动程序(device driver)。由于每个设备都有一个唯一的I/O控制器,因此也就有唯一的
    				 命令和唯一的状态信息,所以大部分I/O设备类型都有自己的驱动程序。
    	
    	5.设备文件调用open()函数执行的操作:
    		a.如果设备驱动程序被包含在一个内核模块中,那么把引用计数器的值加1,以便只有把设备文件关闭之后才能卸载这个模块。
    		b.如果设备驱动程序要处理多个同类型的设备,那么就是用次号来选择合适的驱动程序,如果需要,还要使用专门的文件操作表选择驱动程序。
    		c.检查该设备是否真正存在,现在是否正在工作。
    		d.如果必要,向硬件设备发送一个初始化命令序列。
    		e.初始化设备驱动程序的数据结构。
    				 
    	6.内核支持的级别
    		a.根本不支持:应用程序使用设当的in和out汇编语言指令直接与设备的I/O端口进行交互。
    		b.最小支持:内核不能设别硬件设备,但能识别I/O接口。用户程序把I/O接口是为能够读写字符流的顺序设备。
    		c.扩展支持:内核设备硬件设备,并处理I/O设备本身,事实上,这种设备可能就没有对应的设备文件。
    	
    	7.访问I/O设备的地址,可以从/proc/ioports文件中获得。
    	
    	8.内核对于块设备的支持特点:
    		a.通过VFS提供统一接口
    		b.对磁盘数据进行有效的链接
    		c.为数据提供磁盘高速缓存
    		
    	9.内核基本上把I/O数据传送划分成两类:
    		a.缓冲区I/O操作:所传送的数据保存在缓冲区中,缓冲区是磁盘数据在内核中的普通内存容器。每个缓冲区都和一个特定的块相关联
    		  而这个块由一个设备号和一个块号来标识
    		b.页I/O操作:所传输的数据保存在页框中,每个页框包含的数据都属于正规文件。因为没有必要把这种数据存放在相邻的磁盘中。
    		  所以就是用文件的索引节点和文件内的偏移量来标识这种数据。主要用于读取正规文件、文件内存映射和交换。
    		  
    	10.所谓块(block):就是块设备驱动程序在一次单独操作中所传送的一大块相邻字节。
    	   扇区(sector):扇区是硬件设备传送数据的基本单元。
    	   
    	11.块设备驱动程序的两部分:
    		a.高级驱动程序:处理VFS层。
    		b.低级驱动程序:处理硬件设备。
    		  
    第十五章、访问正规文件
    	1.从正规文件读取数据:generic_file_read()函数实现了大部分文件系统的正规文件的read方法。
    	2.对正规文件进行预读:正规文件的预读需要的算法比物理块的预读需要的算法更复杂:
    		a.由于数据是逐页进行读取的,因此预读算法不必考虑页内偏移量,只考虑所访问的页在文件内部的位置就可以了。
    		b.当前访问与上一次访问不是顺序的时,预读就必须从头开始重新执行。
    		c.当进程一直反复地访问同一页时(该文件只有很少的一部分被使用),应该减慢预读的速度甚至停止执行。
    		d.如果需要,预读算法必须激活低级I/O设备驱动程序来确保新页会被读取。
    		e.内核通过多次调用一个名为:try_to_read_ahead()的函数来执行预读操作(read ahead operation),一次预读一页。
    		f.对于每个请求页,内核都要调用generic_file_readhead()函数,该函数确定是否要执行预读操作。
    		
    	3.写正规文件:write()系统调用会涉及把数据从调用进程的用户态地址空间中移动到内核数据结构中,然后再移动到磁盘上。
    				 文件对象的write方法允许每种文件类型都定义一个专用的写操作。
    		a.写操作发生时,有效数据是在缓冲区高速缓存中,而不是在页高速缓存中,更确切地说,当write方法修改了文件的任何部分时。
    		  与这些部分对应的页高速缓存中的所有页都不再包含有效数据。一个进程可能认为自己在读取正确数据,
                     但是却没看到其他进程对这些数据所做的修改。
    		b.所有基于磁盘的文件系统的write方法都要调用update_vm_cache()函数来修改读操作所使用的页高速缓存。
    		c.通过页高速缓存对正规文件执行的写操作,只能用于网络文件系统。文件的write方法是使用generic_file_write()函数实现。
    		
    	4.内存映射:一个线性区可以和基于磁盘的文件系统中的一个文件(或者文件的一部分)相关联。这就是说,内核会把线性区中对一个页中字节的访问转换成
    			   对正规文件中相对于字节的操作,这种技术成为内存映射。
    		a.共享的:对线性区中的任何写操作都会修改磁盘上的文件。而且,如果进程对共享内存映射中的一个页进行写,
                       那么这种修改对于其他映射了相同文件的所有
    				进程来说都是可见的。
    		b.私有的:当进程创建的映射只是为读文件,而不是写文件时才会使用。处于这种目的,私有映射的效率要比共享映射的效率更高。
    		    但是私有映射页的认识写操作都会使内核不再映射该文件中的页。一个写操作即不会改磁盘上的文件,
                        对访问相同文件的其他进程来说这种改变也是不可见的。
    		c.使用mmap()系统调用创建一个新的内存映射。必须指定MAP_SHARED或MAP_PRIVATE标志。
    		
    第十六章、磁盘数据结构
    	1.任何Ext2分区中的第一个块从不受Ext2文件系统的管理,因为这一块是为启动扇区所保留的。Ext2分区的其余部分被分成块组(block group)。
    	2.块组中的每个块包含下列信息之一:
    		a.一个Ext2文件系统超级块的拷贝
    		b.一组块组描述符的拷贝
    		c.一个数据块位图:标识在一组中块的使用和空闲状况。
    		d.一个索引节点位图
    		e.一组索引节点
    		f.属于文件的一块数据;即一个数据块。
    	3.如果一个块中不包含任何有意义的信息,就说这个块是空的。
    	4.在Ext2文件系统中的所有块组大小相同并被顺序存放,因此,内核可以从块组的整数索引很容易地得到磁盘中一个块组的位置。
    	5.超级块与组描述符被复制到每个块组中。只有块组0中所包含的超级块和描述符才由内核使用,而其余的超级块和组描述符保持不变
    	  事实上,内核甚至不考虑它们。
    	6./sbin/e2fsck程序对Ext2文件系统的状态执行一致性检查时,就引用存放在块组0中的超级块和组描述符,然后把它们拷贝到其他所有的块组中。
    	7.每组之多有8 x b块,b是以字节单位的块大小。
    	8.示例:
    		8GB的Ext2分区
    		块的大小为4KB
    		块位图的大小4KB(b=4KB)
    		最多描述32KB的数据块
    		每个块组的容量:4KB x 32KB = 128MB
    		最多需要的块组数: n = 8GB/128MB = 64
    	9.磁盘数据结构:
    		a.超级块的域:
    			索引节点的总数
    			以块位单位的文件系统的大小
    			保留的块数
    			空闲块计数器
    			空闲索引节点计数器
    			第一次使用的块号(总为1)
    			块的大小
    			片的大小
    			每组中的块数
    			每组中的片数
    			.....
    		b.组描述符:每个块组有自己的组描述符,为ext2_group_desc结构。
    			Ext2组描述符的域
    				块位图的块号
    				索引节点位图的块号
    				第一个索引节点表块的块号
    				组中空闲块的个数
    				组中索引节点的个数
    			组中目录的个数
    			.....
    		c.位图:是位的序列,0表示相应的索引节点块或数据块是空闲的,1表示占用。
    		d.索引节点表:所有索引节点的大小相同,即128字节。索引节点表第一个块的块号存放在组描述符的bg_inode_table域中。
    			Ext2磁盘索引节点的域:
    				文件类型和访问权限
    				拥有者的标识符
    				以字节为单位的文件长度
    				最后一次文件访问的时间
    				.......
    			文件的索引节点号没有必要再磁盘上存放,因为它的值可以从块组号和它在索引节点表中的相对于位置而得出。
    	10.Ext2的文件操作:
    		VFS方法的read和mmap是由很多文件系统共用的通用函数实现的。这些方法存放在
    		ext2_file_operations表中:
    			lseek -> ext2_file_lseek()
    			read  -> generic_file_read()
    			write -> ext2_file_write()
    			.......
    			
    	11.各种文件类型如何使用磁盘块
    		正规文件:正规文件只有在开始有数据时才需要数据块。
    		目录:Ext2以一种特殊的文件实现了目录,这种文件的数据块存放了文件名和相应的所有节点号。
    			 数据块中包含了类型为ext2_dir_entry_2的结构
    			 名字域最大为EXT2_NAME_LEN(通常是255)个字符的边长数组。
    			 目录项的长度是4的倍数
    		符号链:符号链的路径名达到60个字符,就把它存放在索引节点的i_blocks域,该域是由15个4字节整数组成的数组,因此无需数据块
    			    如果路径名大于60个字符,就需要一个单独的数据块。
    		设备文件、管道和套接字:这些类型的文件不需要数据块,所有必须的信息都存放在索引节点中。
    		
    	12.Ext2文件的类型
    		0 未知
    		1 正规文件
    		2 目录
    		3 字符设备
    		4 块设备
    		5 命名管道
    		6 套接字
    		7 符号链
    		
    	13.文件的洞是正规文件的一部分,它是一些空字符但没有存放在磁盘的任何数据块中。
    	    引入文件的洞是为了避免磁盘空间的浪费。它们被广泛地用在数据库引用中,更一般的说,用于文件上散列法的所有应用。
    第十八章 进程通信
    	1.进程间通信的机制:
    		管道和FIFO(管道):最适合在进程之间实现生产者/消费者的交互。有些进程往管道中写入数据,而另外一些进程则从管道中读取数据。
    			无名管道:用户无法打开一个现有的管道。除非管道是由一个公共的祖先进程创建的。
    			有名管道:有磁盘索引节点,因此任何进程都可以访问FIFO,没有使用数据块,使用内核缓冲区。
    		信号量IPC(Interprocess Communicatoin),表示一组系统调用,这组系统调用
    			允许用户态进程:
    				a.通过信号量和其他进程进行同步
    				b.向其他进程发送消息或者从其他进程处接受消息
    				c.和其他进程共享一个线性区
    			使用IPC资源:
    				信号量:semget()
    				消息队列:msgget()
    				共享内存:shmget()
    				
    				
    		消息:允许进程异步地交换信息(小块数据)。可以认为消息是传递附加信息的信号。
    		共享内存:当进程之间在高效地共享大量数据时,这是一种最合适的交互方式。
    		套接字(socket):涉及到网络相关。
    		
    第十九章、程序的执行
    	1.进程概念:在Unix中是用来表示正在运行的一组程序竞争系统资源的行为。
    	2.内核需要处理的问题(把一组指令装入内存并让CPU执行):
    		不同的可执行文件格式:	Linux的一个著名之处就是能执行其他操作系统所编译的二进制文件。
    		共享库:	很多可执行文件并不包含运行程序所需要的所有代码,而是希望在运行时由内核从函数库装入函数。
    		执行上下文中的其他信息:	包括程序员熟悉的命令行参数与环境变量。
    		
    	3.进程的信任状和能力:Unix系统与每个进程的一些信任相关,信任状把进程与一个特定的用户或用户组捆绑在一起。
    	  信任状在多用户系统上尤为重要,因为信任状可以决定每个进程能做什么,不能做什么,这样既保证了每个用户个人数据的完整性
    	  也保证了系统整体上的稳定性。
    		a.进程的能力:一种能力仅仅是一个标志,它表明是否允许进程执行一个特定的操作或一组特定的操作。
    	4.库:所有的全局外部符号名的线性地址。这些地址的分配或解析是由连接程序完成的,链接程序把程序所有的目标文件收集起来并构造可执行文件。
    	5.静态库/共享库
    		动态库的优缺点:进程不需要拷贝目标代码、仅仅执行一个内存映射,把库文件的相关部分映射到进程的地址空间中。
    			缺点也很明显,动态的启动时间较长,移植性也不如静链接的好,系统中所包含的库版本发生变化时,
                           动态库连接的程序可能不适合本地运行。
    		静态库的优缺点:占用大量的磁盘空间。每个静态连接的可执行文件都复制库代码的一部分。 gcc编译器提供-static选项告诉链接程序使用静态库。
    	
    	6.程序段和进程的线性区:
    		逻辑上,Unix程序的线性地址空间传统上被划分为几个叫段(segment)的区间:
    			a.正文段:包含可执行代码。
    			b.数据段:包含初始化的数据,也就是说,初始值存放在可执行文件中的所有静态变量和全局变量(因为程序在启动时必须知道它们的值)
    			c.bss段:包含未初始化的数据,也就是说,初值没有存放在可执行文件中的所有全局变量(因为程序在引用它们之前才赋值)
    			d.堆栈段:包含程序的堆栈,堆栈中有返回地址、参数和被执行函数的局部变量。
    			
    		堆:线性区包含动态分配给进程的内存区。
    		/sbin/init程序,它创建和监视在操作系统外层实现的所有进程的活动。init进程对应的线性区可以从(/proc/1/maps)文件得到这样的信息。
    		
    	7.执行跟踪(execution tracing):是一个程序监视另一个程序执行的一种技术。被跟踪的程序一步一步地执行,直到接受到一个信号或调用一个系统调用。
    	   执行跟踪由调试程序(debugger)广泛应用,当然还使用其他技术(包括在被调试程序中插入断点及运行时访问它的变量)。
    	8.ptrace()系统调用进程执行跟踪。设置了CAP_SYS_PTRACE能力的进程可以跟踪系统中的任何进程(除了init)。
               相反,没有CAP_SYS_PTRACE能力的进程P只能跟踪
    	  与P有相同属主的进程。此外,两个进程不能同时跟踪一个进程。
    		a.ptrace()系统调用修改被跟踪进程描述符的p_pptr域以使它指向跟踪进程,因此,跟踪进程变为被跟踪进程的有效父进程
    		  跟踪结束时,以PTRAC_DETACH命令调用ptrace()时,这个系统调用把p_pptr设置为p_oppter的值,恢复被跟踪进程原来的父进程。
    		b.被跟踪进程相关的几个监控事件为:
    			一条单独汇编指令执行的结束
    			进入一个系统调用
    			从一个系统调用退出
    			接收到一个信号
    		c.当一个监控的事件发生时,被跟踪的程序停止,并将SIGCHID信号发送给它的父进程。当父进程希望恢复子进程的执行时,
                       就是用PTRACE_CONT、PTRACE_SINGLESTEP和
    		  PTRACE_SYSCALL命令中的一条,这取决于父进程要监控那种事件。
    		 
    	9.可执行格式:Linux正式的可执行格式是ELF(Execuable and Linking Format)。
    		类型为linux_binfmt的对象所描述的可执行格式实质上提供以下三种方法:
    			a.loadbinary:通读存放在可执行文件中的信息为当前进程建立一个新的执行环境。
    			b.load_shlib:用于动态地把一个共享库捆绑到一个已经在运行的进程,这个是由uselib()系统调用激活的。
    			c.core_dump:在名为core的文件中存放当前进程的执行上下文。这个文件通常是在进程接收到一个缺省操作为"dump"的信号时被创建的
    			  其格式取决于被执行程序的可执行类型。
    			  
    		linux_binfmt对象:处于一个简单的连接链表中,第一个元素的地址被存放在formats变量中。
                                                      可以通过调用register_binfmt()和unregister_binfmt()函数
    						 在链表中插入和删除元素。
    						 
    		formats链表中的最后一个元素:是一个对解释脚本(interpreted script)的可执行格式进行描述的一个对象。定义了load_binary()方法。
                    其相应的do_load_script()
                           可执行文件是否是以两个#!开始,如果是,这个函数就以另一个可执行文件的路径作为参数解释第一行的其余部分,
                           并把文件名作为参数传递过去以执行这个脚本文件。
    									
    		Linux允许用户注册自己定义的可执行格式:用如下的格式项/proc/sys/fs/binfmt_misc/register文件写入一个字符串
                               :name:type:offset:string:mask:interpreter:
                                   name:新格式的标识符
                                   type:识别类型(M表示魔数,E表示扩展)
                                   offse:魔数在文件中的起始偏移量
                                   string:或者以魔数,或者以扩展名匹配的字节序列
                                   mask:屏蔽string中的一些位的字符串
                                   interpreter:程序解释器的完整路径名
    											  
    		可执行文件的前128字节填充linux_binprm结构的buf域。
    		exec类函数:这些函数能用可执行文件所描述的新上下文代替进程的上下文。
    展开全文
  • Linux 内核编译(三天吐血经历!)

    万次阅读 多人点赞 2018-01-29 16:28:40
    前几天做一个实验:编译Linux内核并向其增加一个系统调用。这个实验实在是太让人无语了,各种坑!昨天这个时候,我还在苦苦煎熬中。在今天凌晨四点才做好。为了让其他人少走一些弯路,鄙人就把自己的经验以及教训写...
    写在前面的话:
    本人大二,东南大学一个软工狗,正在修一门名为《操作系统原理》的坑爹课!前几天做一个实验:编译Linux内核并向其增加一个系统调用。这个实验实在是太让人无语了,各种坑!昨天这个时候,我还在苦苦煎熬中。在今天凌晨四点才做好。为了让其他人少走一些弯路,鄙人就把自己的经验以及教训写下来。里面会有一些不足,希望大家多多指教~

    废话不多说,那就开始吧:

    一、实验前的准备:

    Vmware + ubuntu10.10 (32位)+ linux-2.6.32.71.tar.xz
    安装虚拟机教程:http://jingyan.baidu.com/article/90895e0f95a07564ec6b0bc7.html
    说明:ubuntu 10.10是我试验的最后一个,也是最后成功的那个。当然,更推荐ubuntu 10.04,因为这个支持sudo apt-update 少了一些麻烦。 而由于10.10不支持更新,故我另外新下了linux 2.6.32.71 先将这个文件拖入虚拟机桌面。

    附: ubuntu10.10百度网盘分享:链接:http://pan.baidu.com/s/1bnNr8dD 密码:ybg3
            linux-2.6.32.71.tar.xz 百度网盘分享:链接:http://pan.baidu.com/s/1c1cOOtq 密码:epu9(可能已经失效,大家可以去某度上面进行下载。网上资源很多,造成的不便,很抱歉~)

    二、解压内核

    1、先打开安装好的ubuntu 进入终端 :在桌面按ctrl+alt+T
    2、输入sudo su 获取root权限:(会出现一个输入密码的一个命令行,在终端输入密码时,是不显示星号的。你只管把密码输入回车就行了!用惯了window的小伙伴可能会有些不适应)最后如图:

    3、先把下载好的内核复制到 /usr/src 文件夹中 : 
          终端输入 cd Desktop(定位到桌面) 回车 ; cp  linux-2.6.32.71.tar.xz  /usr/src 回车
    4、解压内核 依次输入以下命令回车执行
          cd /usr/src ;
       xz -d linux-2.6.32.71.tar.xz

         tar xvf  linux-2.6.32.71.tar

    三、增加系统调用

    1、

    打开sys.c文件。

    gedit /usr/src/linux-2.6.32.71/kernel/sys.c

    2、

    在文件末尾增加系统调用。

    asmlinkage intsys_mycall(int number)

    {

     printk("My Student No. is XXXXX,and My Name is XXXXX*** !");

     return number;

    }

    注:printk就是系统调用输出一行文字,你可以自定义里面内容,便于最终检验。

    3、

     注册系统调用:

    gedit /usr/src/linux-2.6.32.71/arch/x86/kernel/syscall_table_32.S

    在.long 类型文件末尾添加:

    .longsys_mycall

    并且按照顺序记住它属于第几个系统调用,在本机中为337。

    4、

    gedit /usr/src/linux-2.6.32.71/arch/x86/include/asm/unistd_32.h

    在一系列#define __NR_之后添加:

     

    # define __NR_mycall 337

    在这里就需要用到之前记住的数字了。


    四、编译内核

    ps:深吸一口气,前面做的只是准备工作!下面才是真正的开始!打好精神,真正的挑战在下面!
    下面的记得一定要一步一步都要做!不要漏掉一步!!!!

    进入解压目录:

    cd /usr/src/linux-2.6.32.71

    make mrproper

    make clean

    make oldconfig

    make bzImage  (这个过程和下面的过程非常非常非常长,亲测,建议泡杯茶,或是看个电影,没有两个小时不行

    make modules

    make modules_install


    五、拷贝内核

    经过了漫长的等待,我们终于到了这一步。
    先检验一下我们的结果:

     


     首先查看一下编译好的内核版本,以便命名 打开 /lib/modules  里面应该多了一个纯数字不带"generic"的文件夹,那就是新内核版本号,我们的是2.62.32.71 如下所示:

    有了这个就代表前面的没有什么错误了。

    接着,就在终端输入: 

    cp /usr/src/linux-2.6.32.71/arch/i386/boot/bzImage  /boot/vmlinuz-2.6.32.71-mykernel


    六、创建initrd文件

    mkinitramfs-o /boot/initrd.img-2.6.32.71


    七、更新grub引导表

    进行到这一步,也许你感觉到自己差不多了,毕竟都这么久了,你也许有些困了,有些疲惫,但是,我告诉你,最难最容易出错的,就在当前这一步!建议你先休息一下,下面需要你投入百分之百的注意力去做,若是出错,你可是要全部重新开始的!
    1.

    gedit /boot/grub/grub.cfg


    2.
    在打开的文件中找到类似如下的字段,并复制并粘贴在前面:

    但必须在同一个

    ### BEGIN /etc/grub.d/10_linux ### 

           ……  

    ### END /etc/grub.d/10_linux ###

    里面

    字段如下:
    menuentry 'Ubuntu, with Linux 2.6.35-22-generic' --class ubuntu --class gnu-linux --class gnu --class os {
    recordfail
    insmod part_msdos
    insmod ext2
    set root='(hd0,msdos1)'
    search --no-floppy --fs-uuid --set 0efd72ba-ba85-470f-8c21-ab68730ca8c9
    linux /boot/vmlinuz-2.6.35-22-generic root=UUID=0efd72ba-ba85-470f-8c21-ab68730ca8c9 ro   quiet splash
    initrd /boot/initrd.img-2.6.35-22-generic
    }
    menuentry 'Ubuntu, with Linux 2.6.35-22-generic (recovery mode)' --class ubuntu --class gnu-linux --class gnu --class os {
    recordfail
    insmod part_msdos
    insmod ext2
    set root='(hd0,msdos1)'
    search --no-floppy --fs-uuid --set 0efd72ba-ba85-470f-8c21-ab68730ca8c9
    echo 'Loading Linux 2.6.35-22-generic ...'
    linux /boot/vmlinuz-2.6.35-22-generic root=UUID=0efd72ba-ba85-470f-8c21-ab68730ca8c9 ro single 
    echo 'Loading initial ramdisk ...'
    initrd /boot/initrd.img-2.6.35-22-generic
    }


    将粘贴后字段里面的
     linux    /boot/vmlinuz-2.6.35-22-generic      initrd    /boot/initrd.img-2.6.35-22-generic  改成你的内核文件地址和initrd 地址:
     linux   /boot/vmlinuz-2.6.32.71-mykernel    initrd    /boot/initrd.img-2.6.32.71

    这一步特别无聊但又必须认认真真做,要不然你就前功尽弃,别问我怎么知道的,我要是牢记这句话,不会到四点才睡觉 ( ╯□╰ )!! 建议全部改完之后,检查几遍。笔者以及室友们都在这步出错,以至于不得不重新开始。全部完成如图所示:

     红框是改过之后的,篮框里面的是你需要复制的内容 可以看到 ,两者在同一个###BEGIN /etc/**** 里面 黄色下划线部分
    (图丑见谅)


    八、收尾工作

    好了,你若已经检查完毕上面的一切工作,那么,扫尾工作就开始了,这时候,也莫要放松 一步一步来,喝点开水,长呼口气,一步一步来,下面的一步一步落实:

    cd /boot

    cp initrd.img-2.6.32.71 initrd-2.6.32.71.old

    depmod–a

    update-initramfs-k 2.6.32.71 –c

    cd /tmp

    gzip-dc /boot/initrd.img-2.6.32.71| cpio –id

    touch lib/modules/2.6.32.71/modules.dep

    find./ | cpio -H newc -o > /boot/initrd.img-2.6.32.71.new

    gzip /boot/initrd.img-2.6.32.71.new

    cd /boot

    mvinitrd.img-2.6.32.71.new.gz initrd.img-2.6.32.71


    九、重启

    终于到了验证结果的一步了,此时你要克制一下自己的激动心情,在终端键入 reboot 点击回车。慢慢等待一会,若是你重启成功,那么恭喜你,你已经要看到胜利的曙光啦!
    重新进入终端,获取权限,过程前面有讲,不再重复。在终端键入 uname -a 回车
    此时若是看到

    linux-2.6.32.71,说明已经成功!

    如下:

    若是看到这个,你就可以大叫一声庆祝一下了,你已经成功啦!!!!

    十、测试自定义系统调用

    打开终端,键入gedit,打开gedit工具,继续键入如下代码:

    #include<stdio.h>

    int main()

    {

           syscall(337, 1);

           return 0;

    }

    保存为mytest.c

    再继续在终端中键入

    gcc-o mytest mytest.c(编译C程序)

    之后 ./mytest

    点击运行编译出来的程序,此时并不会显示出效果,在终端中键入dmesg –c查看系统调用信息。

    此时,你可以看到



    说明之前写的sys_mycall调用成功!
    到这一步,算是全部成功啦!!庆祝一下,去装个逼吧~~~~
     
    写在最后的话:

    由于笔者是新手,里面的步骤有些显得很笨重,希望大手们看到之后多多指教!谢谢!!
    另外,做这个实验一定要有耐心,若是你做的时候一直很小心,每一步都认真做,那么你一次就可以成功!!别像笔者这样做了不知多少遍。
    最后,祝大家成功!!   有不足,也希望大家指出,谢谢@~@
     
    ps:
    参考文档:http://wenku.baidu.com/view/40af3b6727d3240c8447efd8.html?qq-pf-to=pcqq.c2c

    第一篇博文,谢谢大家的浏览o(^▽^)o


    展开全文
  • 深入理解linux内核

    千次阅读 2019-07-10 11:09:02
    内核体系结构 模块优点 文件系统 文件 硬链接和软连接 限制 软连接(符号链接) 文件类型 文件描述符 索引节点(inode) 访问权限和文件模式 文件操作的系统调用 进程/内核模式 激...

    目录

    目录

    操作系统(os)

    必须完成的目标

    多用户系统

    特点

    用户和组

    进程

    进程和程序:几个进程能并发执行同一个程序,同一个进程能顺序的执行几个程序。

    单处理系统

    内核体系结构

    模块优点

    文件系统

    文件

    硬链接和软连接

    限制

    软连接(符号链接)

    文件类型

    文件描述符

    索引节点(inode)

    访问权限和文件模式

    文件操作的系统调用

    进程/内核模式

    激活内核例程

    进程的执行

    可重入内核

    进程地址空间

    同步和临界区

    非抢占式内核

    关中断

    信号量

    自旋锁(spin lock)

    避免死锁

    信号和进程间通信

    进程管理

    僵死进程

    内存管理虚拟内存

    RAM(随机访问内存)使用

    内核内存分配器(KMA)

    特点

    进程虚拟地址空间的处理

    交换与高速缓存

    设备驱动程序

    优点

    第二章 内存寻址

    段寄存器

    段描述符

    段选择符

    段单元

    硬件的分页单元

    等待队列

    进程的使用限制

    进程切换

    硬件上下文

    创建进程

    内核线程

    第四章  中断和异常

    中断

    中断信号的作用

    中断处理须要满足的约束

    异常处理

    第五章  定时测量

    内核必需的2种主要定时测量:

    定时器的作用

    静态定时器(内核使用) 

    第六章  内存管理

    避免外碎片的方法:

    伙伴(buddy)系统算法

    举例:

    称为伙伴的两个块应该满足的条件:

    内存区管理

    第七章  进程地址空间

    第八章 系统调用

    第九章 信号

    内核实现信号处理:

    第十章  进程调度

    第十一章  内核同步

    cpu交错执行和控制路径的情况:

    同步技术:

    第十二章  虚拟文件系统vfs

    第十三章  管理IO设备

    第十四章  磁盘高速缓存



    操作系统(os)

    计算机系统都包含一个基本的程序集合,称为操作系统.在这个集合里,最重要的程序被称为内核(kernel),当操作系统启动时,内核被装入ARM中,内核中包含了系统运行时必不可少的很多过程。

    必须完成的目标

    1.与硬件部分相互作用,为所有包含在硬件平台上的低层可编程部件提供服务

    2.为运行在计算机系统上的应用程序(用户程序)提供可执行环境

     

    当程序想使用硬件资源时,必须向操作系统发出一个请求,内核对整个请求进行评估,如果允许使用这个资源,内核将代表应用程序与相关的硬件部分进行交互。

    为了实施这种机制,os依靠特殊的硬件特性来禁止用户程序直接与底层硬件打交道,或者直接访问任意的物理地址。

    硬件为cpu引入了至少两种执行模式:用户的非特权模式(用户态)和内核的特权模式(内核态)

    多用户系统

    一台能并发和独立的执行分别属于两个或多个用户的若干应用程序的计算机。

    并发:几个应用程序能同时处于活动状态并竞争各种资源,如CPU,内存,硬盘等。

    独立:每个程序能执行自己弄的任务,而无需考虑其他用户的应用程序在干什么。

    特点

    1.认证机制,核实用户身份

    2.保护机制,防止有恶意的用户程序妨碍其他应用程序在系统中运行,防止有恶意的用户程序干涉或窥视其他用户活动。

    3.记账机制,限制分配给每个用户的资源数

    用户和组

    在多用户系统中, 每个用户在机器上有私用空间,如磁盘空间的限额存储文件。操作系统必须保证用户空间的私有部分仅对拥有者可见。

    所有用户由一个唯一的数字来标识,用户标识符(user id ,UID)。

    为了和其他用户有选择的共享资料,每个用户是一个或者多个组的成员,组内有唯一的组标识符(Group ID, GID),每个文件也恰好与一个组相对应。

    任何unix os都有root(超级用户),os对root不会使用常用的保护机制,root能访问系统中每一个文件,能干涉每一个正在执行的用户程序的活动。

    进程

    程序执行的一个实例,或者一个运行程序的“执行上下文”。os允许具有多个执行流的进程(在相同的地址空间可以执行多个指令序列)。

    多用户系统必须实施一种执行环境,在这种环境里,几个进程能并发的活动,并能竞争系统资源。允许经常并发活动的系统被称为多处理技术系统。

    进程和程序:几个进程能并发执行同一个程序,同一个进程能顺序的执行几个程序。

     

    单处理系统

    只有一个进程能占用cpu,在某一时刻,只能有一个执行流,cpu个数有限,只有几个进程能同时执行,选择哪个进程执行时由os的调度程序(scheduler)决定。一些os只允许有非抢占式进程,只有当进程自愿放弃cpu,调度程序(scheduler)才被调用

    多处理系统的scheduler必须是抢占式的,os记录每个进程占用cpu时间,周期性的激活调度程序。

    内核体系结构

    模块是内核的特点。是一个目标文件,代码可以在允许时链接到内核或从内核中取下。

    模块优点

    模块化方法:可以在运行时链接或者卸下模块,开发者必须引入定义明确的软件接口来访问由模块处理的数据结构,让开发新模块变得容易。

    平台无关性:即使模块依赖某些特殊的硬件特点,但不依赖某个固定的硬件平台。

    节省内存:当需要模块的功能时,把他链接到正在允许的内核中,否则,卸下该模块(链接和卸下由内核完成)

    没有性能损失:模块的目标代码一旦被链入到内核的作用与静态链接的内核的目标代码等价。

    文件系统

    文件

    以一系列自己组成的信息载体,内核不解释文件内容。由一系列字符组成。

    硬链接和软连接

    包含在一个目录中的文件名就是一个文件的硬链接,简称链接。

    ln f1 f2 //为路径f1标识的文件创建一个路径名为f2的硬链接

    限制

    1.不允许给目录创建硬链接,因为这个可能把目录树变成环形图,从而就不能通过名字来定位一个文件‘

    2.只有在同一个文件系统中的文件之间才能创建链接。

    软连接(符号链接)

    是一个短文件,这个文件包含了另一个文件的任意一个路径名,这个路径名指向位于任意一个文件系统的任意文件,甚至可以指向一个不存在的文件。

    // 创建一个名为f2的新的软连接指向路径名f1,当这个命令指执行时,文件系统创建一个新的软连接,并把路径名f1写入这个链接,然后插入一个新的目录项,该目录包含路径f2中最后一个名字。以这种方式,任何对f2的引用都会被自动转换称指向f1的一个引用
    ln -s f1 f2 

    文件类型

    正规文件(regular file)

    目录(directory)

    符号链(symbolic link)

    块设备文件(block-orinted device file)

    字符设备文件(character-oriented device file)

    管道(pipe)和命名管道(named pipe, fifo)

    套接字(socket)

    文件描述符和索引节点

    文件描述符

    表示了进程与打开文件之间的相互作用。

    索引节点(inode)

    文件系统用来管理文件的数据结构,每个文件都有自己的索引节点,文件系统用文件来识别文件。

    访问权限和文件模式

    文件的潜在用户:文件所有者,同组(group)但不包括所有者,所有剩下的用户

    访问权限:读,写,执行

    当进程创建一个文件时,文件拥有着的id就是该进程的uid,组id既可以是创建者的gid,也可以是父目录的gid。

    文件操作的系统调用

    当用户访问一个正规文件或者目录文件的内容时,实际上访问的是存储在硬件块设备上的一些数据。从这个意义上说,文件系统是用户级的观点来看硬盘分区的物理组织,因为处于用户态的进程不能直接与底层硬件打交道,所以每个文件的操作必须要在内核下进行。

     

    进程/内核模式

    当应用程序在用户态下执行时,不能直接访问内核数据结或内核程序。当程序在内核态下执行不再有限制。

    进程是动态的实体,在系统内通常只有有限的生存期,创建、车型以及同步已存在进程的任务都委托给内核中的一个例程完成。

    内核本身不是进程,而是进程的管理者,进程/内核模式假定:需要内核服务的进程使用系统调用(system call)机制,每个系统调用都设置了一组参数来识别进程的要求,然后执行与硬件相关的cpu指令完成从用户态到内核态的转换。

    激活内核例程

    1.进程调用一个系统调用

    2.正在执行的cpu发出异常信号

    3.外围设备向cpu发出一个中断信号以通知一个事件的发生。每个中断信号都是由中断处理程序来处理。

    4.内核线程被执行

    进程的执行

    为了内核管理进程,每个进程由一个进程描述符表示。

    当内核暂停一个进程的执行时,在进程描述符中保存处理器寄存器内容:

    1.程序计数器pc和栈指针sp寄存器

    2.通用寄存器

    3.浮点寄存器

    4.包含cpu状态信息的处理器控制寄存器

    5.跟踪进程对RAM访问的内存管理寄存器

    当内核恢复进程时,用进程描述符中合适的域装载cpu的寄存器。因为程序计数器中所存的值指向下一条要执行的指令,进程从他停止的地方恢复执行。

    可重入内核

    几个进程可同时在内核态下执行。

    进程地址空间

    同步和临界区

    非抢占式内核

    关中断

    信号量

    是与数据结构相关的计数器。内核线程访问这个数据结构前都要检查这个信号量。该数据结构组成:

    整数变量,等待进程的链表,原子方法(down(), up())

    down()对信号量的值减1,如果这个值小于0,就把正在允许的进程加入到信号量链表,然后阻塞该进程(调用scheduler)。up()对信号量的值加1,如果这个值大于等于0,激活这个信号量的链表中的一个或者多个进程。

    每一个要保护的数据结构都有自己的信号量,初始值为1 ,当内核控制路径希望访问这个数据结构时,在适当的信号量上执行down()方法,如果新信号量的值不是负数,则允许访问这个数据结构,否则,则把控制路径的进程加入到这个信号量的链表并阻塞他,当另一个进程在那个信号量上执行up()时,允许信号量链表上的进程之一继续执行。

    自旋锁(spin lock)

    信号量并不是解决同步问题的最佳方案。没有进程链表,当一个进程发现锁被另一个进程锁着时,他就不停旋转,不断执行直到锁打开。

    自旋锁是指当一个线程在获取锁的时候,如果锁已经被其他线程获取,那么该线程将循环等待,然后不断地判断是否能够被成功获取,知直到获取到锁才会退出循环。
     获取锁的线程一直处于活跃状态,但是并没有执行任何有效的任务,使用这种锁会造成busy-waiting
     它是为实现保护共享资源而提出的一种锁机制。其实,自旋锁与互斥锁比较类似,它们都是为了解决某项资源的互斥使用。无论是互斥锁,还是自旋锁,在任何时刻,最多只能由一个保持者,也就说,在任何时刻最多只能有一个执行单元获得锁。但是两者在调度机制上略有不同。对于互斥锁,如果资源已经被占用,资源申请者只能进入睡眠状态。但是自旋锁不会引起调用者睡眠,如果自旋锁已经被别的执行单元保持,调用者就一直循环在那里看是否该自旋锁的保持者已经释放了锁,“自旋”一词就是因此而得名。

    https://studygolang.com/articles/16480

    避免死锁

    1.引入有限的信号量类型

    2.以递增的顺序请求信号量

    信号和进程间通信

    通过信号把系统事件报告给进程。每种事件都有自己的信号编号。

    两种事件:异步通告,同步错误或异常

    进程可以以以下2种方式对信号的接收做出反应:

    1。忽略该信号

    2.异步的执行一个指定的过程(信号处理程序)

    如果进程不指定选择以上的两种方式,内核会根据信号的编号执行缺省操作:

    1.终止进程

    2.将执行上下文和进程地址空间的内容下入一个文件,并终止进程

    3.忽略信号

    4.挂起进程

    5.如果进程曾被暂停,则恢复执行

    进程管理

    fork()创建新的进程。调用fork()的是父进程,而新进程是他的子进程,父进程能相互找到对方,因为描述每个进程的数据结构都包含两个指针,一个直接指向他的父进程,另一个直接指向子进程。

    exit()终止进程

    exec()加载新程序

    僵死进程

    父进程查询子进程的终止:wait()进程等待,直到其子进程之一结束,返回被终止的子进程的进程标识符(PID)

     

    内存管理虚拟内存

    1.几个进程可以并发执行

    2.应用程序所需要内存大于可用物理内存时也可以运行

    3.进程可以执行只有部分代码装入大盘内存的程序

    4.允许每个进程访问可以物理内存的一个子集。

    5.进程可以共享库函数或程序的单一的内存映像。

    RAM(随机访问内存)使用

    一部分存储内核映像(内核代码和内核静态数据结构),其余部分由虚拟存储器系统来处理,通过以下3种可能的方式来使用:

    1.满足内核对缓存、描述符以及其他动态内核数据结构的请求

    2.满足进程对一般内存区域的请求及对文件的内存映射的请求

    3.用高速缓存的方法从磁盘及其他缓冲设备获得较好的性能

    每种请求类型都是有价值的,但从另一方面说,ram是有限的,必须在请求类型间做出平衡,尤其当可用内存剩下不多时。当可用内存达到极限时,可用调用页框回收算法释放额外的内存。

    虚拟内存必须解决的主题:内存碎片。理想情况下,只有当空闲页框数太少时,内存请求才会失败。然而,内核通常被迫使用物理上连续的内存区域,因此即使有足够的可用内存,但不能作为一个连续的大块使用时,内存的请求也会失败。

    内核内存分配器(KMA)

    是一个子系统,试图满足系统中所有部分对内存的请求。其他一些请求来自内核其他子系统,他们需要一些内存供内核使用,还有一些请求来自用户程序的系统调用,用来增加用户进程的地址空间。

    特点

    1.必须快。最重要的属性,由所有的内核子系统调用(包括中断处理程序)

    2.必须把内存的浪费减到最少

    3.必须努力减轻内存的碎片问题

    4.必须能与其他内存管理子系统合作,以便借用和释放页框

    进程虚拟地址空间的处理

    一个进程的虚拟地址空间包括所有进程被允许引用的虚拟内存地址。内核通常把进程虚拟地址空间当作内存区域描述符的链表保存

    交换与高速缓存

    为扩充进程所用的虚拟地址空间大小。虚拟内存系统以一个页框的内容作为交换的基本单位。

     

    设备驱动程序

    内核通过设备驱动程序与I/O设备打交道。设备驱动程序包含在内核中,由控制一个或多个设备的数据结构和函数组成。这些设备包括硬盘、键盘、鼠标、监视器、网络接口以及链接到SCSI总线上的设备。通过特点的接口,每个驱动程序与内核中的其余部分相互作用。

    优点

    1.把特点设备的代码封装在特定的模块中

    2.厂商可用不懂内核源代码,只知道接口规范,并且通过相同的接口访问这些设备

    3.可用把设备驱动协程模块,并动态的把他们装进内核,不需要重新启动系统。不再需要时,可用卸下模块,以减少存储在ram中内核映像的大小。

     

    第二章 内存寻址

    内存地址

    段寄存器

    逻辑地址由一个段标识符和一个指向段内相对地址的偏移量。

    cs:代码段寄存器,指向存放程序指令的段。含有一个两位的域,用来指明cpu当前的权级。0代表最高优先级,3代表最低优先级。linux只用0级(内核态)和3级(用户态)。

    ss:栈段寄存器,指向存放当前程序栈的段

    ds:数据段寄存器,指向存放静态数据或者外部数据的段

    段描述符

    段选择符

    段单元

    硬件的分页单元

    把先行地址转为物理地址。把请求的存取类型和线性地址的存取权限相比,如果存取无效,则产生错误页面。

    线性地址被分成以固定长度为单位的组,称为页。页内连续的线性地址被映射到连续的物理地址中。

     

    等待队列

    运行链表把处于task_running状态的所有进程组织在一起,当要把其他状态的进程分组,不同状态处理不同:

    1.task_stopped或者task_zombie状态进程不链接在专门的链表中,也没必要分组,因为父进程可用通过进程的pid或进程间的亲子关系检索到子进程

    2.把task_interruptible或者task_uninterruptible状态的进程再分成很多类,每一类对应一个特定事件。在这种情况下,进程状态提供的信息满足不了快速检索进程,因此有必要引入另外的进程链表,这些附加的链表叫等待队列

    等待队列在内核中有很多用途,尤其对中断处理、进程同步、定时用处更大。表示一组睡眠的进程,当某一条件为真,由内核唤醒。

    等待队列由循环链表实现,元素包括指向进程描述符的指针。

    等待队列

    struct wait_queue {
        // 
        struct task_struct * task;
        // 等待队列指针
        struct wait_queue * next;
    }

    进程的使用限制

    RLIMIT_CPU:进程使用cpu的最长事件,如果进程超过了这个限制,内核就向他发一个sigxcpu信号,如果进程还不终止,再发一个sigkill信号。

    RLIMIT_FSIZE:允许文件大小的最大值。如果进程试图把一个文件的大小扩充到大于这个值,内核就给这个进程发送sigxfsz信号

    RLIMIT_STACK:栈大小的最大值。在扩充进程的用户态堆栈之前,内核检查这个值

    RLIMIT_CORE:内存信息转出文件的大小。当一个进程异常终止时,内核要在进程的当前目录下创建一个内存信息转储文件,在这个文件创建之前,内核检查这个值。如果这个限制为0,那么内核就不创建这个文件。

    RLIMIT_RSS:进程所拥有的页框的最大数。实际上,内核从来不检查这个值,没有实现这个使用限制。

    RLIMIT_NPROC:用户能拥有的进程最大数

    RLIMIT_NOFILE:打开文件的最大数。当打开一个新文件或复制一个文件描述符时,内核检查这个值。

    RLIMIT_MEMLOCK:非交换内存的最大尺寸。当进程试图通过mlock()或者mlockall()系统调用锁住一个页框时,内核检查这个值。

    RLIMIT_AS:进程地址空间的最大尺寸。当进程调用malloc()或相关函数扩大它的地址空间时,内核检查这个值。

    进程切换

    为了控制进程的执行,内核必须有能力挂起正在CPU上允许的进程,并恢复以前挂起的某个进程的执行。这种行为被称为进程切换、任务切换或者上下文切换。

    进程切换主要内容:

    1.硬件上下文

    2.硬件支持

    3.linux代码

    4.保存浮点寄存器

    硬件上下文

    进程恢复执行前必须转入寄存器的一组数据。

    每个进程可以拥有属于自己的地址空间,但所有进程必须共享cpu寄存器。因此,恢复一个进程的执行前,内核必须确保每个寄存器装入了挂起进程时的值。硬件上下文是进程可执行上下文的一个子集,可执行上下文包含了进程执行需要的所有信息。

    进程切换只发生在内核态。在进程切换之前,用户态下的进程使用的所有寄存器的内容都已经被保存,也包括指定用户态对战指针地址的ss和esp寄存器内容。

    创建进程

    传统方式:父进程所拥有的资源被复制,被复制的那份用来运行子进程。低效,因为要拷贝父进程的整个地址空间。

    改进方式(3种):

    1.写时复制技术允许父子进程能读相同的物理页,只要两者中有一个进程试图写一个物理页,内核就把这个页的内容拷贝到一个新的物理页,并把这个新的物理页分配给正在写的进程。

    2.轻量级进程允许父子进程共享每一进程在内核的数据结构,如页表及打开文件表。

    3.vfork()系统调用创建的一个进程能共享其父进程的内存地址空间,为了防止父进程重写子进程需要的数据,阻塞父进程的执行,一直到子进程的退出或者执行一个新的程序。

    内核线程

    内核线程与普通进程的区别:

    1.每个内核线程执行一个单独指定的内核函数,而普通进程只有通过系统调用执行内核函数

    2.内核线程之运行在内核态,而普通进程既可运行在内核态,也可运行在用户态。

    3.地址空间。内核线程使用大于page_offset的线性地址空间。普通进程不管内核态还是用户态用4GB的线性地址空间。

     

    进程可以创建一个子进程执行特定的任务,然后调用wait()类系统调用检查子进程是否终止,如果子进程已经终止,他的终止代号将告诉父进程这个任务是否已经成功进行。内核在进程终止后丢弃包含在进程描述符域中的数据。只有当父进程发布了一个与已终止进程相关的wait()之后才允许这样。(这就是为什么僵死状态的进程的描述符必须保存,直到父进程得到通知)

    如果父进程在子进程之前结束:已僵死的进程可能用可用的task项塞满了系统。必须强迫所有的孤儿进程成为init进程的子进程。init进行在调用wait()检查合法性的子进程终止时,就可以撤销僵死的进程。(调用release()释放僵死进程)

     

    第四章  中断和异常

    中断

    改变处理器执行指令顺序的事件。

    同步中断:当指令执行时由cpu控制单元产生,只有在一条指令终止执行后cpu才会发出中断

    异步中断:由其他硬件设备依照cpu时钟信号随机产生的。

    中断信号的作用

    提供了一种方式,让处理器转而去运行正常控制流之外的代码。当一个中断信号达到时,cpu必须停下他当前正在做的事情,并且切换到一个新的活动。为了做到这样,需要在内核堆栈保存程序计数器的当前值,并把与中断类型相关的一个地址放进程序计数器。

    中断处理须要满足的约束

    1.当内核正打算去做一些其他事情时,中断随时会到来。因此内核的目标就是让中断尽可能快的被处理,尽其所能把其他更多的处理向后推迟。

    2.中断随时到来,内核可能正在处理其中一个中断时,另一个中断又发生。应尽可能多的允许这种情发生,因为能维持更多的IO设备处于忙碌状态。

    3.尽管内核在处理前一个中断时可用接受一个新的中断,但在内核代码中还是存在一些临界区,在那里中断必须关闭,尽可能的限制这种临界区。

    异常处理

    目标:1.向进程发一个信号以同胞一个反常情况;2.处理请求分页

    异常处理程序的标准结构:

    1.在内核堆栈中保存大多数寄存器的内容

    2.用高级C函数处理异常

    3.通过ret_from_exception()函数从高级语言退出

     

    第五章  定时测量

    内核必需的2种主要定时测量:

    1.持续记录当前的时间和日期以便能通过time(), ftime()和gettimeofday()系统调用返回给用户程序。

    2.维持定时器,这种机制能够提醒内核或者用户程序某一时间间隔已经过去了

     

    内核必须显示的与实时时钟、时间标记计数器、可编程间隔定时器

    cpu分时(time-sharing)

    定时中断对于可运行(即处于task_running)的进程之间共享cpu的时间必不可少。通常给进程分配一个时间片,如果时间片到时进程还没终止,那么schedule()函数选择一个新的进程投入运行。

    定时器的作用

    定时器是一种软件工具,允许在将来的某个时刻,当给定的时间间隔用完时调用函数,超时表示一个时刻,到这一时刻,与定时器相关的时间间隔已经用完。内核和进程广泛使用定时器。大多数设备驱动程序利用定时器检测反常情况。

    静态定时器(内核使用) 

    为了激活静态定时器,内核必须简单的:

    1.登记定时器的fn域要执行的函数

    2.计算到期时间,并把这个到期时间存在定时器的expires域

    3.在timer_active种设置合适的标志

    动态定时器(内核使用)

    被动态的创建和撤销,对当前活动动态定时器的个数没有限制

    间隔定时器(进程在用户态创建)

    第六章  内存管理

    内核应该为分配一组连续的页框而建立一种问的的、高效的分配策略。必须解决内存管理的外碎片问题。

    避免外碎片的方法:

    1.利用分页单元把一组非连续的空闲页框映射到连续的线性地址区间

    2.开封一种适当的技术来记录限制的空闲连续页框块的情况,以尽量避免为满足小块的请求而把大块的空间块进行分割。

    伙伴(buddy)系统算法

    以页框作为基本内存区,只适合大块内存的请求。

    把所有空闲页框分组为10块链表,每个块链表分别包含大小为1,2,4,8,16,32,64,128,256和512个连续的页框。

    举例:

    假设要请求一个128页框的块,该算法先在128个页框的链表中检测是否有一个空闲块,如果没有这样的块,该算法会查找下一个更大的页块,也就是在256个页框的链表中找一个空闲块。如果存在这样的块,内核就把256的页框分成两等份,一半用来满足请求,另一半插入到128个页框的链表中。如果在256个页框的链表中也没有找到空闲块,就继续找更大的块512个页框的块。如果这样的块存在,内核把512个页框的块的128个页框做请求,然后从剩余的384个页框中拿256插入到256个页框的链表,再把最后的128个插入到28个页框的链表。如果512个页框的链表还是空的,该算法就放弃并发出错误信号。

    称为伙伴的两个块应该满足的条件:

    1.两个块具有相同的大小,记作b

    2.物理地址连续

    3.第一块的第一个页框的物理地址是2 * b * 2的12次幂的倍数。

    内存区管理

    内存区:具有连续的物理地址和任意长度的内存单元

    内碎片问题:请求内存的大小与分配给它的大小不匹配造成。

    解决:按照几何分布的内存区大小,即内存区大小与2的次幂有关而与所存放的数据大小无关。这样不管请求内存大小是多少,都可以保证内碎片小于50%.

     

    第七章  进程地址空间

    允许进程使用的全部线性地址组成。每个进程所看大的线性地址集合不同。内核通过增加或删除某些线性地址区间而动态的修改进程的地址空间。

    内核通过线性区的资源来表示线性地址区间,线性区与起始线性地址、长度和一些存取权限来描述。

    1.当用户在控制台输入命令,shell进程创建一个新的进程区执行这个命令,结果是,一个全新的地址空间,也就是一组线性区分配给了这个新的进程。

    2.一个正在允许的进程有可能决定装入一个完全不同的程序,此时进程标识符仍保持不变,在装入这个程序以前所使用的线性区被释放,并有一组新的线性区被分配给这个进程。

    3.一个正在允许的进程可能对一个文件执行“内存映射”,在这种情况下,内核给这个进程分配的一个新的线性区来映射这个文件。

    4.进程可能持续向它的用户堆栈增加数据,直到映射这个堆栈的线性区用完为止,在这种情况下,内核也许决定扩展这个线性区的大小。

    5.进程可能创建一个IPC共享线性区来与其他合作进程共享数据,此时,内核给这个进程分配一个新的线性区来实现这个方案

    6.进程可能通过调用类似malloc()来扩展自己的动态区。结果是内核可能决定扩展给这个堆所分配的线性区。

    分配线性地址空间:

    释放线性地址空间:

    扫描进程所拥有的线性区链表,并删除与指定线性地址区间相重叠的所有区;更新进程的页表,并重新调整线性区链表。

     

    第八章 系统调用

    在内核打算满足用户请求之前,必须仔细检查所有系统调用参数。检查类型既依赖系统调用,页依赖特定参数。

    如一个参数指定的是地址,那么内核必须检查它释放在这个进程的地址空间之内。

    1.验证这个线性地址释放属于进程的地址空间,如果是,这个线性地址所在的线性区就具有正确访问权限。

    2.仅仅验证这个线性地址小于page_offset(linux内核用的这个方式)

     

    第九章 信号

     

     

     

    内核区分与信号传递的两个不同阶段:

    1.信号送达:内核更新进程的描述符以表示一个新的信号已被送达

    2. 信号的接收:内核强迫目标进程对这个信号做出反应,通过改变它的执行状态或开始执行一个特定的信号处理程序,或两者都是。

    每个被发送的信号只能被接收一次,信号是可消费资源,一旦被接受,进程描述符中有关这个信号的所有信息被取消。

    挂起信号:已发送但还没被接收的信号被称为挂起信号,任何时候一个进程仅存在给定类型的一个挂起信号,统一进程同样类型的其他信号不被排队,只被简单丢弃。

    给挂起信号保留不可预知的时间要考虑以下因素:

    1.信号通常制备当前正在运行的进程接收

    2.给定类的信号可以由进程选择性的阻塞,此时,在取消这个阻塞前进程将不接收这个信号

    3.当进程执行一个信号处理函数时,通常“屏蔽”相应的信号,即自动阻塞这个信号直到这个处理程序结束。因此已处理的信号另一次出现不能中断信号处理程序。

    内核实现信号处理:

    1.记住每个进程阻塞哪些信号

    2.当从内核状态切换到用户态时,对任何一个进程都要检查释放由信号已经到达,这几乎在每个定时中断时都发生。

    3.确定是否可以忽略该信号,以下条件须同时满足:

     3.1目标进程没有被另一个进程跟踪

    3.2这个信号没有被日志进程阻塞

    3.3这个信号被目标进程忽略

    4.处理信号,这个信号可能在进程运行器间的任一时刻请求把进程切换到一个信号处理函数,并在这个函数返回以后恢复原来执行的上下文。

    第十章  进程调度

    schedule():在运行队列链表中找到一个进程,然后把cpu分配给它。

    1.直接调用:current进程所需的资源无法得到满足而必须被立即阻塞时,直接调度调用程序:

    1.1把current插入到合适的等待队列

    1.2把current当前状态改变为task_interruptible或task_uninterruptible

    1.3调用schedule()

    1.4检查那个资源是否可用,如果不可用,直接转1.2

    1.5一旦那个资源是可以用的,把current从等待队列中删除

    2.松散调用,通过把current的head_resched域设置为1,以松散方式调用调度程序。

    松散调用在下列情况被执行:

    2.1 当current用完它的cpu时间片,通过update_process_times()进行。

    2.2 当一个进程被唤醒,并且它的优先级高于current进程的优先级

    2.3当发出一个sched_SetScheduler()或者sched_yield()系统调用时

     

    第十一章  内核同步

    可用把内核看作是一个不断对请求进行响应的服务器,这些请求可能来自正在cpu上执行的进程,也可能来自正在执行中断请求的外部设备。(内核并不是严格按照顺序执行的,而是采用交错执行的方式)

    cpu交错执行和控制路径的情况:

    1.发生上下文切换

    2.中断发生在当cpu正在执行开中断的内核控制路径时。在这种情况下,第一个内核控制路径还没有完成,cpu就开始执行另一个内核控制路径来处理这个中断

    同步技术:

    1.内核态进程的非抢占式

    linux内核是非抢占式的,当正在运行的进程还处于内核态时,不会被抢占。

    对于系统调用所启用的其他控制路径来说,处理非阻塞的系统调用的内核控制路径是原子的,简化了很多内核函数的实现:不是由中断或者异常处理程序更新的内核数据结构都可以很安全的被访问。然而,如果处于内核态的进程主动放弃cpu,那么它必须保证剩下的所有数据结构处于一致状态。此外,进程恢复执行时,必须对以前访问过的可能被改变的所有数据结构进行重新检测。

    2.原子操作

    是避免竞争条件最简单的方法。操作必须在单独一条指令内执行。

    3.关中断

    确保内核语句序列可以作为一个临界区进行操作的一种主要机制。即使在硬件设备发出IRQ(中断请求)信号,观众段页允许内核控制路径继续执行来保护中断处理程序也会访问数据结构。

    4.加锁

    linux提供的两种锁:内核信号量,自旋锁

    当内核控制路径试图获得由内核信号量所保护的一个繁忙的资源时,相应的进程被挂起,直到该资源被释放时这个进程才变为可运行的。

     

    第十二章  虚拟文件系统vfs

    文件对象

    描述进程怎样与一个打开文件交互的过程。文件对象是在文件被打开时创建的,由一个file结构组成。存放在文件对象中的主要信息对象是文件指针,即文件中当前操作的位置。

    第十三章  管理IO设备

    总线:数据总线,地址总线,控制总线

    当总线连接的是cpu和io设备时,称为IO总线。

    监控IO操作:

    1.轮询模式

    cpu重复检查设备的状态寄存器,直到寄存器的值表明IO操作已经完成

    2.中断模式

    如果IO控制器能够通过IRO线发出IO操作结束信号,中断方式才被利用

    访问IOG共享内存

    内核驱动程序把IO共享内存单元的物理地址转换称内核空间的线性地址。(内核程序作用在线性地址上)

     

    第十四章  磁盘高速缓存

     

     

     

     

     

     

     

     

    展开全文
  • Linux 内核深度剖析与实践

    千次阅读 2019-07-05 10:04:12
    课程简介 Linux 作为最成功的开源项目,无论是在客户端...然后对内核的设备模型机制进行讲解,让读者理解 Linux 驱动的来龙去脉;接着手把手和读者一起定制一个开发板实践案例;最后和读者分享在工作中常用的调试...
  • 非常好的Linux编译内核详解 - -

    万次阅读 2018-03-21 15:58:36
    转载: http://blog.chinaunix.net/uid-263488-id-2138150.html非常好的Linux编译内核详解 - -一、内核... Linux的一个重要的特点就是其源代码的公开性,所有的内核源程序都可以在/usr/src/linux下找到,大部分...
  • Linux内核的整体架构简介

    万次阅读 多人点赞 2017-10-23 17:46:51
    本文是“Linux内核分析”系列文章的第一篇,会以内核的核心功能为出发点,描述Linux内核的整体架构,以及架构之下主要的软件子系统。之后,会介绍Linux内核源文件的目录结构,并和各个软件子系统对应。 注:本文和...
  • Linux配置并编译内核

    万次阅读 2018-08-03 16:31:09
     配置内核代码并不会花费太长时间。配置工具会询问许多问题并且允许开发者配置内核的每个方面。如果你有不确定的问题或者特性,你最好使用配置工具提供的默认值。本系列教程会使读者逐步了解配置内核的整个过程。 ...
  • Linux内核简介

    万次阅读 多人点赞 2018-06-16 18:56:29
    大纲:理解Linux内核最好预备的知识点Linux内核的特点Linux内核的任务内核的组成部分哪些地方用到了内核机制?Linux进程 Linux创建新进程的机制Linux线程 内核线程地址空间与特权级别 虚拟地址与物理地址 特权级别...
  • Linux内核组成部分(一)

    千次阅读 2019-01-11 10:31:38
    内核的任务 在纯技术层面上,内核是硬件与软件之间的一个中间层。其作用是将应用程序的请求传递给硬件,并充当底层驱动程序,对系统中的各种设备和组件进行寻址 从应用程序的视角来看,内核可以被认为是一台增强的...
  • 3---Linux内核及内核编程

    千次阅读 2018-12-21 10:59:06
    什么是操作系统? 指在系统中负责完成最基本功能和系统管理的部分, 操作系统有哪些组成部分?...Linux内核源代码目录结构是什么,各目录有什么含义? arch:包含和硬件体系结构相关的代码,每种平台占一...
  • Linux的用户空间与内核空间

    万次阅读 2018-09-03 10:51:23
    Linux 操作系统和驱动程序运行在内核空间,应用程序运行在用户空间。两者不能简单地使用指针传递数据,因为Linux使用的虚拟内存机制,用户空间的数据可能被换出,当内核空间使用用户空间指针时,对应的数据可能不在...
  • Linux内核源码阅读以及工具(转)

    万次阅读 2020-01-21 18:26:01
    Linux内核源码阅读以及工具(转) 转载地址:Linux内核源码阅读以及工具(转)
  • 查看Linux内核版本命令

    万次阅读 2020-08-17 16:35:55
    1.umane -a 2.cat /proc/version 3.lsb_release -a
  • 如何查看linux内核版本

    万次阅读 2018-04-09 09:50:56
    第一种:登录linux,在终端输入 cat /proc/version 运行效果如下图: 第二种:登录linux,在终端输入 uname -a 即列出linux内核版本号 运行效果如下图: 第三种:在Linux终端输入 unmae -a 即可查看linux的...
  • Linux内核下载地址(官方网站)

    万次阅读 2018-03-10 09:25:38
    https://www.kernel.org/pub/linux/kernel/
  • linux内核源码下载地址

    万次阅读 多人点赞 2018-08-25 22:36:38
    官网链接: https://www.kernel.org/ HTTP ... GIT ... 官网下载经常速度太慢,无法下载,提供另一个链接: http://ftp.sjtu.edu.cn/sites/ftp.kernel.org/pub/linux/kernel/ 可以根据需要,下...
  • Linux内核国内镜像下载地址

    万次阅读 2019-08-02 17:15:44
    http://mirror.bjtu.edu.cn/kernel/linux/kernel/ 苦于官方网站的访问速度,这里记录一下。
  • 删除Ubuntu中不用的内核

    万次阅读 2018-03-20 19:42:37
    最近在学习Linux相关的知识,免不了查看Linux源代码和修改源代码,并且编译Linux内核。下面简单介绍一下如何删除Ubuntu中不用的内核。1、查看Linux中当前使用的内核:使用 uname -a 或者 uname -r 命令2、查看Linux...
  • Linux内核学习笔记(1)—— 什么是Linux内核? 1. 什么是操作系统? 操作系统是负责完成整个系统中最基本功能和系统管理,包括内核、设备驱动程序、启动引导程序、命令行shell或其它种类的用户界面、基本的文件...
1 2 3 4 5 ... 20
收藏数 668,762
精华内容 267,504
关键字:

linux内核