Linux源代码分析_存储管理

合集下载

Linux 操作系统内核基本实验

Linux 操作系统内核基本实验
1. 以超级用户身份编程,计算某一时段中所有程序平均运行时间。
2. 通过编写 shell 程序,了解子进程的创建和父进程与子进程间的协同,获得 多进程程序的编程经验。
1.2.4 第 4 组 存储管理
实验 4.1 观察实验 1. 在 Linux 下,使用 gdb 程序观察一个程序文件的内容和结构。启动该程序 执行,再用 GDB 观察其内存映象的内容和结构。 2. 在 Linux 下,用 free 和 vmstat 命令观察内存使用情况。 3. 在 Linux 下,查看/proc 与内存管理相关的文件,并解释显示结果。 4. 在 Linux 下,用 malloc()函数实现 cat 或 copy 命令。
2. 系统安装实验
2.1 实验 1.1 Linux 系统安装
1、实验目的
从 CD-ROM 安装 Red Hat Linux 操作系统,如 Red Hat Linux7.2,建立后续各个实验的 运行环境。
2、实验内容(以 Red Hat Linux7.2 为例)
Red Hat Linux7.2 安装光盘共有两张,第一张可直接从光盘启动,包含大部分的软件包 和一些安装工具。第二张光盘包含许多附加软件包。以下为安装过程和注意事项。 (1)启动安装程序。用 Linux 的第一张光盘,从光驱引导启动程序,进入启动界面,显示 提示符 ”boot: ”,选择图形模式进行安装。 (2)选择安装界面的使用语言 (3)选择默认的键盘设置 (4)选择默认的鼠标设置 (5)选择安装类型。Red Hat Linux 提供了个人桌面、工作站、服务器和定制等多种安装类
了解 Linux 的设备驱动程序的组织结构和设备管理机制,编写简单的字符设 备和块设备驱动程序。
1.2.7 第 7 组 文件系统管理

Linux 0.1.1文件系统的源码阅读

Linux 0.1.1文件系统的源码阅读

Linux 0.11文件系统的源码阅读总结1.minix文件系统对于linux 0.11内核的文件系统的开发,Linus主要参考了Andrew S.Tanenbaum 所写的《MINIX操作系统设计与实现》,使用的是其中的1.0版本的MINIX文件系统。

而高速缓冲区的工作原理参见M.J.Bach的《UNIX操作系统设计》第三章内容。

通过对源代码的分析,我们可以将minix文件系统分为四个部分,如下如1-1。

●高速缓冲区的管理程序。

主要实现了对硬盘等块设备进行数据高速存取的函数。

●文件系统的底层通用函数。

包括文件索引节点的管理、磁盘数据块的分配和释放以及文件名与i节点的转换算法。

●有关对文件中的数据进行读写操作的函数。

包括字符设备、块设备、管道、常规文件的读写操作,由read_write.c函数进行总调度。

●涉及到文件的系统调用接口的实现,这里主要涉及文件的打开、关闭、创建以及文件目录等系统调用,分布在namei和inode等文件中。

图1-1 文件系统四部分之间关系图1.1超级块首先我们了解一下MINIX文件系统的组成,主要包括六部分。

对于一个360K软盘,其各部分的分布如下图1-2所示:图 1-2 建有MINIX文件系统的一个360K软盘中文件系统各部分的布局示意图注释1:硬盘的一个扇区是512B,而文件系统的数据块正好是两个扇区。

注释2:引导块是计算机自动加电启动时可由ROM BIOS自动读入得执行代码和数据。

注释3:逻辑块一般是数据块的2幂次方倍数。

MINIX文件系统的逻辑块和数据块同等大小对于硬盘块设备,通常会划分几个分区,每个分区所存放的不同的文件系统。

硬盘的第一个扇区是主引导扇区,其中存放着硬盘引导程序和分区表信息。

分区表中得信息指明了硬盘上每个分区的类型、在硬盘中其实位置参数和结束位置参数以及占用的扇区总数。

其结构如下图1-3所示。

图1-3 硬盘设备上的分区和文件系统对于可以建立不同的多个文件系统的硬盘设备来说,minix文件系统引入超级块进行管理硬盘的文件系统结构信息。

Linux操作系统的内核设计分析

Linux操作系统的内核设计分析

Linux操作系统的内核设计分析Linux操作系统作为开源操作系统的代表,已经在各个领域得到了广泛应用。

而Linux操作系统的内核则是这个系统之所以能够运转的关键所在。

本文将就Linux操作系统的内核设计进行分析,并探讨其优劣之处。

一、Linux内核设计的基础Linux内核的设计基础主要包括以下几个方面:1. 开放源码Linux内核采用的是GPL协议,这意味着它是一个开放源码的项目。

这为世界各地的开发人员提供了极大的便利,方便他们进行开发和修改。

同时,这也确保了Linux内核的透明度,并且鼓励开发者贡献代码的同时,深度参与到Linux开源社区的构建和升级中。

2. 模块化Linux内核的构造采用的是模块化设计。

这种设计方式将内核代码分成独立的模块,每个模块都可以独立编译、加载和卸载。

采用模块化的设计,能够使得开发人员能够更加细致地打包、编译、并部署只包含他们需要的模块的系统。

3. 多任务Linux内核是一个基于多任务设计的系统。

这意味着它能够使得多个程序同时运行,并能够平滑高效地进行任务的切换。

这给开发人员提供了各种各样的自由,使得他们能够更加高效地进行开发。

4. 支持众多处理器架构Linux内核的支持范围非常广泛,它可以适配众多处理器架构。

这意味着一个制造商可以使用不同的处理器架构去生产设备,并且这些设备都能够安装和运行Linux操作系统。

5. 外层调用接口Linux内核支持开放式的外层调用接口。

这使得用户层可以很容易地调用Linux 内核执行某个任务。

这些用户层应用包括网上购物网站、应用程序和各种驱动程序。

6. 子系统Linux内核的子系统主要包括进程管理、内存管理、I/O管理和网络管理等。

二、Linux内核的优点Linux内核具有以下主要优点:1. 开源性Linux内核本身是一个开源的、由社区驱动的项目。

这意味着在它的附加组件和周边产品中,广大的开发者社区都可以为用户提供帮助和支持。

2. 安全性相比其他闭源操作系统,Linux内核在安全性方面更具优势。

linux内核源码分析-nvme设备的初始化

linux内核源码分析-nvme设备的初始化

