当前位置: 首页 > news >正文

业网站建设黄埔网站建设设计

业网站建设,黄埔网站建设设计,宽屏网站js,深圳制作网站流程最近在看《深入理解Android内核设计思想#xff08;第2版#xff09;》#xff0c;个人感觉很不错#xff0c;内容很多#xff0c;现将书里个人认为比较重要的内容摘录一下#xff0c;方便后期随时翻看。 计算机体系结构 硬件是软件的基石#xff0c;所有的软件功能最…最近在看《深入理解Android内核设计思想第2版》个人感觉很不错内容很多现将书里个人认为比较重要的内容摘录一下方便后期随时翻看。 计算机体系结构 硬件是软件的基石所有的软件功能最终都是由硬件来实现的。计算机体系结构作为一门学科是软件和硬件的抽象体。 1.1 冯·诺依曼结构 1.2 哈佛结构 哈佛结构Harvard Architecture并不是作为冯诺依曼结构的对立面出现的相反它们都属于stored-program类型体系。区别就在于前者的指令与数据并不保存在同一个存储器中即哈佛结构是对冯诺依曼结构的改进与完善。 由于取指令和数据无法同步进行冯诺依曼结构的执行速率并不占优势。而采用哈佛结构的计算机由于指令和数据的单独存储可以在执行操作的同时预读下一条指令所以在一定程度上可以提高其吞吐量。 哈佛结构的缺点在于构架复杂且需要两个存储器因而通常会被运用在对速度有特殊需求且成本预算相对较高的场合。目前市面上采用哈佛结构的芯片包括ARM公司的ARM9、ARM11以及ATMEL公司的AVR系列等。 不论是何种结构它们所包含的基本元素都是不变的即 CPU中央处理器 内存储器 输入设备 输出设备。 其中输入和输出设备一般会统称为I/O设备外存储器实际上也归于这一类。因而最后可以把计算机结构简化为 中央处理器 内存储器 I/O设备。 什么是操作系统 操作系统的共性 操作系统对硬件设备本身是有要求的。 同一款操作系统可以安装在不同型号的机器上。 操作系统提供可用的人机交互界面。 操作系统支持用户编写和安装程序。 操作系统“肩负”两大重任。 1.面向下层 管理硬件。这里的硬件是笼统的概念它包含了CPU、内存、Flash、各种I/O设备等系统中所有硬件组成元素。 2.面向上层 一方面操作系统需要为用户提供可用的人机交互界面另一方面它还负责为第三方程序的研发提供便捷、可靠、高效的APIApplication Programming Interface。这样上层应用的设计实现就可以不用直接面向硬件从而大大缩短了应用开发的时间。 操作系统定义计算机操作系统是负责管理系统硬件并为上层应用提供稳定编程接口和人机交互界面的软件集合。 进程间通信的经典实现 操作系统中的各个进程通常运行于独立的内存空间中并且有严格的机制来防止进程间的非法访问。但是这并不代表进程与进程间不允许互相通信 广义地讲进程间通信Inter-process communicationIPC是指运行在不同进程不论是否在同一台机器中的若干线程间的数据交换具体如图所示 从这个定义可以看到 IPC中参与通信的进程既可运行在同一台机器上也允许它们存在于各自的设备环境中RPC。如果进程是跨机器运行的则通常由网络连接在一起这无疑给进程间通信的实现增加了难度。 实现方式多种多样。原则上任何跨进程的数据交换都可以称为进程间通信。除了传统意义上的消息传递、管道等还可以使用一些简单的方法来实现对性能要求不高的进程通信。比如 文件共享 比如两个进程间约定以磁盘上的某个文件作为信息交互的媒介。在这种情况下要特别注意不同进程访问共享文件时的同步问题。 操作系统提供的公共信息机制 比如Windows上的注册表对于所有进程来说都是可以访问的因而在特定情况下也能作为进程间信息交换的平台。 虽然各操作系统所采用的进程间通信机制可以说五花八门但以下将要讨论的几种却因其高效、稳定等优点而几乎被广泛应用于所有操作系统中。 1.1 共享内存Shared Memory 共享内存是一种常用的进程间通信机制。由于两个进程可以直接共享访问同一块内存区域减少了数据的复制操作因而速度上的优势比较明显。一般情况下实现内存共享的步骤如图所示。 Step1. 创建内存共享区 进程1首先通过操作系统提供的API从内存中申请一块共享区域——比如在Linux环境中可以通过shmget函数来实现。生成的共享内存块将与某个特定的key即shmget的第一个参数进行绑定。 Step2. 映射内存共享区 成功创建了内存共享区后我们需要将它映射到进程1的空间中才能进一步操作。在Linux环境下这一步可以通过shmat来实现。 Step3.访问内存共享区 进程1已经创建了内存共享区那么进程2如何访问到它呢没错就是利用第一步中的key。具体而言进程2只要通过shmget并传入同一个key值即可。然后进程2执行shmat将这块内存映射到它的 空间中。 Step4. 进程间通信 共享内存的各个进程实现了内存映射后便可以利用该区域进行信息交换。由于内存共享本身并没有同步的机制所以参与通信的诸进程需要自己协商处理。 Step5. 撤销内存映射区 完成了进程间通信后各个进程都需要撤销之前的映射操作。在Linux中这一步可以通过shmdt来实现。 Step6. 删除内存共享区 最后必须删除共享区域以便回收内存。在Linux环境中可以通过shctl函数来实现。 1.2 管道Pipe 管道也是操作系统中常见的一种进程间通信方式它适用于所有POSIX系统以及Windows系列产品。 Pipe这个词很形象地描述了通信双方的行为即进程A与进程B。 1.分立管道的两边进行数据的传输通信。 2.管道是单向的意味着一个进程中如果既要“读”也要“写”的话那么就得建立两根管道。这点很像水管的特性通常水流只做正向或反向行进。 3.一根管道同时具有“读取”端read end和“写入”端write end。比如进程A从write end写入数据那么进程B就可以从read end读取到数据。 4.管道有容量限制。即当pipe满时写操作write将阻塞反之读操作read也会阻塞。 1.3 UNIX Domain Socket UNIX Domain SocketUDS是专门针对单机内的进程间通信提出来的有时也被称为IPC Socket。 大家所熟识的Network Socket是以TCP/IP协议栈为基础的需要分包、重组等一系列操作。而UDS因为是本机内的“安全可靠操作”实现机制上并不依赖于这些协议。 Android中使用最多的一种IPC机制是Binder其次就是UDS。相关资料显示2.2版本以前的Android系统曾使用Binder作为整个GUI架构中的进程间通信基础。后来因某些原因不得不弃之而用UDS可见后者还是有一定优势的。 使用UDS进行进程间通信的典型流程如图所示 UDS的基本流程与传统Socket一致只是在参数上有所区分。下面提供一个UDS的范例功能如下 服务器端监听IPC请求 客户端发起IPC申请 双方成功建立起IPC连接 客户端向服务器端发送数据证明IPC通信是有效的。 1.4 RPCRemote Procedure Calls RPC涉及的通信双方通常运行于两台不同的机器中。在RPC机制中开发人员不需要特别关心具体的中间传输过程是如何实现的这种“透明性”可以较大程度地降低研发难度如图所示。 一般而言一个完整的RPC通信需要以下几个步骤 1.客户端进程调用Stub接口 2.Stub根据操作系统的要求进行打包并执行相应的系统调用 3.由内核来完成与服务器端的具体交互它负责将客户端的数据包发给服务器端的内核 4.服务器端Stub解包并调用与数据包匹配的进程 5.进程执行操作 6.服务器以上述步骤的逆向过程将结果返回给客户端。 同步机制的经典实现 既然操作系统支持多个进程多线程的并发执行那么它们之间难免会出现相互制约的情况。比如两个进程线程需要共享唯一的硬件设备或者同一块内存区域又或者像生产流水线一样进程 线程的工作依赖于另一方对共享资源的执行结果——换句话说它们存在协同关系。 从定义上来讲如果多个包括两个进程间存在时序关系需要协同工作以完成一项任务则称为同步如果它们并不满足协同的条件而只是因为共享具有排他性的资源时所产生的关系则称为互 斥。 操作系统中常见的几种同步机制。 信号量Semaphore 信号量与PV原语操作是由Dijkstra发明的也是使用最为广泛的互斥方法之一。它包括以下几个元素 Semaphore S信号量 Operation P来自荷兰语proberen意为test有时也表达为wait() Operation V来自荷兰语verhogen意为increment有时也表达为signal()。 Semaphore S用于指示共享资源的可用数量。P原语可以减小S计数V则增加它的计数。由此可知当某个进程想进入共享区时首先要执行P操作同理想退出共享区时执行V操作。PV原语都属于原子操 作Atomic Operations意味着它们的执行过程是不允许被中断的。 P操作的执行过程 信号量S自减1 如果此时S仍然≥0说明共享资源此时是允许访问的因而调用者将直接返回然后开始操作共享资源 否则的话就要等待别人主动释放资源这种情况下调用者会被加入等待队列中直到后续被唤醒 当某人释放了共享资源后处于等待队列中的相关取决于具体情况对象会被唤醒此时该对象就具备了资源的访问权。 V操作的执行过程 信号量S自增1 此时如果S0说明当前没有希望访问资源的等待者所以直接返回 否则V操作要唤醒等待队列中的相关对象对应P操作中的最后一步。 Mutex Mutex是Mutual Exclusion的缩写其释义为互斥体。那么它和Semaphore有什么区别和联系呢 根据计算机领域的普遍观点如果资源允许多个对象同时访问称为Counting Semaphores而对于只允许取值0或1即locked/unlocked的Semaphore则叫作Binary Semaphore。后者可以认为与Mutex具有相同的性质。换句话说Mutex通常是对某一排他资源的共享控制——要么这个资源被占用locked要么就是可以访问的unlocked。在很多操作系统中Binary Semaphore和Mutex没有本质差异前者是特定的Semaphore机制而后者相较于Semaphore在实现上则更为简单。 管程Monitor 管程实际上是对Semaphore机制的延伸和改善是一种控制更为简单的同步手段。 采用Semaphore机制的程序易读性相对较差对于信号量的管理也分散在各个参与对象中因而有可能由此引发一系列问题如死锁、进程饿死等。为了使资源的互斥访问更利于维护科学家们提出了管 程的概念。如下 管程Monitor是可以被多个进程/线程安全访问的对象object或模块module。 管程中的方法都是受mutual exclusion保护的意味着在同一时刻只允许有一个访问者使用它们。另外管程还具备如下属性 安全性 互斥性 共享性。 Linux Futex FutexFast Userspace muTEXes是由Hubertus Franke等人发明的一种同步机制首次出现在Linux 2.5.7版本并在2.6.x中成为内核主基线的一部分。它的核心优势已经体现在其名称中了即“Fast”。Futex的“快”主要体现于它在应用程序空间中就可以应对大多数的同步场景只有当需要仲裁时才会进入内核空间这样一来节省了不少系统调用和上下文切换的时间。 Futex在Android中的一个重要应用场景是ART虚拟机。如果Android版本中开启了ART_USE_FUTEXES宏那么ART虚拟中的同步机制就会以Futex为基石来实现。 对于不存在竞争的情况下采用futex机制在用户态就可以完成锁的获取而不需要通过系统调用进入内核态从而提高了效率。 同步范例 理解了以上几种同步机制的原理后我们再来分析一个范例即经典的生产者与消费者问题。Android系统源码中有多个地方都应用了这个经典模型如音频子系统里AudioTrack和AudioFlinger间的数据交互。 “The producer-consumer problem”描述如下 两个进程共享一块大小为N的缓冲区——其中一个进程负责往里填充数据生产者而另一个进程则负责往里读取数据消费者。 问题的核心有两点 当缓冲区满时禁止生产者继续添加数据直到消费者读取了部分数据后 当缓冲区空时消费者应等待对方继续生产后再执行操作。 如果使用信号量来解决这个问题需要用到3个Semaphore。功能分别如下 S_emptyCount用于生产者获取可用的缓冲区空间大小初始值为N。 S_fillCount用于消费者获取可用的数据大小初始值为0。 S_mutex用于操作缓冲区初始值为1。 对于生产者来说执行步骤如下 循环开始 Produce_item PS_emptyCount PS_mutex Put_item_to_buffer VS_mutex VS_fillCount 继续循环。 对于消费者来说执行步骤如下 循环开始 PS_fillCount PS_mutex Read_item_from_buffer VS_mutex VS_emptyCount Consume 继续循环。 一开始S_emptyCount的值为NS_fillCount的值为0所以消费者在PS_fillCount后处于等待状态。而生产者首先生产一件产品然后获取S_emptyCount——因为它为N处于可访问状态所以产 品可以被放入buffer中。之后生产者通过VS_fillCount来增加可用产品的计数并唤醒正于这个Semaphore上等待的消费者。于是消费者开始读取数据并利用VS_emptyCount来表明buffer又空出了一个位置。 生产者和消费者就是如此循环反复来完成整个工作这样我们就通过Semaphore机制保证了生产者和消费者的有序执行。 Android中的同步机制 Android封装的同步类包括 Mutex 头文件是frameworks/native/include/utils/Mutex.h。 Android中的Mutex只是对pthread提供的API的简单再封装所以函数声明和实现体都放在同一个头文件中这样做也方便了调用者的操作。 另外Mutex中还包含一个AutoLock的嵌套类它是利用变量生命周期特点而设计的一个辅助类。 Condition 头文件是frameworks/native/include/utils/Condition.h Condition是“条件变量”在Android系统中的实现类它是依赖Mutex来完成的。 Barrier 头文件是frameworks/native/services/surfaceflinger/Barrier.h Barrier是同时基于Mutex和Condition实现的一个模型。 进程间同步——Mutex class Mutex { public:enum {PRIVATE 0,//只限同一进程间的同步SHARED 1//支持跨进程间的同步};这说明Mutex既可以处理进程内同步的情况也能完美解决进程间同步的问题。 如果在Mutex构造时指定它的type是SHARED的话说明它是适用于跨进程共享的。此时Mutex将进一步调用pthread_mutexattr_setpshared接口来为这个互斥量设置PTHREAD_PROCESS_SHARED属性。 与Semaphore不同的是Mutex只有两种状态即0和1。所以这个类只提供了3个重要的接口函数 status_t lock(); //获取资源锁void unlock(); //释放资源锁status_t tryLock(); /*不论成功与否都会及时返回而不是等待*/当调用者希望访问临界资源时它必须先通过lock()来获得资源锁。如果此时资源可用这个函数将马上返回否则会进入阻塞等待直到有人释放了资源锁并唤醒它。释放资源锁调用的是unlock()同时正在等待使用这个锁的其他对象就会被唤醒然后继续执行它的任务。另外Mutex还特别提供了一个tryLock()来满足程序的多样化需求。这个函数仅会“试探性”地查询资源锁是否可用——如果答案是肯定的就获取它然后成功返回返回值为0从这一点看它和lock()的表现是一样的但在资源暂不可用的情况下它并不会进入等待而同样是立即返回只是返回值不为0。 这三个函数的实现很简单具体源码如下 inline status_t Mutex::lock() {return -pthread_mutex_lock(mMutex);//变量mMutex的类型是 pthread_mutex_t } inline void Mutex::unlock() {pthread_mutex_unlock(mMutex); } inline status_t Mutex::tryLock() {return -pthread_mutex_trylock(mMutex); }Mutex实际上只是基于pthread接口的再封装。 条件判断——Condition Condition的字面意思是“条件”。换句话说它的核心思想是判断“条件是否已经满足”——满足的话马上返回继续执行未完成的动作否则就进入休眠等待直到条件满足时有人唤醒它。 这种情况用Mutex能实现吗理论上讲的确是可以的。举一个例子假设两个线程A和B共享一个全局变量vari且它们的行为如下。 Thread A不断去修改vari每次改变后的值未知。 Thread B当vari为0时它需要做某些动作。 显而易见A和B都想访问vari这个共享资源属于Mutex的问题领域。但需要商榷的细节是线程A的“企图”仅仅是获得vari的访问权而线程B则“醉翁之意不在酒”其真正等待的条件是“vari等于0”。 那么如果用Mutex去完成的话线程B就只能通过不断地读取vari来判断条件是否满足有点类似于下面的伪代码 while(1)//死循环直到条件满足时退出 {acquire_mutex_lock(vari);//获取vari的Mutex锁if(0 vari) //条件满足{release_mutex_lock(vari);//释放锁break;}else{release_mutex_lock(vari);//释放锁sleep();//休眠一段时间} }对于线程B而言什么时候达到条件vari0是未知的这点和其他只是使用vari的线程比如线程A有很大不同因而采用轮询的方式显然极大地浪费了CPU时间 再举一个生活中的例子以加深大家的理解。比如有一个公共厕所假设同时只能供一个人使用。现在把希望使用这一资源的人分为两类其一当然是正常使用厕所的用户类似于线程A其二就是更换厕纸的工作人员类似于线程B。如果我们使用Mutex来解决这一资源同步共享问题会发生什么情 况呢 首先对于用户来说并不会产生太大的影响他们仍然正常排队、使用、归还。 但对于工作人员来说就有点麻烦了。在Mutex机制下工作人员也需要和其他人一样正常排队。只有等排到他时他才能进去看下厕纸是否用完——用完就更换否则就什么也不做直接退出然后继续排队等待如此循环往复。假设排一次队需要5分钟而工作人员进去后需要更换厕纸的概率只有1/10。那么可想而知这位工作人员的效率是相当低的因为他的时间都浪费在等待“厕纸为空”上了。 所以我们需要寻找另一种模型来解决这一特殊的同步场景。可行的方法之一是工作人员不需要排队而是由其他人通知他厕所缺纸的事件。这样做既减少了排队人员的数量提高了资源的使用率同时也改善了工作人员的办事效率可谓一举两得。 class Condition { public:enum { //和Mutex一样它支持跨进程共享PRIVATE 0,SHARED 1};…status_t wait(Mutex mutex); //在某个条件上等待status_t waitRelative(Mutex mutex, nsecs_t reltime); /*也是在某个条 件上等待增加了超时退出功能*/void signal(); //条件满足时通知相应等待者void broadcast(); //条件满足时通知所有等待者 private: #if defined(HAVE_PTHREADS)pthread_cond_t mCond; #elsevoid* mState; #endif };“栅栏、障碍”——Barrier Condition表示“条件”而Barrier表示“栅栏、障碍”。后者是对前者的一个应用即Barrier是填充了“具体条件”的Condition这给我们理解Condition提供了一个很好的范例 class Barrier { public:inline Barrier() : state(CLOSED) { }inline Barrier() { }void open() {Mutex::Autolock _l(lock);state OPENED;cv.broadcast();}void close() {Mutex::Autolock _l(lock);state CLOSED;}void wait() const {Mutex::Autolock _l(lock);while (state CLOSED) {cv.wait(lock);}} private:enum { OPENED, CLOSED };mutable Mutex lock;mutable Condition cv;volatile int state; };Barrier总共提供了3个接口函数即wait()open()和close()。。既然说它是Condition的实例那么“条件”是什么呢稍微观察一下就会发现是其中的变量stateOPENED另一个状态当然就是CLOSED——这有点类似于汽车栅栏的开启和关闭。在汽车通过前它必须要先确认栅栏是开启的于是调用wait()。如果条件不满足那么汽车就只能停下来等待。这个函数首先获取一个Mutex锁然后才是调用Condition对象cv为什么呢我们知道Mutex是用于保证共享资源的互斥使用的这说明wait()中接下来的操作涉及了对某一互斥资源的访问即state这个变量。可以想象一下假如没有一把对state访问的锁那么当wait与open/close同时去操作它时有没有可能引起问题呢 假设有如下步骤 Step 1. 线程A通过wait()取得state值发现是CLOSED。 Step 2. 线程B通过open()取得state值将其改为OPENED。 Step 3. open()唤醒正在等待的线程。因为此时线程A还没有进入睡眠所以实际上没有线程需要唤醒。 Step4. 另外线程A因为state CLOSED所以进入等待但这时候的栅栏实际上已经开启了这将导致wait()调用者所在线程得不到唤醒。 这样就很清楚了对于state的访问必须有一个互斥锁的保护。 接下来我们分析Condition::wait()的实现 inline status_t Condition::wait(Mutex mutex) {return -pthread_cond_wait(mCond, mutex.mMutex); }和Mutex一样直接调用了pthread提供的API方法。 pthread_cond_wait的逻辑语义如下。 Step1. 释放锁mutex。 Step2. 进入休眠等待。 Step3. 唤醒后再获取mutex锁。 也就是经历了先释放再获取锁的过程为什么这么设计呢 由于wait即将进入休眠等待假如此时它不先释放Mutex锁那么open()/close()又如何能访问“条件变量”state呢这无疑会使程序陷入互相等待的死锁状态。所以它需要先行释放锁再进入睡眠。之后因为open()操作完毕会释放锁也就让wait()有机会再次获得这一Mutex锁。 加解锁的自动化操作——Autolock 在Mutex类的内部还有一个Autolock嵌套类从字面上看它应该是为了实 现加、解锁的自动化操作那么如何实现呢 其实很简单看看这个类的构造和析构函数大家就明白了 class Autolock {public:inline Autolock(Mutex mutex) : mLock(mutex) { mLock.lock(); }inline Autolock(Mutex* mutex) : mLock(*mutex) { mLock.lock(); }inline Autolock() { mLock.unlock(); }private:Mutex mLock;};当Autolock构造时会主动调用内部成员变量mLock的lock()方法来获取一个锁。而析构时的情况正好相反调用它的unlock()方法释放锁 读写锁——ReaderWriterMutex 重点分析一下Art虚拟机中的一种特殊mutex即ReaderWriterMutex。从字面意思上来理解它代表的是“读写锁”。与普通的mutex相比它主要提供了如下一些差异接口 void ExclusiveLock(Thread* self) ACQUIRE(); void ExclusiveUnlock(Thread* self) RELEASE(); bool ExclusiveLockWithTimeout(Thread* self, int64_t ms, int32_t ns)EXCLUSIVE_TRYLOCK_FUNCTION(true); void SharedLock(Thread* self) ACQUIRE_SHARED() ALWAYS_INLINE; void SharedUnlock(Thread* self) RELEASE_SHARED() ALWAYS_INLINE;其中Exclusive和Shared分别代表Write和Read权限这也很好地诠释了这种锁的特点是允许有多个对象共享Read锁但同时却只能有唯一一个对象拥有Write锁。ReaderWriterMutex可以有以下3种状态。 Free还没有被任何对象所持有的情况。 Exclusive当前被唯一一个对象持有的情况。 Shared被多个对象持有的情况。 各个状态间所允许的操作及操作结果如表所示。 操作系统内存管理基础 内存管理Memory Management旨在为系统中的所有Task提供稳定可靠的内存分配、释放与保护机制。 内存需要理解几个核心 虚拟内存 内存分配与回收 内存保护。 虚拟内存Virtual Memory 虚拟内存的出现为大体积程序的运行提供了可能。它的基本思想是。 将外存储器的部分空间作为内存的扩展如从硬盘划分出4GB大小。 当内存资源不足时系统将按照一定算法自动挑选优先级低的数据块并把它们存储到硬盘中。 后续如果需要用到硬盘中的这些数据块系统将产生“缺页”指令然后把它们交换回内存中。 这些操作都是由操作系统内核自动完成的对上层应用“完全透明”。 内存分配与回收 对应用程序而言内存的分配和回收是它们最关心的。换句话说这是程序开发者与操作系统内存管理模块间的直接交互点 既然是操作系统的重要组成部分内存管理模块也同样遵循操作系统的定义即为“上层建筑”控制和使用硬件内存储器提供有效的接口方法。Linux Kernel所面对的核心问题包括但不限于 1保证硬件无关性 每个硬件平台的物理内存型号、大小甚至架构比如不同的体系结构等都可能是不一样的。这种差异绝不能体现在应用程序上操作系统应尽可能实现向上的“透明”。 2动态分配内存和回收 需要考虑的问题很多如如何为内存划分不同的使用区域分配的粒度问题即分配的最小单位如何管理和区别已经使用和未使用的内存如何回收和再利用等。 3内存碎片 和磁盘管理一样内存也一样会有碎片的问题。举个简单的例子如图所示。 在这个例子中假设初始状态有6块未使用的内存单元。随着程序不断地申请使用前面5块已经成功分配。此时若某程序释放了第2块单元那么最后将会有两块不连续的未使用内存单元存在——碎片形 成 在Android系统中内存的分配与回收分为两个方向 Native层 本地层的程序基本上是由C/C编写的与开发人员直接相关的内存函数包括malloc/freenew/delete等。 Java层 大多数Android的应用程序都由Java语言编写。Java相对于C在内存管理上做了很多努力可以帮助开发人员在一定程度上摆脱内存的各种困扰。但是Java本身并不是万能的需要研发者在程序设计过 程中保持良好的内存使用规范并对Android提供的内存管理机制有比较深入的理解。 进程间通信——mmap 上层应用在使用Binder驱动前就必须通过mmap()来为其正常工作提供环境。 正如其名所示Memory Mapmmap可以将某个设备或者文件映射到应用进程的内存空间中这样访问这块内存就相当于对设备/文件进行读写而不需要再通过read()write()了。由此可见理论上mmap也可以用于进程间通信即通过映射同一块物理内存来共享内存。这种方式因为减少了数据复制的次数在一定程度上能提高进程间通信的效率 写时拷贝技术Copy on Write COWCopy on Write是Linux中一个非常关键的技术。它的基本思想用一句话来概括就是多个对象在起始时共享某些资源如代码段、数据段直到某个对象需要修改该资源时才拥有自己的一份拷贝。 当我们调用fork()函数生成一个子进程时内核并没有马上为这个“另立门户”的孩子分配自己的物理内存而是仍然共享父进程的 固有资源。这样一来“分家”过程就非常快了——理论上只需要注册一个“门户”就可以了。而如果新进程对现有资源“不是很满意” 希望自己去做些修改那么此时才需要为它提供自己的“施展空间”。特别是如果子进程在fork()之后很快地调用了exec概率很 大从而载入与父进程迥异的映像那么COW的存在显然可以较大程度地避免不必要的资源操作从而提升了运行速度。 Android中的Low Memory Killer 嵌入式设备的一个普遍特点是内存容量相对有限。当运行的程序超过一定数量或者涉及复杂的计算时很可能出现内存不足进而导致系统卡顿的现象。Android系统也不例外它同样面临着设备物理内存短缺的困境。另外细心的开发者应该已经注意到了对于已经启动过一次的Android程序再一次启动所花的时间明显减少了。原因就在于Android系统并不马上清理那些已经“淡出视野”的程序比如调用Activity.finish退出UI界面。换句话说它们在一定的时间里仍然驻留在内存中虽然用户已经感觉不到它们的存在。这样做的好处是明显的即下一次启动不需要再为程序重新创建一个进程坏处也同样存在那就是加大了内存OOMOut Of Memory的概率。 那么应该如何掌握平衡点呢 熟悉Linux的开发人员应该知道底层内核有自己的内存监控机制即OOMKiller。一旦发现系统的可用内存达到临界值这个OOM的管理者就会自动跳出来“收拾残局”。根据策略的不同OOM的处理手段略有差异。不过它的核心思想始终是 按照优先级顺序从低到高逐步杀掉进程回收内存。 优先级的设定策略一方面要考虑对系统的损害程度例如系统的核心进程优先级通常较高另一方面也希望尽可能多地释放无用内存。根据经验一个合理的策略至少要综合以下几个因素 进程消耗的内存 进程占用的CPU时间 oom_adjOOM权重。 Android匿名共享内存Anonymous SharedMemory Anonymous Shared Memory简称Ashmem是Android特有的内存共享机制它可以将指定的物理内存分别映射到各个进程自己的虚拟地址空间中从而便捷地实现进程间的内存共享。 JNI JNIJava NativeInterface是一种允许运行于JVM的Java程序去调用反向亦然本地代码通常JNI面向的本地代码是用C、C以及汇编语言编写的的编程框架。本地代码通常与硬件或者操作系统有关联因而会在一定程度上破坏Java本身的可移植性。不过有时这种方法是必需的如Android系统中就采用了大量JNI手段去调用本地层的实现库。 通常有以下3种情况需要用到JNI 1.应用程序需要一些平台相关的feature的支持而Java无法满足。 2.兼容以前的用其他语言书写的代码库。使用JNI技术可以让Java层的代码访问到这些旧库实现一定程度的代码复用。 3.应用程序的某些关键操作对运行速度要求较高。这部分代码可以用底层语言如汇编来编写再通过JNI向Java层提供访问接口。 Java函数的本地实现 创建一个可供Java代码调用的本地函数的步骤如下 1.将需要本地实现的Java方法加上native声明 2.使用javac命令编译Java类 3.使用javah生成.h头文件 4.在本地代码中实现native方法 5.编译上述的本地方法生成动态链接库 6.在Java类中加载这一动态链接库 7.Java代码中的其他地方可以正常调用这一native方法。 Java中的反射机制 Java中创建一个Class类最常见的方式如下 FileOutputStream fout new FileOutputStream(fd); 这种情况意味着我们在编译期便可以确定Class类型。此时编译器可以对new关键字做很多优化工作所以这种场景下的运行效率通常是最好的是开发人员的首选方式。 而对于那些无法在编译阶段就得到确定的Class类的情况我们希望有一种技术可以在程序运行过程中去动态地创建一个对象——这就是反射机制。 反射机制能够赋予程序检查和修正运行时行为的能力下面我们就以Class类的动态创建为例简单分析一下其内部的实现原 理 Class? clazz null;try {clazz Class.forName(android.media.MediaMetadataRetriever);instance clazz.newInstance();Method method clazz.getMethod(setDataSource, String.class);method.invoke(instance, filePath);Class.forName通过native函数classForName调用到本地层在Android N版本中对应的具体函数如下 /*art/runtime/native/java_lang_Class.cc*/ static jclass Class_classForName(JNIEnv* env, jclass, jstring javaName, jboolean initialize, jobject javaLoader) {ScopedFastNativeObjectAccess soa(env);ScopedUtfChars name(env, javaName);…Handlemirror::ClassLoader class_loader(hs.NewHandle(soa.Decodemirror::ClassLoader * (javaLoader)));ClassLinker* class_linker Runtime::Current()- GetClassLinker();Handlemirror::Class c(hs.NewHandle(class_linker-FindClass(soa.Self(), descriptor.c_str(), class_loader)));…return soa.AddLocalReferencejclass(c.Get()); }可见forName最终是通过ClassLinker::FindClass来找到目标类对象的。反射机制提供的其他接口也是类似的——只有经过虚拟机的统一管理才有可能既为程序提供灵活多样的动态能力同时又保证了程序的正常运行。
http://www.dnsts.com.cn/news/202373.html

