Binder驱动的初始化 syscall原理(一)

2023-11-11 08:40

本文主要是介绍Binder驱动的初始化 syscall原理(一),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在说明Binder之前。我们来想想Binder作为一个进程间通信的通道之前。我们常用的进程间通信有几种。

Linux中,我们常见有一下几种进程间通信:

1.pipe 管道

2.FIFO named pipe 有名管道

3.signal 信号

4.消息队列

5.socket 套子节

6.SharedMemory 共享内存

用户空间(用户态)和内核空间(内核态)

一些操作系统允许所有用户与硬件做交互。但是,类unix操作系统在用户应用程序钱把计算机物理组织相关的底层细节都隐藏起来。当程序想试用硬件资源时候,必须向操作系统发出请求。内核会对这个请求做评估,允许使用,那么内核将会代表应用与硬件交互。 为了实现这个机制,现代操作系统依赖特殊硬件特性来禁止用户程序直接与底层打交道,或者直接访问任意的物理地址。硬件为cpu引入了至少两种执行模式:用户的非特权模式和内核的特权模式。也就是我们常说的用户态和内核态。

这是来自深入理解Linux一书中的定义。举个简单的例子,当我们需要运行文件操作的时候,使用open等操作方法,就会从用户程序就会从用户态进入到内核态,当open结束之后,用户程序进入回到用户态。

为什么Linux系统要这么设计。最大的原因就是为了让内核底层透明化,同时如果用户程序出现了问题,将不会影响内核。

换我们思考一下,如果是我们,怎么在两个隔离的进程任务块中联系彼此。很常见的一个思路,我们一般能想到的是,让一个进程把需要交互的信息存到一个文件里面,另一个进程从文件中读取数据。

管道pipe

这种思路,被广泛运用到Linux系统中。比如pipe实际上是创建了两个文件(文件描述符实际上是内核缓存区),一个专门用来读,一个专门用来写。而pipe是一个半双工的通道,换句话说就是在一个时间内只能在一个单方向的进程通信。这样就可能的减少因为多个进程来回竞争文件内容,导致传输过程中出错。

而此时pipe必定是先通过内核调用copy_from_user方法把初始化数据拷贝一份内核空间中,此时通过alloc_file通过kmalloc在调用slab在内核空间创建2个文件描述符。

大致上示意图如下:

image.png

记住fd[0]是读通道,fd[1] 是写通道

FIFO named pipe

有名管道,这中管道在原来的基础上做了处理,依赖了Linux的文件系统。从名字上就能得出这种管道是先入先出的原则,能够让数据通信按照顺序来。然而有名管道更大的意义是让管道命名。原来的管道是无名管道,所以只能在自己控制下的子进程做沟通。而多了名字之后,就不需要想法子把地址交给第二个进程,而是通过名字去找文件,就能建立通道。

signal 信号

信号这个东西,我们其实早就有所耳闻。比如说我们常说的中断信号就是指的是信号的一种。而在linux内核中内置一些通知事件。每一次发出这个事件内核将会通知进程接收这些事件,并且做处理。

内核实现:

1.为了做到对应的信号能够发送到正确需要进程。内核需要记住当前进程被哪些信号阻塞。

2.当从内核态切换到用户态,检查进程是否产生信号。这种检测每个时钟都会触发一次,一般在几个毫秒内触发一次。

3.还要检测哪些信号被忽略。以下条件都满足时表示信号被忽略

  • 进程没有被追踪,task_struct(用来描述进程的结构体)PT_PTRACED标示为0.

  • 进程没有阻塞这种信号

  • 进程忽略这种信号

4.处理信号

此时我们需要注意的是,在内核态我们是不会处理信号的,往往都会抛到用户空间,通过copy_to_user拷贝交给用户空间去处理。

消息队列

这个听起来有点像Android里面的消息队列。两者相比,设计上确实相似。消息队列的使用,先通过ftok生成一个key,再通过key用msgget创建一个消息队列(文件)。之后用msgsnd或者msgrcv发送或者接收东西。