linux内核源码分析-nvme设备的初始化本⽂基于3.18.3内核的分析,nvme设备为pcie接⼝的ssd,其驱动名称为nvme.ko,驱动代码在drivers/block/nvme-core.c.驱动的加载 驱动加载实际就是module的加载,⽽module加载时会对整个module进⾏初始化,nvme驱动的module初始化函数为nvme_init(),如下:static struct pci_driver nvme_driver = {.name = "nvme",.id_table = nvme_id_table,.probe = nvme_probe,.remove = nvme_remove,.shutdown = nvme_shutdown,.driver = {.pm = &nvme_dev_pm_ops,},.err_handler = &nvme_err_handler,};static int __init nvme_init(void){int result;/* 初始化等待队列nvme_kthread_wait,此等待队列⽤于创建nvme_kthread(只允许单进程创建nvme_kthread) */init_waitqueue_head(&nvme_kthread_wait);/* 创建⼀个workqueue叫nvme */nvme_workq = create_singlethread_workqueue("nvme");if (!nvme_workq)return -ENOMEM;/* 在内核中注册新的⼀类块设备驱动,名字叫nvme,注意这⾥只是注册,表⽰kernel⽀持了nvme类的块设备,返回⼀个major,之后所有的nvme设备的major都是此值 */result = register_blkdev(nvme_major, "nvme");if (result < 0)goto kill_workq;else if (result > 0)nvme_major = result;/* 注册⼀些通知信息 */nvme_nb.notifier_call = &nvme_cpu_notify;result = register_hotcpu_notifier(&nvme_nb);if (result)goto unregister_blkdev;/* 注册pci nvme驱动 */result = pci_register_driver(&nvme_driver);if (result)goto unregister_hotcpu;return0;unregister_hotcpu:unregister_hotcpu_notifier(&nvme_nb);unregister_blkdev:unregister_blkdev(nvme_major, "nvme");kill_workq:destroy_workqueue(nvme_workq);return result;} 这⾥⾯其实最重要的就是做了两件事,⼀件事是register_blkdev,注册nvme这类块设备,返回⼀个major,另⼀件事是注册了nvme_driver,注册了nvme_driver后,当有nvme设备插⼊后系统后,系统会⾃动调⽤nvme_driver->nvme_probe去初始化这个nvme设备.这时候可能会有疑问,系统是如何知道插⼊的设备是nvme设备的呢,注意看struct pci_driver nvme_driver这个结构体,⾥⾯有⼀个nvme_id_table,其内容如下:/* Move to pci_ids.h later */#define PCI_CLASS_STORAGE_EXPRESS 0x010802static const struct pci_device_id nvme_id_table[] = {{ PCI_DEVICE_CLASS(PCI_CLASS_STORAGE_EXPRESS, 0xffffff) },{ 0, }};再看看PCI_DEVICE_CLASS宏是如何定义的#define PCI_DEVICE_CLASS(dev_class,dev_class_mask) \.class = (dev_class), .class_mask = (dev_class_mask), \.vendor = PCI_ANY_ID, .device = PCI_ANY_ID, \.subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID也就是当pci class为PCI_CLASS_STORAGE_EXPRESS时,就表⽰是nvme设备,并且这个是写在设备⾥的,当设备插⼊host时,pci driver(并不是nvme driver)回去读取这个值,然后判断它需要哪个驱动去做处理.nvme数据结构 现在假设nvme.ko已经加载完了(注册了nvme类块设备,并且注册了nvme driver),这时候如果有nvme盘插⼊pcie插槽,pci会⾃动识别到,并交给nvme driver去处理,⽽nvme driver就是调⽤nvme_probe去处理这个新加⼊的设备. 在说nvme_probe之前,先说⼀下nvme设备的数据结构,⾸先,内核使⽤⼀个nvme_dev结构体来描述⼀个nvme设备, ⼀个nvme设备对应⼀个nvme_dev,nvme_dev如下:/* nvme设备描述符,描述⼀个nvme设备 */struct nvme_dev {struct list_head node;/* 设备的queue,⼀个nvme设备⾄少有2个queue,⼀个admin queue,⼀个io queue,实际情况⼀般都是⼀个admin queue,多个io queue,并且io queue会与CPU做绑定 */ struct nvme_queue __rcu **queues;/* unsigned short的数组,每个CPU占⼀个,主要⽤于存放CPU上绑定的io queue的qid,⼀个CPU绑定⼀个queues,⼀个queues绑定到1到多个CPU上 */unsigned short __percpu *io_queue;/* ((void __iomem *)dev->bar) + 4096 */u32 __iomem *dbs;/* 此nvme设备对应的pci dev */struct pci_dev *pci_dev;/* dma池,主要是以4k为⼤⼩的dma块,⽤于dma分配 */struct dma_pool *prp_page_pool;/* 也是dma池,但是不是以4k为⼤⼩的,是⼩于4k时使⽤ */struct dma_pool *prp_small_pool;/* 实例的id,第⼀个加⼊的nvme dev,它的instance为0,第⼆个加⼊的nvme,instance为1,也⽤于做/dev/nvme%d的显⽰,%d实际就是instance的数值 */int instance;/* queue的数量, 等于admin queue + io queue */unsigned queue_count;/* 在线可以使⽤的queue数量,跟online cpu有关 */unsigned online_queues;/* 最⼤的queue id */unsigned max_qid;/* nvme queue⽀持的最⼤cmd数量,为((bar->cap) & 0xffff)或者1024的最⼩值 */int q_depth;/* 1 << (((bar->cap) >> 32) & 0xf),应该是每个io queue占⽤的bar空间 */u32 db_stride;/* 初始化设置的值* dev->ctrl_config = NVME_CC_ENABLE | NVME_CC_CSS_NVM;* dev->ctrl_config |= (PAGE_SHIFT - 12) << NVME_CC_MPS_SHIFT;* dev->ctrl_config |= NVME_CC_ARB_RR | NVME_CC_SHN_NONE;* dev->ctrl_config |= NVME_CC_IOSQES | NVME_CC_IOCQES;*/u32 ctrl_config;/* msix中断所使⽤的entry,指针表⽰会使⽤多个msix中断,使⽤的中断的个数与io queue对等,多少个io queue就会申请多少个中断* 并且让每个io queue的中断尽量分到不同的CPU上运⾏*/struct msix_entry *entry;/* bar的映射地址,默认是映射8192,当io queue过多时,有可能会⼤于8192 */struct nvme_bar __iomem *bar;/* 其实就是块设备,⼀张nvme卡有可能会有多个块设备 */struct list_head namespaces;/* 对应的在/sys下的结构 */struct kref kref;/* 对应的字符设备,⽤于ioctl操作 */struct miscdevice miscdev;/* 2个work,暂时还不知道什么⽤ */work_func_t reset_workfn;struct work_struct reset_work;struct work_struct cpu_work;/* 这个nvme设备的名字,为nvme%d */char name[12];/* SN号 */char serial[20];char model[40];char firmware_rev[8];/* 这些值都是从nvme盘上获取 */u32 max_hw_sectors;u32 stripe_size;u16 oncs;u16 abort_limit;u8 vwc;u8 initialized;}; 在nvme_dev结构中,最最重要的数据就是nvme_queue,struct nvme_queue⽤来表⽰⼀个nvme的queue,每⼀个nvme_queue会申请⾃⼰的中断,也有⾃⼰的中断处理函数,也就是每个nvme_queue在驱动层⾯是完全独⽴的.nvme_queue有两种,⼀种是admin queue,⼀种是io queue,这两种queue都⽤struct nvme_queue来描述,⽽这两种queue的区别如下:admin queue: ⽤于发送控制命令的queue,所有⾮io命令都会通过此queue发送给nvme设备,⼀个nvme设备只有⼀个admin queue,在nvme_dev中,使⽤queues[0]来描述.io queue: ⽤于发送io命令的queue,所有io命令都是通过此queue发送给nvme设备,简单来说读/写操作都是通过io queue发送给nvme设备的,⼀个nvme设备有⼀个或多个io queue,每个io queue的中断会绑定到不同的⼀个或多个CPU上.在nvme_dev中,使⽤queues[1~N]来描述. 以上说的io命令和⾮io命令都是nvme命令,⽐如快层下发⼀个写request,nvme驱动就会根据此request构造出⼀个写命令,将这个写命令放⼊某个io queue中,当controller完成了这个写命令后,会通过此io queue的中断返回完成信息,驱动再将此完成信息返回给块层.明⽩了两种队列的作⽤,我们看看具体的数据结构struct nvme_queue/* nvme的命令队列,其中包括sq和cq。

