系统和进程信息与文件IO缓冲

系统和进程信息与文件IO缓冲

本文是作者阅读TLPI(The Linux Programer Interface的总结),为了突出重点,避免一刀砍,我不会过多的去介绍基本的概念和用法,我重点会去介绍原理和细节。因此 对于本文的读者,至少要求读过APUE,或者是实际有写过相关代码的程序员,因为知识有点零散,所以我会尽可能以FAQ的形式呈现给读者。

系统和进程信息

如何自定义procfs条目?

procfs其实是一种内核将内核中的数据结构暴露到用户空间的一种方式,通过查看proc目录下的文件就可以获取的内核的一些数据结构,可是我们如何在proc目录下,创建自己的文件来将内核中的一些数据导出到用户空间呢?,这就需要涉及到内核模块编程和proc文件系统相关的内核API了,我推荐给大家我之前写的两篇关于proc内核态编程相关的文章

procfs和sysfs目录的区别?

procfs — The proc filesystem is a pseudo-filesystem which provides an interface to kernel data structures.
sysfs — The filesystem for exporting kernel objects.

procfs 历史最早,最初就是用来跟内核交互的唯一方式,用来获取处理器、内存、设备驱动、进程等各种信息。sysfs是 Linux 内核中设计较新的一种虚拟的基于内存的文件系统,它的作用与 proc 有些类似,但除了与 proc相同的具有查看和设定内核参数功能之外,还有为 Linux 统一设备模型作为管理之用。相比于 proc 文件系统,使用 sysfs导出内核数据的方式更为统一,并且组织的方式更好,它的设计从 proc 中吸取了很多教训。sysfs 跟 kobject 框架紧密联系,而 kobject 是为设备驱动模型而存在的,所以 sysfs 是为设备驱动服务的。

如何通过procfs查看线程信息?

procfs中查看线程并没有像进程那样只管,要想查看线程,需要知道这个线程对应的进程PID号,通过PID号,进入/proc/PID/task/TID/ 其中TID是线程ID,这个目录包含了该线程的一些信息。

文件IO缓冲

出于速度和效率的考虑。系统I/O调用(即内核)和标准C语言库I/O函数,在操作磁盘文件时会对数据进行缓冲。

stdio库的缓冲有哪些类型?

  • 不对I/O进行缓冲,等同于write或read忽略其buf和size参数,分别指定为NULL和0。stderr默认属于这个类型。
  • 行缓冲,对于输出流,在输出一个换行符前(除非缓冲区满了)将缓冲数据,对于输入流,每次读取一行数据。
  • 全缓冲,对于输出流,会一直缓冲数据知道缓冲区满为止。

如何控制stdio库的缓冲?

       #include <stdio.h>
       int setvbuf(FILE *stream, char *buf, int mode, size_t size);

mode:
    _IONBF  不缓冲
    _IOLBF  行缓冲
    _IOFBF  全缓冲

通过setvbuf结合不同的mode,可以设置缓冲的类型,和大小,我们也可以通过fflush来刷新缓冲区。

       #include <stdio.h>
       int fflush(FILE *stream);

什么是同步IO的数据完整性和文件完整性?

SUSv3将同步I/O完成定义为: 某一I/O操作,要么已成功完成到磁盘的数据传递,要么被诊断为不成功。SUSv3定义了两种不同类型的同步I/O完成。

  • 同步I/O数据完整性 确保数据已传递到磁盘,和要获取的文件元数据也传递到磁盘,并非需要把所有修改的元数据都传递到磁盘。
  • 同步I/O文件完整性 确保要将所有发生更新的文件元数据都传递到磁盘上,即使有些在后续对文件的读操作中并不需要。

如何控制内核缓冲?

内核缓冲的缓冲区是在内核态的,没办法通过像stdio库的setvbuf那样给它指定一个用户态的缓冲区,只能控制缓冲区的刷新策略等。

       #include <unistd.h>
       int fsync(int fd);
       int fdatasync(int fd);
       void sync(void)

fsync用于将描述符fd相关的所有元数据都刷新到磁盘上,相当于强制使文件处于同步I/O文件完整性。
fdatasync作用类似于fsync,只是强制文件处于数据完整性,确保数据已经传递到磁盘上。
sync仅在所有数据(数据块,元数据)已传递到磁盘上时才返回。

