自学内容网 自学内容网

【Linux】基础IO

目录

一、什么是文件?

二、C语言如何操作文件

三、文件相关的系统调用

(一)系统调用open

1、接口介绍

2、代码示例

(二)系统调用write

1、接口介绍

2、代码示例

(三)系统调用read

1、接口介绍

2、代码示例

四、文件描述符

(一)什么是文件描述符

(二)文件描述符与进程的关系

(三)文件描述符的分配规则

五、重定向

六、Linux下一切皆文件


一、什么是文件?

        文件=内容+属性。

        文件存储于磁盘上,即使是没有内容的空文件,因为属性信息仍占有一定的空间。Linux在文件的内容和属性是分开存储的。

        文件的操作本质是 进程与被打开文件的关系。

二、C语言如何操作文件

        C语言下的文件操作:文件常用操作_file *fout2-CSDN博客

        在这里只做简单的回顾,以下是文件打开方式以及示例代码:

        

 #include <stdio.h>
 #include <string.h>
 #define FILE_NAME "log.txt"
 int main()
 {
    FILE * fp = fopen(FILE_NAME, "w+");
    if (fp == NULL)
    {
       perror("FILE");
        return 1;
     }
    fputs("Hello world\n", fp);
     fclose(fp);
     return 0;
 }

三、文件相关的系统调用

(一)系统调用open

1、接口介绍

NAME
       open, creat - open and possibly create a file or device
SYNOPSIS
       #include <sys/types.h>
       #include <sys/stat.h>
       #include <fcntl.h>
       int open(const char *pathname, int flags);
       int open(const char *pathname, int flags, mode_t mode);
RETURN VALUE
       open() return the new file descriptor, or -1 if an error occurred
NOTES
       O_RDONLY(只读)    O_WRONLY(只写)    O_RDWR (读写)
       O_CREAT(创建)     O_APPEND(追加)    O_TRUNC(清空)

        其中参数 pathname 为文件名,flag 为打开模式,如果是新创建文件的话,mode 创建文件的权限。

        其中 flag 参数比较特别,其采用位图的思想,其每个比特位代表着不同的涵义,参数信息按照不同比特位的 01 来传递打开文件模式的信息。而上述的 NOTES 中,各个参数都为宏定义,下面是采用相同思想的示例代码:

#include <stdio.h>
#define ONE(1 << 0)
#define TWO(1 << 1)
#define THREE(1 << 2)
#define FOUR(1 << 3)
void show(int flag)
{
if (flag & ONE) printf("one ");
if (flag & TWO) printf("two ");
if (flag & THREE) printf("three ");
if (flag & FOUR) printf("four ");
printf("\n");
}
int main()
{
show(ONE | TWO);
show(ONE | TWO | THREE | FOUR);
return 0;
}

2、代码示例

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#define FILE_NAME "log.txt"
int main()
{
//设置umask值 
//文件的权限由默认生成的文件权限值和umask值异或产生
umask(0);
//以读写方式打开文件log.txt 若存在则清空内容并打开 若不存在则创建文件 
int fd = open(FILE_NAME, O_CREAT | O_RDWR | O_TRUNC, 0666);
if (fd == -1)
{
perror("file open");
return 1;
}
//关闭文件
close(fd);
return 0;
}

        如果目标文件不存在,open并不会像fopen直接创建对应文件,需要按位或 O_CREAT,加上创建文件功能,才会生成对应文件。

        第三个参数是创建文件的默认权限(文件最终的权限需要与umask值异或),如果不写,系统创建的文件会是乱码。

(二)系统调用write

1、接口介绍

NAME
       write - write to a file descriptor

SYNOPSIS
       #include <unistd.h>
       ssize_t write(int fd, const void *buf, size_t count);
RETURN VALUE
       On success, the number of bytes written is returned 
       On error, -1 is returned, and errno is set appropriately.

        其中 fd 为文件描述符,buf 为写入的数据,count 为写入数据的个数。函数成功时返回写入数据的字节数,失败时返回-1。

2、代码示例

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#define FILE_NAME "log.txt"
int main()
{
    // 打开文件
umask(0);
int fd = open(FILE_NAME, O_CREAT | O_RDWR | O_TRUNC);
if (fd == -1)
{
perror("file open");
return 1;
}
const char* str = "Hello world\n";
    //写入文件
write(fd, str, strlen(str));
    //关闭文件
close(fd);
return 0;
}

        上述代码向 log.txt 文件中写入"Hello world\n" ,因为我们是按照 O_CREAT | O_RDWR | O_TRUNC 的方式打开,因此当我们重复再写入文件时,会先清空文件内容再进行写入,如果需要追加内容,只需要将 O_TRUNC 替换为 O_APPEND即可。

