跳转至内容
  • 版块
  • 最新
  • 标签
  • 热门
  • Online Tools
  • 用户
  • 群组
折叠
品牌标识

D2Learn Forums

月

月仁不吃五饼

@月仁不吃五饼
关于
帖子
8
主题
4
群组
0
粉丝
0
关注
0

帖子

最新 最佳 有争议的

  • C++学习资料开源
    月 月仁不吃五饼

    由于时间不多,来不及更新帖子,现已将部分C++资料上传github(因为平时习惯不好,归档分类有点乱)
    https://github.com/liuguoqingdq/C-Line
    内容中有不正确的地方还望指正


  • Linux系统编程(2)文件IO
    月 月仁不吃五饼

    @sunrisepeak 在 Linux系统编程(2)文件IO 中说:

    感觉可以把这个系列整理成一个GitHub 仓库 包含文档和对应的练习或演示代码
    让读者上手实操/改改东西 用 strace 等一些工具 追一追 系统调用 观察真实机器上的效果 之类的
    体验感应该能增强不少

    时间比较少,有空可以整理一下


  • Linux系统编程(3)文件读写操作
    月 月仁不吃五饼

    Linux系统编程(4)文件读写操作

    通过read()读文件

    #include <unistd.h>
    
    ssize_t read(int fd, void *buf, size_t count);
    

    每个参数分别是什么

    参数 1:int fd

    • fd 是文件描述符,一般来自:

      • open() 打开文件

      • 或者标准输入 0(STDIN_FILENO)

    • 内核用 fd 找到你打开的“文件对象 + 当前偏移量”等状态。

    参数 2:void *buf

    • 你提供的缓冲区地址,用来装读出来的数据。

    • 必须保证这块内存可写并且至少有 count 个字节容量。

    • 例如:

    char buf[4096];
    read(fd,buf,sizeof(buf));
    

    参数 3:size_t count

    • 你希望这次最多读取多少字节。

    • 注意:这是“最多”,不是“必须读满”。


    每次调用read()函数,会从fd指向的文件的当前偏移开始读取len字节到buf所指向的内存中。执行成功时,返回写入buf中的字节数;出错时,返回-1,并设置errno值。fd的文件位置指针会向前移动,移动的长度由读取到的字节数决定。如果fd所指向的对象不支持seek操作(比如字符设备文件),则读操作总是从“当前”位置开始。
    基本用法很简单。下面这个例子就是从文件描述符fd所指向的文件中读取数据并保存到word中。读取的字节数即unsigned long类型的大小,在Linux的32位系统上是4字节,在64位系统上是8字节。成功时,返回读取的字节数;出错时,返回-1:**

    usigned long world;
    ssize_t nr
    nr = read(fd,&world,sizeof(world));
    if(nr==-1)......
    

    返回值三种情况(必须背熟)

    情况 A:返回值 > 0

    • 表示实际读取到的字节数,比如返回 1000,就说明 buf[0..999] 有效。

    • 不保证等于 count:读文件也可能因为系统缓冲、信号等原因“读不满”。

    情况 B:返回值 == 0

    • 表示 EOF(文件结束):已经读到文件末尾,再读就没有数据了。

    • 对普通文件,这是“读完”的标志。

    情况 C:返回值 == -1

    • 表示出错,具体原因在全局变量 errno 里。

    • 最常见的你需要认识两个:

      • errno == EINTR:被信号打断,通常重试即可

      • errno == EAGAIN / EWOULDBLOCK:非阻塞读时没数据(普通文件很少遇到)


    读入所有字节

    诚如前面所描述的,由于调用read()会有很多不同情况,如果希望处理所有错误并且真正每次读入len个字节(至少读到EOF),那么之前简单“粗暴”的read()调用并不合理。要实现这一点,需要有个循环和一些条件语句,如下:

    ssize_t ret;//声明返回值
    size_t length;//声明需要读取的字节长度
    while(len>0 && ret=read(fd,buf,length) ){
      if(ret==-1){
       if(error==EINTR) continue;
      perror("read"); 
      break;
    }
      len-=(size_t)ret;
      buf+=ret;
    }
    

    read() 读出来的是原始字节,它只返回“实际读了多少字节”,不会自动在末尾补 '\0'。

    所以:

    • 如果把 read 的结果当作 C 字符串来用,必须自己加终止符,并且要预留一个字节空间。
      实例:
    char buf[1024];
    ssize_t n = read(fd, buf, sizeof(buf) - 1); // 预留 1 字节给 '\0'
    if (n > 0) {
        buf[n] = '\0';  // 手动补 '\0'
        printf("%s", buf);
    }
    

    补充两点常见坑:

    • read 读到的内容里可能本来就包含 '\0'(比如二进制文件),这时即使你手动补了终止符,printf("%s") 也会在中间的 '\0' 提前截断。

    • 如果 n == sizeof(buf)-1,上面这种写法仍然安全,因为你预留了 1 字节。


    阻塞IO(先了解即可)

    有时,开发人员不希望read()调用在没有数据可读时阻塞在那里。相反地,他们希望调用立即返回,表示没有数据可读。这种方式称为非阻塞I/O,它支持应用以非阻塞模式执行I/O操作,因而如果是读取多个文件,以防错过其他文件中的可用数据。
    因此,需要额外检查errno值是否为EAGAIN。正如前面所讨论的,如果文件描述符以非阻塞模式打开(即open()调用中指定参数为O_NONBLOCK,open()调用的参数”),并且没有数据可读,read()调用会返回-1,并设置errno值为EAGAIN,而不是阻塞模式。当以非阻塞模式读文件时,必须检查EAGAIN,否则可能因为丢失数据导致严重错误。你可能会用到如下代码:

     char buf[MAX_SIZE];
     usigned int len;
     start:
     ssize_t ret = read(fd,buf,len);
     if (ret==-1){
    	 if(error==ENTER) goto satrt;
    	else if(error==EAGAIN)....;
    	else...;
     }
    

    怎么打开非阻塞设置

    方法 A:open 时带 O_NONBLOCK

    int fd = open("a.txt",O_RDONLY | O_NONBLOCK); 
    

    方法 B:对已有 fd 用 fcntl 打开非阻塞

    #include <fcntl.h>
    
    int flags = fcntl(fd, F_GETFL, 0);
    fcntl(fd, F_SETFL, flags | O_NONBLOCK);
    

    非阻塞读的正确姿势:配合 select/poll/epoll

    非阻塞不是让你疯狂 while(read...) 空转(那会 CPU 100%),而是:

    1)先等“可读事件”(有数据了再读)
    2)再 read(),并且通常要一直读到 EAGAIN 才停(把缓冲区读空)

    #include <unistd.h>
    #include <fcntl.h>
    #include <errno.h>
    #include <stdio.h>
    #include <poll.h>
    
    int main() {
        int fd = STDIN_FILENO;
    
        int flags = fcntl(fd, F_GETFL, 0);
        fcntl(fd, F_SETFL, flags | O_NONBLOCK);
    
        char buf[1024];
    
        while (1) {
            struct pollfd pfd = { .fd = fd, .events = POLLIN };
            int r = poll(&pfd, 1, 5000); // 等 5 秒
            if (r == 0) { puts("timeout..."); continue; }
            if (r < 0) { perror("poll"); break; }
    
            // 可读了:把当前能读的都读出来(直到 EAGAIN)
            while (1) {
                ssize_t n = read(fd, buf, sizeof(buf));
                if (n > 0) {
                    write(STDOUT_FILENO, buf, (size_t)n);
                    continue;
                }
                if (n == 0) return 0; // EOF
                if (errno == EINTR) continue;
                if (errno == EAGAIN || errno == EWOULDBLOCK) break;
                perror("read");
                return 1;
            }
        }
        return 0;
    }
    

  • Linux系统编程(2)文件IO
    月 月仁不吃五饼

    Linux系统编程(2)文件IO

    最基本的文件访问方法是系统调用read()和write()。但是,在访问文件之前,必须先通过open()或creat()打开该文件。一旦完成文件读写,还应该调用系统调用close()关闭该文件。
    大致流程如下

    open/create->read/write->close
    

    open系统调用

    open系统调用整个流程如下:

    ┌──────────────┐
    │  Your Code   │
    │  open(...)   │
    └──────┬───────┘
           ▼
    ┌──────────────────┐
    │ glibc (libc.so)  │   ← 用户态库
    │ open() wrapper   │
    └──────┬───────────┘
           ▼
    ┌──────────────────┐
    │ syscall entry    │   ← 用户态 / 内核态边界
    │ SYS_openat       │
    └──────┬───────────┘
           ▼
    ┌──────────────────┐
    │ Linux Kernel     │
    │ sys_openat()     │
    └──────────────────┘
    

    而在使用上,我们需要先包含下述头文件

    #include <unistd.h>    // read, write, close, lseek
    #include <fcntl.h>     // open, O_RDONLY/O_CREAT/... flags
    #include <sys/stat.h>  // mode_t, 权限位 S_IRUSR 等(创建文件常用)
    #include <sys/types.h> // ssize_t, off_t 等(有时可省,现代系统常被间接包含)
    

    通过系统调用open(),可以打开文件并获取其文件描述符:
    open系统调用有两个函数签名

    int open(const char *name, int flags);
    int open(const char *name, int flags, mode_t mode);
    

    它们的区别是 第三个参数 mode 要不要传(后面补充这第三个参数的作用)

    open的返回值是文件描述符,linux下一切皆文件,如果返回-1代表发生错误


    open()的flags参数

    flags参数是由一个或多个标志位的按位或组合。它支持三种访问模式:O_RDONLY、O_WRONLY或O_RDWR,(其实是单词的缩写,不用死记硬背)这三种模式分别表示以只读、只写或读写模式打开文件。
    举个例子,以下代码以只读模式打开文件/home/kidd/madagascar:

    int fd =open("/home/kidd/madagascar",O_RDONLY);
    if(fd==-1){
    //这里是错误处理,要养成好习惯,随手处理错误
    }
    

    使用open打开文件之后,我们就获得了这个文件的句柄。用这个句柄就可以对这个文件施加操作了,但是要尤为注意的是,对这个文件能够施加怎样的操作,取决于打开方式,即flags的参数。
    那么问题来了,如果文件不存在怎么办? 这就涉及到flags其他可选参数:

    1) 创建/覆盖类

    • O_CREAT:文件不存在就创建(这时必须提供第三个参数 mode)

      open("a.txt", O_WRONLY | O_CREAT, 0644);
      
    • O_EXCL:配合 O_CREAT 使用;如果文件已存在则失败(避免覆盖)
      当和标志位O_CREAT一起使用时,如果参数name指定的文件已经存在,会导致open()调用失败。用于防止创建文件时出现竞争。如何没有和标志位O_CREAT一起使用,该标志位就没有任何含义。

    open("a.txt", O_WRONLY | O_CREAT | O_EXCL, 0644); // 存在则报 EEXIST
    

    上面涉及到O_CREAT的操作就需要第三个参数了0644,这个参数的意义如下:

    0 6 4 4
      ↑ ↑ ↑
      │ │ └─ 其他用户 (other)
      │ └── 同组用户 (group)
      └──── 文件所有者 (owner)
    

    0644 的三位分别代表谁?

    权限格式是:

    [ owner ][ group ][ other ]

    每一位是 0~7 的八进制数:

    数字 二进制 含义
    4 100 读 (r)
    2 010 写 (w)
    1 001 执行 (x)
    所以0644代表:文件所有者rw权限,同组用户和其他用户有r权限
    使用O_CREAT必须使用第三个权限参数


    • O_TRUNC:文件已存在且以写方式打开时,把文件长度截断为 0(清空重写)

      open("a.txt", O_WRONLY | O_TRUNC);

    2) 追加/定位类

    • O_APPEND:每次 write 都追加到文件末尾(内核保证追加定位的原子性)

      open("log.txt", O_WRONLY | O_CREAT | O_APPEND, 0644);

    3) 阻塞行为/同步类

    • O_NONBLOCK:非阻塞 I/O(对 pipe/终端/socket 影响大,对普通磁盘文件通常意义不大,后面需要时再讲)

      open("/dev/tty", O_RDONLY | O_NONBLOCK);

    • O_SYNC:更强的同步写语义(write 返回前尽量保证数据落到存储设备,性能会下降)

    • O_DSYNC:只保证数据(不强制元数据)同步(比 O_SYNC 轻一些)

    • O_RSYNC: O_RSYNC标志位指定读请求和写请求之间的同步。该标志位必须和O_SYNC或O_DSYNC一起使用。

    • O_LARGEFILE
      文件偏移使用64位整数表示,可以支持大于2GB的文件。64位操作系统中打开文件时,默认使用该参数。

    fd = open("a.txt",O_LARGEFILE | O_RDONLY);
    

    4) 安全/继承类

    • O_CLOEXEC:执行 exec() 时自动关闭该 fd,防止泄漏到子进程

      open("a.txt", O_RDONLY | O_CLOEXEC);
      (close execute的缩写)

    5) 其他你可能会见到的

    • O_NOCTTY:打开终端设备时不把它变成进程的控制终端

    • O_NOFOLLOW:不跟随符号链接(用于安全场景,避免被 symlink 攻击)

    由前面的例子可以明白,这些参数都是位操作符
    是可以做|运算把不同操作拼接在一起的

    小练习

    1. 只读打开
    int fd=open("a.txt",O_RDONLY);
    if(fd==-1){
      perror("open");
    }
    
    1. 覆盖写(不存在就创建,存在就清空)
    int fd =open("a.txt",O_WRONLY | O_CREAT | O_TRUNC,0644);
    if(fd==-1){
      perror("open");
    }
    
    1. 追加写日志
    int fd=open("a.txt",O_WRONLY | O_APPEND |O_CREAT,0644);
    if(fd==-1){
        perror("open");
    }
    
    1. 只允许“新建”,不允许覆盖
    int fd=open("a.txt",O_WRONLY |O_CREAT |O_EXCL,0644);
    
    

    这里额外讲一下,其实是有creat()系统调用的,但是用起来方便,支持的操作也比较少,一般用open就可以
    creat函数签名

    #include <fcntl.h>  
    int creat(const char *pathname, mode_t mode);
    

  • Linux系统编程(1)文件
    月 月仁不吃五饼

    Linux系统编程(1)文件操作的层次结构

    我将在此持续更新Linux系统编程的教学,有不妥,不正确的地方望看到的人指正

    首先对于IO的操作,或者各种IO函数,他应当可以被分为下述的几个部分,我将采用总分的结构,在系统描述每一个部分之前,先对整个linux IO有一个大致的认识

    第 0 层:用户态缓冲 I/O(不是系统调用,但最“上层”)

    特点:你操作的是 FILE*,数据先进入 用户态缓冲区,再由 libc 在合适时机调用 write/read 等进入内核。
    典型 API:fopen/fclose/fread/fwrite/fgets/fputs/fgetc/fputc/printf/scanf,setvbuf/fflush/ferror/feof
    要点:这层决定“行缓冲/全缓冲/无缓冲”,以及很多“看起来一次写入”,底下可能拆成多次系统调用。

    第 1 层:文件描述符(fd)上的“无缓冲”I/O 系统调用(VFS 入口)

    特点:你提供用户缓冲区地址,内核负责把数据在 用户内存 ↔ 内核 间拷贝,并通过 VFS/页缓存/文件系统把它落到磁盘或读出来。

    1.1 基础数据读写

    • open/openat/creat, close

    • read, write

    • lseek(改变文件偏移)

    1.2 更“语义化/性能化”的读写

    • 定位读写(不改文件偏移):pread, pwrite

    • 向量 I/O(scatter/gather):readv, writev

    • 按大小截断:truncate, ftruncate

    这一层是“系统编程”的主干:大多数 I/O 都能归结为这些 fd syscalls 的组合。

    第 2 层:内核缓冲/页缓存语义相关(同样还是 fd 接口,但语义更“接近内核缓存”)

    特点:你不只是“读写字节”,还在控制“什么时候算写成功、什么时候必须落盘、缓存如何使用”。

    2.1 同步与持久化

    • fsync, fdatasync, sync, syncfs
      解决“写进页缓存了但还没真正落盘”的一致性/崩溃恢复问题。

    2.2 访问模式与预取/淘汰建议(属于“提示/策略”)

    • posix_fadvise(libc 封装,底层可能用 fcntl/内核建议路径)

    • readahead(Linux 专有,提示预读)

    这类接口不一定“立刻改变数据”,但会影响页缓存、预读、回写策略,从而影响性能与时延。

    第 3 层:绕开“显式 read/write”的 I/O 路径

    3.1 内存映射 I/O(mmap 路径)

    特点:文件数据映射到进程虚拟内存,通过缺页异常把页拉进来;写回由回写机制处理。

    • mmap, munmap, msync(以及 mprotect/mlock 等相关)

    直觉:read/write 是“你显式搬运”,mmap 是“让内核分页系统替你搬运”。

    3.2 零拷贝/内核内搬运(减少 user<->kernel 拷贝)

    • sendfile(文件→socket 常用)

    • splice, tee, vmsplice(在 fd 之间搬运/管道相关)

    第 4 层:I/O 多路复用与事件驱动(“等 I/O”也是 I/O 的一部分)

    特点:面向大量 fd 时,不是忙等,而是让内核告诉你“哪个 fd 可读/可写/出错”。

    • select, pselect

    • poll, ppoll

    • epoll_create/epoll_ctl/epoll_wait

    (扩展:inotify 用于文件系统事件通知,也常被归到“事件 I/O”这一层。)

    第 5 层:异步 I/O(发起后不阻塞当前线程)

    特点:读写请求提交给内核,之后再取完成事件;适合高并发、减少线程阻塞。

    • POSIX AIO:aio_read/aio_write/aio_error/aio_return(多为库接口,底层实现依系统而异)

    • Linux AIO:io_setup/io_submit/io_getevents(更“Linux 内核味”)

    更现代的 io_uring 也属于这一类(但它比 Love 那本书年代新很多,书里大概率没有重点展开)。

    第 6 层:控制类系统调用(不直接搬运数据,但决定“怎么 I/O”)

    6.1 文件描述符控制与状态

    • fcntl:设置/获取 fd 标志(如 O_NONBLOCK)、记录锁(F_SETLK 等)、复制 fd 等

    • dup/dup2/dup3

    6.2 设备/驱动专用控制面(“像 I/O,但本质是控制”)

    • ioctl:字符设备/块设备/终端/网络设备等大量控制都走它

    第 7 层:命名空间与元数据 I/O(“文件系统操作”,不等于数据读写)

    特点:改的是目录项、inode 元数据、权限、链接关系;不直接等价于读写文件内容。

    • 元数据查询:stat/fstat/lstat

    • 权限属主:chmod/fchmod, chown/fchown, umask, access

    • 链接与重命名:link/unlink/symlink/readlink, rename

    • 目录与节点:mkdir/rmdir, mknod

    • 目录读取:getdents(系统调用层),上层常用 opendir/readdir(库封装)

    总体结构如下

    ┌───────────────────────────────────────────────────────────────────────────┐
    │                         USER SPACE                                        │
    │                                                                           │
    │  ┌─────────────────────────────────────────────────────────────────────┐ │
    │  │ Level 0: User-mode Buffering (glibc FILE*)                            │ │
    │  │                                                                     │ │
    │  │  fopen / fread / fwrite / printf                                     │ │
    │  │  ┌──────────────────────────┐   ┌──────────────────────────────┐   │ │
    │  │  │ User Buffer (glibc)       │   │ Line / Full Buffering         │   │ │
    │  │  └──────────────────────────┘   └──────────────────────────────┘   │ │
    │  └─────────────────────────────────────────────────────────────────────┘ │
    │                                                                           │
    ├──────────────────── SYSTEM CALL INTERFACE / BOUNDARY ─────────────────────┤
    │                                                                           │
    │                         KERNEL SPACE                                      │
    │                                                                           │
    │  ┌─────────────────────────────────────────────────────────────────────┐ │
    │  │ Level 1: FD Standard Syscalls & VFS Entry                             │ │
    │  │                                                                     │ │
    │  │   open / read / write                                                 │ │
    │  │   ┌───────────────────────┐    ┌───────────────────────────────┐   │ │
    │  │   │ User → Kernel Copy     │    │ VFS (file / inode / dentry)    │   │ │
    │  │   └───────────────────────┘    └───────────────────────────────┘   │ │
    │  └─────────────────────────────────────────────────────────────────────┘ │
    │                                                                           │
    │  ┌───────────────────────┐   ┌──────────────────────────┐   ┌─────────┐ │
    │  │ Level 3: Zero-copy     │   │ Level 2: Page Cache      │   │ Level 4 │ │
    │  │ mmap / sendfile /     │   │ fsync / fdatasync        │   │ epoll   │ │
    │  │ splice                │   │ readahead               │   │ poll    │ │
    │  └───────────────────────┘   └──────────────────────────┘   └─────────┘ │
    │                                                                           │
    │                              │                          │               │
    │                              ▼                          ▼               │
    │                    ┌──────────────────────────┐   ┌───────────────┐   │
    │                    │ Level 5: Async I/O       │   │ Level 6:       │   │
    │                    │ AIO / io_uring           │   │ fcntl / ioctl  │   │
    │                    └──────────────────────────┘   └───────────────┘   │
    │                                                                           │
    │  ┌─────────────────────────────────────────────────────────────────────┐ │
    │  │ Level 7: Metadata & Namespace Ops                                    │ │
    │  │                                                                     │ │
    │  │   stat / chmod / rename                                              │ │
    │  └─────────────────────────────────────────────────────────────────────┘ │
    │                                                                           │
    │  ┌─────────────────────────────────────────────────────────────────────┐ │
    │  │ Block Device Drivers & I/O Scheduler                                 │ │
    │  │   (bio / request / elevator)                                        │ │
    │  └─────────────────────────────────────────────────────────────────────┘ │
    │                                                                           │
    │                 ↓                    ↓                     ↓             │
    │                                                                           │
    │  ┌─────────────────────────────────────────────────────────────────────┐ │
    │  │ HARDWARE STORAGE (Disk / SSD / NVMe)                                 │ │
    │  └─────────────────────────────────────────────────────────────────────┘ │
    │                                                                           │
    └───────────────────────────────────────────────────────────────────────────┘
    

    用户操作缓冲IO结构

    ┌──────────────────────────────────────────────────────────────────────────────┐
    │                              USER SPACE                                      │
    │                                                                              │
    │  ┌────────────────────────────────────────────────────────────────────────┐ │
    │  │ Level 0: User-mode Buffering (glibc FILE*)                               │ │
    │  │                                                                        │ │
    │  │  Application calls:                                                    │ │
    │  │    fopen(path, mode)                                                   │ │
    │  │        │                                                               │ │
    │  │        ▼                                                               │ │
    │  │  glibc fopen implementation                                            │ │
    │  │  ┌──────────────────────────────────────────────────────────────────┐ │ │
    │  │  │ FILE struct                                                       │ │ │
    │  │  │                                                                  │ │ │
    │  │  │  int fd  ────────────────┐                                       │ │ │
    │  │  │  User-mode Buffer        │ char* base / ptr / size               │ │ │
    │  │  │  Flags (mode, buffering) │ line / full / none                    │ │ │
    │  │  └──────────────────────────────────────────────────────────────────┘ │ │
    │  │        │                                                               │ │
    │  │        └── returns FILE* fp                                            │ │
    │  │                                                                        │ │
    │  │  Application I/O:                                                      │ │
    │  │                                                                        │ │
    │  │  fwrite(ptr, size, nmemb, fp)                                          │ │
    │  │  fread (ptr, size, nmemb, fp)                                          │ │
    │  │        │                                                               │ │
    │  │        ▼                                                               │ │
    │  │  ┌──────────────────────────────────────────────────────────────────┐ │ │
    │  │  │ User-mode Buffer (glibc memory)                                   │ │ │
    │  │  │                                                                  │ │ │
    │  │  │  [ data ][ data ][ data ][ .... ][ free space ]                  │ │ │
    │  │  │                                                                  │ │ │
    │  │  │  Data copied between application buffer and libc buffer          │ │ │
    │  │  └──────────────────────────────────────────────────────────────────┘ │ │
    │  │        │                                                               │ │
    │  │        │  (buffer full / fflush)                                      │ │
    │  │        ▼                                                               │ │
    ├──────────────────────── SYSTEM CALL INTERFACE / BOUNDARY ────────────────────┤
    │                                                                              │
    │                              KERNEL SPACE                                    │
    │                                                                              │
    │  ┌───────────────────────────────┐     ┌───────────────────────────────┐   │
    │  │ open(path, flags, mode)       │     │ write(fd, buffer, count)       │   │
    │  │ System Call                   │     │ read (fd, buffer, count)       │   │
    │  └───────────────────────────────┘     └───────────────────────────────┘   │
    │             │                                 │                              │
    │             ▼                                 ▼                              │
    │  ┌────────────────────────────────────────────────────────────────────────┐ │
    │  │ Kernel Page Cache (VFS)                                                 │ │
    │  │                                                                        │ │
    │  │  - file / inode / dentry                                               │ │
    │  │  - page cache                                                         │ │
    │  │  - readahead / writeback                                              │ │
    │  └────────────────────────────────────────────────────────────────────────┘ │
    │             │                                                               │
    │             ▼                                                               │
    │  ┌────────────────────────────────────────────────────────────────────────┐ │
    │  │ Block Device Driver                                                    │ │
    │  │                                                                        │ │
    │  │  - bio / request                                                      │ │
    │  │  - I/O scheduler                                                      │ │
    │  └────────────────────────────────────────────────────────────────────────┘ │
    │             │                                                               │
    │             ▼                                                               │
    │  ┌────────────────────────────────────────────────────────────────────────┐ │
    │  │ HARDWARE STORAGE (Disk / SSD / NVMe)                                   │ │
    │  └────────────────────────────────────────────────────────────────────────┘ │
    │                                                                              │
    └──────────────────────────────────────────────────────────────────────────────┘
    

    其大致结构基本如下,后续,将对每一个模块进行详细系统的论述


  • C++中的initializer_list获取列表长度并用于模板参数的编译期计算小示例
    月 月仁不吃五饼

    @FrozenLemonTee 在 C++中的initializer_list获取列表长度并用于模板参数的编译期计算小示例 中说:

    clang最小可编译的版本和标准是clang3.4.1+cpp11:

    image.png

    这样


  • C++中的initializer_list获取列表长度并用于模板参数的编译期计算小示例
    月 月仁不吃五饼

    我觉得应该写template<typename T,size_t t>好一点,如果这个数组非常大,那被转换到int有符号数可能溢出


  • C++中的initializer_list获取列表长度并用于模板参数的编译期计算小示例
    月 月仁不吃五饼

    这个在C++17后的版本是可以通过的,但是C++14以前应该是没法编译通过的吧,对于兼容性来讲还是有一些欠缺

  • 登录

  • 没有帐号? 注册

  • 登录或注册以进行搜索。
d2learn forums Powered by NodeBB
  • 第一个帖子
    最后一个帖子
0
  • 版块
  • 最新
  • 标签
  • 热门
  • Online Tools
  • 用户
  • 群组