一切皆文件

概念上来说,Linux中的所有资源,都可以通过文件的方式来进行访问和管理。

  1. 普通文件:包含数据的文件,可以是文本文件、二进制文件、脚本文件等。
  2. 目录文件:包含本文件和子目录文件名;本目录和子目录对应的inode号。
  3. 设备文件:代表硬件设备的文件,分为字符设备和块设备。设备文件通常位于 /dev 目录下,通过读写这些文件来与硬件设备进行交互。
    • 字符设备:如键盘、串口等,数据以字符流方式处理。
    • 块设备:如硬盘、光驱等,数据以块方式处理。
  4. 符号链接(软链接):指向另一个文件或目录的文件,包含目标路径的字符串,使用时,通过路径找到相应的inode块,再拿inode块中的地址。
  5. 硬链接:指向某一个inode块,inode块的指向可以随意修改
  6. 管道文件:用于进程间通信的文件,分为命名管道和匿名管道。命名管道文件通常位于 /tmp 目录下,通过管道文件,进程可以进行数据交换。
  7. 套接字文件:用于网络通信的文件,通常位于 /tmp 目录下,通过套接字文件,进程可以进行网络数据交换。

包管理工具apt

同mac的homebrew,通俗的说就是用命令行的360软件管家
homebrew的镜像源默认在github上,访问速度时快时慢

Vi工具使用

环境变量的设置

三种设置方式

Gitee的使用

由于之前基本都是在macOS+git+github,设置git的hook等操作。本次将会尝试国内很多人可能会使用的Windows+git+gitee的情况。不过gitee部署pages要上传身份证。。。就不弄pages了。

1、下载git

直接百度下?不好,骇客一点,使用Windows的命令行中的包管理工具winget下git

1
2
winget seach git
winget install $id

我丢,骇客个屁,设置环境变量,没有能在命令行下的编辑器,还得下。。直接图形界面搜索环境变量一改

2、搞一个公钥

一般会生成在用户的.ssh文件夹下

1
ssh-keygen -t rsa -b 4096 -C "example@example.com"

粘贴到Gitee去

3、创建本地git,初始化,绑定,提交

在一个dir下,进行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 初始化
git init
git config --global user.email "you@example.com"
git config --global user.name "Your Name"

# 绑定远端仓库
git remote add origin <your_gitee_repo_url>

# 创建一个新东西
echo "新东西" > README.md

# 添加暂存,提交,推送
git add
git commit -m "first commit"
git push

4、熟悉一下git可能遇到的情况和应对操作

一、获得公司项目仓库代码 : clone

二、将仓库新的更改拉取到本地版本库的远程分支 : fetch

1
2
3
# fetch后使用branch -a查看所有分支状态
git branch -a

三、查看更新了什么东西,确定是否需要合并到本地

1
2
git log master..origin/master
git diff master..origin/master

四、合并到本地 : merge

二四步也可以用pull直接合并,使用前保证工作区、暂存区、本地分支应当内容同步。merge和pull不会覆盖本地其他内容,只会补充没有的内容

1
2
3
4
5
6
7
# 先将活动分支设为需要合并的分支,假设为master
git checkout master
# 合并到活动分支
git merge origin/master
# 当然,在merge之前,需要保证暂存区和工作区没有冲突文件,否则需要先commit


五、修改分支上的版本:reset

1
2
3
4
5
6

# 将版本库的指针指向某版本,不删除任何版本,指向的版本后的版本将无索引,无索引的版本在log不可见,可在reflog中找到
# 三种回退版本的方案,根据对暂存区和工作区的修改策略选择
# soft:都不改 mixed: 改暂存区 hard: 都改
git reset --soft mixed hard <commit>

六、工作区删错文件需要从暂存区或版本库拿回:checkout

1
2
3
4
5

git checkout -- README.md #从暂存区里拿文件回工作区

git checkout commit_hash -- README.md # 从版本库中拿回文件

交叉编译环境

尝试在服务器端配置交叉编译环境,和物理机操作一样,安装即可

本次尝试在mac M1 端配置交叉编译环境看看是否可行

1
2
brew tap ArmMbed/homebrew-formulae #添加arm源
brew install arm-none-eabi-gcc

tftp匿名传输协议

Mac OS

尝试使用mac M1 端自带的tftp
找到tftp文件夹,将需要上传的文件放入

1
cd /private/tftpboot

在开发版上:

1
tftp -g -r fileName macIP

即可获得mac上的文件
但是在使用
tftp -p -r fileName macIP时却显示失败。原因是mac M1下的tftp.plist配置文件没有打开tftp接收。需要修改配置文件,但是这个配置文件连root用户都不能修改。一说是要让电脑进入安全模式禁用SIP,但是我禁用了还是没有权限啊。因此在mac M1上暂时无法解决tftp获取开发版上文件的方法。。有大神会吗求求

Windows

Windows当然是图形窗口好用,直接去下一个tftp图形界面就可以,put和get都可以完美使用

SCP文件传输协议

1
2
3
4
5
6
# 本地文件复制发送到远端
scp -p /path/to/local/file username@remote_host:/path/to/remote/directory

# 从远程主机的目录拿文件
scp -p username@remote_host:/path/to/remote/file /path/to/local/directory

文件的读写

光标指针

无论使用哪一个IO,同一个文件描述符或FILE都共享同一个文件光标。
也就是说,只要文件描述符相同,同时读写将会出现不确定行为,因为光标在两个函数的操作下同时移动

