亚洲精品一二区_国产黄色片网站_99久久久成人国产精品_蜜臀网_国产精品一区二区三区免费_成人av中文字幕_91精品国产欧美一区二区成人

當(dāng)前位置:首頁 > 嵌入式培訓(xùn) > 嵌入式學(xué)習(xí) > 講師博文 > 匿名共享內(nèi)存

匿名共享內(nèi)存 時間:2017-10-27      來源:未知

課程目標(biāo):

1. 匿名共享內(nèi)存驅(qū)動ashmen.c

2. 匿名共享內(nèi)存的框架結(jié)構(gòu)

 

1. 匿名共享內(nèi)存的驅(qū)動

首先我們應(yīng)該找到匿名共享內(nèi)存驅(qū)動的目錄,/home/linux/fspad-733/lichee/linux-3.4/drivers/staging/android/ashmem.c。

 

static struct miscdevice ashmem_misc = {

    .minor = MISC_DYNAMIC_MINOR,

    .name = "ashmem",

    .fops = &ashmem_fops,

};

 

static int __init ashmem_init(void)

{

    int ret;

 

    ashmem_area_cachep = kmem_cache_create("ashmem_area_cache",

                      sizeof(struct ashmem_area),

                      0, 0, NULL);

    ashmem_range_cachep= kmem_cache_create("ashmem_range_cache",

                      sizeof(struct ashmem_range),

                      0, 0, NULL);

 

    ret = misc_register(&ashmem_misc);

    register_shrinker(&ashmem_shrinker);

    return 0;

}

 

static void __exit ashmem_exit(void)

{

    int ret;

 

    unregister_shrinker(&ashmem_shrinker);

    ret = misc_deregister(&ashmem_misc);

    kmem_cache_destroy(ashmem_range_cachep);

    kmem_cache_destroy(ashmem_area_cachep);

    printk(KERN_INFO "ashmem: unloaded\n");

}

 

module_init(ashmem_init);

module_exit(ashmem_exit);

        這里就是通過kmem_cache_create函數(shù)來創(chuàng)建一個名為"ashmem_area_cache"、對象大小為sizeof(struct ashmem_area)的緩沖區(qū)了。緩沖區(qū)創(chuàng)建了以后,就可以每次從它分配一個struct ashmem_area對象了。關(guān)于Linux內(nèi)核的slab緩沖區(qū)的相關(guān)知識,slab分配內(nèi)存請參考linux內(nèi)存管理。我們可以看到這里的ashmem是采用的misc架構(gòu)的。它采用動態(tài)分配次設(shè)備號。這里我們重點看它的操作方法集。

static const struct file_operations ashmem_fops = {

    .owner = THIS_MODULE,

    .open = ashmem_open,

    .release = ashmem_release,

    .read = ashmem_read,

    .llseek = ashmem_llseek,

    .mmap = ashmem_mmap,

    .unlocked_ioctl = ashmem_ioctl,

    .compat_ioctl = ashmem_ioctl,

};

當(dāng)用戶層的open,read,ioctl函數(shù)別調(diào)用的時候,驅(qū)動中的相應(yīng)的函數(shù)就會執(zhí)行,所以接下來我們重點分析open,read,mmap,ioctl函數(shù)的執(zhí)行過程。首先我們從open函數(shù)開始看

static int ashmem_open(struct inode *inode, struct file *file)

{

    struct ashmem_area *asma;

    int ret;

 

    ret = generic_file_open(inode, file);

    asma = kmem_cache_zalloc(ashmem_area_cachep, GFP_KERNEL);                                                                    

    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;

}

generic_file_open函數(shù)從注釋上我們就能夠看到,它是阻止在32位系統(tǒng)上打開一個過大的文件。注釋如下:

/*  

 * Called when an inode is about to be open.

 * We use this to disallow opening large files on 32bit systems if

 * the caller didn't specify O_LARGEFILE.  On 64bit systems we force

 * on this flag in sys_open.*/  

kmem_cache_zalloc函數(shù)就是從剛才我們分配的緩沖區(qū)中分配一個結(jié)構(gòu)體空間的大小,接下來就是對這個結(jié)構(gòu)體信息的填充。這個結(jié)構(gòu)體的名字被分配為如下這個宏。

#define ASHMEM_NAME_PREFIX "dev/ashmem/"   

struct ashmem_area {                                                                                                             

    char name[ASHMEM_FULL_NAME_LEN]; /* optional name in /proc/pid/maps */