相关文章:

  • 天津做胎儿鉴定网站wordpress邮箱美化
  • 有没有专做烘焙的网站给 小企业 建设网站
  • 推广网站文案素材ps怎么做电商网站
  • 青岛市黄岛区网站建设html5手机论坛网站模板
  • 衡阳网站设计嘉兴做网络推广的公司
  • 购物网站成功案例手机网站的好外
  • 西苑做网站公司网站素材类型
  • 网站建设公司效果网站改版数据来源表改怎么做
  • 中航建设集团有限公司网站广州建设集团
  • 做网站的准备什么软件做网站最好的
  • 长沙seo制作网站seo分析案例
  • 免费个人域名邮箱杭州seo运营
  • 抽奖机网站怎么做wordpress获取站点链接
  • 做移动网站优化优亚马逊电子商务网站的建设
  • 上海网站设计公司推荐亿企邦淮南建设网
  • 济南网站制作费用赚钱软件的套路
  • 求职网站开发如何修改网站图片
  • 义乌本地网站开发网站开发实训心得800
  • 企业网站的推广形式有wordpress百度收录之自动推送设置
  • 做 了一个 家教 网站四川省城镇建设二次供水网站南京尔顺科技发展有限公司表扬信息
  • 简述商务网站建设小蝌蚪紧急自动跳转中
  • 网站制作完成后创世网络网站建设怎么样
  • 帝国cms做企业网站生鲜农产品网站建设
  • 长春微建站是哪个平台的帝国cms做网站怎样维护
  • 有没有做文创的网站准备纸巾
  • 韩国男女做那个视频网站深圳外贸建网站
  • 怎么创建网站与网页网站建设相关岗位名称
  • 哪个网站做音基的题不花钱应用关键词优化
  • 网上购物网站建设的实训报告建设厅网站的秘钥怎么买
  • 河北网站seo企业咨询公司有哪些