(三)系统调用read

1、接口介绍

NAME
       read - read from a file descriptor
SYNOPSIS
       #include <unistd.h>
       ssize_t read(int fd, void *buf, size_t count);
RETURN VALUE
       On  success,  the  number  of  bytes read is returned

        其中 fd 为文件描述符,而 buf 则是读入的数据,count 是读入数据的个数。函数返回读入字节的个数。

2、代码示例

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#define FILE_NAME "log.txt"
int main()
{
//打开文件
umask(0);
int fd = open(FILE_NAME, O_RDONLY);
if (fd == -1)
{
perror("file open");
return 1;
}
//const char* str = "Hello world\n";
//write(fd, str, strlen(str));
//读入文件
char arr[20] = { 0 };
read(fd, arr, sizeof(arr));
printf("%s", arr);
//关闭文件
close(fd);
return 0;
}

四、文件描述符

(一)什么是文件描述符

        在Linux操作系统中,文件描述符是一个非负整数,用于在程序中表示被打开的文件资源。文件描述符是操作系统用来跟踪打开的文件的一种方式。

        当打开一个文件时,系统会分配一个文件描述符给这个文件。这个文件描述符可以被用来读取、写入或关闭文件。

(二)文件描述符与进程的关系

        一个进程可能会打开多个文件,而一个文件也有可能被多个进程打开。那么在多个文件中,进程是如何区分哪些文件是自己打开的呢?

        实际上,在进程PCB中有一个指向含有文件描述符表的结构体的指针,通过这个指针可以找到对应的结构体,再通过结构体便可以获取文件描述符表。文件描述符表实际上是一个指针数组,文件描述符则是其数组下标,而数组内元素为指向文件结构体对象的指针,通过该指针便可以找到进程所打开的全部文件。

(三)文件描述符的分配规则

        在C语言中,文件描述符 fd 是被包含在 FILE 结构体内的。除此之外,在C语言中会默认打开文件描述符表中的前三项元素,也就是文件描述符为0、1和2的元素分别为标准输入流,标准输出流以及标准错误文件。后续进程打开新文件的话,找到当前没有被使用的最小的一个下标,作为新的文件描述符。

        下面是示例代码:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#define FILE_NAME "log.txt"
int main()
{
umask(0);
//关闭文件描述符为0的文件
close(0);
int fd = open(FILE_NAME, O_RDWR, 0666);
if (fd == -1)
{
perror("file open");
return 1;
}
    //最终输出为 log.txt:0
printf("log.txt:%d\n", fd);
close(fd);
return 0;
}

        上述过程与重定向功能类同。

五、重定向

 >   输出重定向
>>   追加重定向
 <   输入重定向

        重定向:对上层来说,使用的文件描述符没有变化,但其文件描述符对应的指针所指向的内容发送了改变。

        文件描述符的本质是数组下标,由上文可知,当系统运行时,默认加载3个文件描述符,分别是标准输入、标准输出和标准错误。

        在上述代码中,使用close(0)关闭标准输入,再重新打开一个文件,按照文件描述符分配规则,新打开的文件会占据数组为0的位置(标准输入),即文件描述符为1的文件对应的是新打开的文件,而不是标准输入了。

        除了上述方式重定向以外,还可以使用 dup2() 达到重定向的目的。

int dup2(int oldfd, int newfd);

        上述函数表示将文件描述符表下标为 oldfd 的指针赋值给 下标为 newfd 的指针。

        下面是示例代码:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#define FILE_NAME "log.txt"
int main()
{
umask(0);
int fd = open(FILE_NAME, O_CREAT | O_RDWR | O_TRUNC, 0666);
if (fd == -1)
{
perror("file open");
return 1;
}
// 将文件描述符为fd的指针赋值给文件描述符为1(标准输出)的指针
dup2(fd, 1);
printf("Hello world\n");
close(fd);
return 0;
}

        上述代码中,文件描述符为1的标准输出被覆盖为文件描述符为3的文件,因此再执行该程序后,并不会在屏幕上打印字符串,而是将字符串输出至新打开的文件 log.txt 中。

        

        输入重定向和追加重定向的使用与上述代码类同。

六、Linux下一切皆文件

        struct file中会有一个函数指针,指向对应驱动层的读写方法,通过读写方法,完成对应设备的读写。

        从用户的角度来看,不论是普通的文件还是各种设备,它们都是通过struct file这个结构体来表示的。用户不需要关心struct file内部是如何通过函数指针来调用特定设备或文件的读写操作。因此,对于用户而言,在Linux系统中,所有的资源都可以被看作是文件。


原文地址:https://blog.csdn.net/Sweet_0115/article/details/143718876

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!