91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Android匿名內存怎么實現

發布時間:2023-03-15 14:15:20 來源:億速云 閱讀:88 作者:iii 欄目:開發技術

本篇內容主要講解“Android匿名內存怎么實現”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“Android匿名內存怎么實現”吧!

    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)//創建MemoryFile
            val descriptorMethod = file.javaClass.getDeclaredMethod("getFileDescriptor")
            val fd=descriptorMethod.invoke(file)//反射拿到fd
            file.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_MEMORY
                val pfd = reply.readParcelable<ParcelFileDescriptor>(javaClass.classLoader)
                val descriptor = pfd?.fileDescriptor//拿到fd
                val size = reply.readInt()//拿到長度
                val input = FileInputStream(descriptor)
                val bytes = input.readBytes()
                val message = String(bytes, 0, size, Charsets.UTF_8)//生成string
                Toast.makeText(this@MainActivity,message,Toast.LENGTH_SHORT).show()
            }
    
            override fun onServiceDisconnected(name: ComponentName?) {
            }
    
        }
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
            findViewById<TextView>(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);//調用了系統的mmap
            boolean 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_area
        if (!asma)
            return -ENOMEM;
    
        INIT_LIST_HEAD(&asma->unpinned_list);//初始化unpinned_list
        memcpy(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) {//判斷設置了size
            ret = -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 can't 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 we're in staging. -jstultz
         */
        if (vma->vm_flags & VM_SHARED) {//這塊內存是不是需要跨進程
            ret = shmem_zero_setup(vma);//設置文件
            if (ret) {
                fput(asma->file);
                goto out;
            }
        } else {
        /**
        實現就是把vm_ops設置為NULL
        static 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_cast<uint32_t>(offset), static_cast<uint32_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緩存了,那么他們操作的就是同一塊物理內存了。

    到此,相信大家對“Android匿名內存怎么實現”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!

    向AI問一下細節

    免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

    AI

    颍上县| 禄丰县| 乐陵市| 泰州市| 安阳县| 益阳市| 吴桥县| 新和县| 南雄市| 巴马| 岳西县| 乌恰县| 公安县| 江达县| 逊克县| 景谷| 双鸭山市| 桃江县| 平果县| 师宗县| 科技| 尼勒克县| 密山市| 邯郸市| 甘泉县| 炉霍县| 云浮市| 响水县| 故城县| 五华县| 淮安市| 通海县| 宝应县| 扶风县| 定远县| 灵寿县| 泰宁县| 沐川县| 巴林右旗| 宜都市| 金塔县|