    struct list_head unpinned_list;  /* list of all ashmem areas */

    struct file *file;       /* the shmem-based backing file */

    size_t size;             /* size of the mapping, in bytes */

    unsigned long prot_mask;     /* allowed prot bits, as vm_flags */

};

分配的這個結(jié)構(gòu)體的成員信息如下: 域name表示這塊共享內(nèi)存的名字,這個名字會顯示/proc/<pid>/maps文件中,<pid>表示打開這個共享內(nèi)存文件的進程ID;域unpinned_list是一個列表頭,它把這塊共享內(nèi)存中所有被解鎖的內(nèi)存塊連接在一起,下面我們講內(nèi)存塊的鎖定和解鎖操作時會看到它的用法;域file表示這個共享內(nèi)存在臨時文件系統(tǒng)tmpfs中對應(yīng)的文件,在內(nèi)核決定要把這塊共享內(nèi)存對應(yīng)的物理頁面回收時,就會把它的內(nèi)容交換到這個臨時文件中去;域size表示這塊共享內(nèi)存的大小;域prot_mask表示這塊共享內(nèi)存的訪問保護位。

 

ashmem_read函數(shù),我們會發(fā)現(xiàn)它在函數(shù)內(nèi)部調(diào)用自己,它的作用如注釋所說:

    /*

     * asma and asma->file are used outside the lock here.  We assume

     * once asma->file is set it will never be changed, and will not

     * be destroyed until all references to the file are dropped and

     * ashmem_release is called.

     */

只要程序開始運行那么它會一直運行下去(遞歸執(zhí)行),知道asma中的file結(jié)構(gòu)體被銷毀。它主要是保證file結(jié)構(gòu)體不能被修改的。

 

ashmem_ioctl函數(shù)

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_GET_NAME:

        ret = get_name(asma, (void __user *) arg);

        break;

    case ASHMEM_SET_SIZE:

        ret = -EINVAL;

        if (!asma->file) {

            ret = 0;

            asma->size = (size_t) arg;

        }

        break;

    case ASHMEM_GET_SIZE:

        ret = asma->size;

        break;

    case ASHMEM_SET_PROT_MASK:

        ret = set_prot_mask(asma, arg);

        break;

    case ASHMEM_GET_PROT_MASK:

        ret = asma->prot_mask;

        break;                                                                                                                                                                                                                                                     

    case ASHMEM_PIN:

    case ASHMEM_UNPIN:

    case ASHMEM_GET_PIN_STATUS:

        ret = ashmem_pin_unpin(asma, cmd, (void __user *) arg);

        break;

    case ASHMEM_PURGE_ALL_CACHES:

        ret = -EPERM;

        if (capable(CAP_SYS_ADMIN)) {

            struct shrink_control sc = {

                .gfp_mask = GFP_KERNEL,

                .nr_to_scan = 0,

            };

            ret = ashmem_shrink(&ashmem_shrinker, &sc);

            sc.nr_to_scan = ret;

            ashmem_shrink(&ashmem_shrinker, &sc);

        }

        break;

    }

 

    return ret;

}

從這個ioctl函數(shù)中我們能清楚的看到,它里面有ASHMEM_SET_NAME,ASHMEM_GET_NAME,ASHMEM_SET_SIZE,ASHMEM_GET_SIZE等命令碼,這些命令碼就是來設(shè)置ashmem_area結(jié)構(gòu)體中的變量的。通過ASHMEM_PIN,ASHMEM_UNPIN這兩個命令碼實現(xiàn)對匿名共享內(nèi)存的鎖定和解鎖。

struct ashmem_pin {

    __u32 offset;   /* offset into region, in bytes, page-aligned */

    __u32 len;  /* length forward from offset, in bytes, page-aligned */

};

這個結(jié)構(gòu)體只有兩個域,分別表示要鎖定或者要解鎖的內(nèi)塊塊的起始大小以及大小。

 在分析這兩個操作之前,我們先來看一下Ashmem驅(qū)動程序中的一個數(shù)據(jù)結(jié)構(gòu)struct ashmem_range,這個數(shù)據(jù)結(jié)構(gòu)就是用來表示某一塊被解鎖(unpinnd)的內(nèi)存:

/*

 * ashmem_range - represents an interval of unpinned (evictable) pages

 * Lifecycle: From unpin to pin

 * Locking: Protected by `ashmem_mutex'

 */

struct ashmem_range {                                                                                                            

