【Linux编程】存储映射I/O

存储映射I/O使一个磁盘文件与存储空间中的一个缓冲区相映射,对缓冲区的读、写操作就是对文件的读、写操作,从而可以不再使用read、write系统调用。

将文件映射到存储区的函数由mmap完成,函数原型如下:

#include <sys/mman.h>

/* 成功返回映射区起始地址,出错返回MAP_FAILED */
void *mmap(void *addr, size_t len, int prot, int flag, int filedes, off_t off);

参数说明:

  • addr:指定映射存储区的起始地址,通常为0表示由系统选择起始地址。
  • len:需要映射的字节数。
  • prot:对映射存储区的保护要求,不能超过open文件时的权限。
    • PROT_READ:映射区可读
    • PROT_WRITE:映射区可写
    • PROT_EXEC:映射区可执行
    • PROT_NONE:映射区不可访问
  • flag:影响映射存储区的属性。
    • MAP_FIXED:返回值必须等于addr,不利于移植,不鼓励使用。
    • MAP_SHARED:表示存储操作相当于对该文件的write。
    • MAP_PRIVATE:对映射区的存储操作导致创建该映射文件的一个私有副本。
  • filedes:指定要被映射的文件描述符,映射之前需要先打开该文件。
  • off:要映射字节在文件中的起始偏移量,通常为0。

存储映射如下所示:

测试代码:

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

#define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)

int main(int argc, char *argv[])
{
    int fdin, fdout;
    void *src, *dst;
    struct stat statbuf;

    if (argc != 3)
    {
        printf("usage: %s <fromfile> <tofile>\n", argv[0]);
        return -1;
    }

    fdin = open(argv[1], O_RDONLY);
    fdout = open(argv[2], O_RDWR | O_CREAT | O_TRUNC, FILE_MODE);

    fstat(fdin, &statbuf);

    lseek(fdout, statbuf.st_size - 1, SEEK_SET);
    write(fdout, " ", 1);   /* lseek偏移量大于文件长度时,写操作将加长文件 */

    src = mmap(0, statbuf.st_size, PROT_READ, MAP_SHARED, fdin, 0);
    dst = mmap(0, statbuf.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fdout, 0);

    memcpy(dst, src, statbuf.st_size);  /* 数据复制 */

    munmap(src, statbuf.st_size);
    munmap(dst, statbuf.st_size);

    return 0;
}

此函数实现了文件内容之间的拷贝。lseek + write的组合操作使得目标文件的大小增加到和源文件大小相同。因为当lseek设置的文件偏移量大于文件当前长度时,下一个写操作将会使文件增大。如果没有对目标文件扩大,那么进程会接收到SIGBUS信号,表示存储区中有地址无法映射到文件中。

mmap实际上是将包含文件内容的内核缓冲区映射到应用程序地址空间,然后用memcpy直接进行数据的拷贝。其优势在于避免了类似read、write系统调用,在内核空间和用户空间之间的数据传递。

参考:

《unix环境高级编程》 P390-P395.

【Linux编程】存储映射I/O,布布扣,bubuko.com

时间: 2024-10-24 18:25:45

【Linux编程】存储映射I/O的相关文章

Linux编程---I/O部分

非常多函数都能够在网上找到,也比較基础,所以原型仅仅给出了函数名.详细用到再man吧. 输入输出是个非常重要的一块内容.差点儿网络相关的东西基本都是靠底层IO调用来实现的. 好吧.还是先踏踏实实的介绍一下C标准库中的IO函数吧.个别函数我也是第一次见.对于不太常见的我就多解释一下,反正通常这些函数百度一下就清楚了,我就不多解释了~ 1.C标准库IO函数 1.1流的关闭开启与重定向 fopen:打开一个流 fclose:关闭一个流 freopen:又一次打开一个流 1.2 读与写 读: fgetc

【Linux编程】XSI IPC