此时在内核上实际上可以看出创建了一个文件。把对应的消息传进去消息队列中。此时读取方和写入方由于有ftok生成一个key,就能在内核空间找到对应的消息队,就能借助这个队列完成消息的传递。其中为了让数据能够来回在用户态和内核态来回切换,还是使用到了copy _from_user,copy_to_user.其数据结构是一个链表。

socket 套子节

这个我们所有人都十分的熟悉。我们做网络编程离不开它。实际上从原理上它也是一种特殊的文件,我们也是不断的监听socket的状态来回应。既然是文件操作,那么一定会经历一次用户态到内核态,内核态到用户态的转化。这种本地监听的运用,在zyogte孵化进程的时候经常用。

共享内存

共享内存的设计,是最接近binder的设计。其核心也是用过mmap内存映射技术。其设计上也和消息队列相似。也是通过ftok生成一个key,再通过这个key申请内存shmget之后,就能对这段地址做操作。

信号量

实际上信号量最主要的作用是对进程进行加锁,如果有进程访问这个正在使用的资源会进入睡眠状态。

Binder 的概述

介绍了Linux的几种基础IPC(进程间通信),我们发现一个很有趣的现象。大部分的IPC通信都通过文件作为中转站来回通讯。这样势必会造成用户态,内核态在来回切换,那么必定造成这种数据拷贝两次的情况。那么我们有没有办法处理优化这种通信方法呢?Binder就诞生了。

image.png

那么我们要设计Binder的话,又能怎么设计呢?首先为了让整个透明并且可靠化,我们能采用TCP/IP这一套思路来保证信息的可靠性。其次为了减少来回的在用户往内核中拷贝空间能够创造模仿共享内存的方式。

image.png

我们可以关注到Binder中四种角色:

Binder 驱动

ServiceManager

Binder Client

Binder Service

从这里我们明白,在内核空间中,存在这一个Binder的驱动,而这个驱动正是作为整个IPC通信的中转站。也就是类似TCP通信中的路由地位,我不在乎你究竟要是干啥,我只需要找到你,并且把消息交给你就好。

此时service manager充当的是Binder驱动的守护进程,类似于TCP通信中的DNS地位。我们会把相关的Binder注册到里面,最后会通过service manager这个服务去查找binder的远程端。而实际上这个service manager在Andrioid Binder体系中,承当了Android系统中第一个注册进入Binder的服务。

Binder Client binder的客户端,相当于C/S架构中的客户端的概念。

Binder Service binder的服务端,相当于C/S架构中中服务端的概念。

Binder驱动,本身充当一个一个类似路由表,路由分发器。每当一个client想去寻找service的时候,都会经过binder驱动,binder并不关心传输的内容是什么,只需要帮助你分发到服务。

在这里我重新申明一次,所谓的服务端和客户端的概念只是为了更加好理解,实际上在Binder驱动看来并没有所谓的服务和客户端概念,仅仅只有远程端(或者代理端)和本地端的概念。因此Binder在整个IPC进程通信中,谁发出了请求此时就是作为本地端也就是客户端,而远程响应这个请求的则是代理端/远程端,也就是上面说的服务端。

大致知道这些角色之后,也就能清楚上方这个图的意义了。很简单,在Android系统启动的时候,会去启动一个Service Manager的进程。而这个进程会初始化好内核的Binder驱动。此时DNS和路由都准备好了。只要等到服务端注册进来,客户端取链接交互即可。

下面是根据binder的设计的示意图。

image.png

为什么我说Binder和TCP十分相似。首先在我们开发中从来不会注意到binder的存在,更加不会注意到Android开发中我们居然会有信息做了跨进程通信。这也侧面说明了binder的设计优秀以及binder已经对上层来说几乎透明化。

那么让我们略去service manager 和binder 驱动看看service和client之间的关系。

这篇关于Binder驱动的初始化 syscall原理(一)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/388863

相关文章

