- shell相关
概念:shell就是命令解析器,shell将用户输入的命令解释称内核能够识别的指令;shell相当于翻译官
常用的命令解析器:
shell--Bourne Shell /bin/sh
bash -- Bourne Again Shell /bin/bash
查看当前系统所使用的shell: echo $SHELL
查看当前系统下有哪些shell: cat/etc/shells
- 常用的快捷命令
2.1补齐命令:tab键
优点:减少用户的输入,加快输入速度,不会出错
2.2遍历输入的历史命令:
向上遍历:ctrl+p(↑)
向下遍历:ctrl+n(↓)
使用history命令可以显示用户输入的所有命令
2.3 光标位置移动
光标左移:ctrl+b(←)
光标右移:ctrl+f(→)
移动到头部:ctrl+a(home)
移动到尾部:ctrl+e(end)
2.4删除字符
删除光标前面的字符:ctrl+h
删除光标后面的字符:ctrl+d
删除光标前所有内容:ctrl+u
删除光标后所有内容:ctrl+k
3.linux系统的目录结构
倒立树状结构,用/表示
/bin:binary,二进制文件,可执行程序
ls、rm、mv、cp等命令
/sbin:Super User的意思,存放系统管理员使用的系统管理程序
ifconfig、halt、shutdown、reboot等命令
/lib:linux运行的时候需要加载的一些动态库
lib.so、libpthred.so等
/root:Linux的超级用户root的家目录
/dev:字符设备和块设备。 在linux下,一切皆是文件
/home:用户的家目录
/usr:相当于windows的program file
/etc:系统级别的配置文件
/etc/passwd 查看passwd文件的格式信息
/etc/group 查看group文件的格式信息
/etc/profile 系统的配置文件,修改该文件会影响这个系统下面所有的用户
/mnt:手动挂载目录,如U盘等
/media:外设的自动挂载目录,如光驱等
/tmp:存放临时文件,新建在这个目录下的文件会在系统重启后自动清除
4.相对路径和绝对路径
相对路径:从当前目录开始表示的目录
绝对路径:从/目录开始表示的目录
. 表示当前目录
… 表示当前目录的上一级目录
5.文件和目录相关命令
5.1 tree命令:以树状形式查看指定目录内容
安装:sudo apt-get install tree
tree 目录:树形结构显示指定目录下的文件信息
5.2 ls命令:查看指定目录下的文件信息
ls:显示当前目录下文件信息
ls 目录或文件名 显示指定目录下文件信息
相关参数:
-a:列出当前目录下的所有文件
. 当前目录
.. 当前目录的上一级目录
隐藏文件:以.开头的文件名
普通文件
-R递归列出所有目录中的内容
-l列出文件的详情信息,7部分
1.第一个字符:
-:普通文件 d:目录 l:符号链接 s:套接字 p:管道 b:块设备 c:字符设备
用户的操作权限:(2-10个字符)
字符2,3,4:文件所有者对文件的操作权限 rwx可读可写可执行
字符5,6,7:文件所属组用户对文件的操作权限
字符8,9,10:其他人对文件的操作权限 r--只能读
2.硬链接计数:该目录下所有的目录总数(包含.和..),但是不包含该目录的子目录下的目录文件数量
3.文件所有者:itcast
4.文件所属组:itcast
5.文件大小:
如果是目录:表示目录大小,不包含目录中的内容
如果是文件:表示文件大小
6.文件的创建日期或最后修改事件
7.文件名
5.3 cd命令:切换目录,cd+路径,路径可以使用相对路径和绝对路径
切换到家目录:cd、cd~、cd /home、cd $home
临近两个目录直接切换:cd-,一开始在/home/...目录下,执行cd命令切换到其他目录,执行cd-就回到了原来的目录下
5.4 pwd(printf working directory):查看用户当前所处的工作目录
5.5 which:显示命令所在的目录,如which ls, which cp
5.6 touch命令:如果文件不存在,创建新文件,如果文件存在,更新文件的最后修改时间,touch文件名
5.7 mkdir命令:创建新目录(make directory) ,创建方式:mkdir 目录名
在当前目录下创建目录:mkdir mydir
在宿主目录下创建多级目录:mkdir -p /aa/bb/cc
5.8 rmdir命令:删除空目录,但是只能删除空目录,rmdir 目录名
5.9 rm命令:
删除文件:rm 文件名
删除目录:rm -r 目录名
参数:
-r:递归删除目录,删除目录必须添加此参数
-i:提示用户是否删除文件或目录
-f:强制删除
使用命令rm删除的文件或目录不会放入回收站中,数据不易恢复
5.10 cp命令 :cp 源文件/目录 目标文件/目录
cp 要拷贝的文件(file1) file(不存在) :创建file,将file1中的内容拷贝到file
cp file1 file(存在) : file1覆盖file
cp file dir(存在) : 拷贝file到dir目录
cp -r dir(存在) dir1(存在) : 将dir目录拷贝到dir1目录中,包括dir目录
cp -r dir(存在) dir1(不存在) :创建dir1,将dir中的内容拷贝到dir1中,不包括dir目录
cp -a dir dir1 :原模原样的复制,包括创建时间等信息
5.11 mv命令:改名或者移动文件 mv file1 file2
改名:
mv file(存在) file1(不存在) file的名字就变成file1了
mv dir(存在) dir1(不存在)
mv file(存在) file2(存在):file文件覆盖file2文件,file改名为file2
移动:
mv file(文件) dir(存在目录):将file文件移动到dir中
mv dir(目录存在) dir1(目录存在):将dir移动到dir1中,dir就会作为dir1的子目录而存在
5.12 cat命令:将文件内容一次性输入到终端
cat 文件名
缺点:终端显示的内容有限,如果文件太长无法全部显示
可用于文件重定向:cat file1>file2,相当于cp file1 file2
5.13 more命令:文件内容分页显示到终端,但是只能一直向下浏览,不能回退
more 文件名
显示下一行:回车
显示下一页:空格
退出q或者ctrl+c
5.14 less命令:文件内容分页显示到终端,可以自由上下浏览
less 文件名
显示下一行:回车、ctrl+p、键盘向下键↓
显示上一行:ctrl+n、键盘上键↑
显示下一页:空格、pageDown
显示上一页:pageUp
退出:q
5.15 head命令:从文件头部开始查看前n行的内容
使用方式:
head -n(行数) 文件名
如果没有指定行数,默认显示前10行内容
5.16 tail命令:文件尾部向上查看最后n行的内容
使用方式:
tail -n(行数) 文件名
如果没有指定行数,默认显示最后10行内容
显示日志:tail -f test.log,可实时显示日志文件信息(比如跑代码的时候)
echo "hello world">>test.log:向文件中追加这句话
5.17 软连接:软连接类似于windows下的快捷方式
ln -s 文件名 快捷方式的名字 ln -s test.txt text.txt.s
目录也可以创建软连接
创建软连接要用绝对路径,如果不使用绝对路径,一旦这个连接文件位置发生变动,就找不到那个文件了
软连接文件的大小是:路径+文件名的总字节数
5.18 硬链接:硬链接的本质是不同的文件名所在的inode节点是相同的,相同的inode节点指向了相同的数据块,所以文件内容是一样的,文件内容会同步
作用:可以起到同步文件和保护文件的作用
ln 文件名 硬链接的名字 例:ln test.log test.log.h
注意事项:
硬链接不能建立在目录上
硬链接对绝对路径没有要求
硬链接不能跨文件系统:硬链接文件和源文件的inode是相同的,文件系统的inode要求唯一,跨文件系统可能会使inode不同,所有不能跨文件系统
ls -i 文件名 : 可以查看文件的i节点
stat 文件名 : 可以查看i节点信息
file.hard是file的硬链接,这俩文件指向了同一个inode,同一个inode指向了相同的数据块
- 当新创建了一个文件,硬链接计数为1
- 当给文件创建了一个硬链接后,硬链接计数加1
- 删除一个硬链接后,硬链接计数减1
- 如果删除硬链接后,硬链接计数为0,则文件会删除,起到保护文件的作用
5.19 wc(world count):显示文件行数,字节数,单词数
wc -l file :显示文件的总行数
wc -c file :显示文件的总字节数
wc -w file :显示文件的总单词数
wc file :显示文件的总行数,单词数和总字节数
5.20 whoami:显示当前登录的用户名
6 用户权限、用户、用户组
6.1 修改文件权限chmod
文字设定法:命令 chmod[who][+|-|=][mode] 文件名
操作对象[who]
u 用户(user)
g 同组用户(group)
o 其他用户(other)
a 所有用户(all)默认
操作符:[+\-\=]
+ 添加权限
- 取消权限
= 赋予给定权限并取消其他权限
权限:[mode]
r 读
w 写
x 执行
数字设定法:命令:chmod[+|-|=][mode] 文件名
操作符[+-=]
数字表示的含义:
- 0 没有权限
- 1 执行权限 二进制:001
- 2 写权限 二进制:010
- 4 读权限 二进制:100
使用数字设定法,一定要使用3位的8进制数
例:给user和group加读写权限4+2=6,给all加读权限4
6 6 4
user group all
6.2 修改文件所有者和所属组
修改文件所有者chown
用法:chown 文件所有者 文件名
修改文件所有者和所属组chown
用法:chown 文件所有者:文件所属组 文件名
sudo chown mytest:mytest file.txt
sudo chown mytest.mytest file.txt
普通用户需要使用管理员用户权限执行该命令
6.3 修改文件所属组
chgrp命令:chgrp 用户组 文件或者目录名
sudo chgrp mytest file.txt
7 find命令:
按文件名查询:使用参数 -name
命令: find 路径 -name "文件名"
按文件类型查询:使用参数 -type
命令: find 路径 -type 类型
类型:
普通文件类型用f表示而不是-
d 目录
l 符号链接
b 块设备文件
c 字符设备文件
s socket文件
p 管道文件
按文件大小查询:使用参数 -size
范围:
大于:+
小于:-
等于:不需要添加符号
大小:
M 必须大写
k 必须小写
c 表示字节数
例:大于50k, 小于100k的文件: find ~/ -size +50k -size -100k
按文件日期:
创建日期:-ctime -n/+n -n:n天以内 +n:n天以外
修改日期:-mtime -n/+n
访问日期:-atime -n/+n
按深度:
-maxdepth n(层数) :搜索n层以下的目录,搜索的层数不超过n层
-mindepth n :搜索n层以上的目录,搜索的层数不能小于n层
高级查找:查找指定目录下所有目录,并列出目录中文件详细信息
find ./ -type d -exec shell命令 {} \
find ./ -type d -ok shell命令 {} \ ok比较安全,特别是在执行rm删除文件的时候
find ./ -type d | xargs shell 命令
8 grep命令:查找内容
grep -r "查找的内容" 搜索的路径
-r :若是目录,则可以递归搜索
-n :参数可以显示该查找内容所在的行号
-i :参数可以忽略大小写及进行查找
-v :参数不显示含有某字符串
搜索当前目录下包含hello字符串的文件
grep -r -n "hello" ./ 显示行号
grep -r -i -n "Hello" ./ 忽略大小写查找
9 find和grep命令结合使用
先使用find命令查找文件,然后使用grep命令查找那些文件包含某个字符串
find . -name "*.c" | xargs grep -n "main"
10 软件的安装和卸载
在线安装:
软件安装:sudo apt-get install 软件名
软件卸载:sudo apt-get remove 软件名
更新软件列表:sudo apt-get update
清理安装包:sudo apt-get clean
软件包安装:在Ubuntu系统下必须有deb格式的安装包
软件安装:sudo apkg -i xxx.deb dpkg: d是deb pkg是package
软件卸载:sudo dpkg -r 软件名
11 常用的压缩软件
gzip和bzip2:不能压缩目录,只能一个一个文件进行压缩,压缩之后会使原文件消失
tar工具:
参数:
z:用gzip来压缩/解压缩文件
j:用bzip2来压缩/解压缩文件
c:create创建新的压缩文件,与x互斥使用
x:从压缩文件中释放文件,与c互斥使用
v:详细报告tar处理的文件信息
f:指定压缩文件的名字
t:查看压缩包中有那些文件
压缩:
tar cvf 压缩包名字.tar 原材料[要打包压缩的文件或目录]
tar zcvf 压缩包名字.tar.gz 原材料[要打包压缩的文件或目录]
tar jcvf 压缩包名字.tar.bz2 原材料[要打包压缩的文件或目录]
解压缩:
tar xvf 已有的压缩包
tar zxvf 已有的压缩包
tar jxvf 已有的压缩包
解压到指定目录中:-C 解压目录 ,例:tar zxvf test.tar -C 解压目录
查看压缩包中有那些文件:tar -tvf test.tar
rar工具:使用前需要安装rar工具 sudo apt-get install rar
压缩:rar a-r 要压缩的文件(含文件或者目录),压缩目录需要使用参数-r,打包生成的新文件不需要指定后缀
解压缩:rar x filename.rar 压缩目录
zip工具:
压缩:
zip-r 压缩包名 要压缩的文件(含文件或目录)
压缩目录需要使用参数-r
不需要指定压缩包后缀
解压缩:unzip 压缩包名
解压到指定目录:添加参数 -d 解压目录
unzip filename.zip -d 路径
vim-gcc库的制作和使用:
1 vim
1.1 vim的三种模式:命令模式、文本输入模式、末行模式
1.2 vim基本操作
1.2.1命令模式下的基本操作:用户按下esc键,就可以使vi进入命令模式下;当使用vi打开一个新文件开始也是进入命令模式
保存退出:
zz :保存退出
代码格式化:
gg=G :代码格式化
光标移动:
h :光标左移
j :光标下移
k :光标上移
l :光标右移
w :移动一个单词
gg :光标移动到文件开头
G :光标移动到文件末尾
0 :光标移到行首
$ :光标移动到行尾
nG :行跳转,例12G,跳到12行处
删除命令:
x :删除光标后一个字符,相当于del
X :删除光标前一个字符,相当于Backspace
dw :删除光标开始位置的字,包含光标所在字符
d0 :删除光标前本行所有内容,不包含光标所在字符
D[d$]:删除光标后本行所有内容,包含光标所在字符
dd :删除光标所在行(剪切)
ndd :从光标当前行向下删除指定的行数,如15dd
v/ctrl+v:使用h、j、k、l移动选择内容,然后按d删除,其中ctrl+v是列模式,v是非列模式
撤销和反撤销命令:
u :一步一步撤销,相当于ctrl+z
ctrl+r:反撤销
复制粘贴:
yy :复制当前行
nyy :复制n行,如10yy
p :在光标所在位置向下新开辟一行,粘贴
P :在光标所在位置向上新开辟一行,粘贴
剪切 :按dd或者ndd删除,将删除的行保存到剪切板中,然后按p/P就可以粘贴了
可视模式:
v/ctrl+v:使用h、j、k、l移动选择内容,使用d删除,y复制,p粘贴到光标的后面,P粘贴到光标的前面
替换操作:
r :替换当前字符
R :替换当前行光标后的字符
查找命令:
* / :/xxx,从光标所在的位置开始搜索,按n向下搜索,按N向上搜索
? :?xxx,从光标所在的位置开始搜索,按n向上搜索,按N向下搜索
# :将光标移动到待搜索的字符串上,然后按n向上搜索,按N向下搜索
shift+k:在待搜索的字符串上按shift+k或者K,可以查看相关的帮助文档
切换到文本输入模式:(*重点掌握)
* i :在光标前插入
* a :在光标后插入
I :在光标所在行的行首插入
A :在光标所在行的行尾插入
* o :在光标所在行的下面创建一行,行首插入
O :在光标所在行的上面创建一行,行首插入
s :删除光标后面的字符,从光标当前位置插入
S :删除光标所在当前行,从行首插入
按列模式插入:先ctrl+v进入列模式,按hjkl移动选定某列,按I或者shift+i向前插入,然后插入字符,最后按两次esc
1.2.2从命令模式切换到末行模式,输入(:)
保存退出:
q :退出
q! :强制退出,不保存修改内容
w :保存修改内容,不退出
wq :保存并退出
x :相当于wq
替换操作:
:s/old/new/ : 光标所在行的第一个old替换为new
:s/old/new/g : 光标所在行的所有old替换为new
:m,ns/old/new/g: 将第m行至第n行之间所有old替换为new
:%s/old/new/g : 将当前文件的所有old替换为new
:1,$s/old/new/g: 将当前文件的所有old替换为new
:%s/old/new/gc : 同上,但每次需要用户确认
快速翻屏:
ctrl+u : 向下翻半屏(up) 光标向上移动
ctrl+d : 向上翻半屏(down) 光标向下移动
ctrl+f : 向上翻一屏(front)
ctrl+b : 向后翻一屏(back)
末行模式下执行shell命令:
!shell命令 : 按下两次esc可以回到命令模式
分屏操作:
在文件打开之后分屏:
sp : 当前文件水平分屏
vsp : 当前文件垂直分屏
sp 文件名 : 当前文件和另一个文件水平分屏
vsp 文件名: 当前文件和另一个文件垂直分屏
ctrl+ww : 在多个窗口切换光标
wall/wqall/xall/qall/qall! : 保存/保存退出/保存退出/退出/强制退出分屏窗口
在打开文件之前分屏:
分屏 : vim -o file1 file2...
垂直分屏 : vim -O file1 file2...
从末行模式切换回命令模式 : 按两次esc,退格(backspace)或者回车键
1.2.3 vim的配置文件
用户级别配置文件:~/.vimrc修改用户级别的配置文件只会影响当前用户,不会影响其他用户
set tabstop = 4 设置缩进4个空格
set nu 设置行号
set shiftwidth=4 设置gg=G缩进4个空格,默认是8个空格
2 gcc编译器
2.1 gcc的工作流程
1.预处理:cpp预处理器,去掉注释,展开头文件,宏替换 gcc -E test.c -o test.i
2.编译:gcc,将源代码文件编译成汇编语言代码 gcc -S test.i -o test.s
3.汇编:as,将汇编语言代码编译成了二进制文件(目标代码) gcc -C test.s -o test.o
4.连接:ld,链接test.c代码中调用的库函数 gcc -o test.o test
一步生成最终的可执行程序:gcc test.c -o test
2.2 gcc常用参数
-v : 查看gcc版本号,--version也可以
-E : 生成预处理文件
-S : 生成汇编文件
* -c : 只编译,生成.o文件,通常称为目标文件
* -I : 指定头文件所在的路径
* -L : 指定库文件所在的路径
* -l : 指定库的名字
* -o : 指定生成的目标文件的名字
* -g : 包含调试信息,使用gbd调试需要添加-g参数
-On(n=0~3):编译优化,n越大优化的越多
-Wall:提示更多警告信息
-D :编译时定义宏 gcc -o test test.c -D MAX=10
3 静态库和动态(共享)库
3.1 库的介绍:库是二进制文件,是源代码的另一种表现形式,是加了密的源代码
好处:提高代码的复用性,和程序的健壮性,减少开发者的代码开发量,缩短开发周期
库制作完成后,如何给用户使用:
头文件 包含了库函数的声明
库文件 包含了库函数的代码实现
库分为静态库的动态库!
3.2 静态库
一些目标代码的集合,是在可执行程序运行之前就已经加入到执行码中,成为执行程序的一部分,以.a作为文件后缀名。
静态库的命名: libtest.a
- 前缀:lib
- 库名称:自定义即可,如test
- 后缀:a
静态库的制作:
1.将c源文件生成对应的.o文件 : gcc -c fun1.c fun2.c
分别生成.o文件: gcc -c fun1.c -o fun1.o
gcc -c fun2.c -o fun2.o
2.使用打包工具ar将准备好的.o文件打包为.a文件
使用ar工具时需要添加参数rcs:r更新、c创建、s建立索引
命令: ar rcs 静态库名 .o文件 例如:ar rcs libtest1.a fun1.o fun2.o
静态库的使用:制作完成后,需要将.a文件和头文件发布给用户
假设测试文件为main.c,静态库文件为libtest1.a,头文件为head.h,用到的参数:
-L 指定要连接的库所在的目录
-l 指定连接时需要的静态库,去掉前缀和后缀
-I 指定main.c文件用到的头文件head.h所在的路径
gcc -o main1 main.c -L./ -ltest1 -I./
静态库的优缺点:
优点:函数库最终被打包到应用程序中,实现函数本地化,寻址方便,速度快,移植方便
缺点:消耗系统资源较大,每个进程使用都需要复制一份,更新部署和发布带来麻烦,静态库更新了,所有使用它的程序都需要重新编译,发布给用户
3.3 共享库/动态库
共享库在程序编译时不会被链接到目标代码中,而是在程序运行时才被载入,不同的应用程序如果调用相同的库,那么在内存里只需要有一份该共享库的拷贝,避免了内存的浪费。
命名:libtest.so
- 前缀:lib
- 库名称:自己定义,如test
- 后缀:.so
共享库的制作:
1.生成目标文件.o,此时要加编译选项:-fPIC(fpic) gcc -fpic -c fun1.c fun2.c
-fpic创建与地址无关的编译程序,目的就是为了能够在多个应用程序间共享
2.生成共享库,此时要加链接器选项:-shared(指定生成动态链接库):gcc -shared fun1.o fun2.o -o libtest2.so
共享库的使用: gcc main.c -I./ -L./ -ltest2 -o main2
-L : 指定要连接的库的所在目录
-l : 指定链接时需要的动态库,去掉前缀和后缀
-I : 指定main.c文件用到的头文件head.h所在的路径
如何让系统找到共享库:
永久设置,把export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:库路径,设置到~/.bashrc文件中,然后在执行下面三种方法之一:
- 执行 . ~/.bashrc使配置文件生效,第一个.后面有空格
- 执行source ~/.bashrc配置文件生效
- 退出当前终端,然后再次登录也可以使配置文件生效
3.4 比较静态库和动态库的优缺点
静态库的优点:
- 执行速度快,静态库已经编译到可执行文件内部; 移植方便,不依赖域或其他的库文件。
缺点:
- 耗费内存,每一个静态库的可执行程序都会加载一次;部署更新麻烦,因为静态库修改之后所有的调用到这个静态库的可执行文件都需要重新编译。
动态库的优点:
- 节省内存;部署升级更新方便,只需要替换动态库即可,然后再重启服务器。
缺点:
- 加载速度比静态库慢;移植性差,需要把所有用到的动态库都移植。
由于静态库生成的可执行文件是把静态库加载到了其内部,所以静态库生成的可执行文件一般会比动态库大
4 makefile文件
makefile文件是用来编译管理项目工程文件,通过执行make命令,make就会解析并执行makefile文件
makefile命令:makefile或者Makefile
4.1 makefile的基本规则
目标:依赖
(tab)命令
makefile基本规则三要素:
- 目标:要生成的目标文件
- 依赖:目标文件由哪些文件生成
- 命令:通过执行该命令由依赖文件生成目标
第一个版本:
直接vi makefile,然后写makefile的基本命令
gcc -o main(起个名字,可执行文件) main.c fun1.c … :生成main执行文件,依赖于后面的.c文件
执行make命令就会生成可执行文件main
执行该文件就会出现结果
第二个版本:
检查规则:要想生成目标文件,先要检查依赖条件是否都存在:
若都存在,则比较目标时间和依赖生成的时间,如果依赖的时间比目标的时间新,则重新生成目标;否则不重新生成。若不存在,则往下找有没有生成依赖的规则,有则生成,如果没有则报错
更新了fun1.c的生成时间
4.2 makefile中的变量
第三个版本:
使用变量对内容做替换,易于维护,有三种变量:普通变量、自带变量、自动变量
普通变量:
变量定义直接用=使用变量值用$(变量名)
在编译执行即可!
makefile中提供的一些变量可以直接使用:
CC = gcc#arm-linux-gcc
CPPFLAGS: C预处理的选项 -I
CF:C 编译器的选项 -Wall-g-c
LDFLAGS:链接器选项 -L -l
自动变量:只能用在命令当
$@ : 表示规则中的目标
$< : 表示规则中的第一个条件
$^ : 表示规则中的所有条件,组成一个列表,以空格隔开,如果这个列表中有重复的项,则消除重复项
模式规则:至少在规则的目标定义中要包含%,%表示一个或多个,在依赖条件中同样可以使用%。例如:main.o:main.c,就是xxx.o:xxx.c
(同时注释多行:ctrl+v 然后向下选择多行,按i插入一个#,连续按两次esc)
makefile函数:
常用的两个:
1.wildcard- 查找指定目录下的指定类型的文件
src=$(wildcard *.c) //找到当前目录下所有后缀为.c的文件,赋值给src
等价于:src=main.c fun1.c fun2.c sum.c
2.patsubst-匹配替换
obj=$(patsubst %.c,%.o,$(src)) //把src变量里所有后缀为.c的文件替换为.o
obj=main.o fun1.o fun2.o sum.o
缺点:每次重新编译都需要手工清理中间.o文件和最终目标文件
4.3 makefile的清理操作
用途:清除编译生成的中间.o文件和最终目标文件
make clean 如果当前目录下有同名clean文件,则不执行clean对应的命令
解决方案:
伪目标声明: .PHONY:clean 声明目标为伪目标之后,makefile将不会检查该目标是否存在或者该目标是否需要更新
clean命令中的特殊符号:
" - " 此条命令出错,make也会继续执行后续的命令
" @ " 不显示命令本身,只显示结果。若删除文件会提示,但给删除命令前面加@符号则不会提示
其他:
-make 默认执行第一个出现的目标,可通过make dest指定要执行的目标
-make -f:-f执行一个makefile文件名称,使用make执行指定的makefile:make -f mainmak
5. gdb调试
gdb是gcc的调试工具,在运行过程中与预期不符合的时候,可以使用gdb进行调试,特别注意的是:使用gdb调试需要在编译的时候加-g参数。
5.1启动gdb
启动: gdb program program就是执行文件
设置运行参数:
set args 可指定运行时参数,例如:set args hello world i am here
show args 命令可以查看设置好的运行参数
启动程序:
run: 程序开始执行,如果有断点,停在第一个断点处
start: 程序向下执行一行
5.2 显示源代码
list : 显示当前行后面的源程序
list - : 显示当前文件开始处的源程序
list linenum : 打印第linenum行的上下文内容
list func : 显示函数名为func的函数的源程序
list file:linenum :显示file文件下第n行
list file:func :显示file文件的函数名为func的函数的源程序
set listsize count : 设置一次显示源代码的行数
show listsize : 查看当前listsize的设置
5.3设置断点
当前文件:
break 设置断点,可简写为b
b 10 设置断点,在源程序第10行设置
b func 设置断点,在func函数入口处
其他文件:
b filename:linenum 在源文件filename的linenum行处停住
b filename:function 在源文件filename的function函数的入口处停住
查询所有断点:
info b == info break == i break == i b
维护断点:
delete 删除指定的断点,简写为d
delete num 删除某个断点
delete num1 num2 删除多个断点
delete n-m 删除连读的多个断点
delete 删除所有断点
disable/enable : 使指定断点无效/有效 如果什么都不指定,表示disable/enable所有断点
disable/enable num : 使一个断点无效/有效
disable/enable num1 num2 使多个断点无效/有效
disable/enable n-m 使多个连续的断点无效/有效
disable/enable 使所有断点无效/有效
条件断点:
b test.c:8if intValue==5 在断点处设置条件
5.4 调试代码
run : 运行程序,可简写为r
next : 单步追踪,函数调用当作一条简单语句执行,简写为n
step : 单步追踪,函数调用进入被调用函数体内,简写为s
finish : 退出进入的函数,如果出不去,看一下函数体中的循环是否有断点,如果有删掉,或者设置无效
until : 在一个循环体内单步跟踪时,这个命令可以运行程序直到退出循环体,简写为u,如果出不去,看一下函数体中的循环中是否有断点,如果有删掉,或者设置无效
continue : 继续运行程序,简写为c,若有断点则跳到下一个断点处
5.5 查看变量的值
打印变量的值 :
print 打印变量、字符串、表达式的值,简写为p p count 打印count的值
自动显示变量的值:
display : 变量名
info display : 查看display设置的自动显示的信息
undisplay num (info display时显示的编号)
delete display dnums : 删除自动显示,dnums为所设置好了的自动显示的编号,若要删除多个,编号可以用空格分隔,若要删除一个范围内的编号,可以n-m 表示
disable display dnums : 使自动显示无效,若使多个自动显示无效,编号中间用空格分开,使一个范围内的自动显示无效,用n-m
enable display dnums : 显示有效,规则同上
disable和enable不删除自动显示的设置,只让其失效和恢复
查看修改变量的值:
ptype width 查看变量width的类型
p width 打印变量width的值
6 文件IO
6.1
c语言操作文件相关问题:使用fopen函数打开一个文件,返回一个FILE *fp,这个指针指向的结构体有三个重要的成员。
- 文件描述符:通过文件描述可以找到文件的inode,通过inode可以找到对应的数据块
- 文件指针:读和写共享一个文件指针,读或写都会引起文件指针的变化
- 文件缓冲区:读或写会先通过文件缓冲区,主要目的是为了减少对磁盘的读写次数,提高读写磁盘的效率
6.2 库函数和系统函数的关系
库函数和系统函数的关系:调用与被调用的关系,库函数是对系统函数的进一步封装
6.3 虚拟地址空间
进程的虚地址空间分为用户区和内核区,其中内核区是受保护的,用户是不能够对其进行读写操作的;
内核区中很重要的一个就是进程管理,进程管理中有一个区域就是PCB(本质是一个结构体);PCB中有文件描述符表,文件描述符表中存放着打开的文件描述符,涉及到文件的IO操作都会用到这个文件描述符。
6.4pcb和文件描述符表
pcb:结构体task_stuct
一个进程有一个文件描述符表:1024
- 前三个被占用,分别是STDIN_FULENO,STDOUT_FILENO,STDERR_FILENO
- 文件描述符作用:通过文件描述符找到inode,通过inode找到磁盘数据块
虚拟地址空间->内核区->PCB->文件描述表->文件描述符->文件IO操作使用文件描述符
标准函数
open/close
文件描述符:一个进程启动之后,默认打开三个文件描述符:
#define STDIN_FILENO 0
#define STDOUT_FILENO 1
#define STDERR_FILENO 2
open函数: 打开或者新建一个文件
函数原型:
int open(const char *pathname,int flags);
int open(const char *pathname,int flags,mode_t mode);
函数参数:
pathname参数是要打开或创建的文件名和fopen一样,pathname既可以是相对路径也可以是绝对路径
flag参数有一系列常数值可选择,可以同时选择多个常数用按位或运算符链接起来,所以这些常输的宏定义都用O_开头
必选项: 必须指定一个,且仅允许指定一个
O_RDONLY 只读打开
O_WRONLY 只写打开
O_RDWR 可读可写打开
可选项:指定0个或多个,和必选项按位或起来作为flags参数
O_APPEND 表示追加,如果文件已有内容,这次打开文件所写的数据附加到文件的末尾而不覆盖原来的内容
O_CREAT 若此文件不存在则创建他。使用此选项时需要提供第三个参数mode,表示该文件的访问权限
O_EXCL 如果同时制定了O_CREAT并且文件已存在,则出错返回
O_TRUNC 如果文件已经存在,将其长度截断为0字节
O_NONBLOCK 对于设备文件,以O_NONBLOCK方式打开可以做非阻塞I/O
函数返回值:
成功: 返回一个最小且未被占用的文件描述符
失败: 返回-1,并设置errno值
close函数:关闭文件
函数原型:
int close(int fd);
函数参数:
fd文件描述符
函数返回值:
成功返回0
失败返回-1,并设置errno值
read/write:
read函数:从打开的设备或文件中读取数据
函数原型:ssize_t read(int fd,void *buf,size_t count);
函数参数:
fd:文件描述符
buf:读上来的数据保存在缓冲区buf中
count:buf缓冲区存放的最大字节数
函数返回值:
>0: 读取到的字节数
=0:文件读取完毕
-1:出错,并设置errno
write:向打开的设备或文件中写数据
函数原型:ssize_t write(int fd,const void *buf,size_t count);
函数参数:
fd:文件描述符
buf:缓冲区,要写入文件或设备的数据
count:buf中数据的长度
函数返回值:
成功:返回写入的字节数
错误:返回-1并设置errno
所有打开的文件都有一个文件偏移量,表明文件开始处到文件当前位置的字节数,文件被打开时,cfo会被初始化为0,除非使用了O_APPEND
lseek:移动文件指针
包含的头文件:
#include<sys/types.h>
#include<unistd.h>
off_t lseek(int fd,of_t offset,int whence);
函数原型:off_t lseek(int fd,off_t offset,int whence);
函数参数:
fd: 文件描述符
offset的含义取决于参数whence:
如果whence是SEEK_SET,文件偏移量将设置为offset。
如果whence是SEEK_CUR,文件偏移量将被设置为cfo加上offset,offset可以为正也可以为负。
如果whence是SEEK_END,文件偏移量将被设置为文件长度加上offset,offset可以为正也可以为负。
函数返回值:若lseek成功执行,则返回新的偏移量。
lseek函数常用操作:
文件指针移动到头部: lseek(fd,0,SEEK_SET);
获取文件指针当前位置: int len =lseek(fd,0,SEEK_CUR);
获取文件长度: int len = lseek(fd,0,SEEK_END);
lseek实现文件拓展:off_t currpos;
lseek获取文件大小:
默认生成a.log文件,使用gcc -c (自定义命名) lseek.c 运行:./lseek test.log
lseek函数实现拓展文件:
文件填充必须有一次写入操作,所以必须有write(fd,“H”,1);,不然会报错
原因:若给硬盘里下载东西需要先拓展,否则不知道空间是否满足要求,类似于先拓展至满足的要求,再写入
test中的内容:
perror和errno:errno是一个全局变量,当系统调用后若出错会将errno进行设置,perror可以将errno对应的描述信息打印出来
man errno 查看errno信息
阻塞与非阻塞:
- 读取普通文件是阻塞还是非阻塞? read读取普通文件是非阻塞的,read函数在读完文件内容之后,若再次read,则read函数会立刻返回,表明read函数读普通文件是非阻塞的
- 测试终端设备文件/dev/tty是阻塞还是非阻塞:标准输入STDIN_FILENO,通过读/dev/tty终端设备文件,表明read函数读设备文件是阻塞的。(文件描述符默认打开)
- 结论:阻塞和非阻塞不是read函数的属性,而是文件本身的属性
stat/lstat函数:
函数描述:获取文件属性
函数原型:
int stat(const char *pathname,struct stat *but);
int lstat(const char *pathname,struct stat *buf);
函数返回值:
成功返回0,失败返回-1
struct stat {
dev_t st_dev; //文件的设备编号
ino_t st_ino; //节点
mode_t st_mode; //文件的类型和存取的权限
nlink_t st_nlink; //连到该文件的硬连接数目,刚建立的文件值为1
uid_t st_uid; //用户ID
gid_t st_gid; //组ID
dev_t st_rdev; //(设备类型)若此文件为设备文件,则为其设备编号
off_t st_size; //文件字节数(文件大小)
blksize_t st_blksize; //块大小(文件系统的I/O 缓冲区大小)
blkcnt_t st_blocks; //块数
time_t st_atime; //最后一次访问时间
time_t st_mtime; //最后一次修改时间
time_t st_ctime; //最后一次改变时间(指属性)
};
- st_mode -- 16位整数
0-2 bit -- 其他人权限
S_IROTH 00004 读权限
S_IWOTH 00002 写权限
S_IXOTH 00001 执行权限
S_IRWXO 00007 掩码, 过滤 st_mode中除其他人权限以外的信息
3-5 bit -- 所属组权限
S_IRGRP 00040 读权限
S_IWGRP 00020 写权限
S_IXGRP 00010 执行权限
S_IRWXG 00070 掩码, 过滤 st_mode中除所属组权限以外的信息
6-8 bit -- 文件所有者权限
S_IRUSR 00400 读权限
S_IWUSR 00200 写权限
S_IXUSR 00100 执行权限
S_IRWXU 00700 掩码, 过滤 st_mode中除文件所有者权限以外的信息
If (st_mode & S_IRUSR) -----为真表明可读
If (st_mode & S_IWUSR) ------为真表明可写
If (st_mode & S_IXUSR) ------为真表明可执行
12-15 bit -- 文件类型
S_IFSOCK 0140000 套接字
S_IFLNK 0120000 符号链接(软链接)
S_IFREG 0100000 普通文件
S_IFBLK 0060000 块设备
S_IFDIR 0040000 目录
S_IFCHR 0020000 字符设备
S_IFIFO 0010000 管道
S_IFMT 0170000 掩码,过滤 st_mode中除文件类型以外的信息
If ((st_mode & S_IFMT)==S_IFREG) ----为真普通文件
if(S_ISREG(st_mode)) ------为真表示普通文件
if(S_ISDIR(st.st_mode)) ------为真表示目录文件
获取文件大小,uid,gid等信息
#include<stdio.h>
#include<stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc ,char *argv[])[
struct stat st;
stat(argv[1],&st);
printf("size:[%d],uid:[%d],gid:[%d]\n",st.st_size,st.st_uid,st.st_gid);
return 0;
}
测试文件类型:
int main(int argc ,char *argv[]){
struct stat sb;
stat(argv[1],&sb);
//判断文件类型
if((sb.st_mode & S_IFMT)==S_IFREG){
printf("普通文件\n");
}
else if((sb.st_mode & S_IFMT)==S_IFDIR){
printf("目录文件\n");
}
//ln -s test.log test.log.s
//若测试test.log.s文件并不能打印链接文件,因为他判断的是链接文件中链接的文件类型
else if((sb.st_mode & S_IFMT)==S_IFLNK){
printf("链接文件\n");
}
//判断文件类型
if(S_ISREG(sb.st_mode)){
printf("普通文件\n");
}
else if(S_ISDIR(sb.st_mode)){
printf("目录文件\n");
}
else if(S_ISLNK(sb.st_mode)){
printf("链接文件\n");
}
//判断文件读写权限
if(sb.st_mode & S_IROTH){
printf("---R---")
}
if(sb.st_mode & S_IWOTH){
printf("---W---");
}
if(sb.st_mode & S_IXOTH){
printf("---X---");
}
printf("\n");
return 0;
}
stat函数和lstat函数的区别:
- 对于普通文件,这两个函数没有区别
- 对于链接文件,调用lstat函数获取的是链接文件本身的属性信息,而stat函数获取的是链接文件指向的文件的属性信息
目录操作相关函数:
opendir函数:打开一个目录
函数原型:DIR *opendir(const char *name);
函数返回值:指向目录的指针
函数参数:要遍历的目录(相对路径或绝对路径)
readdir函数:读取目录内容
函数原型:struct dirent *readdir(DIR *dirp);
函数返回值:读取目录项指针
函数参数:opendir函数的返回值
struct dirent
{
ino_t d_ino; // 此目录进入点的inode
off_t d_off; // 目录文件开头至此目录进入点的位移
signed short int d_reclen; // d_name 的长度, 不包含NULL 字符
unsigned char d_type; // d_name 所指的文件类型
char d_name[256]; // 文件名
};
d_type的取值:
DT_BLK - 块设备
DT_CHR - 字符设备
DT_DIR - 目录
DT_LNK - 软连接
DT_FIFO - 管道
DT_REG - 普通文件
DT_SOCK - 套接字
DT_UNKNOWN - 未知
closedir函数:关闭目录
函数原型:int closedir(DIR *dirp);
函数返回值:成功返回0,失败返回-1
函数参数:opendir函数的返回值
读取目录内容的一般步骤:
DIR *pDir = opendir("dir"); //打开目录
while((p=readdir(pDir))!=NULL){} //循环读取文件
closedir(pDir); //关闭目录
遍历目录下所有文件,并判断文件类型
int main(int argc ,char *argv[]){
//open dir
//DIR *opendir(const char *name);
DIR *pDir = opendir(argv[1]);
if(pDir==NULL){
perror("opendir error");
return -1;
}
//循环读取目录项
// struct dirent *readdir(DIR *dirp);
struct dirent *pDent = NULL;
while((pDent=readdir(pDir))!=NULL){
//过滤掉.和..文件
if(strcmp(pDent->d_name,".")==0 || strcmp(pDent->d_name,"..")==0)
continue;
printf("[%s]---->",pDent->d_name);
//判断文件类型
switch(pDent->d_type){
case DT_REG:
printf("普通文件");
break;
case DT_DIR:
printf("目录文件");
break;
case DT_LNK:
printf("链接文件");
break;
default:
printf("未知文件");
}
printf("\n");
}
//close dir
closedir(pDir);
return 0;
}
dup函数:复制文件描述符
函数原型:int dup(int oldfd);
函数参数:oldfd 要复制的文件描述符
函数返回值:
成功:返回最小且没被占用的文件描述符
失败:返回-1,设置errno值
//调用dup函数之后,fd和newfd指向同一个文件,内核会在内部维护一个计数,此时计数为2,当close一个文件描述符之后,计数边为1,只有当计数为0时,文件才会被真正关闭
//通过fd对文件进行写操作,使用newfd对文件进行读操作,若读到了文件内存,则认为fd和newfd指向相同的文件
int main(int argc ,char *argv[]){
//open file
int fd = open(argv[1],O_RDWR);
if(fd<0){
perror("opendir error");
return -1;
}
// 调用dup函数复制fd
int newfd = dup(fd);
printf("newfd:[%d], fd[%d]\n",newfd,fd);
//使用fd读文件
write(fd,"hello world",strlen("hello world"));
//移动文件指针,否则在newfd读取的时候指针在文件尾,使其读不到内容
lseek( fd,0,SEEK_SET);
//newfd读文件
char buf[64];
memset(buf,0x00,sizeof(buf));
int n = read(newfd,buf,sizeof(buf));
printf("read over :n==[%d] , buf==[%s]\n",n, buf);
//close file
close(fd);
close(newfd);
return 0;
}
dup2函数:
//调用dup2(oldfd,newfd)之后:
//若newfd原来已经打开了一个文件,则先关闭这个文件,然后newfd指向了和oldfd相同的文件
//若newfd原来没有打开文件,则newfd直接指向和oldfd相同的文件
//调用dup2函数之后,内核会修改内部的计数,计数为2
int main(int argc ,char *argv[]){
//open file,
//验证:打开两个文件,调用dup2函数,newfd做写操作,用fd读取,能读到说明二者指向同一个文件
int fd = open(argv[1],0_RDWR);
int newfd = open(argv[2],0_RDWR);
if(fd<0){
perror("opendir error");
return -1;
}
if(newfd<0){
perror("opendir error");
return -1;
}
printf("newfd:[%d], fd[%d]\n",newfd,fd);
dup2(fd,newfd);
printf("newfd:[%d], fd[%d] n",newfd,fd);
write(newfd,"hello world",strlen("hello world"));
lseek(newfd,0,SEEK_SET);
char buf[64];
memset(buf,0x00,sizeof(buf));
int n = read(fd,buf,sizeof(buf));
printf("read over :n==[%d] , buf==[%s] n",n, buf);
fileclose(fd);
close(newfd)
return 0;
}
dup2函数实现文件重定向:
int main(int argc ,char *argv[]){
//open file
int fd = open(argv[1],0_RDWR 0_CREAT,0777);
if(fd<0){
perror("opendir error");
return -1;
}
dup2(fd,STDOUT_FILENO);
//不会输出这段话,而是打印到fd指向的文件中
printf("NI hao hello world");
//close file
close(fd);
close(STDOUT_FILENO);
return 0;
}
fcntl函数:改变已经打开的文件的属性
函数原型:int fcntl(int fd,int cmd,.../*arg*/);
若cmd为F_DUPFD,复制文件描述符,与dup相同
若cmd为F_GETFL,获取文件描述符的flag属性值
若cmd为F_SETFL,设置文件描述符的flag属性
函数返回值:返回值取决于cmd
成功:
若cmd为F_DUPFD,返回一个新的文件描述符
若cmd为F_GETFL,返回文件描述符的flags值
若cmd为F_SETFL,返回0
失败返回-1,并设置errno值
fcntl函数常用的操作:
复制一个新的文件描述符:int nwefd=fcntl(fd,F_DUPFD,0);
获取文件的属性标志:int flag=fcntl(fd,F_GETFL,0);
设置文件状态标志:flag=flag | O_APPEND; fcntl(fd,F_SETFL,flag);
常用的属性标志:
O_APPEND :设置文件打开为末尾添加
O_NONBLOCK:设置打开的文件描述符为非阻塞