1、文件 = 文件内容 + 属性(也是数据)
2、文件的所有操作,无外乎: 1、对内容 2、对属性
3、文件在磁盘上(硬件)上放着,我们访问文件,先写代码——编译——exe——运行——访问文件:本质是谁在访问文件呢?进程(需要通过接口访问【语言C、C++、Java】).
要向硬件写入,只有 操作系统有权利{通过驱动程序}。
普通用户、也想写入呢? 必须让OS提供接口。文件类的系统调用接口!
跨平台
比较难 语言上对接口做一下封装,为了让接口更好的使用。导致了不同的语言,有不同的语言级别的文件访问接口。
- 如果语言不提供对文件的系统接口的封装,是不是所有的访问文件的操作,都必须直接使用OS的接口? 是的
- 而用语言的客户,要不要访问文件呢?当然要
- 一旦使用系统接口,编写所谓的文件代码,无法在其他平台中直接运行了不具备跨平台性!
4、显示器是硬件么?printf向显示器打印,也是一种写入 和磁盘写入到文件没有区别。
**
什么叫做文件?
**
- 站在系统的角度,能狗被input读取,或者output写出的设备就叫做文件!
- 侠义上的文件:普通的磁盘文件。
- 广义上的文件:显示器,键盘、网卡、声卡、显卡、磁盘、几乎所有的外设都有可以被称之为文件。
1、C接口
[LHL@VM-8-7-centos 105]$ cd lesson19
[LHL@VM-8-7-centos lesson19]$ ll
total 0
[LHL@VM-8-7-centos lesson19]$ touch myfile.c
[LHL@VM-8-7-centos lesson19]$ ll
total 0
-rw-rw-r-- 1 LHL LHL 0 Mar 11 15:26 myfile.c
[LHL@VM-8-7-centos lesson19]$ ls > makefile
[LHL@VM-8-7-centos lesson19]$ vim makefile
[LHL@VM-8-7-centos lesson19]$ cat makefile
myfile:myfile.c
gcc -o $@ $^
.PHONY:clean
clean:
rm -f myfile
2、直接使用系统接口
- 进程要访问文件,必须要先打开文件!
- 一个进程可以打开多个文件,一般而言 进程:打开的文件 = 1:n 的
- 文件要被访问,前提是加载到内存中,才能被直接访问!
- 进程:打开的问价 = 1 :n 的 -> 如果是多个进程都要打开自己的文件呢?系统中会存在大量的被打开的文件!所以,OS要不要把如此之多的文件也管理起来呢? 先描述,在组织!!
推荐使用语言级别的文件调用接口。具有平台移植性。
文件:
磁盘文件(没有被打开)
内存文件(被进程在内存中打开)
文件描述符的本质是数组下标。
#include <stdio.h>
2 #include <unistd.h>
3 #include <sys/types.h>
4 #include <sys/stat.h>
5 #include <fcntl.h>
6
7 int main()
8 {
9 close(0); 10 //这里的fd分配的规则是:最小的没有被占用的文件描述> 符
11 int fd = open("log.txt",O_WRONLY | O_CREAT | O_TRUN C,0666);
12 if(fd < 0)
13 {
14 perror("open");
15 return 1;
16 }
17 printf("fd:%d\n",fd);
18 close(fd);
19 return 0;
20 }
~
输出重定向的原理:本质是在操作系统内部更改fd对应的内容指向。
输出重定向代码:使用 dup2(oldfp,newfp);
[LHL@VM-8-7-centos lesson20]$ ./myfile aaaaaaaaaaaaaaaaa
[LHL@VM-8-7-centos lesson20]$ cat log.txt
aaaaaaaaaaaaaaaaa
[LHL@VM-8-7-centos lesson20]$ ./myfile aaaaaaaaaaaaaaaaabbbbbbbbbbbbbbb
[LHL@VM-8-7-centos lesson20]$ cat log.txt
aaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaabbbbbbbbbbbbbbb
[LHL@VM-8-7-centos lesson20]$ ./myfile aaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbcccc
[LHL@VM-8-7-centos lesson20]$ cat log.txt
aaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaabbbbbbbbbbbbbbb
aaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbcccc
[LHL@VM-8-7-centos lesson20]$ cat myfile.c
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc,char *argv[])
{
if(argc != 2)
{
return 2;
}
// int fd = open("log.txt",O_WRONLY | O_CREAT | O_TRUNC);
int fd = open("log.txt",O_WRONLY | O_CREAT | O_APPEND);
if(fd < 0)
{
perror("open");
return 1;
}
dup2(fd,1);
fprintf(stdout,"%s\n",argv[1]); //stdou->1->显示器
close(fd);
return 0;
}
[LHL@VM-8-7-centos lesson20]$
如何使用C语言实现面向对象甚至是运行时多态?
3、分析系统接口的细节,引入fd(文件描述符)
如何理解一切皆为文件?
底层不同的硬件,一定对应不同的操作方法!
但是上面的设备都是外设,所以每一个设备的核心访问函数都是可以 read、write I/O
因此函数指针层面(驱动开发之上)以及之上所有的设备都可以看作文件
称之为 VFS (虚拟文件管理系统)
缓冲区
1、什么是缓冲区:就是一段内存空间(这个空间是谁提供的?
2、为什么要有缓冲区?
写透模式 ,WT 成本高 慢
缓冲区最大的意义:提高整机效率。
3、缓冲区在哪里?
- 缓冲区的刷新策略:1、立即刷新 2、行刷新 3.满刷新(全缓冲)
- 绝对不是操作系统提供的。C标准库给我们提供的用户级缓冲区。
- C语言中
myshell 简易的shell
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#define NUM 1024
#define SIZE 32
#define SEP " "
//保存完整的命令行字符串
char cmd_line[NUM];
//保存打散之后的命令行字符串
char *g_argv[SIZE];
// 写一个环境变量的buffer,用来测试
char g_myval[64];
// shell 运行原理 : 通过让子进程执行命令,父进程等待&&解析命令
int main()
{
extern char**environ;
//0. 命令行解释器,一定是一个常驻内存的进程,不退出
while(1)
{
//1. 打印出提示信息 [whb@localhost myshell]#
printf("[root@localhost myshell]# ");
fflush(stdout);
memset(cmd_line, '\0', sizeof cmd_line);
//2. 获取用户的键盘输入[输入的是各种指令和选项: "ls -a -l -i"]
if(fgets(cmd_line, sizeof cmd_line, stdin) == NULL)
{
continue;
}
cmd_line[strlen(cmd_line)-1] = '\0';
//"ls -a -l -i\n\0"
//printf("echo: %s\n", cmd_line);
//3. 命令行字符串解析:"ls -a -l -i" -> "ls" "-a" "-i"
// export myval=105
g_argv[0] = strtok(cmd_line, SEP); //第一次调用,要传入原始字符串
int index = 1;
if(strcmp(g_argv[0], "ls") == 0)
{
g_argv[index++] = "--color=auto";
}
if(strcmp(g_argv[0], "ll") == 0)
{
g_argv[0] = "ls";
g_argv[index++] = "-l";
g_argv[index++] = "--color=auto";
}
//?
while(g_argv[index++] = strtok(NULL, SEP)); //第二次,如果还要解析原始字符串,传入NULL
if(strcmp(g_argv[0], "export") == 0 && g_argv[1] != NULL)
{
strcpy(g_myval, g_argv[1]);
int ret = putenv(g_myval);
if(ret == 0) printf("%s export success\n", g_argv[1]);
//for(int i = 0; environ[i]; i++)
// printf("%d: %s\n", i, environ[i]);
continue;
}
//for debug
//for(index = 0; g_argv[index]; index++)
// printf("g_argv[%d]: %s\n", index, g_argv[index]);
//4.内置命令, 让父进程(shell)自己执行的命令,我们叫做内置命令,内建命令
//内建命令本质其实就是shell中的一个函数调用
if(strcmp(g_argv[0], "cd") == 0) //not child execute, father execute
{
if(g_argv[1] != NULL) chdir(g_argv[1]); //cd path, cd ..
continue;
}
//5. fork()
pid_t id = fork();
if(id == 0) //child
{
printf("下面功能让子进程进行的\n");
printf("child, MYVAL: %s\n", getenv("MYVAL"));
printf("child, PATH: %s\n", getenv("PATH"));
//cd cmd , current child path
//execvpe(g_argv[0], g_argv, environ); // ls -a -l -i
//不是说好的程序替换会替换代码和数据吗??
//环境变量相关的数据,会被替换吗??没有!
execvp(g_argv[0], g_argv); // ls -a -l -i
exit(1);
}
//father
int status = 0;
pid_t ret = waitpid(id, &status, 0);
if(ret > 0) printf("exit code: %d\n", WEXITSTATUS(status));
}
}
一般的目前互联网工作,更偏向于业务。面向应用端,能不能给公司创造价值。
./myfile >ok.txt 2>err.txt 将正确的结果和报错信息分开打印到不同的文档中。错误重定向。
./myfile > log.txt 2>&1 所有内容都放到1中 将2的指向文件地址重定向到1指向的位置。
文件系统
背景知识:
- 1、有没有被打开的文件呢?当然存在。在哪里?磁盘—磁盘级文件
- 2、我们学习磁盘级别的文件,我们侧重点在哪里?
单个文件角度——这个文件的属性是什么?
站在系统角度——一共有多少个文件?各自属性在哪里?如何快速找到?
如何进行对磁盘文件进行分门别类的存储,用来支持更好的存取! - 3、磁盘文件 内存:掉电易失存储介质 磁盘:永久性存储介质 ssd,u盘,flash卡,光盘,磁带
- 4、磁盘结构:
- 磁盘的存储结构:
- 为什么不以512为单位?1、太小了有可能会导致多次IO进而导致效率的降低 2、
[LHL@VM-8-7-centos lesson22]$ ln testLink2.txt hard.link
ln: failed to create hard link ‘hard.link’: File exists
[LHL@VM-8-7-centos lesson22]$ ln testLink2.txt hard.link
ln: failed to create hard link ‘hard.link’: File exists
[LHL@VM-8-7-centos lesson22]$ rm hard.link
[LHL@VM-8-7-centos lesson22]$ ln testLink2.txt hard.link
[LHL@VM-8-7-centos lesson22]$ ls -li
total 8
1051633 drwxrwxr-x 2 LHL LHL 4096 Mar 15 18:34 dir
1051635 -rw-rw-r-- 2 LHL LHL 0 Mar 15 18:35 hard.link
1051637 lrwxrwxrwx 1 LHL LHL 12 Mar 15 18:37 soft.link -> testLink.txt
1051634 -rw-rw-r-- 1 LHL LHL 0 Mar 15 18:35 testLink1.txt
1051635 -rw-rw-r-- 2 LHL LHL 0 Mar 15 18:35 testLink2.txt
1051636 -rw-rw-r-- 1 LHL LHL 0 Mar 15 18:35 testLink3.txt
1051632 -rw-rw-r-- 1 LHL LHL 11 Mar 15 18:33 testLink.txt
[LHL@VM-8-7-centos lesson22]$
在你的命令输出中,你尝试通过 ln 命令为 testLink2.txt 创建一个硬链接叫做 hard.link。然而,命令失败了,并显示错误信息:“ln: failed to create hard link ‘hard.link’: File exists”。这意味着 hard.link 这个名称的文件或链接已经存在于当前目录中。
错误的根源在于你的命令尝试创建一个硬链接的名称(hard.link)与已存在的一个符号链接(软链接)的名称相同。在UNIX和Linux文件系统中,硬链接和软链接都使用文件名作为标识符,所以任何类型的链接都不能有相同的名称。
要解决这个问题,你有几个选择:
删除已存在的链接,然后重新尝试:
如果你确定不再需要名为 hard.link 的软链接,可以首先删除它,然后再次尝试创建硬链接。
bash
Copy code
rm hard.link
ln testLink2.txt hard.link
使用不同的名称创建硬链接:
如果你想保留现有的软链接,可以选择一个不同的名称为你的硬链接。
bash
Copy code
ln testLink2.txt new_hard_link_name
理解硬链接与软链接的区别:
硬链接:硬链接是文件系统中另一个指向相同磁盘数据(inode)的指针。硬链接与原始文件功能上相同,删除任一链接,文件的内容依旧存在,直到所有的链接都被删除。
软链接(符号链接):软链接相当于是对另一个文件的快捷方式或引用,它包含的是另一个文件的路径名。如果原始文件被删除,软链接将失效。相当于Windows的快捷方式。
记住,文件名是指向inode的链接,硬链接就是创建另一个文件名指向同一个inode。由于 hard.link 已经存在,系统不允许你创建一个新的指向不同内容的链接(无论是硬链接还是软链接)而不先删除或重命名现有的链接。