安卓进阶系列-系统基础

我喜欢

我喜欢花
我喜欢绿色树叶上面的被雨打过留下的露珠
我喜欢暖风
我喜欢干净
我喜欢咸咸的海风
喜欢喜欢的感觉
愿我此感觉常驻
同样祝福你

计算机结构

冯·诺依曼结构

冯·诺依曼结构(也称为存储程序计算机)是一种广泛使用的计算机体系结构,是现代计算机设计的基础。它是由冯·诺依曼(John von Neumann)于1945年提出的,这种结构使用一个单一的存储器来存储指令和数据,通过一个公共总线来传输指令和数据。

冯·诺依曼结构的计算机由五个主要部件组成:中央处理器(CPU)、存储器、输入设备、输出设备和控制器。CPU包括算术逻辑单元(ALU)和控制单元(CU),其中ALU负责执行算术和逻辑操作,CU负责指令的解码和执行。

在冯·诺依曼结构中,指令和数据存储在同一个存储器中,存储器被划分为一系列地址,每个地址存储一个字节。CPU通过总线来访问存储器,可以从任意地址读取指令和数据,并可以将结果写回到存储器中。指令由操作码和操作数组成,CPU从存储器中读取指令,并根据操作码来执行相应的操作。

冯·诺依曼结构的优点是灵活性高,易于实现和扩展,使得计算机可以执行不同类型的任务。但由于指令和数据共享同一条总线,因此读取和写入指令和数据的速度会受到瓶颈的限制,这可能会影响计算机的性能。因此,在高性能计算机和嵌入式系统中,人们使用了一些改进的结构来克服这个问题,如缓存和流水线等。

哈弗结构

哈弗结构(也称为分布式存储器系统)是一种常用于嵌入式系统的计算机体系结构。哈弗结构与冯·诺依曼结构不同之处在于它使用了两个独立的存储器,一个用于存储指令,另一个用于存储数据,这两个存储器可以同时访问。

哈弗结构的CPU包含两个独立的总线接口,一个用于访问指令存储器,另一个用于访问数据存储器。指令和数据分别存储在两个不同的存储器中,因此可以在相同的时钟周期内同时从两个存储器中读取数据。这种结构可以提高计算机的运行效率,减少指令和数据访问的冲突。

由于哈弗结构使用了两个独立的存储器,因此需要更多的硬件资源来实现它。此外,由于指令和数据存储在不同的存储器中,可能会导致一些困难,例如在程序中传递指针时,需要将指针的值从数据存储器中传递到指令存储器中。这些问题可以通过使用高级编程语言和编译器来解决。

总的来说,哈弗结构具有高效的优点,但也需要更多的硬件资源来实现。在嵌入式系统中,由于对运行效率和资源的限制,哈弗结构通常被广泛使用。

冯·诺依曼结构与哈弗结构对比

冯·诺依曼结构和哈佛结构都是计算机体系结构的基本形式,它们之间的主要区别在于存储器和处理器之间的通信方式。

冯·诺依曼结构使用同一条总线来传输指令和数据。指令和数据存储在同一块存储器中,处理器通过总线读取和写入存储器中的指令和数据。这种结构的优点是灵活性高,易于实现和扩展。然而,由于指令和数据共享同一条总线,导致指令和数据的读取和写入不能同时进行,因此会出现瓶颈问题。

哈佛结构则将指令存储器和数据存储器分开,分别使用不同的总线进行通信。处理器可以同时从指令存储器和数据存储器中读取数据,因此具有更高的运行效率。但是,由于指令和数据存储在不同的存储器中,需要更多的硬件资源来实现这种结构。

总的来说,冯·诺依曼结构具有灵活性高的优点,但指令和数据通信会出现瓶颈问题;哈佛结构具有更高的运行效率,但需要更多的硬件资源来实现。
在这里插入图片描述

安卓采用的架构

安卓采用的是冯·诺依曼结构。

安卓是一个基于Linux内核的移动操作系统,它使用了冯·诺依曼结构作为计算机体系结构。这是因为冯·诺依曼结构具有灵活性高、易于实现和扩展等优点,可以让计算机执行不同类型的任务。

在安卓中,所有的应用程序和系统服务都是以二进制代码的形式存储在内存中,并且都使用相同的CPU和内存。因此,采用冯·诺依曼结构的计算机可以让安卓系统更加高效地运行,从而提供更好的用户体验。

