2.1同步机制
现代操作系统的特征:中断处理、多任务环境、多处理器。
内核需要提供并发控制机制,对公共资源的访问进行同步控制,确保共享资源的安全性。
LINUX操作系统中包含众多的同步机制,包括信号量(semaphore)、自旋锁(spinlock)、原子操作(atomic operation)、读写锁(rwlock)、RCU(包含在linux2.6内核中)和seqlock(包含在linux2.6内核中).
2.1.1自旋锁主要用在SMP环境下。在单处理器环境中,spin_lock和spin_unlock的作用仅限于禁止和允许内核抢占。自旋锁不会引起调用者的睡眠。这种特性避免了调用进程的挂起,用自旋来取代进程切换。
2.1.2信号量的调用会引起调用者的睡眠,除非获得锁。信号量适用于单处理器系统或多处理器系统中。信号量允许并行访问,即可以有多个内核控制路径同时掌握该信号量,它所允许的并行访问数目是在信号量创建时定义的。互斥对象(mutex)是一种特殊的信号量,它所保护的资源在同一时刻只允许一个内核控制路径访问。
2.1.3原子操作
原子操作是一系列不可中断的操作的集合,它的执行过程是封闭的,不可打断。原子操作的使用与其他锁同步机制的差异很大,可以将要保护的资源定义成原子型整数atomic_t类型,然后调用原子操作的接口对这些资源进行修改。
2.1.4读写锁
实际是一种特殊的自旋锁,它把对共享资源的访问划分为读者和写着,读者只对共享资源进行读访问,写者则需要对共享资源进行写操作。读者和写者需要不同的获得和释放锁的函数。允许同时有多个读者来访问共享资源,最大可能的读者数为实际的逻辑CPU数。写者具有排他性。一个读写锁同时只能有一个写者或多个读者(与CPU数相关),但不能同时既有读者又有写者。在读写锁保持期间,也是抢占失效的。
2.1.5seqlock
顺序锁是一种免锁机制,可提供共享资源的快速访问。主要用于:1少量的数据保护;2数据比较简单(没有指针),并且使用频率很高;3写入访问很少发生,并且快速的执行;4写操作比读操作的优先级高。seqlock是一种写优先锁,读者进入临界区,不需要关闭内核抢占,而写者进入临界区,会自动关闭内核抢占。
2.1.6RCU
RCU(read-copy-update)是一种高性能的锁机制,具有很好的扩展性,但是使用范围比较窄,只适用于读多写少的情况。读-备份-修改,读者不需要获得任何锁就可以访问它,但写者在访问它时首先备份一个副本,然后对副本进行修改,最后使用一个回调(callback)机制在适当的时机把指向原来数据的指针重新指向新的被修改的数据。这个时机就是所有引用该数据的CPU都退出对共享数据的操作时。
2.3阻塞与非阻塞
阻塞操作是指在执行I/O操作时,若不能获得资源,则进程挂起直到满足可操作的条件时再进行操作。非阻塞操作是指在执行I/O操作时,如果设备没有准备好,并不挂起,立即返回。被挂起的进程进入睡眠状态,直到等待的条件被满足。显式的非阻塞I/O由flip->f_flags中的O_NONBLOCK标志决定。
poll和select用于查询设备的状态,以便用户程序获知是否能对设备进行非阻塞地访问,它们都需要设备驱动程序中的poll函数支持。
2.4时间
2.4.1LINUX下延迟
Linux内核代码中有一个全局变量jiffies,这是一个32位无符号整数,用来表示自内核上一次启动以来的时钟滴答次数。每发生一次时钟滴答,内核的时钟中断处理函数timer_interrupt()会将该全局变量jiffies加1.
2.4.2内核定时器
linux内核中定义了一个timer_list结构,利用它可以实现内核定时器功能。
2.5内核分配与映射
linux操作系统包括以下类型的地址类型:
用户虚拟地址:用户空间程序的地址
物理地址:处理器与系统内存之间使用的地址
总线地址:外围总线与内存之间使用的地址
内核逻辑地址:内存的部分或全部映射,大多数情况下,它与相关联的物理地址仅差一个偏移量,两者的映射是线性的,如kmalloc分配的内存
内核虚拟地址:内核空间的地址映射到物理地址上,但映射不必是线性的。所有的逻辑地址都是内核虚拟地址,如vmalloc分配的内存。
2.5.1内存分配与释放
在LINUX内核模式下,不能使用用户态的malloc()和free()函数申请和释放内存。进行内核编程时,最常用的内存申请和释放函数为在include/linux/kernel.h文件声明的kmalloc()和kfree()。kmalloc一般用来分配小于128kb内存,如果要分配大块的内存,应使用面向页的技术。
另外一种分配方法是vmalloc。这个函数分配一片连续的虚拟内存。但映射到物理内存是不连续的。在使用分配到的内存时,页表的查询比较频繁,所以效率相对较低。
2.5.2用户态和内核态内存的交互
交互函数在include/asm/uaccess.h中被声明。
2.5.3内存池
满足无间断的内存分配。内存池可以用来分配整页的内存或小内存,它所处理的内存单元通常称为内存元素。内存池只能被它的拥有者使用。内存池拥有者通常情况下不使用内存池,只有在动态内存不足时才使用。内存池用mempool_t来描述。
2.5.4物理地址到虚拟地址的映射
CPU对外设I/O端口物理地址的编址方式有两种:I/O映射方式(x86体系)和内存映射方式(PowerPC、M68K、ARM)。
2.5.5内核空间到用户空间的映射
如果想在用户空间访问内核地址,可以采用mmap方法。应用程序通过内存映射可以直接访问设备的I/O存储区或DMA缓冲。
2.6中断处理
2.6.1硬件中断
IRQ。有两种,短类型和长类型。短IRQ需要在很短的时间,在此期间机器的其他部分被锁定,而且没有其他中断被处理。一个长IRQ需要较长的时间,在此期间可能发生其他中断(但不是发自同一个设备)。LINUX中的中断处理程序分为两个部分:上半部和下半部。之所以会有上半部和下半部之分,完全是考虑到中断处理的效率。
上半部的功能是响应中断。是完全屏蔽中断的,所以要快。
下半部功能是处理比较复杂的过程。可以中断,几乎完成了中断处理程序所有的事情。可以使用tasklet机制或软中断机制(softirq)来实现中断下半部处理,而tasklet是基于softirq的。
2.6.2软中断机制
利用硬件中断的概念,用软件方式进行模拟,实现异步处理。
2.7/proc系统
Linux内核提供一种/proc文件系统,可以在运行时访问内核内部数据结构、改变内核设置,用来向进程发送信息。与其他文件系统不同,/proc存在于内存之中,而不是硬盘上。由内核控制,没有承载设备。主要存放内核控制的状态信息,所以大部分这些信息的逻辑位置位于内核控制的内存。