首页 > 系统相关 >0708,文件流,目录流,MMAP内存映射

0708,文件流,目录流,MMAP内存映射

时间:2024-09-18 20:52:36浏览次数:3  
标签:文件 int MMAP argv st 0708 内存 include mode

目录

目录相关操作

目录流及相关操作

文件描述符和相关操作

fopen()和open()的关系

内存映射mmap

mmap相关函数

01_chdir.c

02_mkdir.c

03_rewinddir.c

04_ftruncate.c

05_mmap.c

目录相关操作

#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>

char *getcwd(char *buf, size_t size);
int chdir(const char *path);
int mkdir(const char *pathname, mode_t mode);
int rmdir(const char *pathname);  

/*
//getcwd   如果 char* buff=NULL,getcwd会调用malloc,但是不负责free
//chdir    当前工作目录是进程的属性,也就是说每一个进程都有自己的当前工作目录。且父进程创建 
           (fork)子进程的时候,子进程会继承父进程的当前工作目录
//mkdir    mode: 目录的权限位,会受文件创建掩码umask的影响,实际的权限为(mode & ~umask& 
           0777)
//rmdir    *pathname要删除的目录(空)

目录流及相关操作

#include <sys/types.h>
#include <dirent.h>

DIR* opendir(const char *name);      //可以打开一个目录,得到一个指向目录流的指针 DIR*
int closedir(DIR *dirp);
struct dirent* readdir(DIR *dirp);   //读目录流,得到指向下一个目录项的指针/NULL
void seekdir(DIR *dirp, long loc);   //移动 loc位置,是前面调用telldir函数的返回值。
long telldir(DIR *dirp);             //返回目录流中现在的位置
void rewinddir(DIR *dirp);          //重置目录流,即移动到目录流的起始位置
The  readdir()  function returns a pointer to a dirent structure representing the next directory entry in  the  directory  stream pointed  to by dirp. 

struct dirent {
    ino_t          d_ino;       /* Inode number */
    off_t          d_off;       /* Not an offset; see below */
    unsigned short d_reclen;    /* Length of this record 数组长度*/
    unsigned char  d_type;      /* Type of file; not supported 文件类型
                                  by all filesystem types */
    char           d_name[256]; /* Null-terminated filename 文件名/实现上可变数组*/
};

//d_type的可选值
              DT_BLK      This is a block device.
              DT_CHR      This is a character device.
              DT_DIR      This is a directory.
              DT_FIFO     This is a named pipe (FIFO).
              DT_LNK      This is a symbolic link.
              DT_REG      This is a regular file.
              DT_SOCK     This is a UNIX domain socket.
              DT_UNKNOWN  The file type could not be determined.

文件描述符和相关操作

//文件描述符是一种整数,用于标识被打开的文件。在操作系统层面,文件描述符是一个抽象的句柄,它指向操作系统内核中与文件相关的数据结构。

 kernel space
//process table     //open file table     //vnode table
  进程控制块PCB        进程之间共享

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

int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);//flag==O_CREAT --> mode
int close(int fd);
ssize_t read(int fd, void *buf, size_t count);
ssize_t write(int fd, const void *buf, size_t count);
off_t lseek(int fd, off_t offset, int whence);         //移动文件位置

int fsync(int fd);                    //把内核中和文件描述符 fd 相关的脏页刷新到磁盘
int ftruncate(int fd, off_t length);  //将文件截断为指定长度 length/after
int fstat(int fd, struct stat *statbuf);  //获取文件的元数据信息  stat
int dup(int oldfd);                       //copy fd,return new_fd(min)
int dup2(int oldfd, int newfd);           //会自动close(oldfd)


//open    成功:新的文件描述符(最小可用的文件描述符)
//read    返回实际读取的字节数目(0表示读到了文件的末尾),并且文件位置会向前移动这么多个字节
//write   返回实际写入的字节数目
//lseek   return 移动后文件位置   whence(SEEK_SET,SEEK_CUR,SEEK_END)
//flag 标志位

O_RDONLY  只读
O_WRONLY  只写
O_RDWR    可读/可写
O_CREAT   如果文件不存在,则创建文件
O_EXCL    和 O_CREAT 连用,如果文件存在, open() 会失败
O_TRUNC   如果文件存在,则将文件长度截断为 0 (清空文件内容)
O_APPEND  追加模式,即每次调用 write() 的时候,都会将文件位置移动到文件的末尾