此外,安卓系统在架构上也支持哈弗结构,但这种结构通常用于一些嵌入式系统,而不是移动设备。因为哈弗结构需要更多的硬件资源来实现,这对于移动设备来说可能会影响性能和功耗。因此,冯·诺依曼结构更适合用于移动设备和操作系统。

安卓操作系统

安卓操作系统是基于Linux内核开发的移动操作系统,而Linux操作系统是一种开源的自由操作系统,其内核也是Linux。

虽然安卓和Linux都使用了Linux内核,但是它们在很多方面有所不同。下面列举一些主要的区别:

应用程序框架不同:Linux操作系统的应用程序主要是基于X11等桌面环境运行的,而安卓的应用程序框架是基于Java语言的。

用户界面不同:Linux通常使用GNOME、KDE、XFCE等桌面环境,而安卓则使用自己的用户界面。

设备驱动程序不同:安卓使用特定于移动设备的硬件驱动程序,而Linux的驱动程序则更加通用。

应用程序管理方式不同:在Linux系统中,应用程序的管理通常是基于包管理器的,而在安卓系统中则是通过应用商店或APK安装包的方式进行管理。

安全性和隐私保护不同:安卓操作系统通常需要应用程序请求权限才能访问用户的敏感数据,而Linux的用户通常会通过sudo命令或者root权限来管理系统,这样可能会增加安全风险。

总之,虽然安卓操作系统和Linux操作系统都使用了Linux内核,但是它们在很多方面都有所不同。安卓操作系统是专门为移动设备而开发的,而Linux操作系统则是通用的操作系统,可以运行在不同的计算机和设备上。

进程间通讯(IPC)

内存共享

linux内存共享

Linux系统中实现共享内存的方式也有多种,其中比较常用的方式是使用shmget()、shmat()等系统调用。

  • 创建共享区域 shmget()
    shmget()用于创建或访问一个共享内存区域,如果该共享内存区域不存在,则创建一个新的区域。如果区域已经存在,则返回该区域的标识符。
#include <sys/ipc.h>
#include <sys/shm.h>

int shmget(key_t key, size_t size, int shmflg);

其中,key是共享内存区域的键值,size是共享内存区域的大小,shmflg是共享内存区域的访问权限。

  • shmat() 映射内存共享
    shmat()用于将一个共享内存区域连接到调用进程的地址空间中,并返回该内存区域的起始地址。
#include <sys/types.h>
#include <sys/shm.h>

void *shmat(int shmid, const void *shmaddr, int shmflg);

其中,shmid是共享内存区域的标识符,shmaddr是指定连接的地址,如果为NULL,则由系统自动分配地址,shmflg是连接的选项。

安卓内存共享

实现共享内存的方式有多种,其中比较常用的方式是使用匿名映射和共享文件映射。

  • 匿名映射

匿名映射是指创建一个匿名的内存区域,并映射到不同的进程中。在安卓系统中,使用mmap()函数来创建匿名映射。

在进程A中,可以通过以下方式创建一个匿名映射区域:

void *shared_memory = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);

其中,size表示要创建的映射区域的大小,PROT_READ和PROT_WRITE表示映射区域可以读写,MAP_SHARED表示映射区域是共享的,-1表示使用系统自动分配的地址,0表示起始偏移量为0。

在进程B中,也可以通过相同的方式映射同一个地址:

void *shared_memory = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);

此时进程A和进程B都可以通过shared_memory来访问同一块物理内存区域,从而实现共享内存的目的。

  • 共享文件映射

共享文件映射是指将同一个文件映射到不同进程的地址空间中,从而实现不同进程之间的内存共享。在安卓系统中,使用mmap()函数来创建共享文件映射。

在进程A中,可以通过以下方式创建一个共享文件映射区域:

int fd = open("shared_file", O_RDWR|O_CREAT, 0666);
ftruncate(fd, size);
void *shared_memory = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);

其中,open()函数用于打开一个共享文件,ftruncate()函数用于设置文件大小,mmap()函数用于将文件映射到进程A的地址空间中。

在进程B中,也可以通过相同的方式映射同一个文件:

int fd = open("shared_file", O_RDWR, 0666);
void *shared_memory = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);