这些函数都只能刷新一次缓冲,此后每次发生的I/O操作都需要再次调用上面的三个系统调用来刷新缓冲,为此可以通过设置描述符的属性来保证每次IO的刷新缓冲。打开文件的时候设置O_SYNC,相当于每次发生IO操作后都调用fsyncfdatasync系统调用,使用O_DSYNC标志则要求写操作是同步I/O数据完整性的也就是相当于调用fdatasyncO_RSYNC标志需要结合O_DSYNCO_SYNC标志一起使用,将这些标志的写操作作用结合到读操作中,也就说在读操作之前,会先按照O_DSYNCO_SYNC对写操作的要求,完成所有待处理的写操作后才开始读。

注: glibc库中将O_FSYNCO_SYNC定义为同义。
注: Linux提供了非标准的sync_file_range,当刷新文件数据时该调用提供比fdatasync调用更为精确的刷新控制,调用者能够指定待刷新的文件区域,并且还能制定标志,以控制该系统调用在遭遇写磁盘时是否阻塞。具体的使用方式详见man文档。

       #define _GNU_SOURCE         /* See feature_test_macros(7) */
       #include <fcntl.h>
       int sync_file_range(int fd, off64_t offset, off64_t nbytes,
                           unsigned int flags);

注:linux提供了posix_fadvise系统调用允许进程就自身访问文件数据时可能采取的模式通知内核,内核可以根据posix_fadvise所提供的信息来优化缓冲区告诉缓存的使用,进而提高进程和整个系统的性能。

什么是直接IO?

Linux允许应用程序在执行磁盘I/O时绕过缓冲区高速缓存,从用户空间直接将数据传递到文件或磁盘设备上,这我们称之为直接I/O,或者裸I/O。直接I/O只适用于特定I/O需求的应用,例如:数据库系统,其高速缓存和I/O优化机制自成一体,无需内核消耗CPU时间和内存去完成相同任务。通过在打开文件的时候指定O_DIRECT标志设置文件读写为直接IO的方式。使用直接IO存在一个问题,就是I/O得对齐限制,因为直接I/O涉及对磁盘的直接访问,所以在执行I/O时,必须遵守一些限制。

  • 用于传递数据的缓冲区,其内存边界必须对齐为块大小的整数倍。
  • 数据传输的开始点,亦即文件和设备的偏移量,必须是块大小的整数倍。
  • 待传递数据的颤抖股必须是块大小的整数倍。

不遵守上述任一限制将导致EINVAL错误,下面是一个使用直接IO的例子:

#define _GNU_SOURCE
#include <fcntl.h>
#include <malloc.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>


int main(int argc,char *argv[])
{
    int fd;
    ssize_t length,alignment;
    off_t offset;
    void *buf;
    if(argc < 2 || strcmp(argv[1],"--help") == 0) {
        fprintf(stderr,"%s file \n",argv[0]);
        exit(EXIT_FAILURE);
    }
//length和offset必须是块大小的整数倍,只要其中一个不是,那么read将返回EINVAL错误,
//这里的512和0都是块大小的整数倍(块大小一般为512字节),如果offset或length改成256,那么将导致返回EINVAL错误。
    length = 512;
    offset = 0;
    alignment = 4096;

    fd = open(argv[1],O_RDONLY|O_DIRECT);
    if(fd == -1) {
        perror("open fd");
        exit(EXIT_FAILURE);
    }

    //分配一个地址是alignment*2的整数倍,大小是length+alignment的内存块,
    //其中alignment *2 需要是的2的幂
    buf = (char*)memalign(alignment * 2,length + alignment) + alignment;
    if(buf == NULL) {
        perror("memalign");
        exit(EXIT_FAILURE);
    }

    if(lseek(fd,offset,SEEK_SET) == -1) {
        perror("lseek");
        exit(EXIT_FAILURE);
    }
    int numRead = read(fd,buf,length);
    if(numRead == -1) {
        perror("read");
        exit(EXIT_FAILURE);
    }
    printf("read %ld bytes\n",(long)numRead);
    exit(EXIT_SUCCESS);
}

文件IO缓冲概览

©️2020 CSDN 皮肤主题: 编程工作室 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值