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

源码制作网站重庆巴南区网站开发

源码制作网站,重庆巴南区网站开发,网站设计素养,怎么在阿里云建网站Android 匿名内存解析 有了binder机制为什么还需要匿名内存来实现IPC呢#xff1f;我觉得很大的原因就是binder传输是有大小限制的#xff0c;不说应用层的限制。在驱动中binder的传输大小被限制在了4M#xff0c;分享一张图片可能就超过了这个限制。匿名内存的主要解决思路…Android 匿名内存解析 有了binder机制为什么还需要匿名内存来实现IPC呢我觉得很大的原因就是binder传输是有大小限制的不说应用层的限制。在驱动中binder的传输大小被限制在了4M分享一张图片可能就超过了这个限制。匿名内存的主要解决思路就是通过binder传输文件描述符使得两个进程都能访问同一个地址来实现共享。 MemoryFile使用 在平常开发中android提供了MemoryFile来实现匿名内存。看下最简单的实现。 Service端 ​ const val GET_ASH_MEMORY 1000 class MyService : Service() {val ashData AshDemo.toByteArray()override fun onBind(intent: Intent): IBinder {return object : Binder() {override fun onTransact(code: Int, data: Parcel, reply: Parcel?, flags: Int): Boolean {when(code){GET_ASH_MEMORY-{//收到客户端请求的时候会烦val descriptor createMemoryFile()reply?.writeParcelable(descriptor, 0)reply?.writeInt(ashData.size)return true}else-{return super.onTransact(code, data, reply, flags)}}}}}private fun createMemoryFile(): ParcelFileDescriptor? {val file MemoryFile(AshFile, 1024)//创建MemoryFileval descriptorMethod file.javaClass.getDeclaredMethod(getFileDescriptor)val fddescriptorMethod.invoke(file)//反射拿到fdfile.writeBytes(ashData, 0, 0,ashData.size)//写入字符串return ParcelFileDescriptor.dup(fd as FileDescriptor?)//返回一个封装的fd} }Server的功能很简单收到GET_ASH_MEMORY请求的时候创建一个MemoryFile往里写入一个字符串的byte数组然后将fd和字符长度写入reply中返回给客户端。 Client端 ​ class MainActivity : AppCompatActivity() {val connect object :ServiceConnection{override fun onServiceConnected(name: ComponentName?, service: IBinder?) {val reply Parcel.obtain()val sendData Parcel.obtain()service?.transact(GET_ASH_MEMORY, sendData, reply, 0)//传输信号GET_ASH_MEMORYval pfd reply.readParcelableParcelFileDescriptor(javaClass.classLoader)val descriptor pfd?.fileDescriptor//拿到fdval size reply.readInt()//拿到长度val input FileInputStream(descriptor)val bytes input.readBytes()val message String(bytes, 0, size, Charsets.UTF_8)//生成stringToast.makeText(thisMainActivity,message,Toast.LENGTH_SHORT).show()} ​override fun onServiceDisconnected(name: ComponentName?) {} ​}override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)findViewByIdTextView(R.id.intent).setOnClickListener {//启动服务bindService(Intent(this,MyService::class.java),connect, Context.BIND_AUTO_CREATE)}} }客户端也很简单启动服务发送一个获取MemoryFile的请求然后通过reply拿到fd和长度用FileInputStream读取fd中的内容最后通过toast可以验证这个message已经拿到了。 AshMemory 创建原理 public MemoryFile(String name, int length) throws IOException {try {mSharedMemory SharedMemory.create(name, length);mMapping mSharedMemory.mapReadWrite();} catch (ErrnoException ex) {ex.rethrowAsIOException();}}MemoryFile就是对SharedMemory的一层封装具体的工能都是SharedMemory实现的。看SharedMemory的实现。 public static NonNull SharedMemory create(Nullable String name, int size)throws ErrnoException {if (size 0) {throw new IllegalArgumentException(Size must be greater than zero);}return new SharedMemory(nCreate(name, size));}private static native FileDescriptor nCreate(String name, int size) throws ErrnoException;通过一个JNI获得fd从这里可以推断出java层也只是一个封装拿到的已经是创建好的fd。 //frameworks/base/core/jni/android_os_SharedMemory.cpp jobject SharedMemory_nCreate(JNIEnv* env, jobject, jstring jname, jint size) {const char* name jname ? env-GetStringUTFChars(jname, nullptr) : nullptr;int fd ashmem_create_region(name, size);//创建匿名内存块int err fd 0 ? errno : 0;if (name) {env-ReleaseStringUTFChars(jname, name);}if (fd 0) {jniThrowErrnoException(env, SharedMemory_create, err);return nullptr;}jobject jifd jniCreateFileDescriptor(env, fd);//创建java fd返回if (jifd nullptr) {close(fd);}return jifd; }通过cutils中的ashmem_create_region函数实现的创建 //system/core/libcutils/ashmem-dev.cpp int ashmem_create_region(const char *name, size_t size) {int ret, save_errno; ​if (has_memfd_support()) {//老版本兼容用return memfd_create_region(name ? name : none, size);} ​int fd __ashmem_open();//打开Ashmem驱动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));//通过ioctl设置名字if (ret 0) {goto error;}}ret TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_SET_SIZE, size));//通过ioctl设置大小if (ret 0) {goto error;}return fd; error:save_errno errno;close(fd);errno save_errno;return ret; } ​标准的驱动交互操作 1.open打开驱动 2.通过ioctl与驱动进行交互 下面看下open的流程 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() {static const std::string ashmem_device_path get_ashmem_device_path();//拿到Ashmem驱动路径if (ashmem_device_path.empty()) {return -1;}int fd TEMP_FAILURE_RETRY(open(ashmem_device_path.c_str(), O_RDWR | O_CLOEXEC));return fd; }回到MemoryFile的构造函数中拿到了驱动的fd之后调用了mapReadWrite 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);if (offset 0) {throw new IllegalArgumentException(Offset must be 0);}if (length 0) {throw new IllegalArgumentException(Length must be 0);}if (offset length mSize) {throw new IllegalArgumentException(offset length must not exceed getSize());}long address Os.mmap(0, length, prot, OsConstants.MAP_SHARED, mFileDescriptor, offset);//调用了系统的mmapboolean readOnly (prot OsConstants.PROT_WRITE) 0;Runnable unmapper new Unmapper(address, length, mMemoryRegistration.acquire());return new DirectByteBuffer(length, address, mFileDescriptor, unmapper, readOnly);} ​到这里就有一个疑问Linux就有共享内存android为什么要自己搞一套只能看下Ashmemory驱动的实现了。 驱动第一步看init和file_operations static int __init ashmem_init(void) {int ret -ENOMEM; ​ashmem_area_cachep kmem_cache_create(ashmem_area_cache,sizeof(struct ashmem_area),0, 0, NULL);//创建if (!ashmem_area_cachep) {pr_err(failed to create slab cache\n);goto out;} ​ashmem_range_cachep kmem_cache_create(ashmem_range_cache,sizeof(struct ashmem_range),0, SLAB_RECLAIM_ACCOUNT, NULL);//创建if (!ashmem_range_cachep) {pr_err(failed to create slab cache\n);goto out_free1;} ​ret misc_register(ashmem_misc);//注册为了一个misc设备........return ret; }创建了两个内存分配器ashmem_area_cachep和ashmem_range_cachep用于分配ashmem_area和ashmem_range //common/drivers/staging/android/ashmem.c static const struct file_operations ashmem_fops {.owner THIS_MODULE,.open ashmem_open,.release ashmem_release,.read_iter ashmem_read_iter,.llseek ashmem_llseek,.mmap ashmem_mmap,.unlocked_ioctl ashmem_ioctl, #ifdef CONFIG_COMPAT.compat_ioctl compat_ashmem_ioctl, #endif #ifdef CONFIG_PROC_FS.show_fdinfo ashmem_show_fdinfo, #endif }; ​open调用的就是ashmem_open static int ashmem_open(struct inode *inode, struct file *file) {struct ashmem_area *asma;int ret; ​ret generic_file_open(inode, file);if (ret)return ret; ​asma kmem_cache_zalloc(ashmem_area_cachep, GFP_KERNEL);//分配一个ashmem_areaif (!asma)return -ENOMEM; ​INIT_LIST_HEAD(asma-unpinned_list);//初始化unpinned_listmemcpy(asma-name, ASHMEM_NAME_PREFIX, ASHMEM_NAME_PREFIX_LEN);//初始化一个名字asma-prot_mask PROT_MASK;file-private_data asma;return 0; }ioctl设置名字和长度调用的就是ashmem_ioctl static long ashmem_ioctl(struct file *file, unsigned int cmd, unsigned long arg) {struct ashmem_area *asma file-private_data;long ret -ENOTTY; ​switch (cmd) {case ASHMEM_SET_NAME:ret set_name(asma, (void __user *)arg);break;case ASHMEM_SET_SIZE:ret -EINVAL;mutex_lock(ashmem_mutex);if (!asma-file) {ret 0;asma-size (size_t)arg;}mutex_unlock(ashmem_mutex);break;}........}实现也都很简单就是改变了一下asma里的值。接下来就是重点mmap了具体是怎么分配内存的。 ​static int ashmem_mmap(struct file *file, struct vm_area_struct *vma) {static struct file_operations vmfile_fops;struct ashmem_area *asma file-private_data;int ret 0; ​mutex_lock(ashmem_mutex); ​/* user needs to SET_SIZE before mapping */if (!asma-size) {//判断设置了sizeret -EINVAL;goto out;} ​/* requested mapping size larger than object 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 */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); ​if (!asma-file) {//是否创建过临时文件没创建过进入char *name ASHMEM_NAME_DEF;struct file *vmfile;struct inode *inode; ​if (asma-name[ASHMEM_NAME_PREFIX_LEN] ! \0)name asma-name; ​/* ... and allocate the backing shmem file */vmfile shmem_file_setup(name, asma-size, vma-vm_flags);//调用linux函数在tmpfs中创建临时文件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) {//设置了临时文件的文件操作防止有其他程序mmap这个临时文件vmfile_fops *vmfile-f_op;vmfile_fops.mmap ashmem_vmfile_mmap;vmfile_fops.get_unmapped_area ashmem_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*/if (vma-vm_flags VM_SHARED) {//这块内存是不是需要跨进程ret shmem_zero_setup(vma);//设置文件if (ret) {fput(asma-file);goto out;}} else {/**实现就是把vm_ops设置为NULLstatic inline void vma_set_anonymous(struct vm_area_struct *vma){vma-vm_ops NULL;}*/vma_set_anonymous(vma);} ​vma_set_file(vma, asma-file);/* XXX: merge this with the get_file() above if possible */fput(asma-file); ​ out:mutex_unlock(ashmem_mutex);return ret; }函数很长但是思路还是很清晰的。创建临时文件设置文件操作。其中调用的都是linux的系统函数了看真正设置的shmem_zero_setup函数 int shmem_zero_setup(struct vm_area_struct *vma) {struct file *file;loff_t size vma-vm_end - vma-vm_start; ​/** Cloning a new file under mmap_lock leads to a lock ordering conflict* between XFS directory reading and selinux: since this file is only* accessible to the user through its mapping, use S_PRIVATE flag to* bypass file security, in the same way as shmem_kernel_file_setup().*/file shmem_kernel_file_setup(dev/zero, size, vma-vm_flags);if (IS_ERR(file))return PTR_ERR(file); ​if (vma-vm_file)fput(vma-vm_file);vma-vm_file file;vma-vm_ops shmem_vm_ops;//很重要的操作将这块虚拟内存的vm_ops设置为shmem_vm_ops ​if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) ((vma-vm_start ~HPAGE_PMD_MASK) HPAGE_PMD_MASK) (vma-vm_end HPAGE_PMD_MASK)) {khugepaged_enter(vma, vma-vm_flags);} ​return 0; } static const struct vm_operations_struct shmem_vm_ops {.fault shmem_fault,//Linux的共享内存实现的基础.map_pages filemap_map_pages, #ifdef CONFIG_NUMA.set_policy shmem_set_policy,.get_policy shmem_get_policy, #endif };到这里共享内存的初始化就结束了。 AshMemory 读写 ​//frameworks/base/core/java/android/os/MemoryFile.java public void writeBytes(byte[] buffer, int srcOffset, int destOffset, int count)throws IOException {beginAccess();try {mMapping.position(destOffset);mMapping.put(buffer, srcOffset, count);} finally {endAccess();}}private void beginAccess() throws IOException {checkActive();if (mAllowPurging) {if (native_pin(mSharedMemory.getFileDescriptor(), true)) {throw new IOException(MemoryFile has been purged);}}} ​private void endAccess() throws IOException {if (mAllowPurging) {native_pin(mSharedMemory.getFileDescriptor(), false);}}其中beginAccess和endAccess是对应的。调用的都是native_pin是一个native函数一个参数是true一个是false。pin的作用就是锁住这块内存不被系统回收当不使用的时候就解锁。 static jboolean android_os_MemoryFile_pin(JNIEnv* env, jobject clazz, jobject fileDescriptor,jboolean pin) {int fd jniGetFDFromFileDescriptor(env, fileDescriptor);int result (pin ? ashmem_pin_region(fd, 0, 0) : ashmem_unpin_region(fd, 0, 0));if (result 0) {jniThrowException(env, java/io/IOException, NULL);}return result ASHMEM_WAS_PURGED; }调用的ashmem_pin_region和ashmem_unpin_region来实现解锁和解锁。实现还是在ashmem-dev.cpp //system/core/libcutils/ashmem-dev.cpp int ashmem_pin_region(int fd, size_t offset, size_t len) {.......ashmem_pin pin { static_castuint32_t(offset), static_castuint32_t(len) };return __ashmem_check_failure(fd, TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_PIN, pin))); }通过的也是ioclt通知的驱动。加锁的细节就不展开了。具体的写入就是利用linux的共享内存机制实现的共享。 Linux共享机制简介 共享简单的实现方式就是通过mmap同一个文件来实现。但是真实文件的读写速度实在是太慢了所以利用tmpfs这个虚拟文件系统创建了一个虚拟文件来读写。同时这块虚拟内存在上面也写到重写了vm_ops。当有进程操作这个虚拟内存的时候会触发缺页错误接着会去查找Page缓存由于是第一次所以没有缓存读取物理内存同时加入Page缓存当第二个进程进来的时也触发缺页错误时就能找到Page缓存了那么他们操作的就是同一块物理内存了。 总结 看完之后发现AshMemory是基于Linux的共享内存实现的。做了几点改造 首先把一整块内存变成了一个个region这样在不用的时候可以解锁来让系统回收。将Linux共享内存的整数标记共享内存而AshMemory是用的fd让它可以利用binder机制的fd传输。读写设置都做了加锁的处理减少了用户使用的难度。
http://mrfarshtey.net/news/83991/