此时进程A和进程B都可以通过shared_memory来访问同一块物理内存区域,从而实现共享内存的目的。

管道

在Linux系统中,进程间通信是一个重要的概念。管道是一种进程间通信的方式,它可以将一个进程的输出传递给另一个进程的输入,从而实现两个进程之间的通信。

管道的基本原理是通过创建一个临时文件来实现数据传输。在Linux系统中,管道被实现为一种特殊的文件,它具有以下特点:

  • 管道是一个半双工的通道,即只能从一端读取数据,从另一端写入数据;
  • 管道通常用于父子进程之间的通信,父进程可以将数据写入管道,子进程可以从管道中读取数据;
  • 管道有一个固定的缓冲区大小,当缓冲区满时,写入操作会被阻塞,直到有足够的空间;
  • 管道是一个临时文件,当所有相关的进程都关闭了管道之后,操作系统会自动删除这个文件。

Unix Domain Socket

Unix Domain Socket是一种Unix/Linux操作系统中的进程间通信方式。它是基于套接字(Socket)技术实现的,与网络套接字不同的是,Unix Domain Socket不需要通过网络协议栈,而是直接在内核中进行进程间通信。

Unix Domain Socket可以看作是一种本地IPC(Inter-Process Communication)方式,它可以在同一台计算机上的不同进程之间进行通信。使用Unix Domain Socket进行通信的进程之间可以是任意两个进程,它们可以是父子进程、兄弟进程、甚至不在同一个进程组中的进程。

Unix Domain Socket通常使用流式套接字(SOCK_STREAM)或数据报套接字(SOCK_DGRAM)进行通信。流式套接字提供一种可靠的、面向连接的通信方式,类似于TCP协议;数据报套接字则提供一种不可靠的、无连接的通信方式,类似于UDP协议。Unix Domain Socket支持多种通信方式,包括点对点通信、广播通信和多播通信。

Unix Domain Socket(UDS)是专门针对单机内的进程间通信提出来的,有时也被称为IPC
Socket。两者虽然在使用方法上类似,但内部实现原理却有着很大区别。大家所熟识的Network
Socket是以TCP/IP协议栈为基础的,需要分包、重组等一系列操作。而 UDS因为是本机内的“安
全可靠操作”,实现机制上并不依赖于这些协议。
Android中使用最多的一种IPC机制是Binder,其次就是UDS。相关资料显示,2.2版本以前
的Android系统,曾使用Binder作为整个GUI架构中的进程间通信基础。后来因某些原因不得不
弃之而用UDS,可见后者还是有一定优势的。

Unix Domain Socket的优点包括:

它是一种高效的进程间通信方式,因为它不需要经过网络协议栈,通信速度比较快;
它支持多种通信方式,能够满足不同的通信需求;
它可以在同一台计算机上进行通信,避免了网络传输带来的延迟和不稳定性;
它可以通过文件系统进行权限控制,确保通信的安全性。
在Unix/Linux系统中,使用Unix Domain Socket进行进程间通信非常常见,很多系统服务和应用程序都使用Unix Domain Socket来实现进程间通信。例如,Apache Web服务器和PHP-FPM之间就使用Unix Domain Socket进行通信,以提高Web服务器的性能。

同步

常见同步机制

信号量

信号量(Semaphore)是一种用于多进程或多线程之间同步和互斥的机制。它通常被用于控制对共享资源的访问,以避免多个进程或线程同时访问共享资源而引发的问题,如竞态条件、死锁等。

信号量的实现通常基于一个计数器和一个等待队列。计数器用于记录可用的资源数量,等待队列用于记录等待该资源的进程或线程。当一个进程或线程需要访问共享资源时,它会尝试获取信号量。如果计数器的值大于0,则该进程或线程可以访问共享资源,并将计数器的值减1;否则,该进程或线程会进入等待队列等待资源的释放。

当另一个进程或线程释放了该共享资源时,它会调用信号量的V操作(也称为“发信号”操作),将计数器的值加1,同时唤醒等待队列中的一个进程或线程,让其可以访问共享资源。

在Linux系统中,信号量的实现是通过系统调用semget、semop和semctl来完成的。其中,semget用于创建或获取一个信号量集;semop用于对信号量进行操作,如P操作(也称为“等待”操作)和V操作(也称为“发信号”操作);semctl用于对信号量进行控制操作,如获取或设置信号量的值、删除信号量等。