linux-0.11调试教程,mkfs.c源代码分析

linux-0.11调试教程,mkfs.c源代码分析

linux-0.11调试教程,mkfs.c源代码分析(1)下面是mkfs命令的一个例子mkfs /dev/hd6 60000结果:20000个inodes,60000个blocks,第一个数据块块号为638指导思想:不看源代码的话,格式化一个文件系统,应该改变的是:(1),文件系统的超级块信息,需要用户输入的块的总数算出i节点的个数和i节点位图块的个数和逻辑块位图块的个数及第一个数据块的块号。

setup_tables()函数完成这个任务。

(2),建立根目录,需要申请一个数据块,需要申请一个根目录对应的i节点,是文件系统中的第一个节点。

修改根目录对应的i节点在i节点位图中对应的位和根目录所在的块对应的逻辑块位图中的位。

make_root_inode();函数完成这个任务。

(3)源程序里还有第三个任务,就是统计磁盘分区中坏块的数目,并把所有的坏块看成一个坏块文件,这个坏块文件对应第二个节点。

make_bad_inode()函数完成这个任务。

思路分析:main()函数首先取得用户给出的块数放到BLOCKS里。

然后调用setup_tables()函数,setup_tables()函数的作用是根据块数算出i节点的总数既块数的三分之一。

然后算出i节点位图的块数和逻辑块位图的块数,还有第一个数据块的块号。

然后把逻辑块位图块清零(范围是数据区对应的位图既FIRSTZONE之后的块对应的位)和把i节点位图块清零(第一个位没有清零,第一个位对应根节点)。

然后初始化了i节点缓冲区。

最后打印出超级块信息。

区块的数目ZONES既块数BLOCKS为60000,INODES的数目为块数的三分之一既20000。

i节点位图的块数IMAPS为3。

#define NORM_FIRSTZONE (2+IMAPS+ZMAPS+INODE_BLOCKS)NORM_FIRSTZONE数目既数据区前面的块数。

ZMAPS = 0;while (ZMAPS != UPPER(BLOCKS - NORM_FIRSTZONE,BITS_PER_BLOCK))ZMAPS = UPPER(BLOCKS - NORM_FIRSTZONE,BITS_PER_BLOCK);逻辑块位图的块数ZMAPS为8。

uCLinux开发介绍

uCLinux开发介绍

uCLinux开发介绍严永红Linux是当前一种非常受欢迎的操作系统,它与UNIX系统兼容,并开放源代码。

它包含所有现代操作系统所具有的一切特性,包括多任务,虚拟内存,代码共享,按需载入,内存管理,以及TCP/IP网络。

并且,它遵循POSIX标准,只要是遵循POSIX API的应用程序很容易被移植。

目前,随着嵌入式系统的蓬勃发展。

Linux也已对嵌入式系统的开发产生具大影响。

大多数流行的CPU都被移植上去,ARM, PowerPC , MIPS, 68K, SPARC, Alpha, SH 等等. 这些CPU都含有一种叫做内存管理单元(MMU)的硬件,来支持标准Linux所需要的虚拟内存。

但在嵌入式世界里,还有很多CPU是没有MMU的,象ARM7、68328等等。

uClinux 正是为了解决这种没有MMU的CPU而产生的。

在uCLinux这个英文单词中,u表示Micro,小的意思,C表示Control,控制的意思,连起来就是Micro-Control-Linux, ―运行在微控制器上的Linux.‖针对这种没有MMU的CPU架构,uCLinux采用了一种平板式(Flat)的内存模型来去除对MMU的依赖, 并且改变了用户程序的加载方式,开发了运用于uCLinux的C函数库--uCLibc. 由于这些变化,一般的Linux开发工具(例如GDB)在开发uCLinux时会碰到一些困难,包括内核的移植,驱动程序及应用程序的调试。

针对这样状况。

Hitool System公司开发了Hitool for uClinux开发套件,来帮助用户开发基于uClinux的系统。

Hitool for uClinux与其它的Linux开发工具相比,有几个优点:A.整个开发过程只在Windows环境下完成,包括内核的配臵、编译,应用程序的编译,文件系统的生成,内核的调试,用户程序的调试。

B.可以采用多种调试方式,既可以采用JTAG方式来调试,也可通过网口用Hitool自己的监控程序(MDB)来调试。

用Source Insight打开linux内核源代码

用Source Insight打开linux内核源代码

用Source Insight打开linux内核源代码2008-01-09 19:06Linux的内核源代码可以从很多途径得到。

一般来讲,在安装的linux系统下,/usr/src/linux目录下的东西就是内核源代码。

另外还可以从互连网上下载,解压缩后文件一般也都位于linux目录下。

内核源代码有很多版本,目前最新的稳定版是2.2.14。

许多人对于阅读Linux内核有一种恐惧感,其实大可不必。

当然,象Linux内核这样大而复杂的系统代码,阅读起来确实有很多困难,但是也不象想象的那么高不可攀。

只要有恒心,困难都是可以克服的。

也不用担心水平不够的问题,事实上,有很多事情我们不都是从不会到会,边干边学的吗?任何事情做起来都需要有方法和工具。

正确的方法可以指导工作,良好的工具可以事半功倍。

对于Linux 内核源代码的阅读也同样如此。

下面我就把自己阅读内核源代码的一点经验介绍一下,最后介绍Window平台下的一种阅读工具。

对于源代码的阅读,要想比较顺利,事先最好对源代码的知识背景有一定的了解。

对于linux内核源代码来讲,我认为,基本要求是:1、操作系统的基本知识;2、对C语言比较熟悉,最好要有汇编语言的知识和GNU C对标准C的扩展的知识的了解。

另外在阅读之前,还应该知道Linux内核源代码的整体分布情况。

我们知道现代的操作系统一般由进程管理、内存管理、文件系统、驱动程序、网络等组成。

看一下Linux内核源代码就可看出,各个目录大致对应了这些方面。

Linux内核源代码的组成如下(假设相对于linux目录):arch 这个子目录包含了此核心源代码所支持的硬件体系结构相关的核心代码。

如对于X86平台就是i386。

include 这个目录包括了核心的大多数include文件。

另外对于每种支持的体系结构分别有一个子目录。

init 此目录包含核心启动代码。

mm 此目录包含了所有的内存管理代码。