    struct list_head lru;       /* entry in LRU list */

    struct list_head unpinned;  /* entry in its area's unpinned list */

    struct ashmem_area *asma;   /* associated area */

    size_t pgstart;         /* starting page, inclusive */

    size_t pgend;           /* ending page, inclusive */

    unsigned int purged;        /* ASHMEM_NOT or ASHMEM_WAS_PURGED */

};

  域asma表示這塊被解鎖的內(nèi)存所屬于的匿名共享內(nèi)存,它通過域unpinned連接在asma->unpinned_list表示的列表中;域pgstart和paend表示這個內(nèi)存塊的開始和結(jié)束頁面號,它們表示一個前后閉合的區(qū)間;域purged表示這個內(nèi)存塊占用的物理內(nèi)存是否已經(jīng)被回收;這塊被解鎖的內(nèi)存塊除了保存在它所屬的匿名共享內(nèi)存asma的解鎖列表unpinned_list之外,還通過域lru保存在一個全局的近少使用列表ashmem_lru_list列表中

static long ashmem_ioctl(struct file *file, unsigned int cmd, unsigned long arg)

{

  case ASHMEM_PIN:

  case ASHMEM_UNPIN:

  case ASHMEM_GET_PIN_STATUS:                                                                                                  

       ret = ashmem_pin_unpin(asma, cmd, (void __user *) arg);

break;

}

 

static int ashmem_pin_unpin(struct ashmem_area *asma, unsigned long cmd,

                void __user *p)

{

    mutex_lock(&ashmem_mutex);

 

    switch (cmd) {

    case ASHMEM_PIN:

        ret = ashmem_pin(asma, pgstart, pgend);

        break;

    case ASHMEM_UNPIN:

        ret = ashmem_unpin(asma, pgstart, pgend);

        break;

    case ASHMEM_GET_PIN_STATUS:

        ret = ashmem_get_pin_status(asma, pgstart, pgend);

        break;

    }

 

    mutex_unlock(&ashmem_mutex);

 

    return ret;

}

        函數(shù)后根據(jù)當(dāng)前要執(zhí)行的是ASHMEM_PIN操作還是ASHMEM_UNPIN操作來分別執(zhí)行ashmem_pin和ashmem_unpin來進一步處理。創(chuàng)建匿名共享內(nèi)存時,默認所有的內(nèi)存都是pinned狀態(tài)的,只有用戶告訴Ashmem驅(qū)動程序要unpin某一塊內(nèi)存時,Ashmem驅(qū)動程序才會把這塊內(nèi)存unpin,之后,用戶可以再告訴Ashmem驅(qū)動程序要重新pin某一塊之前被unpin過的內(nèi)塊,從而把這塊內(nèi)存從unpinned狀態(tài)改為pinned狀態(tài),也就是說,執(zhí)行ASHMEM_PIN操作時,目標(biāo)對象必須是一塊當(dāng)前處于unpinned狀態(tài)的內(nèi)存塊。

 

首先看一下Ashmem驅(qū)動程序模塊初始化函數(shù)ashmem_init:

static struct shrinker ashmem_shrinker = {  

    .shrink = ashmem_shrink,  

    .seeks = DEFAULT_SEEKS * 4,  

};  

  

static int __init ashmem_init(void)  

{  

    int ret;  

    register_shrinker(&ashmem_shrinker);  

  

    printk(KERN_INFO "ashmem: initialized\n");  

  

    return 0;  

}  

 這里通過調(diào)用register_shrinker函數(shù)向內(nèi)存管理系統(tǒng)注冊一個內(nèi)存回收算法函數(shù)。在Linux內(nèi)核中,當(dāng)系統(tǒng)內(nèi)存緊張時,內(nèi)存管理系統(tǒng)就會進行內(nèi)存回收算法,將一些近沒有用過的內(nèi)存換出物理內(nèi)存去,這樣可以增加物理內(nèi)存的供應(yīng)。因此,當(dāng)內(nèi)存管理系統(tǒng)進行內(nèi)存回收時,就會調(diào)用到這里的ashmem_shrink函數(shù),讓Ashmem驅(qū)動程序執(zhí)行內(nèi)存回收操作,涉及到內(nèi)存分配的內(nèi)容我暫時不需要關(guān)心太多,這里只需要知道怎么分配的就行了。

static int ashmem_mmap(struct file *file, struct vm_area_struct *vma)