使用信号量可以有效地实现进程或线程之间的同步和互斥,避免了资源竞争和死锁等问题。但是,使用不当也可能会引发一些问题,如信号量的数量过多会增加系统的开销,信号量的使用不当可能会引发饥饿问题等。因此,在使用信号量时需要仔细设计和实现,以确保程序的正确性和性能。

Mutex

Mutex是一种用于多线程之间同步和互斥的机制。它通常被用于控制对共享资源的访问,以避免多个线程同时访问共享资源而引发的问题,如竞态条件、死锁等。

Mutex的实现通常基于一个标志位和一个等待队列。标志位用于记录当前是否有线程正在访问共享资源,等待队列用于记录等待该资源的线程。当一个线程需要访问共享资源时,它会尝试获取Mutex。如果标志位的值为0,则该线程可以访问共享资源,并将标志位的值设置为1;否则,该线程会进入等待队列等待资源的释放。

当另一个线程释放了该共享资源时,它会将标志位的值设置为0,并唤醒等待队列中的一个线程,让其可以访问共享资源。

在Linux系统中,Mutex的实现通常基于互斥锁(Mutex Lock)来完成。互斥锁是一种可重入的、线程安全的锁,它提供了两个主要操作:lock和unlock。当一个线程需要访问共享资源时,它会调用lock操作来获取互斥锁;当它完成访问后,会调用unlock操作来释放互斥锁。如果另一个线程试图获取已经被锁定的互斥锁,它会被阻塞直到锁被释放。

管程

管程(Monitor)是一种高级同步机制,它提供了一种结构化的方式来实现并发程序的同步和互斥。它通常用于多个线程之间共享资源,如共享内存区域、共享文件、网络连接等。

管程由一个包含多个过程(Procedure)和数据的单元组成,其中过程可以被多个线程调用来访问管程中的数据。管程提供了一种机制,确保同一时间只有一个线程可以访问管程中的数据,从而避免了多个线程同时访问数据的问题。

管程中通常包含了互斥量、条件变量等同步机制,用于实现同步和互斥。当一个线程需要访问管程中的数据时,它会首先尝试获取互斥量,以避免多个线程同时访问管程中的数据;然后,它会进入等待状态,直到条件变量满足某个条件。当另一个线程修改了数据并满足了条件时,它会通知等待线程,并将互斥量释放,让等待线程可以访问数据。

管程的实现通常依赖于操作系统提供的同步机制,如互斥锁、条件变量等。在不同的操作系统中,管程的实现可能会有所不同。在一些现代的编程语言中,如Java、Python等,也提供了管程的原语和API,方便开发者使用。

总的来说,管程是一种高级的同步机制,可以提供一种结构化的方式来实现并发程序的同步和互斥,避免了多个线程同时访问共享资源的问题,提高了程序的可维护性和可读性

安卓同步机制

安卓中的Mutex

在Android中,Mutex(互斥锁)是一种同步机制,用于控制多个线程对共享资源的访问。Mutex可以用于实现多个线程对某一资源的互斥访问,以避免多个线程同时访问该资源导致的数据竞争和不一致性。在Android中,可以使用Java中的java.util.concurrent.locks包中的ReentrantLock来实现Mutex。

ReentrantLock是一个可重入的互斥锁,支持公平或非公平的锁。ReentrantLock提供了lock()和unlock()方法来获取和释放锁,与synchronized关键字类似,但是它提供了更多的灵活性和控制。例如,ReentrantLock提供了tryLock()方法来尝试获取锁,如果锁已被占用则返回false,而不是像synchronized关键字一样一直阻塞等待锁的释放。此外,ReentrantLock还提供了lockInterruptibly()方法来实现可中断的锁,即在等待锁的过程中可以响应中断信号。

除了lock()和unlock()方法外,ReentrantLock还提供了其他方法和特性。例如,ReentrantLock可以实现公平锁,即多个线程等待锁时会按照先后顺序获取锁,从而避免饥饿现象。此外,ReentrantLock还提供了Condition对象来实现更为复杂的线程间通信和同步机制。通过Condition对象,线程可以等待某个条件满足时再继续执行,或者唤醒其他等待条件的线程。

