Andoird Native&Linux&C/C++&随机读写

核心问题:
Android Native层如何用Open(不是File)去实现对文件的随机读写呢?
背景:
1:题主正在写一个测试磁盘速度的Tool(直接访问磁盘就需要O_DIRECT字段,所以就排除了File的方式)
2:开发环境是Windows系统+Andoird手机
思考:
1:Windows的API?例如CreateFile可以实现这个需求,但是Android是基于Linux系统的,无法使用,目前还在尝试使用NDK倒windows的库,但是Windows的库太多了,关联性也比较强,比较难以实现,
2Java的RandomAccessFile源码,但是没有是使用O_DIRECT,而且追到IoTracker就没下文了,有点难顶
3:通过随机修改文件内的文件指针,来达到随机读写,但是性能好像会缺失,目前正在实践

由于时间紧迫,想请教大家,非常感谢!

Andoird Native&Linux&C/C++&随机读写 核心问题: Android Native层如何用Open(不是File)去实现对文件的随机读写呢? 背景: 1:题主正在写一个测试磁盘速度的Tool(直接访问磁盘就需要O_DIRECT字段,所以就排除了File的方式) 2:开发环境是Windows系统+Andoird手机 思考: 1:Windows的API?例如CreateFile可以实现这个需求,但是Android是基于Linux系统的,无法使用,目前还在尝试使用NDK倒windows的库,但是Windows的库太多了,关联性也比较强,比较难以实现, 2:Java的RandomAccessFile源码,但是没有是使用O_DIRECT,而且追到IoTracker就没下文了,有点难顶 3:通过随机修改文件内的文件指针,来达到随机读写,但是性能好像会缺失,目前正在实践 由于时间紧迫,想请教大家,非常感谢!

在 Android Native 层中使用 open() 函数进行文件的随机读写是可行的。以下是一些注意事项和步骤:

  1. 首先需要在 C/C++ 代码中包含 <fcntl.h> 头文件和 <unistd.h> 头文件。
  2. 使用 open() 函数打开文件,并使用 O_DIRECT 标志以进行直接 I/O 操作,如下所示: python
    int fd = open("/path/to/file", O_RDWR | O_DIRECT);
    
  3. 对文件进行读写操作时,可以使用 pread()pwrite() 函数,这些函数可以直接在文件的指定偏移量处进行读写操作。例如,下面的代码片段使用 pread() 从文件的第 10 个字节开始读取 100 个字节的数据: scss
    char buffer[100];
    ssize_t n = pread(fd, buffer, sizeof(buffer), 10);
    
  4. 为了实现随机读写,可以在 pread()pwrite() 函数中使用不同的偏移量进行读写操作。例如,可以使用 rand() 函数生成随机偏移量,并使用 lseek() 函数将文件指针移动到该偏移量处,然后进行读写操作。以下是示例代码: scss
    // 生成随机偏移量
    off_t offset = rand() % file_size;
    
    // 将文件指针移动到随机偏移量处
    lseek(fd, offset, SEEK_SET);
    
    // 从随机偏移量处读取 100 个字节的数据
    char buffer[100];
    ssize_t n = pread(fd, buffer, sizeof(buffer), offset);
    
    // 将随机偏移量处写入 100 个字节的数据
    ssize_t n = pwrite(fd, buffer, sizeof(buffer), offset);
    

注意:使用 O_DIRECT 标志进行直接 I/O 操作可能会对性能产生影响。此外,随机读写可能会导致文件系统的碎片化,因此应该谨慎使用。

可以尝试使用Linux系统调用函数 open 和 lseek 来实现随机读写。其中 open 可以通过指定 O_DIRECT 标志来打开文件,实现直接访问磁盘,类似于 Windows 的 CreateFile 函数。lseek 可以用于移动文件指针实现随机读写。

以下是一个示例代码:


#include <fcntl.h>
#include <unistd.h>

int main() {
    const char* filepath = "/sdcard/testfile.bin";
    int fd = open(filepath, O_RDWR | O_CREAT | O_DIRECT, S_IRUSR | S_IWUSR);
    if (fd < 0) {
        // handle error
    }

    // write to the 1024th byte of the file
    int offset = 1024;
    if (lseek(fd, offset, SEEK_SET) != offset) {
        // handle error
    }
    char buf[1024] = "data to be written";
    if (write(fd, buf, sizeof(buf)) != sizeof(buf)) {
        // handle error
    }

    // read from the 2048th byte of the file
    offset = 2048;
    if (lseek(fd, offset, SEEK_SET) != offset) {
        // handle error
    }
    char read_buf[1024];
    if (read(fd, read_buf, sizeof(read_buf)) != sizeof(read_buf)) {
        // handle error
    }

    close(fd);
    return 0;
}