其中 O_RDONLY 、 O_WRONLY 、 O_RDWR 又被称为访问模式 (access modes),我们有且只能选择其中一个。如果 flags 中有设置 O_CREAT 标志位,那么就需要设置第三个参数,用来指定创建文件的权限。
mode : 用来指定文件的权限,会受 umask 的影响,实际权限为 (mode & ~umask) 。
//statbuf

struct stat {
	dev_t st_dev; /* ID of device containing file */
	ino_t st_ino; /* Inode number */
	mode_t st_mode; /* File type and mode */
	nlink_t st_nlink; /* Number of hard links */
	uid_t st_uid; /* User ID of owner */
	gid_t st_gid; /* Group ID of owner */
	dev_t st_rdev; /* Device ID (if special file) */
	off_t st_size; /* Total size, in bytes */
	blksize_t st_blksize; /* Block size for filesystem I/O */
	blkcnt_t st_blocks; /* Number of 512B blocks allocated */
    
	/* Since Linux 2.6, the kernel supports nanosecond
	precision for the following timestamp fields.
	For the details before Linux 2.6, see NOTES. */
    
	struct timespec st_atim; /* Time of last access */
	struct timespec st_mtim; /* Time of last modification */
	struct timespec st_ctim; /* Time of last status change */
    
	#define st_atime st_atim.tv_sec /* Backward compatibility */
	#define st_mtime st_mtim.tv_sec
	#define st_ctime st_ctim.tv_sec
};

fopen()和open()的关系

open() 是系统调用, fopen() 是库函数。库函数是系统调用的封装。 fopen() 的底层是调用open() 实现的,它们之间有一个很好的对应关系。
    
fopen()mode         open() flags
r                   O_RDONLY
w                   O_WRONLY | O_CREAT | O_TRUNC
a                   O_WRONLY | O_CREAT | O_APPEND
r+                  O_RDWR
w+                  O_RDWR | O_CREAT | O_TRUNC
a+                  O_RDWR | O_CREAT | O_APPEND
    
