binder(一)Linux必备知识篇

对linux操作系统中某些概念做简单预习,旨在为后续Android学习提供前提铺垫。例如学习binder、锁机制等等。

进程

Linux操作系统将运行中的程序成为进程。而Linux内核控制这Linux操作系统如何管理运行在系统上的所有进程。

内核创建了第一个进程(init进程)来启动系统上所有其他进程。 当内核启动时,他会将init进程加载到虚拟内存中。内核在启动任何进程时都会在虚拟内存中为该进程分配一块专有区域用于存储该进程用到的数据和代码。

进程隔离

  • Linux中为每一个进程分配内存时都将内存划分为2部分:用户空间内核空间
  • Linux中一个进程在运行时cpu的运行状态是用特权等级来区分,以arm64为例分为4个等级,从低到高分别是EL0~EL3。我们常接触的等级是EL0和EL1,EL0为用户态,EL1为内核态。当cpu处于用户态时不能访问内核空间的数据(除非通过系统调用将当前cpu状态转入内核态),但是反之如果cpu处于内核态时可以访问用户空间也可以访问内核空间。
  • Linux中每个进程之间是相互隔离的,进程之间不能直接进行通信,这样保证了进程的安全性。
  • 2个用户进程想要相互通信必须借助内核空间来完成,大概原理就是在内核空间中开辟出一块内存缓存区域,各个进程通过操作这块区域达到通信的目的。这就涉及到了用户进程想要访问内核空间的情况,此时只能通过系统调用(例如copy_from_user就是一个系统调用) 使cpu从用户态转入内核态,这样该进程就能访问内核空间的数据了,其他进程也通过这种方式来访问该内存缓存就能达到通信的目的。这也是用户进程访问内核空间的唯一方式。 这种机制保证了系统内核的安全和稳定,确保用户进程不能随随便便地操作内核空间给内核空间造成未知安全隐患。

如何做到进程隔离?如何在各个进程共享内核空间?

binder(一)Linux必备知识篇_第1张图片

  • 要将虚拟地址映射到物理内存上需要用到密码本来翻译虚拟地址
  • 每个进程中有2个密码本,一个是用户空间密码本(task_struct)中,一个是内核空间密码本(swapper_pg_dir),用户空间密码本为进程私有,每个进程都不一样,内核空间密码本为各个进程公用的
  • 当访问用户空间时使用task_struct这个密码本,由于每个进程都不一样,所以最终映射到的物理内存地址就是不一样的,别的进程永远都不可能映射到本进程的相对应的用户空间物理内存。
  • 当访问内核空间时使用swapper_pg_dir这个密码本,因为所有进程都是公用的一个密码本所以最终映射的物理内存是一样的。

图中是简化过方便理解的映射,真实情况中物理内存地址极可能是离散的而不是这样连续的内存片段。

设备文件

在Linux中,一切皆对象。也就是说皆可以通过一套统一的接口来控制。

Linux系统将硬件设备当成特殊的文件,称之为设备文件。例如键盘、鼠标、硬盘等等在Linux中都当成文件来处理,Linux提供了一套统一的操作接口来让开发者操作它们。
设备文件分为3类:

  • 字符设备:如键盘。提供连续数据流(字节或字符),应用可顺序读取,但不支持随机存取
  • 块设备:如硬盘等存储设备。应用可随机存取设备上的任意位置的数据。
  • 网络设备 :指采用数据包发送和接受数据的设备,包括各种网卡和一个特殊的回环设备。这种回环设备允许Linux使用常见的网络编程协议同自身通信。

设备节点:

Linux系统会将所有的设备文件(网络设备除外)在/dev目录下生成对应的设备节点,生成的设备节点可以理解为设备文件的操作入口,我们可以Linux提供的统一操作接口(如open()、read()、write()、close()…)来操作这些设备节点来达到对设备的操作。 这样大大的降低了应用程序操作设备的复杂度。

网络设备之所以不和其他设备一样是因为网络设备的工作是采用报文传输,无法做到和其他设备统一接口,所以并未在/dev下生成设备节点。

虚拟设备

虚拟设备是Linux动态虚拟出来的一种设备,Linux会为其分配内存,但其并不存在真实的物理设备。为了进行某些功能的现实化,让操作更加具象化。这就使得用户能像操作一个真实设备一样去做想要的操作。举个例子就像app中的小键盘它不是一个真实的物理键盘,但是你能像操作一个真实物理键盘一样打字。

Android中的binder就是一个虚拟设备,它的设备节点是/dev/binder

设备号

每个设备节点都包含了2个设备号:

  • 主设备号:表示设备类型,也表示与其关联的设备驱动程序,相同设备号所关联的设备驱动是同一个
  • 次设备号:表示某种设备类型下的特定设备,例如有多个usb,可以通过次设备号查找usb0,usb1

可以通俗理解为:主设备号是你家单元号,次设备号是你家门牌号

设备驱动

设备驱动程序是一种可以使系统和设备进行交互的特殊程序,相当于硬件的接口,系统只有通过驱动程序中的接口才能控制设备的工作。加入某设备的驱动程序未能正确工作则系统也无法正确操作设备。因此,驱动程序是设备和系统之间的桥梁。

设备驱动的工作原理

Linux中访问/dev目录下的设备节点时,Linux内核会把这些对设备节点的操作通过主设备号找到对应的驱动程序,然后将操作映射到驱动程序中的对应代码上,然后驱动程序可根据次设备号执行对指定设备的操作。
Linux内核有两种方法来将某个设备驱动插入内核:

  1. 将驱动程序代码之间编译进内核
    早前这是驱动程序进入内核的唯一方式,这种方式会导致内核代码要重新编译。每次添加一个新设备都得重新编译一次内核代码,随着设备不断增加这种方式就变得很低效,所以产生了第二种方式
  2. 将驱动程序编译为驱动模块再动态插入内核
    因为第1种方式的低效,开发者提出了驱动模块的概念。通过将驱动程序编译成驱动模块,然后在使用的时候将驱动模块动态插入到运行中的系统内核中而无需重新编译内核,当不再使用时也可以将驱动模块从运行中的系统内核中移除。这样的好处一个是简化和扩展了硬件设备在Linux上的使用,另一个是使内核不再臃肿。

binder(一)Linux必备知识篇_第2张图片
Android中的binder驱动就是以这种方式插入系统内核的。

所以在Android中打开/dev/binder设备节点时,会执行binder驱动中的相关逻辑。后续的操作都会映射到binder驱动中。

参考文献:
初识Linux命令行与shell
Linux设备驱动程序和设备文件
Linux下编写和加载 .ko 文件(驱动模块文件)
linux内存管理(一)- 虚拟地址和物理地址

你可能感兴趣的:(Android日常学习记录,binder学习,android)