需要注意的是,使用 O_DIRECT 标志打开文件会有一些限制和注意事项,具体可以参考相关文档或资料。此外,还需要在 AndroidManifest.xml 中声明访问外部存储的权限

下面是另一种方式:


#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main(int argc, char* argv[]) {
    int fd;
    char buffer[1024];
    ssize_t ret;
    off_t pos;

    fd = open("/data/data/com.example.test/test.txt", O_RDWR | O_DIRECT);
    if (fd == -1) {
        perror("open");
        exit(EXIT_FAILURE);
    }

    pos = lseek(fd, 0, SEEK_END);
    if (pos == -1) {
        perror("lseek");
        exit(EXIT_FAILURE);
    }

    pos = lseek(fd, 0, SEEK_SET);
    if (pos == -1) {
        perror("lseek");
        exit(EXIT_FAILURE);
    }

    for (int i = 0; i < 10; i++) {
        pos = rand() % pos;
        pos = lseek(fd, pos, SEEK_SET);
        if (pos == -1) {
            perror("lseek");
            exit(EXIT_FAILURE);
        }

        ret = read(fd, buffer, sizeof(buffer));
        if (ret == -1) {
            perror("read");
            exit(EXIT_FAILURE);
        }

        printf("Read %ld bytes at position %lld: %s\n", ret, (long long)pos, buffer);
    }

    close(fd);
    exit(EXIT_SUCCESS);
}

在这个示例中,我们使用了open函数打开文件,并指定O_DIRECT标志以启用直接I/O模式。然后,我们使用lseek函数将文件指针移动到文件结尾,以便获取文件的大小。接下来,我们将文件指针移回文件开头,并使用rand函数生成一个随机的文件指针位置,然后使用lseek函数将文件指针移动到该位置。随后,我们使用read函数读取一定数量的字节,并输出读取的数据。最后,我们关闭文件描述符并退出程序。

请注意,直接I/O模式可能会带来一些性能问题,因此请根据实际情况选择适当的I/O模式

上面代码中的几个点需要注意:

在使用 open 函数时,需要注意设置 O_DIRECT 标志,以确保文件是以直接 I/O 的方式打开的。
在使用 posix_memalign 函数时,需要注意内存对齐的参数必须是 2 的幂次方。
在进行写操作时,需要使用 pread 函数,而不是 write 函数。使用 pread 函数可以确保每次写入都是从文件指定位置开始写入。
在进行读操作时,需要使用 pwrite 函数,而不是 read 函数。使用 pwrite 函数可以确保每次读取都是从文件指定位置开始读取。

在Android Native层中实现对文件的随机读写可以使用系统提供的C/C++库,如libc和libm。这些库中包含了一些文件I/O相关的函数,如fopen、fread、fwrite、fclose、fseek等,可以用来实现文件读写操作。

要实现随机读写,可以使用fseek函数将文件指针定位到指定的位置,然后使用fread或fwrite函数进行读写操作。例如,可以使用以下代码实现对文件的随机读写:

#include <stdio.h>

int main() {
    FILE *fp;
    char buffer[1024];

    // 打开文件
    fp = fopen("/path/to/file", "rb+");
    if (fp == NULL) {
        printf("Failed to open file.\n");
        return 1;
    }

    // 将文件指针定位到指定位置
    fseek(fp, 512, SEEK_SET);

    // 从文件中读取数据
    fread(buffer, sizeof(char), 1024, fp);

    // 将数据写入文件
    fwrite(buffer, sizeof(char), 1024, fp);

    // 关闭文件
    fclose(fp);

    return 0;
}

在这个例子中,打开文件时使用的模式是"rb+",其中'r'表示以只读方式打开文件,'b'表示以二进制方式打开文件,'+'表示可以读写文件。然后使用fseek函数将文件指针定位到512字节处,使用fread函数从文件中读取1024字节的数据,使用fwrite函数将这1024字节的数据写入文件中。最后使用fclose函数关闭文件。

注意,使用fread和fwrite函数进行文件读写操作时,需要注意数据的字节顺序和大小端问题。此外,在使用这些函数时,建议每次读写的数据块大小是磁盘块大小的整数倍,以获得最佳的性能。

