Linux系统编程 - 基础IO(IO操作)

目录

预备知识

复习C文件IO相关操作

printf相关函数

fprintf

snprintf

读取文件

系统文件IO操作

open函数

umask()函数

open函数返回值

预备知识

1.你真的理解文件原理和操作了吗? 不是语言问题,是系统问题
2.是不是只有C/C++有文件操作呢? 不是,Java,python,go都有,他们的文件操作方法是不一样的?如何处理这种现象呢? 有没有一种统一的视角,看待所有的语言文件从操作呢?
3.操作文件的时候,第一件事情,就是打开文件,打开文件时做什么呢?如何理解呢?
4. 文件 = 内容 + 属性 -> 针对文件的操作,对内容的操作,对属性的操作
5.当文件没有被操作的时候,文件一般会 在什么位置磁盘
6.当我们对文件进行操作的时候, 文件需要在哪里 内存,为什么呢? 因为 CPU和内存交互
7.当我们对文件进行操作的时候,文件需要提前被load到内存,load是 内容还是属性属性,因为一个文件至少得有属性
8.当我们对文件进行操作的时候,文件需要提前被load到内存,是不是只有你一个人在load呢?不是, 内存中一定存在大量的不同文件的属性
9.所以综上, 打开文件本质就是将需要的文件属性加载到内存中,OS内部一定会同时存在大量的被打开的文件,那么操作系统要不要管理这些被打开的文件呢? 要, OS需要先描述,在组织。
先描述,构建在内存中的文件结构体struct file{struct file* next},就可以从磁盘来,被打开的文件
a.每一个被打开的文件,都要在OS内对应文件对象的struct结构体,可以将所有的struct file结构体用某种数据结构链接起来--,在OS内部,对被打开的文件进行管理,就被转换成为了对链表的增删查改。
结论:文件被打开,OS要为被打开的文件,创建对应的内核数据结构
struct file
{
//各种属性
//各种链接关系
}
10.文件其实可以被分开两大类: 磁盘文件,被打开的文件( 内存文件)
11.文件被打开,是谁打开呢?OS,但是是谁让OS打开的呢?用户(进程为代表的)
12.我们之前的所有的文件操作,都是进程和被打开文件的关系
13.都是进程和被打开文件的关系: struct stak_structstruct file

复习C文件IO相关操作

下面是用C语言实现对文件log.txt进行操作:

#include 
#define LOG "log.txt"
 
int main()
{
    // w:默认写方式打开文件,如果文件不存在,就创建它
    // 默认如果只是打开,文件内容会自动被清空
    // 同时,每次进行写入的时候,都会从最开始进行写入
    FILE *fp = fopen(LOG, "w");
    if (fp == NULL)
    {
        perror("fopen fail");
        return 1;
    }
    // 正常进行文件操作
    const char *msg = "hello linux\n";
    int cnt = 5;
    while (cnt)
    {
        fputs(msg, fp);
        cnt--;
    }
    fclose(fp); // 关闭文件
    return 0;
}

Linux系统编程 - 基础IO(IO操作)_第1张图片

Linux系统编程 - 基础IO(IO操作)_第2张图片

成功创建了log.txt文件,打开文件

Linux系统编程 - 基础IO(IO操作)_第3张图片

printf相关函数

Linux系统编程 - 基础IO(IO操作)_第4张图片

printf 默认是向显示器读取

int main()
{
    // w:默认写方式打开文件,如果文件不存在,就创建它
    // 1. 默认如果只是打开,文件内容会自动被清空
    // 2. 同时,每次进行写入的时候,都会从最开始进行写入
    FILE *fp = fopen(LOG, "w");
    if (fp == NULL)
    {
        perror("fopen fail");
        return 1;
    }
    // 正常进行文件操作
    const char *msg = "hello linux";
    int cnt = 5;
    while (cnt)
    {
        fprintf(fp, "%s:%d:phw\n", msg, cnt);
        cnt--;
    }
    fclose(fp); // 关闭文件
    return 0;
}

Linux系统编程 - 基础IO(IO操作)_第5张图片

fprintf

fprintf(stdout, "%s:%d:phw\n", msg, cnt); // Linux一切皆文件,stdout也对应一个文件,显示器文件

Linux系统编程 - 基础IO(IO操作)_第6张图片

snprintf

写入到buffer缓冲里

Linux系统编程 - 基础IO(IO操作)_第7张图片

Linux系统编程 - 基础IO(IO操作)_第8张图片

下面测试一下将msg改成phw

Linux系统编程 - 基础IO(IO操作)_第9张图片

Linux系统编程 - 基础IO(IO操作)_第10张图片

这里得出结论, “w"为覆盖式写入

追加式写入"a"选项

Linux系统编程 - 基础IO(IO操作)_第11张图片

Linux系统编程 - 基础IO(IO操作)_第12张图片

读取文件

Linux系统编程 - 基础IO(IO操作)_第13张图片

Linux系统编程 - 基础IO(IO操作)_第14张图片

Linux系统编程 - 基础IO(IO操作)_第15张图片

系统文件IO操作