Spring Boot Interceptor的原理、配置、顺序控制及与Filter的关键区别对比分析

《SpringBootInterceptor的原理、配置、顺序控制及与Filter的关键区别对比分析》本文主要介绍了SpringBoot中的拦截器(Interceptor)及其与过滤器(Filt... 目录前言一、核心功能二、拦截器的实现2.1 定义自定义拦截器2.2 注册拦截器三、多拦截器的执行顺序四、过

Java 队列Queue从原理到实战指南

《Java队列Queue从原理到实战指南》本文介绍了Java中队列(Queue)的底层实现、常见方法及其区别,通过LinkedList和ArrayDeque的实现,以及循环队列的概念,展示了如何高效... 目录一、队列的认识队列的底层与集合框架常见的队列方法插入元素方法对比(add和offer)移除元素方法

SQL 注入攻击(SQL Injection)原理、利用方式与防御策略深度解析

《SQL注入攻击(SQLInjection)原理、利用方式与防御策略深度解析》本文将从SQL注入的基本原理、攻击方式、常见利用手法,到企业级防御方案进行全面讲解,以帮助开发者和安全人员更系统地理解... 目录一、前言二、SQL 注入攻击的基本概念三、SQL 注入常见类型分析1. 基于错误回显的注入(Erro

Spring IOC核心原理详解与运用实战教程

《SpringIOC核心原理详解与运用实战教程》本文详细解析了SpringIOC容器的核心原理,包括BeanFactory体系、依赖注入机制、循环依赖解决和三级缓存机制,同时,介绍了SpringBo... 目录1. Spring IOC核心原理深度解析1.1 BeanFactory体系与内部结构1.1.1

MySQL 批量插入的原理和实战方法(快速提升大数据导入效率)

《MySQL批量插入的原理和实战方法(快速提升大数据导入效率)》在日常开发中,我们经常需要将大量数据批量插入到MySQL数据库中,本文将介绍批量插入的原理、实现方法,并结合Python和PyMySQ... 目录一、批量插入的优势二、mysql 表的创建示例三、python 实现批量插入1. 安装 PyMyS

深入理解Redis线程模型的原理及使用

《深入理解Redis线程模型的原理及使用》Redis的线程模型整体还是多线程的,只是后台执行指令的核心线程是单线程的,整个线程模型可以理解为还是以单线程为主,基于这种单线程为主的线程模型,不同客户端的... 目录1 Redis是单线程www.chinasem.cn还是多线程2 Redis如何保证指令原子性2.

Java中流式并行操作parallelStream的原理和使用方法

《Java中流式并行操作parallelStream的原理和使用方法》本文详细介绍了Java中的并行流(parallelStream)的原理、正确使用方法以及在实际业务中的应用案例,并指出在使用并行流... 目录Java中流式并行操作parallelStream0. 问题的产生1. 什么是parallelS

Java中Redisson 的原理深度解析

《Java中Redisson的原理深度解析》Redisson是一个高性能的Redis客户端,它通过将Redis数据结构映射为Java对象和分布式对象,实现了在Java应用中方便地使用Redis,本文... 目录前言一、核心设计理念二、核心架构与通信层1. 基于 Netty 的异步非阻塞通信2. 编解码器三、

Java HashMap的底层实现原理深度解析

《JavaHashMap的底层实现原理深度解析》HashMap基于数组+链表+红黑树结构,通过哈希算法和扩容机制优化性能,负载因子与树化阈值平衡效率,是Java开发必备的高效数据结构,本文给大家介绍... 目录一、概述:HashMap的宏观结构二、核心数据结构解析1. 数组(桶数组)2. 链表节点(Node

Redis中Hash从使用过程到原理说明

《Redis中Hash从使用过程到原理说明》RedisHash结构用于存储字段-值对,适合对象数据,支持HSET、HGET等命令,采用ziplist或hashtable编码,通过渐进式rehash优化... 目录一、开篇:Hash就像超市的货架二、Hash的基本使用1. 常用命令示例2. Java操作示例三