在Android中,Mutex通常用于控制对共享资源的访问。例如,在多个线程同时访问某一文件时,可以使用Mutex来实现对该文件的互斥访问,以避免数据竞争和不一致性。另外,在Android中使用Binder通信时,也可以使用Mutex来实现对共享资源的同步访问。例如,在多个进程间共享某一资源时,可以使用Mutex来控制进程对该资源的访问,以保证数据的一致性和正确性。

安卓中的Condition

在Android中,Condition是一种同步机制,它是基于Lock(锁)的。Condition允许线程等待某个条件满足后再继续执行,这个条件可以是另一个线程通知的,也可以是其他事件发生的。在Java中,Condition接口定义在java.util.concurrent.locks包中,它是Lock的一个附属对象。

Condition对象可以通过Lock的newCondition()方法创建,每个Condition对象都与一个Lock对象关联。Condition提供了await()、signal()和signalAll()方法来实现等待和通知机制。

await()方法会使当前线程等待,直到另一个线程通知或中断它。调用await()方法会释放当前线程持有的锁,同时让线程进入等待状态。当另一个线程调用相应的signal()或signalAll()方法来通知当前线程时,当前线程才会重新获取锁并继续执行。

signal()方法会通知一个等待在该Condition对象上的线程,使其从等待状态返回。如果有多个线程在等待,则会通知其中的一个线程,通常是等待时间最长的线程。signalAll()方法会通知所有等待在该Condition对象上的线程,使它们从等待状态返回。

在Android中,Condition通常与Lock一起使用,用于实现更为复杂的同步机制。例如,在多个线程之间共享某一资源时,可以使用Lock和Condition来实现对该资源的互斥访问和同步操作,以避免数据竞争和不一致性。另外,在Android中使用Binder通信时,也可以使用Condition来实现对共享资源的同步访问。例如,在多个进程间共享某一资源时,可以使用Lock和Condition来控制进程对该资源的访问,以保证数据的一致性和正确性。

使用场景:

  • 生产者-消费者模型:在多线程环境下,如果一个或多个线程生产数据,另外一个或多个线程消费数据,就会出现数据竞争问题。为了解决这个问题,可以使用Condition来实现线程间的同步。生产者在生产数据后通过Condition唤醒等待的消费者,消费者在消费数据后通过Condition唤醒等待的生产者。

  • 读写锁:在多线程环境下,如果多个线程同时读取共享资源,而只有一个线程写入共享资源,那么就需要使用读写锁来实现对共享资源的同步访问。可以使用Condition来实现读写锁的等待和通知机制,实现对共享资源的同步访问。

  • 任务队列:在多线程环境下,如果有多个线程需要执行一些任务,那么可以使用任务队列来实现对任务的管理和调度。可以使用Condition来实现任务队列的等待和通知机制,以确保线程按照指定的顺序执行任务。

  • 线程池:在多线程环境下,如果需要创建一个线程池来管理多个线程,那么可以使用Condition来实现对线程池的等待和通知机制,以确保线程池中的线程按照指定的顺序执行任务。

Barrier

Barrier是一种线程同步机制,用于实现多个线程之间的同步和协作。它可以使一组线程在某个点上同步等待,直到所有线程都到达该点后才继续执行。在多线程编程中,Barrier被广泛用于控制线程的执行顺序和协调线程之间的交互。它可以用于解决一些并发编程中的难题,比如循环屏障和分治算法等。

在Java中,Barrier通常使用java.util.concurrent.CyclicBarrier类来实现。它的使用方法与CountDownLatch类似,都是通过调用await()方法来等待其他线程的到达。不同之处在于,CyclicBarrier可以重复使用,即在所有线程到达屏障后,它会自动重置为初始状态,从而可以在之后的程序中继续使用。

下面是CyclicBarrier的一些常用方法和特点:

CyclicBarrier(int parties): 创建一个CyclicBarrier实例,指定需要同步的线程数。
await(): 当线程到达屏障时调用,如果还有其他线程未到达,则该线程将阻塞等待。
reset(): 重置屏障状态,使得CyclicBarrier可以被重复使用。
在Android中,CyclicBarrier可以用于实现多线程操作,例如在后台线程中执行耗时任务,然后将结果传递给UI线程进行更新。它也可以用于实现并发算法,比如分治算法,以提高程序的性能和效率。

总之,CyclicBarrier是一种高效的线程同步机制,可以帮助我们处理多线程编程中的复杂问题。在Android中,使用CyclicBarrier可以提高程序的并发性和可靠性,从而满足用户的需求。

Autolock

AutoLock是一种自动锁定机制,用于简化线程锁定的代码实现。在多线程编程中,锁是一种常见的同步机制,用于保证对共享资源的互斥访问,防止竞态条件和数据竞争等问题。传统的锁定方式需要手动加锁和解锁,代码实现比较繁琐且容易出错。而AutoLock可以自动管理锁定和解锁的过程,从而使代码更加简洁和易于维护。

在Java中,AutoLock通常使用try-finally语句块来实现,例如:

Lock lock = new ReentrantLock();
lock.lock();
try {
    // 访问共享资源
} finally {
    lock.unlock();
}

上述代码中,try-finally语句块会确保锁定的释放,即使在访问共享资源时发生异常也能保证锁定的正确释放。这种方式虽然能够确保锁定的正确性,但是代码比较冗长,不易于维护。

为了简化锁定的代码实现,Java提供了AutoLock机制。使用AutoLock可以将锁定和解锁的过程自动化,例如:

Lock lock = new ReentrantLock();
try (AutoLock ignored = AutoLock.lock(lock)) {
    // 访问共享资源
}

上述代码中,AutoLock的lock方法会获取指定的锁,并返回一个AutoLock对象。当代码块执行完毕后,AutoLock对象会自动释放锁定,从而避免了手动解锁的过程。这种方式比传统的try-finally语句块更加简洁和易于使用。

在Android中,AutoLock同样可以用于简化线程锁定的代码实现。它可以帮助我们处理多线程编程中的复杂问题,提高代码的可读性和可维护性,从而更好地满足用户的需求。

内存管理

虚拟内存

虚拟内存是一种计算机内存管理技术,它将计算机主存(RAM)抽象为一种看似无限大的地址空间,这个地址空间被称为虚拟地址空间。虚拟内存的实现方式是通过将主存和磁盘存储器结合使用,使得程序能够访问比实际内存更大的地址空间。

虚拟内存的主要优势在于它能够让多个进程同时运行,并且在运行过程中它们都能够访问到自己的地址空间,从而实现了内存隔离。此外,虚拟内存还能够将磁盘上的文件映射到虚拟地址空间中,这样程序就可以像访问内存一样访问文件。

在虚拟内存中,每个进程都有自己的虚拟地址空间,这个地址空间包含了程序所需的所有内存。进程使用的每个虚拟地址都被映射到实际的物理地址上。当进程访问一个虚拟地址时,虚拟内存会自动将这个虚拟地址映射到实际的物理地址上。如果这个虚拟地址对应的物理地址不在主存中,虚拟内存会从磁盘中读取相应的数据到内存中,并将这个数据的虚拟地址映射到实际的物理地址上。

虚拟内存的实现需要使用到页表和页式存储技术。页表是一个数据结构,用于记录虚拟地址和物理地址之间的映射关系。页式存储是一种存储管理技术,它将物理内存和虚拟地址空间都分成固定大小的页。当进程访问一个虚拟地址时,虚拟内存会将这个地址转换成页号和页内偏移量,然后使用页表查找这个虚拟地址对应的物理地址。如果这个物理地址不在主存中,虚拟内存会将需要的页面从磁盘中读取到主存中,并更新页表中的映射关系。

虚拟内存的实现方式有多种,包括分页式、分段式和混合式等。其中,分页式是最常见的实现方式,它将主存和虚拟地址空间都分成固定大小的页面,并使用页表来实现地址转换。虚拟内存的大小可以通过修改操作系统的参数来调整,不同的操作系统和硬件平台支持的最大虚拟内存大小也不尽相同。
理解虚拟内存机制,首先要学习三种不同的地址空间。

  1. 逻辑地址

逻辑地址是指程序在运行时所使用的地址,也称为虚拟地址。每个程序都有自己独立的虚拟地址空间,相互之间不会干扰。逻辑地址由操作系统管理,由CPU产生,用于指令和数据的寻址。

逻辑地址由两个部分组成:段地址和偏移地址。其中,段地址指明程序中的某一段内存空间,可以是代码段、数据段、堆栈段等,偏移地址则是该段内部的相对地址,表示距离该段起始地址的偏移量。通过这两部分地址可以唯一地确定程序中的某个内存单元。