/*
文件流        用户空间(buff + 用户态缓冲区)  -->  内核空间  -->  磁盘
文件描述符    用户空间(buff)                -->  内核空间  -->  磁盘
//文件流:多复制一次,用户态缓冲区读写数据不需要切换上下文(适合处理文本数据,字符/行
//描述符:少复制一次,读写数据需要切换上下文(适合复制文件

内存映射mmap

//内存映射可以将文件内容直接映射到进程的虚拟地址空间。当进程访问映射区域时,操作系统会负责将相应的文件部分加载到内存中。这种机制利用了操作系统的页面管理技术,可以高效地管理内存和文件I/O

传统的文件 I/O 操作通常涉及多个步骤:

用户空间请求数据
内核从磁盘读取数据到内核空间
数据再从内核空间复制到用户空间

//kernal --> argv,environ --> stack --> unallocated memory --> heap  --> ```
                                         内存映射区(部分)
//MAP_PRIVATE:
逻辑上,每个进程都有自己的一份映射 (copy-on-write, 写时复制)。对映射区域的修改,其它进程是不可见的,修改不会同步到底层文件中
用途是用一个文件的内容来初始化一块内存区域。常见的例子有:加载可执行文件或共享库文件的相应部分来初始化一个进程的代码段和数据段
    
//MAP_SHARED:
多个进程共享同一个映射区域。一个进程对映射区域的修改,其它进程是可见的,修改会同步到底层的文件中
用途:1. 它允许内存映射 I/O,这是一种零拷贝技术,适用于处理大文件。2. 因为多个进程共享同一片内存区域,所以它也可以用于进程间通信(Interprocess Communication, IPC)

mmap相关函数

#include <sys/mman.h>
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
int munmap(void *addr, size_t length);
void* memcpy(void* dest, const void* src, size_t n); //在内存中复制指定数量的字节

/*mmap
void* ->映射区域起始地址
起始地址NULL/OR  
//大小(向上取整下一个页大小的整倍数   //保护位prot/PROT_NONE  offset偏移量/页大小整数倍
PROT_NONE  映射区域不能被访问
PROT_READ  映射区域可读
PROT_WRITE 映射区域可写
PROT_EXEC  映射区域可执行

01_chdir.c

#include <stdio.h>
#include <unistd.h>
#include <error.h>
#include <errno.h>
#include <sys/stat.h>

#define SIZE(A) (sizeof(A)/sizeof(0))

int main(int argc, char* argv[]) {
    // ./chdir dir
    if(argc!=2){
        error(1,errno,"Usage:%s dir mode",argv[0]);
    }

    char buf[256];
    getcwd(buf, SIZE(buf)); // 获取当前工作目录
    puts(buf); // 打印当前工作目录

    int ret=chdir(argv[1]);
    if(ret==-1){
        error(1,errno,"mkdir %s ",argv[1]);
    }

    getcwd(buf, SIZE(buf));
    puts(buf); // 再一次打印当前工作目录,观察一下是否发生了变化


    return 0;
}

02_mkdir.c

#include <stdio.h>
#include <unistd.h>
#include <error.h>
#include <errno.h>
#include <sys/stat.h>

int main(int argc,char *argv[])
{
    // ./t_mkfir dir mode
    if(argc!=3){
        error(1,errno,"Usage:%s dir mode",argv[0]);
    }
    mode_t mode;
    sscanf(argv[2],"%o",&mode);
    //标准输入输出,char* --> mode_t
    
    if(mkdir(argv[1],mode)==-1){
        error(1,errno,"mkdir %s ",argv[1]);
    }

    return 0;
}

03_rewinddir.c

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <errno.h>
#include <error.h>
#include <string.h>

int main(int argc,char** argv)
{
    DIR* pdir=opendir(".");
    if(!pdir){error(1,errno,"opendir");}

    long loc=telldir(pdir); //返回目录流中现在的位置
    struct dirent* pdirent;
    errno=0;

    while(1){
        long tmploc=telldir(pdir);
        pdirent=readdir(pdir);
        if(!pdirent){break;}
        //如果读取失败(没有下一个目录项)跳出循环

        printf("%s ",pdirent->d_name);
        if(strcmp(pdirent->d_name,"03")==0){
            loc=tmploc;
        }
        printf("\n");

        if(errno!=0){error(1,errno,"readdir");}

    }
    
    printf("----------------------------\n");
    seekdir(pdir,loc);//移动loc位置
    pdirent=readdir(pdir);//如果不移动,此时pdirent是NULLL
    puts(pdirent->d_name);
    printf("----------------------------\n");
    rewinddir(pdir);
    pdirent=readdir(pdir);
    puts(pdirent->d_name);

    closedir(pdir);

    return 0;
}

04_ftruncate.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <errno.h>
#include <error.h>
#include <string.h>
#include <fcntl.h>

int main(int argc,char** argv)
{
    //./04 file length
    if(argc!=3){
        error(1,errno,"Usage:%s file length",argv[0]);
    }

    off_t length;
    sscanf(argv[2],"%ld",&length);

    int fd=open(argv[1],O_WRONLY);
    if(fd==-1){error(1,errno,argv[1]);}

    if(ftruncate(fd,length)==-1){
        error(1,errno,"ftruncate %d",ftruncate);
    }
    return 0;
}

05_mmap.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <dirent.h>
#include <errno.h>
#include <error.h>
#include <string.h>
#include <fcntl.h>

#define MMAP_SIZE (4096*10)

int main(int argc,char** argv)
{

    //./05 file1 file2
    if(argc!=3){
        error(1,0,"Usage:%s file1 file2",argv[0]);
    }

    int file1=open(argv[1],O_RDONLY);
    if(file1==-1){error(1,errno,"open file1");}

    int file2=open(argv[2],O_RDWR,O_CREAT,O_TRUNC,0666);
    if(file2==-1){error(1,errno,"open file2");}
    //读写打开,截断为0

    struct stat sb;
    fstat(file1,&sb);//获取文件的元数据信息
    off_t fsize=sb.st_size;
    ftruncate(file2,fsize);//file2 size

    off_t offset=0;
    while(offset<fsize){
        //计算映射区长度(分页映射)
        off_t length;
        if(fsize-offset>=MMAP_SIZE){
            length=MMAP_SIZE;
        }else{
            length=fsize-offset;
        }
        
        printf("%ld\n",offset);
        void* addr1=mmap(NULL,length,PROT_READ,MAP_SHARED,
                         file1,offset);
        if(addr1==MAP_FAILED){error(1,errno,"mmap %s",argv[1]);}

        void* addr2=mmap(NULL,length,PROT_READ|PROT_WRITE,
                        MAP_SHARED,file2,offset);
        if(addr2==MAP_FAILED){error(1,errno,"mmap %s",argv[2]);}

        memcpy(addr2,addr1,length);
        offset+=length;

        int err=munmap(addr1,length);
        if(err){error(1,errno,"munmap");}
        err=munmap(addr2,length);
        if(err){error(1,errno,"munmap2");}

        close(file1);
        close(file2);

    }



    return 0;
}

标签:文件,int,MMAP,argv,st,0708,内存,include,mode
From: https://blog.csdn.net/qq_51583806/article/details/142342666

相关文章

  • 使用firemin降低火狐内存占用
    这些年一直使用火狐浏览器,之前一直在AMD平台的机器使用,没有遇到过内存占用过大的问题(可能也与平台无关)。现在在IntelCPU的机器上使用,时间一久,内存就占用很大。试过Firefox/内存消耗严重里面的办法,效果不明显。也试过修改about:config里面的一些选项,也没有达到想要的效果。网上搜......
  • placement new --特殊的内存分配
    placementnew是C++中的一种特殊的内存分配技术,用来在指定的内存地址上直接构造对象。与普通的new运算符不同,placementnew并不分配新的内存,而是在已经分配好的内存上调用对象的构造函数。placementnew的典型使用场景当需要完全控制内存分配和释放时,程序员可以使用plac......
  • 我在使用C语言编程的时候可以完全了解内存中数据的情况,每一个字节每一个比特都能了如
    在Rust中,虽然编译器默认会帮助你管理内存,提供安全的抽象,但你仍然可以像在C语言中一样,完全掌控每个字节、每个位的操作。Rust提供了多种方式来实现对内存的精细控制,只是默认情况下这些操作受到更多的安全检查和限制。以下是一些方法,帮助你在Rust中实现对内存的精确控制:1.......
  • Rust有指针类型数据吗?是否可以查看一个变量的内存地址?
    在Rust中,虽然不像C/C++那样直接使用传统的指针类型,但Rust依然有指针的概念,且提供了多种安全的方式来操作和管理内存。你可以通过一些Rust的特性查看变量的内存地址并安全地进行指针操作。1.引用(References)Rust的引用(&和&mut)可以理解为一种安全的、受所有权规则和借......
  • 操作系统:进程间通信方式详解(下:消息队列、信号量、共享内存、套接字)
    每日一问:操作系统:进程间通信方式详解(下:消息队列、信号量、共享内存、套接字)进程间通信(Inter-ProcessCommunication,IPC)是操作系统中实现不同进程之间数据交换和协作的关键机制。本文详细介绍了几种常用的IPC方式,包括消息队列、信号量、共享内存和套接字。每种通信方式都......
  • ELF加载内存
    当一个ELF(ExecutableandLinkableFormat)文件加载到内存后,它的各个段会根据文件中的描述被映射到内存的不同区域。ELF文件被广泛用于Unix/Linux系统中的可执行文件、共享库和目标文件。典型的ELF文件包含多个段(sections),这些段被加载到内存中用于不同的目的,例如代码、数据、......
  • C语言:结构体在内存中存储(内存对齐)
    一、结构体是什么1.结构体的声明2.结构体的初始化二、结构体的内存对齐1.对齐规则 2.为什么存在内存对齐总结前言结构体是重要的知识点,其中,结构体的内存对齐是较重要的部分(本人使用的编译器是VisualStudio2022,下面介绍的对齐数也是以VS默认的8为标准),对后面学习数据......
  • C语言:整数和浮点数在内存中的存储--(超好理解)
    目录一、整数在内存中的存储(有符号整数)1.设置反码和补码的的目的二、浮点数在内存中的存储1.浮点数取的过程2.例题解析总结目前学习到C语言的各种数据类型在内存中的存储的方式和过程,自己初学的时候下了很多时间去学习理解,为了帮助和自己一样的在第一次初学C语言存储......
  • 操作系统知识强化内存映射文件(25王道)
    内存映射文件本文根据25王道操作系统内存映射文件这一章所作跟课笔记什么是内存映射文件首先来看看什么是内存映射文件。简单来说,这是操作系统向上层的程序员提供的一个系统调用功能。通过这个功能,程序员可以很方便的去访问文件数据,另外这个功能也可以很方便的让多个进程......
  • 进程的内存分配
    在操作系统中,进程的内存分配是指操作系统为每个进程管理和分配所需的内存资源。内存管理是操作系统的核心功能之一,它涉及到为进程提供虚拟内存、物理内存分配、页表管理、以及地址转换等操作。操作系统通过虚拟内存机制,使每个进程都可以认为自己拥有独立的、连续的内存空间。1.......