【Linux】基础IO
目录
一、什么是文件?
文件=内容+属性。
文件存储于磁盘上,即使是没有内容的空文件,因为属性信息仍占有一定的空间。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)!