Ubuntu 文件/目录操作命令
pwd:查看当前所在目录
cd <路径>:进入输入的路径
mkdir <文件夹名>:创建文件夹
a. mkdir -p <目录以及子目录>:递归的创建目录以及子目录,例如 mkdir -p div1/div2,在当前目录中创建div1文件夹,在div1文件夹下创建div2文件夹。ls:展示本目录下的文件夹以及文件
a. -a:展示全部文件
b. -l:展示文件详细信息
c. -h:以K,M,G展示文件大小
注意:命令的参数都可以组合使用,例如ls -alkcp <文件夹1> <文件夹2>:将文件夹1复制,并且命名为文件夹2,默认复制到所在目录下,也可以在文件夹1/2之前填写路径,会复制到目的路径下,当然cp也可以复制文件。
a. -r:递归的复制
b. -f:如果目的路径下有同名文件夹/文件,则强制覆盖
c. -d:如果复制的是链接文件类似于windows中的快捷方式,则只复制链接文件,而不会把链接的文件也复制过去。rm <文件/文件夹>:删除文件/文件夹。
a. -r:递归的删除目录下所有文件/文件夹
b. -f:强制删除所有文件/文件夹cat <文件>:串联的展示文件,例如cat file1.txt file2.txt,在屏幕上串联的展示两个文件的内容。
touch <文件>:修改文件时间为当前时间/若文件不存在则创建新的文件。
改变文件的权限和属性chgrp:改变文件所属用户组。chgrp hy file1.txt,将file1.txt的用户组改为hy,注意hy必须要在/etc/group中存在才可以。
a. -R:递归的改变文件夹中所有文件的所属组chown:改变文件所有者。
a. -R:递归的改变文件夹中所有文件的拥有者/所属组chmod:改变文件的权限
a. 例如chmod 777 file1.txt。
b. 三个7分别代表:所属者权限,所属组权限,其他用户权限。读写执行代表的数字分别为:4,2,1,当你想给谁某几种权限的时候,只需要给对应的数字即可,上述案例的7代表4+2+1,即读写执行权限都有。也可以给5,即只有读执行。三个7对应不同的用户/组
查找搜寻命令find:find [路径] [匹配条件] [动作]
grep:grep [options] pattern [files]
a. 支持通配符
b. -w:全字匹配
压缩解压命令
压缩格式:gzip,bzip2,bzip2的压缩效率高
压缩命令:
- tar czf 目标文件名 源文件 gzip格式
- tar cjf 目标文件名 源文件 bzip2格式
解压缩命令: - tar xzf 目标文件名 gzip格式
- tar xjf 目标文件名 bzip2格式
● -C:后面可以指定路径
Linux下的磁盘管理
Linux的磁盘管理体系与windows有很大的区别,Windows下经常会遇到分区这个概念,但是再Linux中,我们叫做挂载点,挂载点就是将一个硬盘的一部分做成文件夹的形式,这个文件夹的名字就叫挂载点。
在图中有四个磁盘设备文件,其中 sd 表示是 SATA 硬盘或者其它外部设备,最后 面的数字表示该硬盘上的第 n 个分区,比如/dev/sda1 就表示磁盘 sda 上的第一个分区。图 2.8.1.2 中都是以/dev/sda 开头的,说明当前只有一个硬盘。如果再插上 U 盘、SD 卡啥的就可能会出现 /dev/sdb,/dev/sdc 等等。如果你的 U 盘有两个分区那么可能就会出现/dev/sdb1、dev/sdb2 这样 的设备文件。
磁盘分区命令fdisk
- fdisk [参数]
a. -b<分区大小>:指定每个分区的大小
b. -l:列出指定设备的分区别
c. -s<分区编号>:将指定的分区大小输出到标准的输出上,单位为块
d. -u:搭配-l参数,会使用分区数目取代柱面数目,来表示每个分区的起始地址。 - mkfs [参数] [-t 文件系统类型] [分区名称]
a. fs:指定建立文件系统时的参数
b. -V:显示版本信息和简要的使用方法
c. -v:显示版本信息和详细的使用方法
mkfs命令是用来格式化分区的,或者说用来选择分区的文件系统。当我们分区后,必须要格式化/选择文件系统 - mount [参数] -t [类型] [设备名称] [目的文件夹]
a. -V:显示程序版本
b. -h:显示辅助信息
c. -v:显示执行过程详细信息
d. -o ro:只读模式挂载
e. -o rw:读写模式挂载
f. -s-r:等于-o ro
g. -w:等于 -o rw
挂载点是一个文件夹,因此再挂在之前要先创建一个文件夹,一般我们把挂载点放到/mnt目录下,再/mnt下创建一个tmp文件夹,然后将/dev/sdb1分区挂载到文件夹下。 - umount [参数] -t [文件系统类型] [设备名称]
a. -a:卸载/etc/mtab中的所有文件系统
b. -h:显示帮助
c. -n:卸载时不要把信息存入/etc/mtab文件中
d. -r:如果无法成功卸载,则尝试以只读方式重新挂载
e. -t<文件系统类型>:进卸载选项中指定的文件系统
环境搭建
Ubuntu
- 打开FTP服务
a. sudo apt-get install vsftpd// 下载ftp
b. sudo vi /etc/vsftpd.conf // 打开 vsftpd.conf 文件
c. local_enable=YESwrite_enable=YES修改这两行代码 - 打开NFS服务
a. sudo apt-get install nfs-kernel-server rpcbind //下载nfs
b. 然后在目录下新建一个/Linux/nfs
c. sudo vi /etc/exports // 配置nfs
d. 在文件末尾加上/home/wangze/Linux/nfs *(rw,sync,no_root_squash) // 自己文件夹的路径
*(rw,sync,no_root_squash)详解
1. *:客户端范围
● 作用:表示允许所有客户端(IP 地址或网段)访问该共享目录16。
● 注意:若需限制访问范围,可替换为具体 IP(如 192.168.1.0/24)或主机名,但 * 会开放给任意设备,安全性较低26。
2. rw:读写权限
● 作用:允许客户端对共享目录进行读写操作(包括创建、修改、删除文件)135。
● 对比:若设为 ro(只读),则客户端仅能查看文件,无法修改23。
- sync:数据同步模式
● 作用:强制同步写入,即服务端必须将数据完全写入磁盘后,才向客户端返回操作成功的响应135。
● 优点:确保数据一致性,避免因服务端宕机导致数据丢失。
● 缺点:性能较低(相比 async 异步模式,后者会先缓存到内存再异步写入磁盘)36。 - no_root_squash:root 权限映射
● 作用:允许客户端以 root 用户身份访问共享目录,且保留 root 的完整权限(如修改系统文件)135。
● 风险:此配置会降低安全性,攻击者可能利用 root 权限破坏服务端系统,建议仅在受信任的局域网环境使用26。
● 对比:默认选项为 root_squash,会将客户端的 root 用户映射为匿名用户(如 nobody),限制其权限35。 - 开启ssh服务
a. sudo apt-get install openssh-serve // 安装ssh服务
b. ssh服务的配置文件是/etc/ssh/sshd_config,不用修改,默认即可 - 交叉编译器安装
a. https://releases.linaro.org/components/toolchain/binaries/latest-7/arm-linux-gnueabihf/
b. 推荐下载4.9版本。https://releases.linaro.org/components/toolchain/binaries/4.9-2017.01/arm-linux-gnueabihf/
c. sudo mkdir /usr/local/arm 新建文件夹,将下载好的编译器放到这里来
d. sudo cp gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf.tar.xz /usr/local/arm/ -f
e. sudo tar -vxf gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf.tar.xz
f. sudo vi /etc/profile修改环境变量
g. export PATH=$PATH:/usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/bin 添加这行,然后保存退出重启。
h. sudo apt-get install lsb-core lib32stdc++6安装相关库
i. arm-linux-gnueabihf-gcc -v 验证版本
Windows
- 下载 FileZilla
a. 设置站点管理器,并且设置传输的时候始终为UFT-8
gcc初体验
- gcc -o test main.c sub.c将main.c和sub.c分别预处理,编译,汇编,最后把他俩统一链接起来。形成一个可执行程序。
- 当子文件很多的时候,我们gcc -o test main.c a1.c a2.c … a999.c我们会编译大量的文件。此时我们可以选择先将每一个程序都汇编为.o文件,最后统一链接,如果我们改动了某个文件,那么我们只需要汇编这一个文件即可。
gcc -c -o main.o main.c
gcc -c -o sub.o sub.c
gcc -o test main.o sub.o - -v选项:当我们使用gcc的时候,如果加入了-v选项,那么就会列举出编译的时候去哪里查找了文件路径。这个选项,对于交叉编译工具链同样有效,交叉编译工具链在编译的时候会到交叉编译工具链的路径下去寻找文件。
- -I选项:可以指定去哪里寻找需要的头文件,如果找不到则再去指定的默认路径查找。
- -L选项:可以指定链接的时候
例如:当我们把命令gcc -o test main.c sub.c改为gcc -o test main.c的时候,因为我们用到了sub.c
中的函数名,因此会报错函数未定义,此时我们有两种修改方法,一种是在链接的时候,把sub.c链接进去
另一种是把sub.c制作为一个静态库或者动态库。
静态库:
gcc -c -o main.o main.c
gcc -c -o sub.o sub.c
ar crs libsub.a sub.o sub2.o sub3.o(可以使用多个.o 生成静态库) 制作静态库
gcc -o test main.o libsub.a (如果.a 不在当前目录下,需要指定它的绝对或相对路径) 链接静态库
动态库:
gcc -c -o main.o main.c
gcc -c -o sub.o sub.c
gcc -shared -o libsub.so sub.o sub2.o sub3.o(可以使用多个.o 生成动态库) 制作动态库
gcc -o test2 main.o libsub.so 链接动态库
但是我们还经常使用以下方法来链接动态库
gcc -o test2 main.o -lsub -L libsub.so所在路径
提示:如果我们直接使用gcc -o test main.o -lsub的方式链接,那么会报错找不到链接的库,那么我们应该指定库所在的目录, 对于c语言来说,#include <>包含的头文件与-l链接的库,都会去系统指定的路径下查找。因此我们需要-L来指定目录。
至此我们就编译成功了,但是当我们运行test2的时候,我们发现还是找不到库,那是因为编译与运行不是一回事,当我们运行的时候,系统回去/lib或者/usr/lib的路径下去查找库,我们可以指定运行时库的路径,通过修改环境变量就可以实现:export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:./将原来的值赋值回去并且新加上./这个路径。
总结一下:
● #include <>的头文件:
○ 到指定的目录中去查找
○ -I dir指定特殊目录
● 库 -lxxx链接的库文件:
○ 指定的lib目录
○ -L dir指定特殊目录
● 运行时:.so库文件:
○ /lib,/usr/lib路径
○ 通过修改LD_LIBRARY_PATH变量来指定路径
有用的命令
gcc -E main.c // 查看预处理结果,比如头文件是哪个
gcc -E -dM main.c > 1.txt // 把所有的宏展开,存在 1.txt 里
gcc -Wp,-MD,abc.dep -c -o main.o main.c // 生成依赖文件 abc.dep,后面 Makefile 会用
echo 'main(){}'| gcc -E -v - // 它会列出头文件目录、库目录(LIBRARY_PATH)
echo ‘main(){}’| gcc -E -v - 执行这个命令后,会显示的部分内容
Thread model: posix
gcc version 7.5.0 (Ubuntu 7.5.0-3ubuntu1~18.04)
COLLECT_GCC_OPTIONS='-E' '-v' '-mtune=generic' '-march=x86-64'
/usr/lib/gcc/x86_64-linux-gnu/7/cc1 -E -quiet -v -imultiarch x86_64-linux-gnu - -mtune=generic -march=x86-64 -fstack-protector-strong -Wformat -Wformat-security
ignoring nonexistent directory "/usr/local/include/x86_64-linux-gnu"
ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/7/../../../../x86_64-linux-gnu/include"
#include "..." search starts here:
#include <...> search starts here:
/usr/lib/gcc/x86_64-linux-gnu/7/include
/usr/local/include
/usr/lib/gcc/x86_64-linux-gnu/7/include-fixed
/usr/include/x86_64-linux-gnu
/usr/include
End of search list.
# 1 "<stdin>"
# 1 "<built-in>"
# 1 "<command-line>"
# 31 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 32 "<command-line>" 2
# 1 "<stdin>"
main(){}
COMPILER_PATH=/usr/lib/gcc/x86_64-linux-gnu/7/:/usr/lib/gcc/x86_64-linux-gnu/7/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/7/:/usr/lib/gcc/x86_64-linux-gnu/
LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/7/:/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/7/../../../../lib/:/lib/x86_64-linux-gnu/:/lib/../lib/:/usr/lib/x86_64-linux-gnu/:/usr/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/7/../../../:/lib/:/usr/lib/
COLLECT_GCC_OPTIONS='-E' '-v' '-mtune=generic' '-march=x86-64'
内容分析:
头文件查找路径:
#include "..." search starts here:
#include <...> search starts here:
/usr/lib/gcc/x86_64-linux-gnu/7/include
/usr/local/include
/usr/lib/gcc/x86_64-linux-gnu/7/include-fixed
/usr/include/x86_64-linux-gnu
/usr/include
End of search list.
库文件查找路径:
LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/7/:/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/7/../../../../lib/:/lib/x86_64-linux-gnu/:/lib/../lib/:/usr/lib/x86_64-linux-gnu/:/usr/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/7/../../../:/lib/:/usr/lib/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/7/../../../../lib/
运行时查找路径:
通过修改LD_LIBRARY_PATH变量来指定路径
makefile
当我们通过 gcc -o main main.c这种命令直接编译main.c的时候,虽然命令简单,但是需要从头开始进行预处理,编译,汇编,链接四个步骤,而当我们的文件比较多的时候,这样编译显然是非常浪费时间的。因此我们需要使用makefile,用它来追踪修改过的文件,并且仅仅对修改过的文件进行编译。
test:a.o b.o c.o
gcc -o test $^
%.o : %.c
gcc -c -o $@ $<
1. 通配符:
a. $^表示所有的依赖
b. $@表示目标文件
c. $<表示第一个依赖文件
2. 变量
A := aa // 即时变量,立刻确定A的值
B = aa // 延时变量,在使用的时候才会确定A的值
D ?= kk // 只有在第一次定义的时候才会其效果,如果前面已经定义过了D则忽略。
+= // 附加,它是即时变量还是延时变量取决于前面的定义
Cortex-A7 MPCore架构
Cortex-A7 MPcore 处理器支持 1~4 核,通常是和 Cortex-A15 组成 big.LITTLE 架构的, Cortex-A15 作为大核负责高性能运算,比如玩游戏啥的,Cortex-A7 负责普通应用。 Cortex-A7 MPCore 支持在一个处理器上选配 1~4 个内核,Cortex-A7 MPCore 多核配置.
Cortex-A 处理器运行模型 : User、FIQ、IRQ、Supervisor(SVC)、Abort、Undef 和 System,Monitor。
除了 User(USR)用户模式以外,其它 8 种运行模式都是特权模式。这几个 运行模式可以通过软件进行任意切换,也可以通过中断或者异常来进行切换。大多数的程序都 运行在用户模式,用户模式下是不能访问系统所有资源的,有些资源是受限的,要想访问这些 受限的资源就必须进行模式切换。但是用户模式是不能直接进行切换的,用户模式下需要借助 异常来完成模式切换,当要切换模式的时候,应用程序可以产生异常,在异常的处理过程中完 成处理器模式切换。 当中断或者异常发生以后,处理器就会进入到相应的异常模式种,每一种模式都有一组寄 存器供异常处理程序使用,这样的目的是为了保证在进入异常模式以后,用户模式下的寄存器 不会被破坏。
Cortex-A 寄存器组
ARM 架构提供了 16 个 32 位的通用寄存器(R0~R15)供软件使用,前 15 个(R0~R14)可以用 作通用的数据存储,R15 是程序计数器 PC,用来保存将要执行的指令。ARM 还提供了一个当 前程序状态寄存器 CPSR 和一个备份程序状态寄存器 SPSR,SPSR 寄存器就是 CPSR 寄存器的 备份。这 18 个寄存器如图
上一小节我们讲了 Cortex-A7 有 9 种运行模式,每一种运行模式都有一组与之对应的寄存 器组。每一种模式可见的寄存器包括 15 个通用寄存器(R0~R14)、一两个程序状态寄存器和一个 程序计数器 PC。在这些寄存器中,有些是所有模式所共用的同一个物理寄存器,有一些是各模 式自己所独立拥有的,各个模式所拥有的寄存器如表
浅色字体的是与 User 模式所共有的寄存器,蓝绿色背景的是各个模式所独有 的寄存器。
总结一下,CortexA 内核寄存器组成如下:
①、34 个通用寄存器,包括 R15 程序计数器(PC),这些寄存器都是 32 位的。
②、8 个状态寄存器,包括 CPSR 和 SPSR。
③、Hyp 模式下独有一个 ELR_Hyp 寄存器。
ARM汇编基础
我们在学习 STM32 的时候几乎没有用到过汇编,可能在学习 UCOS、FreeRTOS 等 RTOS 类操作系统移植的时候可能会接触到一点汇编。但是我们在进行嵌入式 Linux 开发的时候是绝 对要掌握基本的 ARM 汇编,因为 Cortex-A 芯片一上电 SP 指针还没初始化,C 环境还没准备 好,所以肯定不能运行 C 代码,必须先用汇编语言设置好 C 环境,比如初始化 DDR、设置 SP 指针等等,当汇编把 C 环境设置好了以后才可以运行 C 代码。所以 Cortex-A 一开始肯定是汇 编代码,其实 STM32 也一样的,一开始也是汇编,以 STM32F103 为例,启动文件 startup_stm32f10x_hd.s 就是汇编文件,只是这个文件 ST 已经写好了,我们根本不用去修改,所 以大部分学习者都没有深入的去研究。
GNU 汇编语法
label:instruction @ comment
label 即标号,表示地址位置,有些指令前面可能会有标号,这样就可以通过这个标号得到
指令的地址,标号也可以用来表示数据地址。注意 label 后面的“:”,任何以“:”结尾的标识
符都会被识别为一个标号。
instruction 即指令,也就是汇编指令或伪指令。
@符号,表示后面的是注释,就跟 C 语言里面的“/”和“/”一样,其实在 GNU 汇编文
件中我们也可以使用“/”和“/”来注释。
comment 就是注释内容。
add:
MOVS R0, #0X12 @设置 R0=0X12
用户可以使用.section 伪操作来定义一个段,汇编系统预定义了一些段名:
.text 表示代码段。
.data 初始化的数据段。
.bss 未初始化的数据段。
.rodata 只读数据段。
我们当然可以自己使用.section 来定义一个段,每个段以段名开始,以下一段名或者文件结 尾结束,比如:
.section .testsection @定义一个 testsetcion 段
汇编程序的默认入口标号是_start,不过我们也可以在链接脚本中使用 ENTRY 来指明其它 的入口点,下面的代码就是使用_start 作为入口标号:
.global _start
_start: ldr r0, =0x12 @r0=0x12
上面代码中.global 是伪操作,表示_start 是一个全局标号,类似 C 语言里面的全局变量一 样,常见的伪操作有:
.byte 定义单字节数据,比如.byte 0x12。
.short 定义双字节数据,比如.short 0x1234。
.long 定义一个 4 字节数据,比如.long 0x12345678。
.equ 赋值语句,格式为:.equ 变量名,表达式,比如.equ num, 0x12,表示 num=0x12。
.align 数据字节对齐,比如:.align 4 表示 4 字节对齐。
.end 表示源文件结束。
.global 定义一个全局符号,格式为:.global symbol,比如:.global _start。
GNU 汇编同样也支持函数,函数格式如下:
函数名:
函数体
返回语句
GNU 汇编函数返回语句不是必须的,如下代码就是用汇编写的 Cortex-A7 中断服务函数:
/* 未定义中断 */
Undefined_Handler:
ldr r0, =Undefined_Handler
bx r0
/* SVC 中断 */
SVC_Handler:
ldr r0, =SVC_Handler
bx r0
/* 预取终止中断 */
PrefAbort_Handler:
ldr r0, =PrefAbort_Handler
bx r0
以函数 Undefined_Handler 为例我们来看一下汇编函数组成, “Undefined_Handler”就是函数名,“ldr r0, =Undefined_Handler”是函数体,“bx r0”是函数 返回语句,“bx”指令是返回指令,函数返回语句不是必须的。
使用处理器做的最多事情就是在处理器内部来回的传递数据,常见的操作有:
①、将数据从一个寄存器传递到另外一个寄存器。
②、将数据从一个寄存器传递到特殊寄存器,如 CPSR 和 SPSR 寄存器。
③、将立即数传递到寄存器。
数据传输常用的指令有三个:MOV、MRS 和 MSR,这三个指令的用法如表 7.2.1.1 所 示: