sequenceDiagram
sharedMemory->>+ashmem_dev: 1: ashmem_create_region
ashmem_dev->>ashmem_dev : fd = __ashmem_open() 创建ashmem_area放入file->private_data
ashmem_dev->>ashmem_dev: ioctl(fd, ASHMEM_SET_NAME, buf)
ashmem_dev->>ashmem_dev: ioctl(fd, ASHMEM_SET_SIZE, size)
ashmem_dev->>-sharedMemory: fd
sharedMemory->>+Os : 2: mmap
Os->>-ashmem : ashmem_mmap
ashmem->>+shmem: vmfile = shmem_file_setup
shmem->>shmem: shmem_get_inode
shmem->>-shmem: alloc_file
ashmem->>shmem: shmem_set_file
sharedMemory->>ashmem_dev: 3: native_write
ashmem_dev->>ashmem: unpinned && ashmem_pin_region
ashmem_dev->>shmem: env->GetByteArrayRegion
shmem->>+shmem: shmem_fault
Note right of shmem: 触发缺页中断
shmem->>-shmem: shmem_getpage分配真实物理页
ashmem_dev->>ashmem: ashmem_unpin_region
public MemoryFile(String name, int length) throws IOException {
try {
mSharedMemory = SharedMemory.create(name, length);
mMapping = mSharedMemory.mapReadWrite();
} catch (ErrnoException ex) {
ex.rethrowAsIOException();
}
}
public static @NonNull SharedMemory create(@Nullable String name, int size)
throws ErrnoException {
return new SharedMemory(nCreate(name, size));
}
private SharedMemory(FileDescriptor fd) {
mFileDescriptor = fd;
mSize = nGetSize(mFileDescriptor);
mMemoryRegistration = new MemoryRegistration(mSize);
mCleaner = Cleaner.create(mFileDescriptor,
new Closer(mFileDescriptor, mMemoryRegistration));
}
public @NonNull ByteBuffer mapReadWrite() throws ErrnoException {
return map(OsConstants.PROT_READ | OsConstants.PROT_WRITE, 0, mSize);
}
public @NonNull ByteBuffer map(int prot, int offset, int length) throws ErrnoException {
checkOpen();
validateProt(prot);
//mmap
long address = Os.mmap(0, length, prot, OsConstants.MAP_SHARED, mFileDescriptor, offset);
boolean readOnly = (prot & OsConstants.PROT_WRITE) == 0;
Runnable unmapper = new Unmapper(address, length, mMemoryRegistration.acquire());
return new DirectByteBuffer(length, address, mFileDescriptor, unmapper, readOnly);
}
public static long mmap(long address, long byteCount, int prot, int flags, FileDescriptor fd, long offset) throws ErrnoException {
// BlockGuardOs extends ForwardingOs中被代理的Os的mmap,也就是Linux的mmap
return Libcore.os.mmap(address, byteCount, prot, flags, fd, offset); }
public final class Libcore {
private Libcore() { }
/**
* Direct access to syscalls. Code should strongly prefer using {@link #os}
* unless it has a strong reason to bypass the helpful checks/guards that it
* provides.
*/
public static Os rawOs = new Linux();
/**
* Access to syscalls with helpful checks/guards.
*/
public static Os os = new BlockGuardOs(rawOs);
}
public native long mmap(long address, long byteCount, int prot, int flags, FileDescriptor fd, long offset) throws ErrnoException;
frameworks/base/core/jni/android_os_SharedMemory.cpp
static jobject SharedMemory_create(JNIEnv* env, jobject, jstring jname, jint size) {
// Name is optional so we can't use ScopedUtfChars for this as it throws NPE on null
const char* name = jname ? env->GetStringUTFChars(jname, nullptr) : nullptr;
int fd = ashmem_create_region(name, size);
// Capture the error, if there is one, before calling ReleaseStringUTFChars
int err = fd < 0 ? errno : 0;
if (name) {
env->ReleaseStringUTFChars(jname, name);
}
return jniCreateFileDescriptor(env, fd);
}
libnativehelper/JNIHelp.cpp
jobject jniCreateFileDescriptor(C_JNIEnv* env, int fd) {
JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
if (fileDescriptorInitMethod == nullptr) {
InitFieldsAndMethods(e);
}
jobject fileDescriptor = (*env)->NewObject(e, JniConstants::fileDescriptorClass,
fileDescriptorInitMethod);
// NOTE: NewObject ensures that an OutOfMemoryError will be seen by the Java
// caller if the alloc fails, so we just return NULL when that happens.
if (fileDescriptor != NULL) {
jniSetFileDescriptorOfFD(env, fileDescriptor, fd);
}
return fileDescriptor;
}
system/core/libcutils/ashmem-dev.cpp
#define ASHMEM_DEVICE "/dev/ashmem"
/*
* ashmem_create_region - creates a new ashmem region and returns the file
* descriptor, or <0 on error
*
* `name' is an optional label to give the region (visible in /proc/pid/maps)
* `size' is the size of the region, in page-aligned bytes
*/
int ashmem_create_region(const char *name, size_t size)
{
int ret, save_errno;
int fd = __ashmem_open();
if (fd < 0) {
return fd;
}
if (name) {
char buf[ASHMEM_NAME_LEN] = {0};
strlcpy(buf, name, sizeof(buf));
ret = TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_SET_NAME, buf));
}
ret = TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_SET_SIZE, size));
return fd;
}
static int __ashmem_open()
{
int fd;
pthread_mutex_lock(&__ashmem_lock);
fd = __ashmem_open_locked();
pthread_mutex_unlock(&__ashmem_lock);
return fd;
}
/* logistics of getting file descriptor for ashmem */
static int __ashmem_open_locked()
{
int ret;
struct stat st;
int fd = TEMP_FAILURE_RETRY(open(ASHMEM_DEVICE, O_RDWR | O_CLOEXEC));
if (fd < 0) {
return fd;
}
ret = TEMP_FAILURE_RETRY(fstat(fd, &st));
__ashmem_rdev = st.st_rdev;
return fd;
}
drivers/staging/android/ashmem.c
#define ASHMEM_NAME_DEF "dev/ashmem"
static int ashmem_open(struct inode *inode, struct file *file)
{
struct ashmem_area *asma;
int ret;
ret = generic_file_open(inode, file);
if (unlikely(ret))
return ret;
asma = kmem_cache_zalloc(ashmem_area_cachep, GFP_KERNEL);
if (unlikely(!asma))
return -ENOMEM;
INIT_LIST_HEAD(&asma->unpinned_list);
memcpy(asma->name, ASHMEM_NAME_PREFIX, ASHMEM_NAME_PREFIX_LEN);
asma->prot_mask = PROT_MASK;
file->private_data = asma;
return 0;
}
static int ashmem_mmap(struct file *file, struct vm_area_struct *vma)
{
struct ashmem_area *asma = file->private_data;
int ret = 0;
if (!asma->file) {
char *name = ASHMEM_NAME_DEF;
struct file *vmfile;
if (asma->name[ASHMEM_NAME_PREFIX_LEN] != '\0')
name = asma->name;
/* ... and allocate the backing shmem file */
//shmem_file_setup是原生linux的共享内存机制,匿名共享内存其实就是在Linux共享内存的基础上做了改进
vmfile = shmem_file_setup(name, asma->size, vma->vm_flags);
if (IS_ERR(vmfile)) {
ret = PTR_ERR(vmfile);
goto out;
}
vmfile->f_mode |= FMODE_LSEEK;
asma->file = vmfile;
}
get_file(asma->file);
if (vma->vm_flags & VM_SHARED)
shmem_set_file(vma, asma->file);
else {
if (vma->vm_file)
fput(vma->vm_file);
vma->vm_file = asma->file;
}
return ret;
}
mm/shmem.c
/**
* shmem_file_setup - get an unlinked file living in tmpfs
* @name: name for dentry (to be seen in /proc/<pid>/maps
* @size: size to be set for the file
* @flags: VM_NORESERVE suppresses pre-accounting of the entire object size
*/
struct file *shmem_file_setup(const char *name, loff_t size, unsigned long flags)
{
return __shmem_file_setup(name, size, flags, 0);
}
static struct file *__shmem_file_setup(const char *name, loff_t size,
unsigned long flags, unsigned int i_flags)
{
inode = shmem_get_inode(sb, NULL, S_IFREG | S_IRWXUGO, 0, flags);//分配inode,分配成功就好比建立了文件,也许并未存在真实文件映射
res = alloc_file(&path, FMODE_WRITE | FMODE_READ,
&shmem_file_operations);
return res;
}
void shmem_set_file(struct vm_area_struct *vma, struct file *file)
{
if (vma->vm_file)
fput(vma->vm_file);
vma->vm_file = file;
vma->vm_ops = &shmem_vm_ops;
}
//TODO shmem_vm_ops结构体的定义是下面两者中的哪一种,通过debug确定
static const struct vm_operations_struct shmem_vm_ops = {
.fault = shmem_fault,
.map_pages = filemap_map_pages,
#ifdef CONFIG_NUMA
.set_policy = shmem_set_policy,
.get_policy = shmem_get_policy,
#endif
};
#define shmem_vm_ops generic_file_vm_ops
const struct vm_operations_struct generic_file_vm_ops = {
.fault = filemap_fault,
.map_pages = filemap_map_pages,
.page_mkwrite = filemap_page_mkwrite,
};