打开文件

int fd = open("/mnt/sdcard/testfile", O_RDWR | O_DIRECT, S_IRUSR | S_IWUSR);
if (fd < 0) {
    // 处理打开文件失败的情况
}

定位文件指针

off_t offset = rand() % filesize; // filesize为文件大小
if (lseek(fd, offset, SEEK_SET) < 0) {
    // 处理定位文件指针失败的情况
}

读写文件

char buffer[4096];
int bytes = read(fd, buffer, sizeof(buffer));
if (bytes < 0) {
    // 处理读文件失败的情况
}
// 对读取的数据进行处理

// 写文件
if (write(fd, buffer, bytes) < 0) {
    // 处理写文件失败的情况
}

char *buffer;
int align_size = 512; // 假设对齐大小为512
if (posix_memalign((void**)&buffer, align_size, sizeof(buffer)) != 0) {
    // 处理内存对齐失败的情况
}

你可以尝试使用Linux的API,比如open()函数,它可以支持O_DIRECT标志,从而实现对文件的随机读写。你也可以尝试使用mmap()函数,它可以将文件映射到内存,从而实现对文件的随机读写。

在 Android Native 层,可以使用 mmap 函数实现文件的随机读写。mmap 函数可以将一个文件或者设备映射到进程的地址空间中,并返回一个指向映射区的指针。

下面是一个简单的使用 mmap 函数实现文件随机读写的例子:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>

int main() {
    const char* filepath = "/sdcard/test.txt";
    const int file_size = 1024*1024;
    int fd = open(filepath, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);

    if (fd == -1) {
        perror("open");
        exit(1);
    }

    if (ftruncate(fd, file_size) == -1) {
        perror("ftruncate");
        exit(1);
    }

    void* ptr = mmap(NULL, file_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);

    if (ptr == MAP_FAILED) {
        perror("mmap");
        exit(1);
    }

    for (int i = 0; i < file_size; i++) {
        ((char*)ptr)[i] = rand() % 256;
    }

    if (msync(ptr, file_size, MS_SYNC) == -1) {
        perror("msync");
        exit(1);
    }

    if (munmap(ptr, file_size) == -1) {
        perror("munmap");
        exit(1);
    }

    close(fd);
    return 0;
}

在这个例子中,我们使用 open 函数打开一个文件,并设置了 O_RDWR 和 O_CREAT 标志,表示读写模式打开文件并且文件不存在时创建文件。

接着,我们使用 ftruncate 函数调整文件大小到 1MB。

然后,我们使用 mmap 函数将文件映射到进程的地址空间中,并返回一个指向映射区的指针。在这里,我们设置了 PROT_READ 和 PROT_WRITE 标志,表示映射区可读可写。

接下来,我们随机写入了整个文件,然后使用 msync 函数将内存中的数据同步到磁盘。

最后,我们使用 munmap 函数解除内存映射,并关闭文件句柄。

这样,就可以实现对文件的随机读写。需要注意的是,mmap 函数返回的指针需要通过 munmap 函数解除映射,否则可能会造成内存泄漏。

我给点提示吧,Android Native层建议你使用POSIX API中的open函数来实现对文件的随机读写,open函数可以接受一个参数O_DIRECT,这个参数可以指定文件的读写模式,这样子就可以实现对文件的随机读写。
我想到的第二个,Android Native层还可以使用mmap函数来实现对文件的随机读写,mmap函数可以将文件映射到内存中,实现了对文件的随机读写。

具体的代码可以参考下面的示例:

//打开文件
int fd = open("/path/to/file", O_RDWR);
if(fd == -1) {
    // 文件打开失败
    return;
}

//定位文件的位置
int offset = lseek(fd, 0, SEEK_SET);
if(offset == -1) {
    // 文件定位失败
    return;
}

//读取文件
char buf[1024];
int n = read(fd, buf, 1024);
if(n == -1) {
    // 文件读取失败
    return;
}

//写入文件
int n = write(fd, buf, 1024);
if(n == -1) {
    // 文件写入失败
    return;
}

//关闭文件
close(fd);

该回答引用chatGPT
Android Native层可以使用mmap函数来实现对文件的随机读写。mmap函数可以将文件映射到内存中,从而可以实现对文件的随机读写。此外,Android Native层还可以使用open函数来实现对文件的随机读写,open函数可以打开文件,从而可以实现对文件的随机读写。