三种IPC被称作XSI IPC,分别是: 消息队列 信号量 共享存储器 下面分别介绍三种IPC的用法. 1.消息队列 消息队列是消息的链接表,具有如下函数接口: msgget:创建一个新队列或打开一个现存的队列. msgsnd:将消息添加到队列尾端. msgrcv:从队列中取消息. 我们可以自行定义一个表示消息的结构体,它由类型字段和实际数据组成: struct mest_t { long type; // 消息类型 char text[512]; // 消息内容 }; 有了消息类型,当我们用m

Linux编程---线程

首先说一下线程的概念.其实就是运行在进程的上下文环境中的一个执行流.普通进程只有一条执行流,但是线程提供了多种执行的路径并行的局面. 同时,线程还分为核心级线程和用户级线程.主要区别在属于核内还是核外. 核心级线程,地位基本和进程相当,由内核调度.也就是说这种系统时间片是按线程来分配的.这种线程的好处就是可以适当的运用SMP,即针对多核CPU进行调度. 用户级线程,在用户态来调度.所以相对来说,切换的调度时间相对核心级线程来说要快不少.但是不能针对SMP进行调度. 对于现在的系统来说,纯粹的用户

第3章 文件I/O(7)_高级文件操作:存储映射

8. 高级文件操作:存储映射 (1)概念: 存储映射是一个磁盘文件与存储空间的一个缓存相映射,对缓存数据的读写就相应的完成了文件的读写. (2)mmap和munmap函数 头文件 #include<sys/types.h> #include<sys/mman.h> 函数 void* mmap(void* addr, size_t length, int prot, int flags,                       int fd, off_t offset); int

Linux编程---信号处理

信号是一种异步的进程间通信的方式.但是这种通知方式能交换的信息很少.只能说给一个事件的标志.类似单片机中的中断,强迫进程停止做当前应当做的事情,而去执行中断执行程序. 信号的产生有如下几种: 1.用户按下了某个终止键,如ctrl-\或ctrl-c.是由终端程序向当前进程发送一个中断信号. 2.程序异常.比如除零错误. 3.kill函数向其发送了一个终止信号 4.进程向自己发送信号.如进程调用alarm函数. 5.企图读写终端的后台进程会得到作业的控制信号SIGTTIN或SIGTOU. 6.当进程

linux编程中接收主函数返回值以及错误码提示

程序A创建子进程,并调用进程B,根据不调用的不同情况,最后显示结果不同. #include <stdio.h> #include <unistd.h> #include <sys/wait.h> #include <sys/types.h> #include <errno.h> int main() { pid_t pid, rpid; int stat; if ((pid = fork()) < 0) { perror("for

Linux编程报错

学习<Linux编程第四版>时遇到问题: 报错: 错误:'sem_union'的存储大小未知 原因: Linux 2.6版内核union sem_union 联合体已被注释 解决方法: 重新定义sem_union union semun{    int val;    struct semid_ds *buf;    unsigned short *array;    struct seminfo *__buf;};

进程和程序:编写shell——《Unix/Linux编程实践教程》读书笔记(第8章)

1.Unix shell的功能 shell是一个管理进程和运行程序的程序.所有常用的shell都有3个主要功能: (1)运行程序: (2)管理输入和输出 (3)可编程 shell同时也是带有变量和流程控制的编程语言. 2.Unix的进程模型 一个程序是存储在文件中的机器指令序列,一般它是由编译器将源代码编译成二进制格式的代码.运行一个程序意味着将这些机器指令序列载入内存然后让处理器(CPU)逐条执行.在Unix术语中,一个可执行程序是一些机器指令机器数据的序列.一个进程是程序运行时的内存空间和设

Linux编程获取本机IP地址

使用函数getifaddrs来枚举网卡IP,其中使用到的结构体如下所示: struct ifaddrs { struct ifaddrs *ifa_next; /* Next item in list */ char *ifa_name; /* Name of interface */ unsigned int ifa_flags; /* Flags from SIOCGIFFLAGS */ struct sockaddr *ifa_addr; /* Address of interface *