Linux操作系统有四大功能:进程管理、内存管理、文件系统和驱动程序管理等。从下面这张图可以看出Linux操作系统概念模型。
当应用进程(如c语言使用malloc)申请内存时,调用系统api,系统从用户态进入内核态,在Linux系统上,程序被载入内存时,内核为用户进程地址空间建立了代码段、数据段和堆栈段,在数据段与堆栈段之间的空闲区域用于动态内存分配。 内核数据结构mm_struct中的成员变量start_code和end_code是进程代码段的起始和终止地址,start_data和 end_data是进程数据段的起始和终止地址,start_stack是进程堆栈段起始地址,start_brk是进程动态内存分配起始地址(堆的起始 地址),还有一个 brk(堆的当前最后地址),就是动态内存分配当前的终止地址。 C语言的动态内存分配基本函数是malloc(),在Linux上的基本实现是通过内核的brk系统调用。brk()是一个非常简单的系统调用,只是简单地改变mm_struct结构的成员变量brk的值。 mmap系统调用实现了更有用的动态内存分配功能,可以将一个磁盘文件的全部或部分内容映射到用户空间中,进程读写文件的操作变成了读写内存的操作。在 linux/mm/mmap.c文件的do_mmap_pgoff()函数,是mmap系统调用实现的核心。do_mmap_pgoff()的代码,只是新建了一个vm_area_struct结构,并把file结构的参数赋值给其成员变量m_file,并没有把文件内容实际装入内存。。
进程分配内存有两种方式,分别由两个系统调用完成:brk和mmap(不考虑共享内存)。
1、brk是将数据段(.data)的最高地址指针_edata往高地址推;
2、mmap是在进程的虚拟地址空间中(堆和栈中间,称为文件映射区域的地方)找一块空闲的虚拟内存
这两种方式分配的都是虚拟内存,没有分配物理内存。在第一次访问已分配的虚拟地址空间的时候,发生缺页中断,操作系统负责分配物理内存,然后建立虚拟内存和物理内存之间的映射关系。当缺页中断发生时,Linux系统进行以下操作:
1、检查要访问的虚拟地址是否合法
2、查找/分配一个物理页
3、填充物理页内容(读取磁盘,或者直接置0,或者啥也不干)
4、建立映射关系(虚拟地址到物理地址)
5、重新执行发生缺页中断的那条指令
只要进程请求内存,内核只要满足要求就会给其分配页面。如果进程不再需要页面,内核就会将其回收。
alloc_page()//该函数用于请求单页
alloc_pages()//该函数用于请求4个页面
__get_free_page()/__get_dma_pages() //返回32为虚拟地址,该地址是分配页面的首地址
__free_page()/__free_pages() //释放页面
每当页面被分配和回收的时候,系统都会遇到外部碎片或内存碎片的问题(即页面散布在内存中,即使可用页面足够多,但是无法分配大块的连续页面)。为了解决这个问题,Linux系统提供了伙伴算法。伙伴系统把内存中空闲块组成链表,将不同大小的空闲内存块组织起来(我猜测是将相同大小的组织在一起),虽然大小不一样,但是都是2的幂次方。当系统中有进程释放没存的时候,伙伴系统就会搜索与所释放块大小相等的可用空闲内存块,如果找到相邻的空闲块,就将其合并成两倍于自身大小的块。这种合并的块称为伙伴。
当内存分配完成,系统从内核态返回用户态。
进程调用库函数read向内核发起读文件请求
触发系统调用sys_read(),获得当前进程的控制块
系统调用read()会触发相应的VFS的read()函数
然后找到file结构,再找到fd数组,以fd为索引找到对应项,然后找到系统打开文件表
执行系统打开文件表里面的file operation里面的read
把应用程序带入模型分析性能
进程创建完,就被作为可运行态进程放入运行队列中,若是没有其他IO请求或资源请求,就等待CPU资源,然后被执行,这里就该采用进程调度算法来进行进程的调度, 采用合适的调度算法可以使进程的性能提高
原文:https://www.cnblogs.com/hedyyu/p/14774028.html