##使用系统IO(初级)文件读写 open close write read lseek
所有的读写操作都直接通过文件描述符进行,进入内核空间
劣势:代码复杂,特别是在处理结构化数据时。错误处理机制不如标准IO。没有缓冲机制,会比较消耗资源
优势:更底层,可以精准控制和精细操作

具体可以man 2 函数名
需要注意的点

  1. 文件读写将会有一个文件指针,即光标。光标可以理解为一个指针,文件在操作时光标会随之偏移。在初级的文件IO中用户可以使用lseek和文件描述符来控制这个指针。
  2. 在给main函数传递参数时,argv[0]是默认留给命令行第一个字符串的,因此文件名应该从argv[1]开始
  3. 使用缓冲区时,常常需要将缓冲区清零。创建时:char buf[10]={0}; 一般时:memset(buf,’\0’,10);

使用标准IO(进阶)文件读写 fopen fclose fread fwrite fleek

标准IO更为常用,因此对标准io的使用应该非常熟悉

fread(缓冲区指针buff,size4,nmemb3,FILE * stream);

比如文件中有10个字符
按4个字符的单位来读,读3次。返回值是读4个的次数。如果某一次发现不够4个字符,就读剩余的,且设置文件流结束标志EOF,且本次不算一次完整读写。因此返回值为3.

fwrite(缓冲区指针buff,size4,nmemb3,FILE * stream);

发现不够4个字符时,函数也会继续读满4个包括后面未定义在内的字符,将其强制写入文件流。可能会导致文件成为二进制文件。

也就是说没有其它错误的情况下,即便buff值不够,也会正确返回nmenb3?????????????好像不是,但是编译器确实给我返回了3

1
2
3
4
5
6
7
8
9
10
//fwrite函数写入未定义字符到文件,会发生什么?是否会算一次完整的写入?
int example5() {
FILE *fp = fopen("test.txt", "w");
char buffer[13]="123456789012";
size_t result = fwrite(buffer, 3,4 , fp);
printf("%ld times write.\n", result);
printf("buffer: %s\n", buffer);
fclose(fp);
return 0;
}

大佬帮我看看,万分感谢

ferror、feof、perror和clearerr

优秀的错误处理机制也是标准IO的优势,必须用

perror(参数)会获取最近一次发生错误的错误信息,打印在参数后面。因此常用在类似fp == NULL的条件下打印具体错误信息

ferror(fp)会判断fp的错误并且返回非零值,因此适合作为可能出错的地方的if的判断值,一般配合perror使用。例如

1
2
3
4
5
写文件代码;
if (ferror(fp)) {
perror("Error writing to file");
}

feof(fd) 返回文件是否有结束标志,常用于fread和fget等函数,当文件流结束时,置非零数

1
2
3
4
5
6
7
FILE *fp = fopen("example.txt", "r");
char buffer[10];
size_t result = fread(buffer, 1, 10, fp);
if (result < 10 && feof(fp)) {
printf("End of file reached.\n");
}
fclose(fp);

clearerr,当有可能出现错误时,我们需要重新打开文件,此时,我们应该清楚文件流的错误和文件流结束标志。使用clearerr(fp);或者使用rewind(fp);可以将文件光标移动到文件的开头且cleareer(fp);

手写copy函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
//使用标准IO,实现文件拷贝功能。
//读写策略为:一次读1024个字节,读1次
void copyFile(char *src, char *dest) {


//使用fopen打开文件src和文件dest
FILE* srcfile = fopen(src, "r");
FILE* destfile = fopen(dest, "w");
if (srcfile == NULL || destfile == NULL) {
perror("open file error");
return;
}

//使用fread读取文件src,按照1024,1策略读取
//创建缓冲区
char buffer[1026]={0};
int times2 = 0;
//无限循环读写,直到读到文件末尾
while (1)
{ //读
int times = fread(buffer, 1024, 1, srcfile);
if (ferror(srcfile)) {
perror("read file error");
return;
}

//读到文件末尾时从这里跳出
if( feof(srcfile)){
printf("read file end\n");
break;
}

//写
fwrite(buffer, 1024, times, destfile);
times2 += times;
//清空缓冲区
memset(buffer, 0, 1024);
printf("write %d times\n", times2);
if (ferror(destfile)) {
perror("write file error");
return;
}
}



//进行最后一次写入
int q = strlen(buffer);
int times = fwrite(buffer, q, 1, destfile);//最后一次写入如果不精准,会造成未定义内容,可能导致目标文件变成二进制文件
if (ferror(destfile)) {
perror("read file error2");
return;
}
printf("write the last times successfully!!!\n");
return;

}

使用常用标准输入输出库函数方便地构建和读取格式化数据

文件的属性

stat fstat istat

我们man 2 stat可以看到stat函数以及结构体stat的内容。从stat

设备号 stat.st_dev

每个文件都会有设备号,一般的文件会归属于一个设备,比如在桌面上创建一个文件,这个文件就归属于硬盘设备,stat.st_dev可以获得文件所属的的设备的设备号,也就是文件系统的设备号
特殊的文件自己会有一个设备号,例如块设备,字符设备。比如说一个键盘,那么我们可以通过stat.st_rdev获取设备号

其次,设备号又分为主设备号和次设备号,通过major和minor函数获取。
主设备号规范了设备类型,次设备号即序号,类似前面的fd

1
2
3
4
5
6
7
8
void print_dev_no(char* dev_name) {
int major, minor;
struct stat st;
stat(dev_name,&st);
printf("major number: %d\n",major(st.st_dev));
printf("minor number: %d\n",minor(st.st_dev));

}

类型与权限 stat.st_mode

stat.st_mode是一个16位二进制数
通过宏定义可以拿到文件类型