open函数

Linux系统编程 - 基础IO(IO操作)_第16张图片

pathname: 要打开或创建的目标文件

flags: 打开文件时,可以传入多个参数选项,用下面的一个或者多个常量进行“或”运算,构成flags。
参数:
 O_RDONLY: 只读打开
 O_WRONLY: 只写打开
 O_RDWR : 读,写打开
 这三个常量,必须指定一个且只能指定一个
 O_CREAT : 若文件不存在,则创建它。需要使用mode选项,来指明新文件的访问权限
 O_APPEND: 追加写

 O_TRUNC:清空文件内容
返回值:
 成功:新打开的文件描述符
 失败:-1

下面是标志位的举例程序:

#define ONE 0x1
#define TWO 0x2
#define THREE 0x4
#define FOUR 0X8
#define FIVE 0X10
 
void Print(int flags)
{
    if (flags & ONE)
        printf("hello 1\n");
    if (flags & TWO)
        printf("hello 2\n");
    if (flags & THREE)
        printf("hello 3\n");
    if (flags & FOUR)
        printf("hello 4\n");
    if (flags & FIVE)
        printf("hello 5\n");
}
 
int main()
{
    printf("-------------------\n");
    printf(ONE);
    printf("-------------------\n");
    printf(TWO);
    printf("-------------------\n");
    printf(FOUR);
    printf("-------------------\n");
    printf(ONE | TWO);
    printf("-------------------\n");
    printf(ONE|TWO|THREE);
    printf("-------------------\n");
    printf(ONE|TWO|THREE|FOUR|FIVE);
    printf("-------------------\n");
    return 0;
}

 Linux系统编程 - 基础IO(IO操作)_第17张图片

Linux系统编程 - 基础IO(IO操作)_第18张图片

open函数测试:

#include 
#include 
#include 
#include 
#define LOG "log2/txt"

// 系统方案
int main()
{
    int fd = open(LOG, O_WRONLY);

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

文件不存在

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define LOG "log2/txt"

// 系统方案
int main()
{
    int fd = open(LOG, O_WRONLY |O_CREAT);

    if (fd == -1)
    {
        printf("fd:%d,errno:%d,errstring:%s\n", fd, errno, strerror(errno));
    }
    else
    {
        printf("fd:%d,errno:%d,errstring:%s\n", fd, errno, strerror(errno));
    }
    close(fd);
    return 0;
}

我们在使用open函数的时候不仅要O_WRONLY (写)还要创建O_CREAT

但是这种方式创建的文件,是没有权限的。

其中参数mode就是权限

Linux系统编程 - 基础IO(IO操作)_第19张图片

因为umask默认权限的原因

umask()函数

umask() 函数的参数为一个八进制数,它的每一位分别表示对应的文件权限是否会被屏蔽掉,例如,umask(022) 表示屏蔽掉写入权限和执行权限。

umask(0)这意味着没有任何权限被屏蔽掉。

Linux系统编程 - 基础IO(IO操作)_第20张图片

 将mask初始化为0

 Linux系统编程 - 基础IO(IO操作)_第21张图片

Linux系统编程 - 基础IO(IO操作)_第22张图片

 成功将文件的权限设置成自己想要的

wirte()函数

Linux系统编程 - 基础IO(IO操作)_第23张图片

 Linux系统编程 - 基础IO(IO操作)_第24张图片

 这里的strlen不需要+1,\0是C语言的规定,不是文件的规定,\0会被解释成乱码

O_WRONLY | O_CREAT 默认不会对原始文件内容做清空,需要加上O_TRUNC

 O_APPEND | O_CREAT 不会追加写,需要加上O_WRONLY

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define LOG "log.txt"

// 系统方案
int main()
{
    umask(0);
    int fd = open(LOG, O_RDONLY, 0666);
    if (fd == -1)
    {
        printf("fd:%d,errno:%d,errstring:%s\n", fd, errno, strerror(errno));
    }
    else
    {
        printf("fd:%d,errno:%d,errstring:%s\n", fd, errno, strerror(errno));
    }

    char buffer[1024];
    // 这里无法做到按行读取,我们是整体读取的
    ssize_t n = read(fd, buffer, sizeof(buffer) - 1); //使用系统接口来进行IO的时候,一定要注意\0的问题
    if (n > 0)
    {
        buffer[n] = '\0';
        printf("%s\n", buffer);
    }
    close(fd);
    return 0;
}

 Linux系统编程 - 基础IO(IO操作)_第25张图片

open函数返回值

在认识返回值之前,先来认识一下两个概念: 系统调用库函数

上面的 fopen fclose fread fwrite 都是C标准库当中的函数,我们称之为库函数(libc)

而, open close read write lseek 都属于系统提供的接口,称之为系统调用接口

回忆一下我们讲操作系统概念时,画的一张图  

Linux系统编程 - 基础IO(IO操作)_第26张图片

 系统调用接口和库函数的关系,一目了然。 所以,可以认为,f系列的函数,都是对系统调用的封装,方便二次开发

你可能感兴趣的:(Linux系统编程,linux)