Linux系统之基础IO

版权声明:非商业性使用,禁止演绎,转载请保留原文链接及作者 https://blog.csdn.net/qq_39478237/article/details/83927723

学过C语言的都知道,在C语言中IO其实就是文件操作,像fopen,fwrite,fread,fseek,fprintf,fclose等都是C语言库函数中的系统调用接口,如下图是常用

先回顾下这些标准c库中文件I/O的接口的使用

回顾C库中的IO

FILE *fopen(const char *path, const char *mode);

size_t  fread(void *ptr, size_t size, size_t nmemb, FILE *stream);

size_t  fwrite(const void *ptr, size_t size, size_t nmemb,

它的返回值:文件流指针

文件流指针 stdin stdout stderr FILE*
文件描述符 0 1 2 3

一个进程运行起来之后默认打开了三个文件,关于文件描述符我在后面有介绍
下面演示一个C语言库中函数的使用

#include <stdio.h>
#include <string.h>
int main()
{
    FILE *fp = NULL;

    fp = fopen("./test.txt", "w");
    if  (fp == NULL) {
        printf("fopen error!!\n");
        return -1;
    }
    char *ptr = "apple!!";
    fwrite(ptr, strlen(ptr), 1, fp);
    fseek(fp, 10, SEEK_END);
    fwrite(ptr, strlen(ptr), 1, fp);
    
    fseek(fp, 0, SEEK_SET);
    char buff[1024] = {0};
    fread(buff, 1024, 1, fp);
    printf("buff:[%s]\n", buff);

    fprintf(fp, "\n%s-%d-%s\n", "apple", 3, "i love to eat!!");

    fclose(fp);
    return 0;
}

系统IO

以下是一些系统调用IO接口

int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
ssize_t write(int fd, const void *buf, size_t count);
int creat(const char *pathname, mode_t mode);
off_t lseek(int fd, off_t offset, int whence);

//off_t lseek(int fd, off_t offset, int whence);
//  fd: 文件描述符
//  offset:偏移值
//  whence:偏移起始位置
//      SEEK_SET    文件起始位置
//      SEEK_CUR    当前读写位置
//      SEEK_END    文件末尾位置

fileno将文件流指针转换成文件描述符
fdopen 将文件描述符转换成文件流指针

文件描述符

文件描述符是一个数字,那么一个数字是如何描述文件的呢?
一个进程要对所有打开的文件进行管理,先将文件描述起来,然后组织管理 进程中对文件进程描述的 结构体叫file(struct file) 进程使用了一个结构体数组来组织这些描述,而文件的描述符就是这个结构体数组的下标
pcb中对文件的描述的一个机构图(file)数组的下标 系统调用open返回的是文件描述符:int 而c库中fopen返回的是一个文件流指针:FILE*

文件描述分配符规则: 寻找最小下未使用的下标

文件描述符fd返回值:正确:非负值(文件描述符) 错误:-1

缓冲区

我们所说的缓冲区(print),用户态的一个缓冲区,是文件流指针自带的fprint,fwrite这些库函数 都是先把数据写入到缓冲区中,等缓冲区写满或者一些其他条件满足后才真正写入到文件中, 而系统调用没有这个用户态的缓冲区,(是直接写入到文件中)


/*
 *      open    close   read    write   lseek
 */

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>   // 文件控制头文件

int main()
{
    //int open(const char *pathname, int flags, mode_t mode);
    //  pathname:   要打开的文件名
    //  flags:选项标志
    //      O_RDONLY    只读 
    //      O_WRONLY    只写
    //      O_RDWR      读写
    //
    //      O_CREAT     文件不存在则创建,存在则打开
    //      O_EXCL      与O_CREAT同用,若文件存在则报错
    //      O_TRUNC     打开文件的同时,清空文件原有内容
    //      O_APPEND    追加
    //  mode:文件权限,用于创建文件的时候
   
    int fd = -1;
    fd = open("./tmp.txt", O_RDWR | O_CREAT, 0777);
    if (fd < 0) {
        perror("open error");
        return -1;
    }
    //ssize_t write(int fd, const void *buf, size_t count);
    //  fd: 文件描述符
    //  buf:要写入的数据
    //  count:要写入的数据长度
    //  返回值:实际的写入数据长度
    char *ptr = "apple!!\n";
    ssize_t wlen = write(fd, ptr, strlen(ptr));
    if (wlen < 0) {
        perror("write error");
        return -1;
    }

    lseek(fd, 0, SEEK_SET);

    //read:返回实际读取到的数据长度
    char buff[1024] = {0};
    ssize_t rlen = read(fd, buff, 1024 - 1);
    if (rlen < 0) {
        perror("read error");
        return -1;
    }
    printf("buf:[%s]\n", buff);

    close(fd);
    
    return 0;
}

文件重定向

原理:将原本描述符所对应的下标的文件修改成另外一个文件,描述符没有变,但是真正通过描述符操作的这个文件已经改变了

int dup2(int oldfd, int newfd);

/*  演示文件描述符分配规则的demo
 *      1. 文件描述符是一个数字,并且这个数字是一个结构体数组的下标
 *  分配规则:
 *      寻找最小的未使用的下标
 *  一个进程运行起来之后,默认打开的三个文件:
 *      标准输入    标准输出    标准错误
 *      stdin       stdout      stderr
 *      0           1           2
 */

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
int main()
{
    int fd = -1;

    fd = open("./tmp.txt", O_RDWR | O_CREAT);
    if (fd < 0) {
        perror("open error");
        return -1;
    }

    dup2(fd, 1);

    printf("fd:%d\n", fd);
    fflush(stdout);
    close(fd);
    return 0;
}

Linux下的文件系统

linux文件系统是由两层结构构建:第一层是虚拟文件系统(VFS),第二层是各种不同的具体文件系统。

VFS是把各种具体的文件系统的公共部分抽取出来,形成一个抽象层,是系统内核的一部分。它位于用户程序和具体的文件系统中间。它对用户

程序提供了标准的文件系统的调用接口,对具体的文件系统,它通过一系列的对不同文件系统公用的函数指针来实际调用具体的文件系统函数,完成实际 的各有的操作。任何使用文件系统的程序必须经过这层接口来使用它。通过这种方式,VFS就对用于屏蔽了底层文件系统的实现细节和差异。

通过 cat /proc/filesystems命令可以查看系统支持哪些文件系统。

open是系统调用级别的操作,调用VFS_open -> 底层的open(包括驱动的open,比如字符设备驱动里面的

file_opreations里面的open等。联想一下)

软硬链接

软硬链接区别:

  • 1、可以针对目录创建,硬链接不可
  • 2、软链接是一个新的文件,而硬链接是源文件的一个别名(跟源文件使用相同的结点inode结点)
  • 3、软链接可以划分区建立,但是硬链接可以
  • 4、源文件删除后,软链接找不到,但是硬链接没有影响,仅仅是链接数减一
  •  

动态库/静态库

动静态库链接:
   gcc默认链接方式:动态链接 ---- 链接是动态库
   静态链接需要加 -static 的gcc链接选项 ---- 链接是静态库
   如何自己生成一个动态库
   程序的编译过程:
   预处理--》编译--》汇编--》链接
   生成一个库其实就是将所有代码打包起来,最终得到一个库文件

区别:

  • 使用静态库时将静态库中代码全部拿过来
  • 动态库使用时记录地址信息
生成动态库:gcc  -- share test.c -o libtest.so
   生成静态库:ar cr libtest.a test.c
   编译选项:-fPIC   :产生位置无关代码

如何链接一个可执行程序:
gcc main.c -o main -L./ -ltest

  • -L 用于指定库的查找路径
  • -l 用于指定链接库的名称

猜你喜欢

转载自blog.csdn.net/qq_39478237/article/details/83927723