网站在只有域名了,ic手机网站开发平台,seo外链北京seo公司,做网站所需技术目录
一、理解文件
狭义的理解#xff1a;
广义理解#xff1a;
文件操作的归类认知
系统角度
二、系统文件I/O
2.1 标志位的传递
系统级接口open
编辑
open返回值
写入文件
读文件
三、文件描述符
3.1#xff08;0 1 2#xff09;
3.2 文件描…目录
一、理解文件
狭义的理解
广义理解
文件操作的归类认知
系统角度
二、系统文件I/O
2.1 标志位的传递
系统级接口open
编辑
open返回值
写入文件
读文件
三、文件描述符
3.10 1 2
3.2 文件描述符的分配规则
3.3 重定向
3.4 dup2系统调用
标准错误 一、理解文件
文件类型
普通文件包含用户数据如文本文件、二进制可执行文件、图像文件、音频文件等。文本文件可以用文本编辑器打开查看和编辑二进制文件则包含了机器可执行的指令或特定格式的数据。目录文件用于组织和管理其他文件和目录类似于 Windows 系统中的文件夹。它包含了指向其他文件和目录的索引信息。设备文件在Linux中硬件设备也被视为文件分为字符设备文件和块设备文件。字符设备文件通常用于像串口、终端这样以字符流方式进行数据传输的设备块设备文件用于如硬盘、U盘等以块为单位进行数据读写的设备。链接文件类似于 Windows系统中的快捷方式分为硬链接和软链接符号链接。硬链接是同一个文件的多个名称它们共享相同的 inode号软链接则是指向另一个文件的特殊文件有自己独立的 inode 号。管道文件主要用于进程间通信允许两个或多个进程之间进行数据的传递和共享。套接字文件用于网络通信或本地进程间通信是网络编程和一些进程间通信机制的重要组成部分。
狭义的理解
• 文件在磁盘里 • 磁盘是永久性存储介质因此文件在磁盘上的存储是永久性的 • 磁盘是外设即是输出设备也是输入设备 • 磁盘上的文件 本质是对文件的所有操作都是对外设的输入和输出 简称 IO
广义理解
Linux 下一切皆文件键盘、显示器、网卡、磁盘…… 这些都是抽象化的过程
文件操作的归类认知
• 对于 0KB 的空文件是占用磁盘空间的 • 文件是文件属性元数据和文件内容的集合文件 属性元数据 内容 • 所有的文件操作本质是文件内容操作和文件属性操作
系统角度
• 对文件的操作本质是进程对文件的操作 • 磁盘的管理者是操作系统 • 文件的读写本质不是通过 C 语言 / C 的库函数来操作的这些库函数只是为用户提供方便而、是通过文件相关的系统调用接口来实现的
二、系统文件I/O
打开文件的方式不仅仅是fopenifstream等流式语言层的方案其实系统才是打开文件最底层的方案。不过在学习系统文件IO之前先要了解下如何给函数传递标志位该方法在系统文件IO接口中会使用到
2.1 标志位的传递
系统级接口open #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);
pathname: 要打开或创建的⽬标⽂件
flags: 打开⽂件时可以传⼊多个参数选项⽤下⾯的⼀个或者多个常量进⾏“或”运算构成
flags。
参数:O_RDONLY: 只读打开O_WRONLY: 只写打开O_RDWR : 读写打开这三个常量必须指定⼀个且只能指定⼀个O_CREAT : 若⽂件不存在则创建它。需要使⽤mode选项来指明新⽂件的访问
权限O_APPEND: 追加写
返回值成功新打开的⽂件描述符失败-1open返回值
在认识返回值之前先来认识⼀下两个概念: 系统调⽤ 和 库函数
• 上⾯的 fopen fclose fread fwrite 都是C标准库当中的函数我们称之为库函数libc。 • ⽽ open close read write lseek 都属于系统提供的接⼝称之为系统调⽤接⼝ 系统调⽤接⼝和库函数的关系⼀⽬了然。 所以可以认为 f# 系列的函数都是对系统调⽤的封装⽅便⼆次开发。
写入文件
清空并写入
#include stdio.h
#include sys/types.h
#include sys/stat.h
#include fcntl.h
#include string.h
#include unistd.hint main()
{umask(0);int fdopen(log.txt,O_CREAT | O_WRONLY | O_TRUNC,0666);if(fd0){perror(open);return 1;}printf(fd: %d\n,fd);const char* msghello hhh;int cnt1;while(cnt--){write(fd,msg,strlen(msg));}close(fd);return 0;
}追加并写入
#include stdio.h
#include sys/types.h
#include sys/stat.h
#include fcntl.h
#include string.h
#include unistd.hint main()
{umask(0);int fdopen(log.txt,O_CREAT | O_WRONLY | O_TRUNC ,0666);if(fd0){perror(open);return 1;}printf(fd: %d\n,fd);const char* msghello bbbb;int cnt1;while(cnt--){write(fd,msg,strlen(msg));}close(fd);return 0;
}注意上面的加入函数umask(0);就可以自己规范权限。
读文件
int main()
{int fd open(myfile, O_RDONLY);if(fd 0){perror(open);return 1;} const char *msg hello bit!\n;char buf[1024];while(1)
{ssize_t s read(fd, buf, strlen(msg));//类⽐writeif(s 0){printf(%s, buf);}else{break;}} close(fd);return 0;
}这里的接口都是系统调用而上面的c语言的文件操作都是语言层面上的调用。其实语言层里面的调用里面都封装着系统级别的调用。
三、文件描述符
文件描述符是一个非负整数它是 Linux 内核为了管理文件操作而给每个打开的文件或其他 I/O 资源如管道、套接字等分配的一个标识符。可以将其理解为一个指向内核中代表打开文件的数据结构的索引通过这个索引程序能够方便地对相应的文件或资源进行各种读写等操作。
在操作系统层面接口层面系统只认文件描述符(fd)。所有根据前面所讲语言层面肯定封装了文件fd。
3.10 1 2
• Linux进程默认情况下会有3个缺省打开的文件描述符分别是标准输入0 标准输出1 标准错误2. • 0,1,2对应的物理设备一般是键盘显示器显示器
所以输⼊输出还可以采⽤如下⽅式
#include stdio.h
#include sys/types.h
#include sys/stat.h
#include fcntl.h
#include string.h
int main()
{char buf[1024];ssize_t s read(0, buf, sizeof(buf));if(s 0){buf[s] 0;write(1, buf, strlen(buf));write(2, buf, strlen(buf));}return 0;
} 而现在知道文件描述符就是从0开始的小整数。当我们打开文件时操作系统在内存中要创建相应的数据结构来描述目标文件。于是就有了file结构体。表示一个已经打开的文件对象。而进程执行open系统调用所以必须让进程和文件关联起来。每个进程都有一个指针*files, 指向一张表files_struct,该表最重要的部分就是包含一个指针数组每个元素都是一个指向打开文件的指针所以本质上文件描述符就是该数组的下标。所以只要拿着文件描述符就可以找到对应的文件。
对于以上原理结论我们可通过内核源码验证 首先要找到task_struct 结构体在内核中为位置地址为 /usr/src/kernels/3.10.0-1160.71.1.el7.x86_64/include/linux/sched.h 3.10.0-1160.71.1.el7.x86_64是内核版本可使用 uname -a 自行查看服务器配置 因为这个文件夹只有一个所以也不用刻意去分辨内核版本其实也随意 3.2 文件描述符的分配规则
#include stdio.h
#include sys/types.h
#include sys/stat.h
#include fcntl.h
int main()
{int fd open(myfile, O_RDONLY);if(fd 0){perror(open);return 1;}printf(fd: %d\n, fd);close(fd);return 0;
}输出发现是fd: 3
关闭0或者2在看
#include stdio.h
#include sys/types.h
#include sys/stat.h
#include fcntl.h
int main()
{close(0);//close(2);int fd open(myfile, O_RDONLY);if(fd 0){perror(open);return 1;}printf(fd: %d\n, fd);close(fd);return 0;
}发现是结果是 fd: 0 或者 fd 2 可⻅文件描述符的分配规则在files_struct数组当中找到当前没有被使⽤的最小的⼀个下标作为新的文件描述符。
3.3 重定向
那如果关闭1呢看代码
#include stdio.h
#include sys/types.h
#include sys/stat.h
#include fcntl.h
#include stdlib.h
int main()
{close(1);int fd open(myfile, O_WRONLY|O_CREAT, 00644);if(fd 0){perror(open);return 1;}printf(fd: %d\n, fd);fflush(stdout);close(fd);exit(0);
}此时我们发现本来应该输出到显⽰器上的内容输出到了⽂件 myfile 当中其中fd1。这种现象叫做输出重定向。常见的重定向有: , ,
重定向的本质 3.4 dup2系统调用 int main() {// 打开文件如果文件不存在则创建同时以读写模式打开int fd open(./log, O_CREAT | O_RDWR);if (fd 0) {perror(open);return 1;}// 关闭标准输出文件描述符close(1);// 将文件描述符 fd 复制到标准输出文件描述符1dup2(fd, 1);for (;;) {char buf[1024] {0};// 从标准输入读取数据到缓冲区ssize_t read_size read(0, buf, sizeof(buf) - 1);if (read_size 0) {perror(read);break;}// 输出读取到的内容printf(%s, buf);// 刷新标准输出缓冲区fflush(stdout);}return 0;
}
标准错误
向标准输出和标准错误里打信息 标准输出和标准错误都是显示器文件想把标准输出和标准错误的信息重定向一个文件。这样是不行的。可以发现两者在两个文件中 用下面这个指令进行重定向重定向到了两个文件 用下面这个指令可以把两者重定向到一个文件
存在一个标准错误可以通过重定向能力把常规消息和错误消息进行分离。以方便后续用户进行操作好区分。 本篇完下篇见