预约网站制作,设计图室内效果图,江西建网站,上海网站制作官网Android12 共享内存驱动实现
SOC#xff1a;RK3568
system#xff1a;Android12
概述#xff1a;
1. 概述 Ashmem#xff08;Anonymous Shared Memory#xff0c;Android 匿名共享内存#xff09;#xff0c;它基于 mmap 系统调用#xff0c;可以让不同进程将同一段…Android12 共享内存驱动实现
SOCRK3568
systemAndroid12
概述
1. 概述 AshmemAnonymous Shared MemoryAndroid 匿名共享内存它基于 mmap 系统调用可以让不同进程将同一段物理内存映射到各自的虚拟地址中从而实现内存共享。
它以驱动程序的形式在内核空间中实现并通过文件描述符来传递共享内存的句柄。
工作上要使用Ashmem但是对于C来讲使用不方便因为涉及到fd文件描述符传递 。于是想参考google 的Ashmem,设计一个简单的进程只通过操作文件即可获取共享的驱动
逻辑原理如下 主要代码 //结构体MyAshmem_area 用来描述一块匿名共享内存struct MyAshmem_area {char name[MYASHMEM_FULL_NAME_LEN];//匿名struct list_head unpinned_list;//暂时无用//每一块匿名共享内存都会在临时文件系统tmpfs中对应一个文件也就是filestruct file *file;size_t size;//文件的大小 sizeunsigned long prot_mask;// prot_mask是访问保护位
};struct MyAshmem_area *asma;
开机启动Myshmem驱动
static int __init MyAshmem_init(void)
{int ret -ENOMEM;//创建一个使用slap缓存 用于分配 MyAshmem_area 的分配器MyAshmem_area_cachep kmem_cache_create(ashmem_area_cache,sizeof(struct MyAshmem_area),0, 0, NULL);if (!MyAshmem_area_cachep) {pr_err(failed to create slab cache\n);goto out;}//注册匿名共享内存设备ret misc_register(MyAshmem_misc);if (ret) {pr_err(failed to register misc device!\n);goto out_free1;}pr_info(initialized\n);return 0;out_free1:kmem_cache_destroy(MyAshmem_area_cachep);
out:return ret;
}
注册时调用
device_initcall(MyAshmem_init); 匿名内存设备是一个misc设备类型,所以它使用一个miscdevice类型的结构体结构体MyAshmem_misc进行注册定义如下
static struct miscdevice MyAshmem_misc {.minor MISC_DYNAMIC_MINOR,.name MyAshmem,.fops MyAshmem_fops,
};
文件操作/dev/MyAshmemMyAshmem_fops 表示其操作方法列表。
static const struct file_operations MyAshmem_fops {.owner THIS_MODULE,.open MyAshmem_open,//当应用open时调用.release MyAshmem_release,.mmap MyAshmem_mmap,当应用mmap 时调用
};
open具体实现方法
static int MyAshmem_open(struct inode *inode, struct file *file)
{int ret;//当open计数器大于1时返回if(op 0){printk(already MyAshmem_open);return 0;}printk(MyAshmem_open);asma kmem_cache_zalloc(MyAshmem_area_cachep, GFP_KERNEL);if (!asma)return -ENOMEM;//初始化链表 暂时没完善INIT_LIST_HEAD(asma-unpinned_list);memcpy(asma-name, MYASHMEM_NAME_PREFIX, MYASHMEM_NAME_PREFIX_LEN);//设置默认匿名strcpy(asma-name MYASHMEM_NAME_PREFIX_LEN, MyAshmemDrive);//设置默认长度asma-size 4096;//设置默认保护位置asma-prot_mask PROT_MASK;//open计数器加一op;return 0;
}
mmap实现
static int MyAshmem_mmap(struct file *file, struct vm_area_struct *vma)
{static struct file_operations vmfile_fops;int ret 0;mutex_lock(MyAshmem_mutex);printk(MyAshmem_mmap);/* user needs to SET_SIZE before mapping */if (!asma-size) {ret -EINVAL;goto out;}/* requested mapping size larger than object size *///比较size 设置大小否则直接失败if (vma-vm_end - vma-vm_start PAGE_ALIGN(asma-size)) {ret -EINVAL;goto out;}/* requested protection bits must match our allowed protection mask *///检测需要映射的虚拟内存vma的保护权限是否超过了匿名共享内存的保护权限//比如vma除了允许读之外还允许写但是asma只允许读这就算超过了会mmap失败直接返回。if ((vma-vm_flags ~calc_vm_prot_bits(asma-prot_mask, 0)) calc_vm_prot_bits(PROT_MASK, 0)) {ret -EPERM;goto out;}vma-vm_flags ~calc_vm_may_flags(~asma-prot_mask);//第一次mmap会创造一个临时文件用来映射共享内存//第二次打开直接从这个映射文件上mmapif (!asma-file) {char *name MYASHMEM_NAME_DEF;struct file *vmfile;struct inode *inode;if (asma-name[MYASHMEM_NAME_PREFIX_LEN] ! \0)name asma-name;/* ... and allocate the backing shmem file *///在tmpfs中创建一个临时文件。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;inode file_inode(vmfile);lockdep_set_class(inode-i_rwsem, backing_shmem_inode_class);//记录临时文件asma-file vmfile;/** override mmap operation of the vmfile so that it cant be* remapped which would lead to creation of a new vma with no* asma permission checks. Have to override get_unmapped_area* as well to prevent VM_BUG_ON check for f_ops modification.*/if (!vmfile_fops.mmap) {vmfile_fops *vmfile-f_op;vmfile_fops.mmap MyAshmem_vmfile_mmap;vmfile_fops.get_unmapped_area MyAshmem_vmfile_get_unmapped_area;}vmfile-f_op vmfile_fops;}get_file(asma-file);/** XXX - Reworked to use shmem_zero_setup() instead of* shmem_set_file while were in staging. -jstultz*///判断映射虚拟内存vma是否需要在不同进程间共享if (vma-vm_flags VM_SHARED) {ret shmem_zero_setup(vma);if (ret) {fput(asma-file);goto out;}} else {vma_set_anonymous(vma);}if (vma-vm_file)fput(vma-vm_file);vma-vm_file asma-file;out:mutex_unlock(MyAshmem_mutex);//返回地址return ret;
} 全部代码drivers\staging\android\MyAshmem.c
#include linux/init.h
#include linux/export.h
#include linux/file.h
#include linux/fs.h
#include linux/falloc.h
#include linux/miscdevice.h
#include linux/security.h
#include linux/mm.h
#include linux/mman.h
#include linux/uaccess.h
#include linux/personality.h
#include linux/bitops.h
#include linux/mutex.h
#include linux/shmem_fs.h#define PROT_MASK (PROT_EXEC | PROT_READ | PROT_WRITE)#define MYASHMEM_NAME_LEN 256
#define MYASHMEM_NAME_PREFIX dev/MyAshmem/
#define MYASHMEM_NAME_PREFIX_LEN (sizeof(MYASHMEM_NAME_PREFIX) - 1)
#define MYASHMEM_FULL_NAME_LEN (MYASHMEM_NAME_LEN MYASHMEM_NAME_PREFIX_LEN)
#define MYASHMEM_NAME_DEF /dev/MyAshmem
static int op 0;
struct MyAshmem_area {char name[MYASHMEM_FULL_NAME_LEN];struct list_head unpinned_list;struct file *file;size_t size;unsigned long prot_mask;
};
struct MyAshmem_area *asma;static DEFINE_MUTEX(MyAshmem_mutex);
static struct kmem_cache *MyAshmem_area_cachep __read_mostly;static int MyAshmem_open(struct inode *inode, struct file *file)
{int ret;//当open计数器大于1时返回if(op 0){printk(already MyAshmem_open);return 0;}printk(MyAshmem_open);asma kmem_cache_zalloc(MyAshmem_area_cachep, GFP_KERNEL);if (!asma)return -ENOMEM;//初始化链表 暂时没完善INIT_LIST_HEAD(asma-unpinned_list);memcpy(asma-name, MYASHMEM_NAME_PREFIX, MYASHMEM_NAME_PREFIX_LEN);//设置默认匿名strcpy(asma-name MYASHMEM_NAME_PREFIX_LEN, MyAshmemDrive);//设置默认长度asma-size 4096;//设置默认保护位置asma-prot_mask PROT_MASK;//open计数器加一op;return 0;
}static int MyAshmem_release(struct inode *ignored, struct file *file)
{if (asma-file)fput(asma-file);kmem_cache_free(MyAshmem_area_cachep, asma);op--;return 0;
}
static int MyAshmem_vmfile_mmap(struct file *file, struct vm_area_struct *vma)
{/* do not allow to mmap ashmem backing shmem file directly */return -EPERM;
}static unsigned long
MyAshmem_vmfile_get_unmapped_area(struct file *file, unsigned long addr,unsigned long len, unsigned long pgoff,unsigned long flags)
{return current-mm-get_unmapped_area(file, addr, len, pgoff, flags);
}static inline vm_flags_t calc_vm_may_flags(unsigned long prot)
{return _calc_vm_trans(prot, PROT_READ, VM_MAYREAD) |_calc_vm_trans(prot, PROT_WRITE, VM_MAYWRITE) |_calc_vm_trans(prot, PROT_EXEC, VM_MAYEXEC);
}static struct lock_class_key backing_shmem_inode_class;
static int MyAshmem_mmap(struct file *file, struct vm_area_struct *vma)
{static struct file_operations vmfile_fops;int ret 0;mutex_lock(MyAshmem_mutex);printk(MyAshmem_mmap);/* user needs to SET_SIZE before mapping */if (!asma-size) {ret -EINVAL;goto out;}/* requested mapping size larger than object size *///比较size 设置大小否则直接失败if (vma-vm_end - vma-vm_start PAGE_ALIGN(asma-size)) {ret -EINVAL;goto out;}/* requested protection bits must match our allowed protection mask *///检测需要映射的虚拟内存vma的保护权限是否超过了匿名共享内存的保护权限//比如vma除了允许读之外还允许写但是asma只允许读这就算超过了会mmap失败直接返回。if ((vma-vm_flags ~calc_vm_prot_bits(asma-prot_mask, 0)) calc_vm_prot_bits(PROT_MASK, 0)) {ret -EPERM;goto out;}vma-vm_flags ~calc_vm_may_flags(~asma-prot_mask);//第一次mmap会创造一个临时文件用来映射共享内存//第二次打开直接从这个映射文件上mmapif (!asma-file) {char *name MYASHMEM_NAME_DEF;struct file *vmfile;struct inode *inode;if (asma-name[MYASHMEM_NAME_PREFIX_LEN] ! \0)name asma-name;/* ... and allocate the backing shmem file *///在tmpfs中创建一个临时文件。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;inode file_inode(vmfile);lockdep_set_class(inode-i_rwsem, backing_shmem_inode_class);//记录临时文件asma-file vmfile;/** override mmap operation of the vmfile so that it cant be* remapped which would lead to creation of a new vma with no* asma permission checks. Have to override get_unmapped_area* as well to prevent VM_BUG_ON check for f_ops modification.*/if (!vmfile_fops.mmap) {vmfile_fops *vmfile-f_op;vmfile_fops.mmap MyAshmem_vmfile_mmap;vmfile_fops.get_unmapped_area MyAshmem_vmfile_get_unmapped_area;}vmfile-f_op vmfile_fops;}get_file(asma-file);/** XXX - Reworked to use shmem_zero_setup() instead of* shmem_set_file while were in staging. -jstultz*///判断映射虚拟内存vma是否需要在不同进程间共享if (vma-vm_flags VM_SHARED) {ret shmem_zero_setup(vma);if (ret) {fput(asma-file);goto out;}} else {vma_set_anonymous(vma);}if (vma-vm_file)fput(vma-vm_file);vma-vm_file asma-file;out:mutex_unlock(MyAshmem_mutex);//返回地址return ret;
}static const struct file_operations MyAshmem_fops {.owner THIS_MODULE,.open MyAshmem_open,.release MyAshmem_release,.mmap MyAshmem_mmap,
};static struct miscdevice MyAshmem_misc {.minor MISC_DYNAMIC_MINOR,.name MyAshmem,.fops MyAshmem_fops,
};static int __init MyAshmem_init(void)
{int ret -ENOMEM;//创建一个使用slap缓存 用于分配 MyAshmem_area 的分配器MyAshmem_area_cachep kmem_cache_create(ashmem_area_cache,sizeof(struct MyAshmem_area),0, 0, NULL);if (!MyAshmem_area_cachep) {pr_err(failed to create slab cache\n);goto out;}//注册匿名共享内存设备ret misc_register(MyAshmem_misc);if (ret) {pr_err(failed to register misc device!\n);goto out_free1;}pr_info(initialized\n);return 0;out_free1:kmem_cache_destroy(MyAshmem_area_cachep);
out:return ret;
}
device_initcall(MyAshmem_init);
drivers/staging/android/Makefile
ccflags-y -I$(src) # needed for trace eventsobj-y ion/
obj-$(CONFIG_FIQ_DEBUGGER) fiq_debugger/obj-$(CONFIG_ASHMEM) ashmem.o
obj-$(CONFIG_ANDROID_VSOC) vsoc.o
obj-y MyAshmem.o
进程write
#include stdio.h
#include stdlib.h
#includesys/types.h
#includesys/stat.h
#includefcntl.h
#includesys/mman.h
#includeunistd.h
#define ASHMEM_DEVICE /dev/MyAshmem
int main(void){int fd open(ASHMEM_DEVICE, O_RDWR);char *addr (char*)mmap(NULL, 4096 , PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);int i 0;while(1){*addr i;printf(write:%d\n,*addr);sleep(1);}return 0;
}
进程read:
#include stdio.h
#include stdlib.h
#includesys/types.h
#includesys/stat.h
#includefcntl.h
#includesys/mman.h
#includeunistd.h
#define ASHMEM_DEVICE /dev/MyAshmem
int main(void){int fd2 open(ASHMEM_DEVICE, O_RDWR);char *addr2 (char*)mmap(NULL, 4096 , PROT_READ | PROT_WRITE, MAP_SHARED, fd2, 0);int i 0;while(1){printf(read:%d\n,*addr2);sleep(1);}return 0;
}
编译/mnt/h/android-ndk-r21e/android-ndk-r21e/install/bin/aarch64-linux-android-g write.cpp -o write.out
/mnt/h/android-ndk-r21e/android-ndk-r21e/install/bin/aarch64-linux-android-g read.cpp -o read.out
结果 BUG1.目前只能使用一次得重新开机才能使用
2.不支持多组只支持一组应用使用
下一章节改进觉得有用喜欢的话就给个点赞收藏