{

    struct ashmem_area *asma = file->private_data;

    int ret = 0;

    mutex_lock(&ashmem_mutex);

    /* user needs to SET_SIZE before mapping */

    if (unlikely(!asma->size)) {

        ret = -EINVAL;

        goto out;

    }

 

    /* requested protection bits must match our allowed protection mask */

    if (unlikely((vma->vm_flags & ~calc_vm_prot_bits(asma->prot_mask)) &

             calc_vm_prot_bits(PROT_MASK))) {

        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;

 

        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);

        if (unlikely(IS_ERR(vmfile))) {

            ret = PTR_ERR(vmfile);

            goto out;

        }

        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;

    }

    vma->vm_flags |= VM_CAN_NONLINEAR;

 

out:                                                                                                                                                                                                                    

    mutex_unlock(&ashmem_mutex);

    return ret;

}

        這個函數(shù)的實現(xiàn)也很簡單,它調(diào)用了Linux內(nèi)核提供的shmem_file_setup函數(shù)來在臨時文件系統(tǒng)tmpfs中創(chuàng)建一個臨時文件,這個臨時文件與Ashmem驅(qū)動程序創(chuàng)建的匿名共享內(nèi)存對應(yīng)。函數(shù)shmem_file_setup是Linux內(nèi)核中用來創(chuàng)建共享內(nèi)存文件的方法,而Linux內(nèi)核中的共享內(nèi)存機制其實是一種進程間通信(IPC)機制,它的實現(xiàn)相對也是比較復(fù)雜,Android系統(tǒng)的匿名共享內(nèi)存機制正是由于直接使用了Linux內(nèi)核共享內(nèi)存機制,它才會很小巧,它站在巨人的肩膀上了。

2. 匿名共享內(nèi)存的框架

fspad-733/androidL/frameworks/base/core/java/android/os/MemoryFile.java在Java框架中匿名共享涉及到的類為MemoryFile類。

    private static native FileDescriptor native_open(String name, int length) throws IOException;

    // returns memory address for ashmem region

    private static native long native_mmap(FileDescriptor fd, int length, int mode)

            throws IOException;

    private static native void native_munmap(long addr, int length) throws IOException;

    private static native void native_close(FileDescriptor fd);

    private static native int native_read(FileDescriptor fd, long address, byte[] buffer,

            int srcOffset, int destOffset, int count, boolean isUnpinned) throws IOException;

    private static native void native_write(FileDescriptor fd, long address, byte[] buffer,

            int srcOffset, int destOffset, int count, boolean isUnpinned) throws IOException;

    private static native void native_pin(FileDescriptor fd, boolean pin) throws IOException;

    private static native int native_get_size(FileDescriptor fd) throws IOException;

    public MemoryFile(String name, int length) throws IOException {

        mLength = length;

        if (length >= 0) {

            mFD = native_open(name, length);

        } else {

            throw new IOException("Invalid length: " + length);

        }

 

        if (length > 0) {

            mAddress = native_mmap(mFD, length, PROT_READ | PROT_WRITE);

        } else {

            mAddress = 0;

        }

}  

 

當(dāng)上層的應(yīng)用程序?qū)嵗疢emoryFile對象的時候,MemoryFile構(gòu)造器就會執(zhí)行,在構(gòu)造方法中我們可以看到有native_open和native_mmap都是使用native聲明的。我們使用grep “native_open” * -nR來搜索它所對應(yīng)的本地的框架。fspad-733/androidL/frameworks/base/core/jni/android_os_MemoryFile.cpp

static const JNINativeMethod methods[] = { 

    {"native_open",  "(Ljava/lang/String;I)Ljava/io/FileDescriptor;", (void*)android_os_MemoryFile_open},                        

};

 

static jobject android_os_MemoryFile_open(JNIEnv* env, jobject clazz, jstring name, jint length)

{

    const char* namestr = (name ? env->GetStringUTFChars(name, NULL) : NULL);

    int result = ashmem_create_region(namestr, length);

    if (name)

        env->ReleaseStringUTFChars(name, namestr);

    if (result < 0) {

        jniThrowException(env, "java/io/IOException", "ashmem_create_region failed");

        return NULL;

    }                                                                                                                          

   return jniCreateFileDescriptor(env, result);

}

 

上層的native_open向下層調(diào)用的時候,就會調(diào)到android_os_MemoryFile_open函數(shù),在這個函數(shù)中我們能夠看到ashmem_create_region(namestr, length);它的實現(xiàn)在/system/core/libcutils/ashmem-dev.c目錄下