与具体硬件体系结构相关的内存管理代码位于arch/*/mm目录下,如对应于X86的就是arch/i386/mm/fault.c 。

读书摘要观后感与总结:《Glibc内存管理:ptmalloc2源代码分析》

读书摘要观后感与总结:《Glibc内存管理:ptmalloc2源代码分析》

读书摘要观后感与总结:《Glibc内存管理:ptmalloc2源代码分析》更新中在Linux平台下做漏洞利⽤的时候,针对于Heap部分总是有些不求甚解,下⾯开个博⽂来记录下《Glibc内存管理:ptmalloc2源代码分析》这本书的读后感和收获,⼀些简单的点将不再记录说明,本博⽂中所有的实验均在Linux Ubuntu16.04的环境下进⾏⽬录树:⼀些关于计算size的宏"chunk to mem" and "mem to chunk"about size分箱式内存管理smallbinslargebins⼀些关于计算size的宏Ptmalloc设计的时候很巧妙的⼀点就是利⽤宏来屏蔽不同平台的差异,⼀些简单的细节⽐如chunk的形式在此我就不再赘述,下⾯记录⼀下读后有收获的点"chunk to mem" and "mem to chunk"/* conversion from malloc headers to user pointers, and back */#define chunk2mem(p) ((void*)((char*)(p) + 2*SIZE_SZ))#define mem2chunk(mem) ((mchunkptr)((char*)(mem) - 2*SIZE_SZ))about sizeMIN_CHUNK_SIZE定义了最⼩的chunk⼤⼩,MINSIZE定义了最⼩的分配的内存⼤⼩,是对MIN_CHUNK_SIZE进⾏了2*SIZE_SZ对齐,对齐后与MIN_CHUNK_SIZE的⼤⼩仍然是⼀样的/* The smallest possible chunk */#define MIN_CHUNK_SIZE (offsetof(struct malloc_chunk, fd_nextsize))/* The smallest size we can malloc is an aligned minimal chunk */#define MINSIZE \(unsigned long)(((MIN_CHUNK_SIZE+MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK))下⾯说明⼀下chunk是如何计算其size的/* size field is or'ed with PREV_INUSE when previous adjacent chunk in use */#define PREV_INUSE 0x1/* extract inuse bit of previous chunk */#define prev_inuse(p) ((p)->mchunk_size & PREV_INUSE)/* size field is or'ed with IS_MMAPPED if the chunk was obtained with mmap() */#define IS_MMAPPED 0x2/* check for mmap()'ed chunk */#define chunk_is_mmapped(p) ((p)->mchunk_size & IS_MMAPPED)/* size field is or'ed with NON_MAIN_ARENA if the chunk was obtainedfrom a non-main arena. This is only set immediately before handingthe chunk to the user, if necessary. */#define NON_MAIN_ARENA 0x4#define SIZE_BITS (PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA)/* Like chunksize, but do not mask SIZE_BITS. */#define chunksize_nomask(p) ((p)->mchunk_size)/* Get size, ignoring use bits */#define chunksize(p) (chunksize_nomask (p) & ~(SIZE_BITS))/* Ptr to next physical malloc_chunk. */#define next_chunk(p) ((mchunkptr) (((char *) (p)) + chunksize (p)))/* Size of the chunk below P. Only valid if !prev_inuse (P). */#define prev_size(p) ((p)->mchunk_prev_size)⽐如做个实验来验证下,我们的chunksize为0x71,那么它本⾝的真实size是如何计算的?根据宏定义来计算可以看到计算得出的结果显然正确下⾯这⼀组宏定义⽤来check/set/clear当前chunk使⽤标志位,有当前chunk的使⽤标志位存储在下⼀个chunk的size的P位,所以下⾯的宏都要⾸先算出来下⼀个chunk的地址然后再做处理/* extract p's inuse bit */#define inuse(p) \((((mchunkptr) (((char *) (p)) + chunksize (p)))->mchunk_size) & PREV_INUSE)/* set/clear chunk as being inuse without otherwise disturbing */#define set_inuse(p) \((mchunkptr) (((char *) (p)) + chunksize (p)))->mchunk_size |= PREV_INUSE#define clear_inuse(p) \((mchunkptr) (((char *) (p)) + chunksize (p)))->mchunk_size &= ~(PREV_INUSE)我们可以简单来实验⼀下define inuse(p) 定义p的inusedefine set_inuse(p) 设置p的inuse位(p的nextchuhnk来设置)define clear_inuse(p) 清理p的inuse位下⾯三个宏⽤来check/set/clear指定chunk的size域中的使⽤标志位/* check/set/clear inuse bits in known places */#define inuse_bit_at_offset(p, s) \(((mchunkptr) (((char *) (p)) + (s)))->mchunk_size & PREV_INUSE)#define set_inuse_bit_at_offset(p, s) \(((mchunkptr) (((char *) (p)) + (s)))->mchunk_size |= PREV_INUSE)#define clear_inuse_bit_at_offset(p, s) \(((mchunkptr) (((char *) (p)) + (s)))->mchunk_size &= ~(PREV_INUSE))分箱式内存管理smallbinssmallbins有64个bin,实际共62个bin,bin[0]和bin[1]不存在chunk_size = 2 * SIZE_SZ * index范围:16B-504B (32B-1008B)ptmalloc维护了62个双向环形链表,每个链表都有头节点,便于管理,每个链表内各个空闲的chunk的⼤⼩⼀致largebins32:⼤于等于512B64:⼤于等于1024B⼀共63个bins每个bin中的chunk⼤⼩不是⼀个固定公差的等差数列,⽽是分成6组bin,每组bin是⼀个固定公差的等差数列每组的bin数量依次为:32,16, 8, 4, 2, 1公差依次为: 64,512,4096,32768,262144可以⽤数学来描述计算largebins的chunk_size第⼀组:chunksize = 512 + 64 * index第⼆组:chunksize = 512 + 64 * 32 + 512 * index……可以看到,其实smallbins和largebins差不多满⾜同样的规律,所以可以将small bins和large bins放在同⼀个包含128个chunk的数组上,数组前⼀部分为small bins,后⼀部分为large bins。

怎样读Linux内核源代码

怎样读Linux内核源代码

Linux内核分析方法2010-9-12Linux的最大的好处之一就是它的源码公开。

同时,公开的核心源码也吸引着无数的电脑爱好者和程序员;他们把解读和分析Linux的核心源码作为自己的最大兴趣,把修改Linux 源码和改造Linux系统作为自己对计算机技术追求的最大目标。

Linux内核源码是很具吸引力的,特别是当你弄懂了一个分析了好久都没搞懂的问题;或者是被你修改过了的内核,顺利通过编译,一切运行正常的时候。

那种成就感真是油然而生!而且,对内核的分析,除了出自对技术的狂热追求之外,这种令人生畏的劳动所带来的回报也是非常令人着迷的,这也正是它拥有众多追随者的主要原因:•首先,你可以从中学到很多的计算机的底层知识,如后面将讲到的系统的引导和硬件提供的中断机制等;其它,象虚拟存储的实现机制,多任务机制,系统保护机制等等,这些都是非都源码不能体会的。

等等,这些都是非读源码不能体会的。

•同时,你还将从操作系统的整体结构中,体会整体设计在软件设计中的份量和作用,以及一些宏观设计的方法和技巧:Linux的内核为上层应用提供一个与具体硬件不相关的平台;同时在内核内部,它又把代码分为与体系结构和硬件相关的部分,和可移植的部分;再例如,Linux虽然不是微内核的,但他把大部分的设备驱动处理成相对独立的内核模块,这样减小了内核运行的开销,增强了内核代码的模块独立性。