逻辑地址的使用可以为程序提供了更大的内存空间,因为操作系统可以将物理内存分成多个虚拟地址空间,每个程序都有自己的虚拟地址空间,而不必担心物理内存的限制。此外,逻辑地址还为操作系统提供了更好的内存管理能力。在使用逻辑地址时,操作系统可以将程序需要的内存空间划分成多个段,对每个段进行保护和管理,从而保证程序之间的内存空间不会互相干扰。

逻辑地址的使用还可以方便地实现动态链接、虚拟内存等功能。动态链接是指程序在运行时才会根据需要链接所需的库文件,而不是在编译时就将库文件链接进程序中。虚拟内存是指将部分程序数据暂时存储在硬盘上,以释放物理内存,待需要时再将其调入内存,从而扩大程序可以使用的内存空间。

  1. 线性地址

线性地址是由虚拟地址通过页表映射得到的,它具有以下两个特征:

  • 线性地址是连续的,这意味着CPU可以将它们视为一个大的地址空间,而不必担心实际内存中的数据在哪里存储。

  • 线性地址是与物理地址分离的。物理地址是在RAM中分配的实际内存地址,而线性地址是虚拟地址,可以映射到任何物理地址。

因此,虚拟内存和线性地址允许操作系统和CPU管理内存,使多个进程可以共享相同的物理内存而不会互相干扰,也可以保护进程不受其他进程的干扰。同时,虚拟内存也为操作系统提供了一种将内存映射到磁盘上的方法,从而使操作系统能够更有效地使用系统资源。

  1. 物理地址

物理地址是指实际的硬件地址,是计算机内存中每个存储单元的唯一地址。在计算机系统中,程序代码和数据必须存储在内存中才能被CPU访问,而CPU访问内存时需要使用物理地址。

物理地址与逻辑地址和线性地址相对应。逻辑地址是程序代码中使用的地址,由程序生成,而不是实际存在于内存中的地址。线性地址是虚拟地址,通过页表映射得到的地址。物理地址是实际存在于内存中的地址,它是通过线性地址再次映射得到的。

物理地址是在计算机系统中非常重要的概念,因为它决定了CPU访问内存时的实际位置。操作系统负责将逻辑地址或线性地址映射到物理地址,这个过程通常是由CPU的内存管理单元(MMU)来处理的。

在访问内存时,CPU将逻辑地址或线性地址发送到MMU中,MMU通过页表将线性地址映射到物理地址。然后,CPU使用物理地址从内存中读取数据或写入数据。

物理地址的大小由计算机系统的架构和硬件决定。在32位系统中,物理地址通常是32位长,可以寻址最多4GB的内存。而在64位系统中,物理地址通常是64位长,可以寻址的内存空间非常巨大,远远超过目前可用的物理内存容量。

mmap函数

mmap() 是一个 UNIX/Linux 操作系统下的系统调用函数,主要用于实现文件的内存映射操作。mmap() 函数可以将一个文件或者其它对象映射到调用进程的地址空间,然后进程就可以像访问内存一样访问该文件。

mmap() 函数原型如下:

void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);

参数解释:

  • addr:指向欲映射的内存起始地址,通常设为 NULL,代表让系统自动选定地址。
  • length:代表将文件中多少字节映射到内存中。
  • prot:映射区域的保护方式,可取值为 PROT_NONE, PROT_READ, PROT_WRITE 和 PROT_EXEC,分别代表该区域不能被访问、该区域可被读取、该区域可被写入和该区域可被执行。这些属性是可以通过或运算组合在一起使用的。
  • flags:指定映射对象的类型,常见的是 MAP_SHARED 和 MAP_PRIVATE。MAP_SHARED 表示映射区域的修改会反映回文件中,而 MAP_PRIVATE 表示映射区域的修改仅仅对该进程可见,不会反映回文件中。在使用 MAP_PRIVATE 时,映射区域是写时拷贝的,即只有当写入时才会发生真正的写操作,此时才会分配物理内存并将文件数据复制到其中。
  • fd:被映射对象的文件描述符。
  • offset:从文件起始位置开始的偏移量,通常设置为 0。
  • mmap() 函数成功执行后,会返回一个指向被映射区域的起始地址的指针,如果操作失败,则返回 MAP_FAILED。