相关文章:

  • 做微信公众号的网站吗管理咨询公司起名大气上口的
  • html5 手机网站 图标淮南发布
  • 怎么建设一个购买卡密的网站境外网站网站有哪些
  • 网站建设软文手机免费做网站怎么做网站
  • 河北软件开发网站建设东莞设计公司排名榜
  • dede小说网站模板北京教育云平台网站建设
  • 嘉兴网站建设多少时间深圳设计公司电话
  • 镇江网站seo外包有哪些网站是可以做宣传的
  • 医保局网站建设中标公告长春做公司网站的
  • 莞城做网站扬州国土资源局网站开发区分局
  • 温江网站建设网红营销英文
  • 河北建设教育培训网站ui设计app界面设计流程
  • 做物流的用什么网站配货做任务领佣金的网站源码
  • 外汇期货喊单网站怎么做的嘉兴做网站美工的工作
  • 网站 抄袭佛山网站建设方案书
  • 免费手机端网站模板下载替网站做任务怎么做的
  • 学校门户网站建设的意义网络营销的概念及内容
  • 免费稳定的网站空间外墙清洗
  • 网站备案需要收费么南宁网站建设方案报价
  • asp网站开发工程师如何创建一个网站的流程
  • 主机屋怎么做网站c2c网站制作
  • 手机网站设计神器网页怎么制作二维码
  • 网站开发项目经理岗位职责商城网站 运营
  • 长沙网站优化技巧房地产开发资质
  • 江苏外贸网站建设推广大地资源在线观看视频在线观看
  • 怎样做电影下载网站wap网站如何推广
  • 网站建设需要准备什么信誉好的徐州网站建设
  • 南京做网站南京乐识专业wordpress内存不足
  • 天津艺匠做网站怎么样浙江杭州下沙做网站
  • 网站如何做关键词排名深圳福田最新消息今天