•而且你还能从对内核源码的分析中,体会到它在解决某个具体细节问题时,方法的巧妙:如后面将分析到了的Linux通过Botoom_half机制来加快系统对中断的处理。

•最重要的是:在源码的分析过程中,你将会被一点一点地、潜移默化地专业化。

一个专业的程序员,总是把代码的清晰性,兼容性,可移植性放在很重要的位置。

他们总是通过定义大量的宏,来增强代码的清晰度和可读性,而又不增加编译后的代码长度和代码的运行效率;他们总是在编码的同时,就考虑到了以后的代码维护和升级。

甚至,只要分析百分之一的代码后,你就会深刻地体会到,什么样的代码才是一个专业的程序员写的,什么样的代码是一个业余爱好者写的。

linux,ip协议栈源代码分析,pdf

linux,ip协议栈源代码分析,pdf

竭诚为您提供优质文档/双击可除linux,ip协议栈源代码分析,pdf篇一:netfilter源代码分析详解一、概述filter/iptables框架简介netfilter/iptables是继2.0.x的ipfwadm、2.2.x的ipchains之后,新一代的linux防火墙机制。

netfilter采用模块化设计,具有良好的可扩充性。

其重要工具模块iptables连接到netfilter的架构中,并允许使用者对数据报进行过滤、地址转换、处理等操作。

netfilter提供了一个框架,将对网络代码的直接干涉降到最低,并允许用规定的接口将其他包处理代码以模块的形式添加到内核中,具有极强的灵活性。