mmap() 函数的优点在于能够快速、方便地实现文件到内存的映射,同时支持共享内存和私有内存两种映射方式。在实现进程间通信和共享数据时,mmap() 函数非常实用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
CruiseYoung提供的带有详细书签的电子书籍目录 http://blog.csdn.net/fksec/article/details/7888251 该资料是《Android高级编程》的源代码 对应的书籍资料见: Android高级编程 基本信息 原书名: Professional Android Application Development 原出版社: Wrox 作者: (英)Reto Meier 译者: 王鹏杰 霍建同 出版社:清华大学出版社 ISBN:9787302228448 上架时间:2010-7-1 出版日期:2010 年6月 开本:16开 页码:398 版次:1-1 内容简介   android提供一个开放的开发环境,为针对移动设备编写创新应用程序带来了激动人心的新机遇。作为使用androidsdk构建这些应用程序的实用指南书籍,《android高级编程》从始至终穿插了一系列示例项目,每个项目都引入android的新功能和新技术,以助您达到最圆满的学习效果。书中介绍android的所有基本功能,并通过简明扼要的示例引导您使用高级功能。    《android高级编程》首先简要介绍android软件栈,接着陈述为手机创建稳定可靠、赏心悦目的应用程序的基本原理。通过学习,您可以打下牢固的理论根基,了解使用当前android 1.0 sdk编写定制移动程序所需的知识,还能灵活快捷地运用未来的增强功能构建最前沿的解决方案。    主要内容    ◆android移动开发的最佳实践    ◆简要介绍活动、intent、清单和资源    ◆如何使用布局和定制view创建用户界面    ◆存储和共享应用程序数据的技术    ◆如何创建基于地图的应用程序,如何使用gps和地理编码位置等基于位置的服务    ◆如何创建和使用后台服务及notification    ◆使用加速计、指南针和摄像头硬件    ◆与电话和网络硬件相关的所有内容,如电话api、sms和网络管理等    ◆高级开发主题,包括安全、ipc以及一些高级图形和用户界面技术    读者对象    本书面向希望在android手机平台上创建应用程序的所有人员。不管是经验丰富的移动开发人员,还是初出茅庐的新手,都能从本书提供的宝贵信息中获益。 作译者   Reto Meier出生于澳大利西南的珀斯市,现居伦敦。 Reto是一位经验丰富的软件开发人员,拥有逾10年的GUI程序架构、设计和开发经验。他在涉足IT行业前曾从事过海洋石油和天然气开发以及金融工作。 Reto始终不渝地追求掌握新技术,从2007年Android发布之初Reto就迷恋上了此项技术。他利用业余时间研究包括WPF在内的多种开发平台以及Google的各种开发工具。 详情请访问Reto的个人网站RadioactiveYak,网址是http://blog.radioactiveyak.com。 目录 封面 -14 封底 -13 扉页 -12 版权 -11 作者简介 -10 前言 -9 目录 -4 第1章 Android简介 1 1.1 一些背景信息 2 1.1.1 不远的过去 2 1.1.2 未来的前景 2 1.2 对Android的误解 3 1.3 开放的移动开发平台 3 1.4 自带的Android应用程序 4 1.5 Android SDK功能 5 1.5.1 对包括摄像头、GPS和加速计在内的硬件的访问 5 1.5.2 自带的Google地图、地理编码和基于位置的服务 6 1.5.3 后台服务 6 1.5.4 SQLite 数据存储和检索数据库 6 1.5.5 共享数据和应用程序间通信 7 1.5.6 使用Google Talk的P2P服务 7 1.5.7 扩展的数据支持和2D/3D图形 7 1.5.8 优化的内存和进程管理 8 1.6 开放手机联盟简介 8 1.7 运行Android的环境 8 1.8 从事Android开发的原因 9 1.8.1 推动Android普及的因素 9 1.8.2 Android的独到之处 10 1.8.3 改变移动开发格局 10 1.9 开发框架简介 11 1.9.1 开发包中的资源 11 1.9.2 理解Android软件栈 12 1.9.3 Dalvik虚拟机 13 1.9.4 Android应用程序架构 14 1.9.5 Android库 14 1.9.6 高级Android库 15 1.10 小结 16 第2章 开始入手 17 2.1 Android开发 18 2.1.1

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

陈德山

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值