int ashmem_create_region(const char *name, size_t size)

{

    int fd, ret;

    fd = open(ASHMEM_DEVICE, O_RDWR);

    if (fd < 0)

        return fd;                                                                                                               

    if (name) { 

        char buf[ASHMEM_NAME_LEN] = {0};

 

        strlcpy(buf, name, sizeof(buf));

        ret = ioctl(fd, ASHMEM_SET_NAME, buf);

        if (ret < 0)

            goto error;

    } 

    ret = ioctl(fd, ASHMEM_SET_SIZE, size);

    if (ret < 0)

        goto error;

    

    return fd;

 

error: 

    close(fd);

    return ret;

}

這個函數(shù)的實現(xiàn)也很簡單,首先在open(ASHMEM_DEVICE, O_RDWR);打開共享內(nèi)存的驅(qū)動

#define ASHMEM_DEVICE   "/dev/ashmem"   

接著在ret = ioctl(fd, ASHMEM_SET_NAME, buf);函數(shù)中,調(diào)用ASHMEM_SET_NAME命令碼設(shè)置共享內(nèi)存的名字,調(diào)用ioctl(fd, ASHMEM_SET_SIZE, size);函數(shù)分配共享內(nèi)存的大小。這個函數(shù)的工作就完成了,是不是很簡單?native_open終得到的是打開驅(qū)動得到的文件描述符fd。

 

Native_mmap函數(shù)的實現(xiàn)過程如下:

native_mmap(mFD, length, PROT_READ | PROT_WRITE);

androidL/frameworks/base/core/jni/android_os_MemoryFile.cpp

static const JNINativeMethod methods[] = { 

{"native_mmap",  "(Ljava/io/FileDescriptor;II)J", (void*)android_os_MemoryFile_mmap},

};

static jlong android_os_MemoryFile_mmap(JNIEnv* env, jobject clazz, jobject fileDescriptor,

        jint length, jint prot)

{

    int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);

    void* result = mmap(NULL, length, prot, MAP_SHARED, fd, 0);

    if (result == MAP_FAILED) {

        jniThrowException(env, "java/io/IOException", "mmap failed");

    }

    return reinterpret_cast<jlong>(result);                                                                                      

}

這個函數(shù)的實現(xiàn)過程也很簡單,首先通過jniGetFDFromFileDescriptor(env, fileDescriptor);函數(shù)獲取文件描述符fd,然后通過mmap函數(shù)進行內(nèi)存的映射,result = mmap(NULL, length, prot, MAP_SHARED, fd, 0);終將映射的首地址通過return的方式返回給java代碼。這樣在java中就能夠拿到匿名共享內(nèi)存的首地址了。當(dāng)我們得到內(nèi)存的首地址之后就可以對內(nèi)存進行讀寫了。接下來我們分析匿名共享內(nèi)存的讀寫操作。這里需要記住它不需要內(nèi)存的拷貝。

    public int readBytes(byte[] buffer, int srcOffset, int destOffset, int count)

            throws IOException {

        if (isDeactivated()) {

            throw new IOException("Can't read from deactivated memory file.");

        }

        if (destOffset < 0 || destOffset > buffer.length || count < 0 

                || count > buffer.length - destOffset

                || srcOffset < 0 || srcOffset > mLength

                || count > mLength - srcOffset) {

            throw new IndexOutOfBoundsException();

        }

        return native_read(mFD, mAddress, buffer, srcOffset, destOffset, count, mAllowPurging);

}  

我們先來分析一下這個函數(shù)的參數(shù)。

Buffer:讀取到的內(nèi)容會放大buffer緩沖區(qū)中

srcOffset:內(nèi)存上的偏移

destOffset:緩沖區(qū)的偏移

count:要讀取內(nèi)存字節(jié)的個數(shù)