2.主要源代码文件linux内核版本:2.4.21netfilter主文件:net/core/netfilter.cnetfilter主头文件:include/linux/netfilter.hipv4相关:c文件:net/ipv4/netfilter/*.c头文件:include/linux/netfilter_ipv4.hinclude/linux/netfilter_ipv4/*.hipv4协议栈主体的部分c文件,特别是与数据报传送过程有关的部分:ip_input.c,ip_forward.c,ip_output.c,ip_fragment.c等二、netfilter/iptables-ipv4总体架构netfilter主要通过表、链实现规则,可以这么说,netfilter是表的容器,表是链的容器,链是规则的容器,最终形成对数据报处理规则的实现。

详细地说,netfilter/iptables的体系结构可以分为三个大部分:filter的hook机制netfilter的通用框架不依赖于具体的协议,而是为每种网络协议定义一套hook函数。

这些hook函数在数据报经过协议栈的几个关键点时被调用,在这几个点中,协议栈将数据报及hook函数标号作为参数,传递给netfilter框架。

Linux 源代码分析

Linux 源代码分析

Linux内核(2.6.13.2)源代码分析苗彦超摘要:1系统启动1.1汇编代码head.S及以前设置CPU状态初值,创建进程0,建立进程堆栈:movq init_rsp(%rip), %rsp,init_rsp定义.globl init_rspinit_rsp:.quad init_thread_union+THREAD_SIZE-8即将虚地址init_thread_union+THREAD_SIZE-8作为当前进程(进程0)核心空间堆栈栈底,init_thread_union定义于文件arch/x86_64/kernel/init_task.c中:union thread_union init_thread_union __attribute__((__section__(".data.init_task"))) ={INIT_THREAD_INFO(init_task)};INIT_THREAD_INFO定义于文件include/asm-x86_64/thread_info.h中,初始化init_thread_union.task = &init_task,init_task同样定义于文件init_task.c中,初始化为:struct task_struct init_task = INIT_TASK(init_task);INIT_TASK宏在include/linux/init_task.h中定义。

全部利用编译时静态设置的初值,将进程0的控制结构设置完成,使进程0可以按普通核心进程访问。

init_task.mm = NULL; init_task.active_mm = INIT_MM(init_mm), init_m = “swapper”INIT_MM将init_mm.pgd初始化为swapper_pg_dir,即init_level4_pgt,定义与head.S中。

北邮操作系统进程管理实验报告及源代码

北邮操作系统进程管理实验报告及源代码

进程管理实验报告1. 实验目的:(1)加深对进程概念的理解, 明确进程和程序的区别;(2)进一步认识并发执行的实质;(3)分析进程争用资源的现象, 学习解决进程互斥的方法;(4)了解Linux系统中进程通信的基本原理。

2. 实验预备内容(1)阅读Linux的sched.h源码文件, 加深对进程管理概念的理解;(2)阅读Linux的fork()源码文件, 分析进程的创建过程。

3.环境说明本次实验使用的是win7下的VMWare workstation虚拟机, 安装了ubuntu系统在ubuntu系统下使用code::blocks IDE编写代码并执行程序的4.实验内容:1.进程的创建:(1)实验题目和要求:编写一段程序, 使用系统调用fork() 创建两个子进程。

当此程序运行时, 在系统中有一个父进程和两个子进程活动。

让每一个进程在屏幕上显示一个字符:父进程显示字符“a”, 子进程分别显示字符“b”和“c”。

试观察记录屏幕上的显示结果, 并分析原因。

(2)程序设计说明:参照书上的例子进行设计, 详见源代码(3)程序运行结果截图:(4)程序分析:a,b,c随机出现, 因为父进程与两个子进程之间并没有同步措施, 所以a,b,c随机打印出来, 也就是三个进程的活动次序是随机进行的, 不同的系统可能有不同的进程调度方式。

(5)源程序:#include<sys/types.h>#include<stdio.h>#include<unistd.h>int main(){pid_t pid1,pid2;if((pid1=fork())<0){printf("Fork Failed.\n");exit(-1);}else if((pid1=fork())==0)printf("b\n");else{if((pid2=fork())<0){printf("Fork Failed.\n");exit(-1);}else if((pid2=fork())==0)printf("c\n");else{wait(NULL);printf("a\n");exit(0);}}return 0;}2.进程的控制:要求一:(1)实验题目和要求:修改已经编写的程序, 将每个进程输出一个字符改为每个进程输出一句话, 再观察程序执行时屏幕上出现的现象, 并分析原因。

操作系统课内实验报告

操作系统课内实验报告

.. 西安交通大学实验报告操作系统实验报告2130505133计算机36班操作系统实验实验一:用户接口实验实验目的1)理解面向操作命令的接口Shell。

2)学会简单的shell编码。

3)理解操作系统调用的运行机制。

4)掌握创建系统调用的方法。

操作系统给用户提供了命令接口和程序接口(系统调用)两种操作方式。

用户接口实验也因此而分为两大部分。

首先要熟悉Linux的基本操作命令,并在此基础上学会简单的shell 编程方法。

然后通过想Linux内核添加一个自己设计的系统调用,来理解系统调用的实现方法和运行机制。

在本次实验中,最具有吸引力的地方是:通过内核编译,将一组源代码变成操作系统的内核,并由此重新引导系统,这对我们初步了解操作系统的生成过程极为有利。

实验内容1)控制台命令接口实验该实验是通过“几种操作系统的控制台命令”、“终端处理程序”、“命令解释程序”和“Linux操作系统的bash”来让实验者理解面向操作命令的接口shell和进行简单的shell编程。

➢查看bash版本。

在shell 提示符下输入:$echo $BASH_VERSION我们的版本是4.3.42(1)-release(2)建立bash 脚本,输出Hello word在编辑器中输入以下内容#!/bin/bashecho Hello World!执行脚本使用指令:$./script➢编写bash脚本,统计/my目录下c语言文件的个数通过bash 脚本,可以有多种方式实现这个功能,而使用函数是其中个一个选择。

在使用函数之前,必须先定义函数。

进入自己的工作目录,编写名为count 的文件脚本程序:#! /bin/bashfunction count{echo –n " Number of matches for $1: " #接收程序的第一个参数ls $1|wc –l #对子程序的第一个参数所在的目录进行操作}将count 文件复制到当前目录下,然后在当前目录下建立文件夹,在my 目录下建立几个c 文件,以便用来进行测试2)系统调用实验该实验是通过实验者对“Linux操作系统的系统调用机制”的进一步了解来理解操作系统调用的运行机制;同时通过“自己创建一个系统调用mycall()”和“编程调用自己创建的系统调用”进一步掌握创建和调用系统调用的方法。

linux_gmtime_源代码简析_概述及解释说明

linux_gmtime_源代码简析_概述及解释说明

linux gmtime 源代码简析概述及解释说明1. 引言1.1 概述本文将详细解析和说明Linux操作系统中的gmtime函数的源代码。

gmtime 是一个十分重要的时间处理函数,在Linux系统中被广泛应用于时间管理和日期处理领域。

通过深入研究gmtime函数的源代码,我们可以更好地理解其原理和功能,从而能够更有效地使用这一函数。

1.2 文章结构本文共分为五个部分来展开对gmtime函数的源代码简析。

首先,引言部分对本文进行了概述,介绍了文章目录和主要内容。

接下来,第二部分将介绍gmtime 函数的基本概念和功能,并深入解读其源代码。

第三部分则探讨了时间处理在Linux系统中的重要性,以及gmtime在时间处理中的具体应用场景。

第四部分将对gmtime函数的源代码进行详尽分析与解释,并分享常见问题的解决方案。

最后,在第五部分中我们将总结已掌握的知识点并思考收获,并展望如何优化和扩展gmtime源代码。

1.3 目的本文旨在帮助读者全面理解并掌握Linux操作系统中gmtime函数的工作原理和应用场景。

通过详细剖析其源代码,并提供使用示例和常见问题解答,读者将能够更加熟练地运用gmtime函数进行时间处理和日期管理。

此外,本文还希望为读者提供关于gmtime源代码优化和扩展的展望,并激发读者对Linux操作系统中时间处理相关领域的兴趣。

2. gmtime函数简介:2.1 gmtime概述:gmtime函数是一个时间处理函数,它被用来将给定的时间戳(秒数)转换为一个结构体,该结构体包含了年、月、日、时、分、秒等具体的时间信息。

它返回的结构体是一个tm类型的对象,tm类型定义在<time.h>头文件中。

2.2 gmtime源代码解读:gmtime函数的源代码位于GNU C库的源码中,这个库提供了很多标准C库的实现,包括时间处理相关的函数。

通过查阅GNU C库的源码可以对gmtime函数进行详细解读。

[重点]linux源码分析

[重点]linux源码分析

[重点]linux源码分析linux源码分析Linux内核源代码中的C语言代码Linux 内核的主体是以 GNU的 C 语言编写的,GNU为此提供了编译工具gcc。

GNU对 C 语言本身(在 ANSI C 基础上)做了不少扩充,可能是读者尚未见到过的。

另一方面,由于内核代码,往往会用到一些在应用程序设计中不常见的语言成分或编程技巧,也许使读者感到陌生。

本书并非介绍 GNU C语言的专著,也非技术手册,所以不在这里一一列举和详细讨论这些扩充和技巧。

再说,离开具体的情景和上下文,罗列一大堆规则,对于读者恐怕也没有多大帮助。

所以,我们在这里只是对可能会影响读者阅读 Linux 内核源程序,或使读者感到困惑的一些扩充和技巧先作一些简单的介绍。

以后,随着具体的情景和代码的展开,在需要时还会结合实际加以补充。

首先,gcc 从 C++语言中吸收了“inline”和“const”。

其实,GNU 的 C 和C++是合为一体的,gcc既是 C 编译又是 C++编译,所以从 C++中吸收一些东西到 C 中是很自然的。

从功能上说,inline 函数的使用与#define 宏定义相似,但更有相对的独立性,也更安全。

使用 inline函数也有利于程序调试。

如果编译时不加优化,则这些inline 就是普通的、独立的函数,更便于调试。

调试好了以后,再采用优化重新编译一次,这些 inline函数就像宏操作一样融入了引用处的代码中,有利于提高运行效率。

由于 inline 函数的大量使用,相当一部分的代码从.c 文件移入了.h 文件中。

还有,为了支持 64 位的CPU结构(Alpha 就是 64 位的),gcc 增加了一种新的基本数据类型“longlong int”,该类型在内核代码中常常用到。

许多 C 语言都支持一些“属性描述符”(attribute),如“aligned”、“packed”等等;gcc 也支持不少这样的描述符。

这些描述符的使用等于是在 C 语言中增加了一些新的保留字。

Linux操作系统源代码详细分析报告

Linux操作系统源代码详细分析报告

Linux操作系统源代码详细分析报告Linux操作系统源代码详细分析容简介:Linux 拥有现代操作系统所有的功能,如真正的抢先式多任务处理、支持多用户,存保护,虚拟存,支持SMP、UP,符合POSIX标准,联网、图形用户接口和桌面环境。

具有快速性、稳定性等特点。

本书通过分析Linux的核源代码,充分揭示了Linux作为操作系统的核是如何完成保证系统正常运行、协调多个并发进程、管理存等工作的。

现实中,能让人自由获取的系统源代码并不多,通过本书的学习,将大大有助于读者编写自己的新程序。

第一部分 Linux 核源代码arch/i386/kernel/entry.S 2arch/i386/kernel/init_task.c 8arch/i386/kernel/irq.c 8arch/i386/kernel/irq.h 19arch/i386/kernel/process.c 22arch/i386/kernel/signal.c 30arch/i386/kernel/smp.c 38arch/i386/kernel/time.c 58arch/i386/kernel/traps.c 65arch/i386/lib/delay.c 73arch/i386/mm/fault.c 74arch/i386/mm/init.c 76fs/binfmt-elf.c 82fs/binfmt_java.c 96fs/exec.c 98include/asm-generic/smplock.h 107include/asm-i386/atomic.h 108include/asm-i386/current.h 109include/asm-i386/dma.h 109include/asm-i386/elf.h 113include/asm-i386/hardirq.h 114include/asm-i386/page.h 114include/asm-i386/pgtable.h 115include/asm-i386/ptrace.h 122include/asm-i386/semaphore.h 123include/asm-i386/shmparam.h 124include/asm-i386/sigcontext.h 125include/asm-i386/siginfo.h 125include/asm-i386/signal.h 127include/asm-i386/smp.h 130include/asm-i386/softirq.h 132include/asm-i386/spinlock.h 133include/asm-i386/system.h 137include/asm-i386/uaccess.h 139include/linux/capability.h 147 include/linux/elf.h 150include/linux/elfcore.h 156 include/linux/interrupt.h 157 include/linux/kernel.h 158 include/linux/kernel_stat.h 159 include/linux/limits.h 160 include/linux/mm.h 160include/linux/module.h 164 include/linux/msg.h 168include/linux/personality.h 169 include/linux/reboot.h 169 include/linux/resource.h 170 include/linux/sched.h 171 include/linux/sem.h 179include/linux/shm.h 180include/linux/signal.h 181 include/linux/slab.h 184 include/linux/smp.h 184include/linux/smp_lock.h 185 include/linux/swap.h 185 include/linux/swapctl.h 187 include/linux/sysctl.h 188 include/linux/tasks.h 194 include/linux/time.h 194include/linux/timer.h 195 include/linux/times.h 196 include/linux/tqueue.h 196 include/linux/wait.h 198init/main.c 198init/version.c 212ipc/msg.c 213ipc/sem.c 218ipc/shm.c 227ipc/util.c 236kernel/capability.c 237kernel/dma.c 240kernel/exec_domain.c 241kernel/exit.c 242kernel/fork.c 248kernel/info.c 255kernel/itimer.c 255kernel/kmod.c 257kernel/module.c 259kernel/panic.c 270kernel/sched.c 275kernel/signal.c 295kernel/softirq.c 307kernel/sys.c 307kernel/sysctl.c 318kernel/time.c 330mm/memory.c 335mm/mlock.c 345mm/mmap.c 348mm/mprotect.c 358mm/mremap.c 361mm/page_alloc.c 363mm/page_io.c 368mm/slab.c 372mm/swap.c 394mm/swap_state.c 395mm/swapfile.c 398mm/vmalloc.c 406mm/vmscan.c 409第二部分 Linux 核源代码分析第1章 Linux 简介让用户很详细地了解大多数现有操作系统的实际工作方式是不可能的,因为大多数操作系统的源代码都是严格的。

SELinux源码分析_1.31

SELinux源码分析_1.31

SELinux源码分析(Federa Core 8)第一章SELinux(Security Enhance Linux,简称SELinux)简介1.1 SELinux的起源SELinux是一个面向政府和行业的产品,由NSA、Network Associates、Tresys以及其他组织设计和开发。

尽管NSA将其作为一个补丁集引入,但从2.6版开始,它就被加入到Linux 内核中。

GUN/Linux非常安全,但它也非常动态:所做的更改会为操作系统带来新的漏洞,这些漏洞可能被攻击者利用,尽管人们都非常关心阻止授权访问,但是发生入侵后会发生什么呢?1.2访问控制大多数操作系统使用访问控制来判断一个实体(用户或程序)是否能够访问给定资源。

基于UNIX的系统使用一种自主访问控制(Discretionary Access Control,简称DAC)的形式。

此方法通常根据对象所属的分组来限制对对象的访问。

例如,GNU/Linux 中的文件有一个所有者、一个分组和一个权限集。

权限定义谁可以访问给定文件、谁可以读取它、谁可以向其写入,以及谁可以执行它。

这些权限被划分到三个用户集中,分别表示用户(文件所有者)、分组(一个用户组的所有成员)和其他(既不是文件所有者,又不是该分组的成员的所有用户)。

很多这样的访问控制都会带来一个问题,因为所利用的程序能够继承用户的访问控制。

这样,该程序就可以在用户的访问层进行操作。

与通过这种方式定义约束相比,使用最小特权原则更安全,程序只能执行完成任务所需的操作。

例如,如果一个程序用于响应socket 请求,但不需要访问文件系统,那么该程序应该能够监听给定的socket,但是不能访问文件系统。

通过这种方式,如果该程序被攻击者利用,其访问权限显然是最小的。

这种控制类型称为强制访问控制(MAC)。

另一种控制访问的方法是基于角色的访问控制(RBAC)。

在RBAC 中,权限是根据安全系统所授予的角色来提供的。

计算机操作系统实验指导linux版王红玲源码

计算机操作系统实验指导linux版王红玲源码

计算机操作系统实验指导linux版王红玲源码计算机操作系统实验指导(Linux版)导言:计算机操作系统是计算机系统中最重要的软件之一,负责管理计算机系统的硬件和软件资源,并为用户提供良好的使用环境。

为了帮助学生更好地理解操作系统的原理和实现,我们开设计算机操作系统实验课程,并提供一份针对Linux操作系统的实验指导。

本实验指导旨在帮助学生通过实际编程来探索和理解操作系统的原理和实现方式。

通过完成本实验,学生将能够熟悉Linux操作系统的基本功能和原理,并学会使用Linux的命令行界面和Shell编程。

同时,本实验还将引导学生通过源代码的阅读和分析,深入理解操作系统内部的工作原理。

实验一:Linux环境搭建在开始实验之前,我们首先需要搭建一个适合的Linux开发环境。

学生可以选择在个人电脑上安装Linux发行版,如Ubuntu或Fedora,也可以使用虚拟机软件,如VirtualBox或VMware,在Windows或Mac OS上安装Linux虚拟机。

实验二:Linux基本操作和Shell编程在本实验中,学生将通过完成一系列实际任务来熟悉Linux的基本操作和Shell编程。

任务包括使用命令行界面进行文件和目录操作、执行Shell脚本、配置系统环境等。

学生需要按照指导完成每个任务,并理解每个任务的目的和原理。

实验三:Linux系统调用和进程管理在本实验中,学生将学习和实现Linux系统调用和进程管理的功能。

学生需要阅读和分析Linux内核源代码中与系统调用和进程管理相关的部分,并完成一系列与之相关的实验任务。

任务包括编写和调试系统调用、创建和管理进程、实现进程间通信等。

实验四:Linux内存管理和文件系统在本实验中,学生将学习和实现Linux内存管理和文件系统的功能。

学生需要阅读和分析Linux内核源代码中与内存管理和文件系统相关的部分,并完成一系列与之相关的实验任务。

任务包括实现内存分配算法、设计和实现文件系统、调试和优化内存和文件系统的性能等。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
/ / 指向 v ma 段 AVL 树的指针 struct semaphor mmap_sem;
struct vm_area_struct* vm_pr ev_share; struct vm_operation_struct* vm_ops;
/ / vm_o ps 规定 了可 对 V M A 段实 施的操作。
unsigned long vm_offset ; / / VM A 段相对于文件或共享内存 的偏移量。
/ / 进程未初 始化 的数 据段 的起 始地 址 和结束地址
unsig ned lo ng star t_stack, start_mmap; unsig ned lo ng arg_start, arg_end;
/ / 调用参数区的起始地址和 结束地址 unsig ned lo ng env_start, env_end;
/ / 进程环境区的起始地址和 结束地址 unsig ned lo ng rss, total_vm, locked_v m;
/ / rss 是进程内容驻留 在物理 内存的 页 面总数
unsig ned lo ng def_flags; struct v m_ar ea_struct* mmap;
/ / 指向 v ma 段的双向链表的指针 struct v m_ar ea_struct* mmap_av l;
L inux 操作系统 是一种能运行于多种平台、源 代码公开、免费、功能强大、与 Unix 兼容的操 作系 统。自其诞生以来, 发展非常迅速, 在我国也受到政 府、企业、科研单位、大专院校的重视。我们自 2000 年开始对 L inux 源代码( 版本号是 Linux 2 2 16) 进 行分析, 首先剖析了进程管理和存储管理部分, 本文 是有关存储管理的一部分。主要介绍了 Linux 虚存 管理所用到的数据结构及其相互间的关系, 据此可 以更好地理解其存储管理机制, 也可以在此基础上 对其进行改进或在此后的研究中提供借鉴作用。作 为一种功能强大的操作系统, Linux 实现了 以虚拟 内存为主的内存管理机制。即能够克服物理内存的 局限, 使用户进程在透明方式下, 拥有比实际物理内 存大得多的内存。本文主要阐述了 L inux 虚存管理 的基本特点和主要实现技术, 并分析了 L inux 虚存 管理的主要数据结构及其相互关系。
第3期
王艳春: L inux 源代码分析 存储管理
31
程中实现的。进程执行时每用到一个地址, 地址转 换机构都要把虚拟地址转化为内存的实际地址。动
态地址映射使 L inux 可以实现进程在主存中的动态 重定位。虚存段的动态扩展和移动, 也为虚存的实 现提供了基础。
2 Linux 虚存管理数据结构
1) mem_m ap Linux 系统中的物 理内存由 mem_map 表描 述
mm h) , 每一个 mem_map_t 描 述系统的一个 关于
内核态、用户态代码和数据的物理页面, 其定义如 下:
typedef struct pag e {
/ / 在 include/ linux / mm h 中:
struct page* nex t, * prev; / / 由于搜索算法 的约定, 这 两项 必须首先定义
struct inode* v m_inode; / / 指向 VM A 所在文件的 inode 结 构。 若 不 涉 及 文 件, 则 为 N U LL 。
unsigned long vm_pte; / / 用于 共享内 存, 含 SHM_SWP_ T Y PE 和共享内存段 id 号
};
图 4 虚拟内存数据结构示意图
1 Linux 虚存管理概述
Linux 的内存管理采用虚拟页式管理, 使用多 级页表, 动态地址变换。进程在运行过程中可以动 态浮动和扩展, 为用户提供了透明的、灵活有效的 内存使用方式。
1) 32 bit 虚拟地址 在 L inux 中, 进程的 4GB 虚存需通过 32 bit 地 址进行寻址。L inux 中虚拟地址与线性地址为同一 概念, 虚拟地址被分成 3 个子位段, 而大小为 4k, 如图 1 所示。 2) L inux 的多级页表结构
/ / 页帧描述表的首地 址
2) free_area
L inux 采用位示图 ( bitm ap 表) 的方式记录所 有物 理 内 存 的 使 用 状 况。 与 mem_map 一 样,
bit map 表在系统初始化时由 f ree_area_init ( ) 函数
创建 ( 见 mm/ page_alloc. c) 。空闲 的物理页 帧用
4) mm_st ruct 进程的虚拟这间由 mm_st ruct 描述, 该数据结 构包含当前执行程序的映象信息 ( 用户进程中与存 储有关的信息) , 以及一些指向 vm_area_st ruct 结构 的指针, 参见图 5。
图 5 用户进程虚存管理数据结构 struct mm_struct { int count; pg d_t* pgd;
struct inode* ino de;
/ / 若该页帧的内 容是文件, 则 inode 和
unsigned long offset;
struct page* next_hash;
/ / offset 指出文件的 inode 和 偏移 位置
/ / pag e cache 的 hash 表中,
链表 的后继指针
指示地址
Hale Waihona Puke unsig ned lo ng swap_unloch_entry;
unsig ned olng map_nr;
/ / 页 帧 在 mem_map 表 中
的下 标, page map_nr
= = page_mem_map
} mem_map_t;
mem_map_t * mem_map= N U LL ;
/ / 此结构的 nex t、prev 指 针与 struct page 匹配
struct page* prev; unsig ned int * map;
/ / 指向 bitmap } static struct free_ar ea_struct free_area [ N R_M EM_ L IST S] ;
/ / 页帧 的年龄, 越 小越 先换
出 struct wait_queue* wait ;
/ / 等待队列指针 struct page* prev_hash;
/ / page_cach 的 hsh 表 中, 链 表的前向指针
struct buffer_head* buffers;
/ / 若该页帧作为 缓冲区, 则
3) 页表项的格式
图 2 Linux 中页目录项和页表项格式
4) 动态地址映射 L inux 虚存采用动态地址映射方式, 即进程的 地址空间和存储空间的对应关系是在程序的执行过
收稿日期: 2003- 05- 10 作者简介: 王艳春, 女 ( 1964 ) , 副教授, 主要从事操作系统、中文信息处理等方面的研究工作。
Linux 源代码分析 存储管理
王艳春 陈 毓 葛明霞
( 长春理工大学 计算机科学技术学院, 吉林 长春 130022)
摘 要: 本文剖析了 L inux 操作系统的存储管理机制。给出了 L inux 存储管理的特点、虚存的实 现方法, 以及主要数据结构之间的关系。 关键词: L inux 操作系统; 存储管理; 虚拟存储 中图分类号: T P316 81 文献标识码: A
f ree_area 数组记录。该数组由 NR- M EM - LIST S 个 f ree_area_st ruct 结构类型的数组元素构成 ( 见图
3) 。每个元素作为一条空闲链表的表头。
图 3 bitmap 表及其与 free_area 的关系 stuct free_ar ea_struct { struct page* next;
struct vm_area_struct* vm_avl_right ; struct vm_area_struct* vm_nex t;
/ / 链接 每个任 务的 V M A 区, 按 地址分类
struct vm_aea_str uct* vm_nex t_shar ; / / VM A 为共 享区 时所使 用的 前、 后向指针。
/ / 进程页目录的起始地址 unsig ned lo ng star t_code. end_code;
/ / 进程代码段的起始地址和 结束地址 unsig ned lo ng star t_data, end_data;
/ / 进程数据段的起始地址和 结束地址 unsig ned lo ng star t_brk, brk;
struct mm_str uct* vm_mm; / / V M A area parameters
32
长春理工大学学报
2003 年
unsigned long vm_start; / / V M A 描 述的 虚 存 段始 于 vm_ start, 终于 vm_end
unsigned long vm_end; pgprot_t vm_page_rot;
( 见 mm \ memory c) , 是一个 mem_mp_t 类型的队
列, 该队列在系统初始化时由核心 f ree_area_init ( )
创建和初始 化 ( 见 mm \ page_alloc c) , 它本身 是 关于 st ruct page mem_map_t 的 数 组 ( 见 linux \
图 1 32 位虚拟地址
标准的 L inux 的虚存页表为三级页表, 依次为页目 录( Pag e Direct ory PGD) 、中间页目录( Pag e Middle Direct ory P MD ) 、页 表 ( Page T able PT E ) 。 在 i386 机器 上 Linux 的 页表结构实 际为两级, PGD 和 PMD 页表是合二为一的。所有有关 PMD 的操作关际上 是对 PGD 的操作。所以源代码中形如* _pgd_* ( ) 和* _ pmd_* ( ) 函数实现的功能也是一样的。
相关文档
最新文档