它的參數(shù)我們已經(jīng)分析完成,我們可以發(fā)現(xiàn)在這個函數(shù)中終會調(diào)用native_read函數(shù)來讀取數(shù)據(jù),在分析這個本地函數(shù)之前我們需要知道readbytes方法我們一般不會直接調(diào)用,因為在這個文件中還有對這個函數(shù)的進一步封裝,封裝完之后我們的操作就更為簡單。我們看一下它的封裝。

    private class MemoryInputStream extends InputStream {

 

        private int mMark = 0;

        private int mOffset = 0;

        private byte[] mSingleByte;

 

        @Override

        public int available() throws IOException {

            if (mOffset >= mLength) {

                return 0;

            }

            return mLength - mOffset;

        }

......

        public int read() throws IOException {

            if (mSingleByte == null) {

                mSingleByte = new byte[1];

            }

            int result = read(mSingleByte, 0, 1);

            return mSingleByte[0];

        }

 

        @Override

        public int read(byte buffer[], int offset, int count) throws IOException {

            int result = readBytes(buffer, mOffset, offset, count);

            if (result > 0) {

                mOffset += result;

            }

            return result;

        }

    }

 

從上述我們可以看到在MemoryInputStream類中它會繼承InputStream類,在這個類中有read的操作方法,其中由無參構(gòu)造器和有三個參數(shù)的構(gòu)造器,終都會調(diào)用readbytes方法。也就是說在Android的app應(yīng)用程序中如果想使用匿名共享內(nèi)存的讀寫只需要獲取一個輸入或者輸出流的對象調(diào)用里面的read或者write方法就行了。接下來我們看native_read函數(shù)的本地實現(xiàn)

base/core/jni/android_os_MemoryFile.cpp  

static const JNINativeMethod methods[] = {

    {"native_read",  "(Ljava/io/FileDescriptor;J[BIIIZ)I", (void*)android_os_MemoryFile_read},                                                                            

};

 

static jint android_os_MemoryFile_read(JNIEnv* env, jobject clazz,                                                               

        jobject fileDescriptor, jlong address, jbyteArray buffer, jint srcOffset, jint destOffset,

        jint count, jboolean unpinned)

{

    int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);

    if (unpinned && ashmem_pin_region(fd, 0, 0) == ASHMEM_WAS_PURGED) {

        ashmem_unpin_region(fd, 0, 0);

        jniThrowException(env, "java/io/IOException", "ashmem region was purged");

        return -1;

    }

    env->SetByteArrayRegion(buffer, destOffset, count, (const jbyte *)address + srcOffset);

    if (unpinned) {

        ashmem_unpin_region(fd, 0, 0);

    }

    return count;

}

從上述的代碼我們能夠看到,它直接通過env->SetByteArrayRegion(buffer, destOffset, count, (const jbyte *)address + srcOffset);這個方法將數(shù)據(jù)設(shè)置出去。寫函數(shù)的使用和這個讀是相同的,這里我們就不在查看了。

上一篇:關(guān)于C++中的友元

下一篇:TCP/IP協(xié)議與OSI協(xié)議詳解

熱點文章推薦
華清學(xué)員就業(yè)榜單
高薪學(xué)員經(jīng)驗分享
熱點新聞推薦
前臺專線:010-82525158 企業(yè)培訓(xùn)洽談專線:010-82525379 院校合作洽談專線:010-82525379 Copyright © 2004-2022 北京華清遠見科技集團有限公司 版權(quán)所有 ,京ICP備16055225號-5京公海網(wǎng)安備11010802025203號

回到頂部

主站蜘蛛池模板: 国产高清大尺度一区二区不卡 | 色综合色综合色综合网址 | 亚洲天堂免费在线 | 就操在线 | 欧美一级色视频 | 青青青免费高清在线观看视频在线 | 欧美黑人xxx | 狠狠久久婷五月综合色啪网 | 奇米狠狠一区二区三区 | 国产精品久久久久久久久久久搜索 | 久久99精品麻豆国产 | 亚洲日本一区二区 | 91精品久久久久亚洲国产 | 国产日韩欧美视频 | 狠狠色丁香久久婷婷 | 精品国产免费人成在线观看 | 亚洲大片免费观看 | 久久天天躁狠狠躁夜夜avai | 国产日韩高清一区二区三区 | 久久99精品这里精品3 | 黄色午夜影院 | 成人爱做日本视频免费 | 日韩免费影视 | 日本三级香港三级人妇三级 | 欧美视频不卡一区二区三区 | www.亚洲天堂| 日本a级影片 | 国内久久久 | 欧美性猛交 | 日本黄视频在线播放 | 国产韩国在线 | 三级斤 | 欧美视频免费在线播放 | 免费黄毛片 | 日本大片在线免费观看 | 日本高清不卡中文字幕 | 久久久久日韩精品免费观看网 | 亚洲成a人v大片在线观看 | 日本一区二区三区在线 视频 | 国产日产欧美一区二区三区 | 久久99精品久久久久久 |