Linux学习笔记-基本操作4
1. gdb调试
2. makefile的编写
3. 系统IO函数
1. gdb调试:
1. 启动gdb + 可执行文件
2. 查看代码:
l==list
l (默认给出main函数的前十行)(再次输入l会给继续列出后面的内容 之后可以按回车即可)
l 行号(或函数名)
l filename:行号(或函数名)
3. 设置断点:
设置当前文件断点:
b == break
b 行号(或函数名)
设置指定文件断点:
b fileName:行号(函数名)
设置条件断点:
b 10 if 某个变量==19
删除断点:
delete == del == d
d 断点的编号
获取编号:
info == i
info b
4. 查看设置的断点
i==info
i b
5. 开始 执行gdb调试
执行一步操作: start (或r==run 直接运行或到断点停止)
单步继续执行: n
执行多步, 直接停在断点处:c
5. 单步调试
进入函数体内部: s
从函数体内部跳出: finish
不进入函数体内部: n
退出当前循环: u
6. 查看变量的值: p == print
p 变量名
7. 查看变量的类型: ptype 变量名
8. 设置变量的值: set var 变量名 = 赋值
9. 设置追踪变量
display + 变量名
取消追踪变量 :undisplay 编号
获取编号: info display
10. 退出gdb调试
quit
2. makefile的编写:项目的代码管理工具,节省编译项目的时间,一次编写终身受益
1. makefile的命名:makefile (或Makefile)
2. makefile的规则:(vi makefile 直接创建文件)
规则中的三要素: 目标(app名字), 依赖(用到的.c文件), 命令(gcc命令)
目标:依赖条件
(tab缩进)命令
子目标和终极目标的关系: 生成最终目标
用户自定义的规则:
clean:
(-makedir \aa 不能成功,不加-则在此处命令暂停,加-则可以忽略该命令向下继续执行)
rm *.o -f (-f强制删除,即使没有)
make clean (执行对应的clean规则的命令)
若当前目录下有名为clean的文件,则不能执行相应规则
解决方案 -->在前一行做伪目标声明:.PHONY:clean
3.执行makefile生成终极目标app:make (会在下一行显示一些gcc命令) 4.若想生成目标,检查规则中的依赖条件是否存在,如果不存在,寻找是否有规则用来生成该依赖文件。检查规则中的目标是否需要更新,必须检查它的所有依赖,依赖中有任意一个被更新,则目标必须更新。依赖文件比目标文件时间晚,则需要更新。
5.可以在makefile 中声明变量如obj=main.o sum.o sub.o
使用变量:$(obj)
6. makefile的两个函数(makefile中所有的函数必须都有返回值)
wildcard:查找指定目录下指定类型的文件,一个参数
src = $(wildcard ./*.c) 找到./src 目录下所有后缀为.c的文件,以串的形式赋给变量src
patsubst:匹配替换,从src中找到所有.c 结尾的文件,并将其替换为.o
obj = $(patsubst %.c ,%.o ,$(src)) 把src变量中所有后缀为.c的文件替换成.o
7. makefile中的自动变量
$<: 规则中的第一个依赖
$@: 规则中的目标
$^: 规则中的所有依赖
只能在规则的命令中使用
app:main.o sub.o mul.o
gcc main.o sub.o mul.o -o app
(gcc $^ -o $@)
%.o:%.c (第一个%逐个匹配终极目标的依赖,第二个%匹配第一个%内容)
gcc -c $< -o $@
8.Makefile自己维护的变量,都是大写的。
CC = cc (gcc)
CPPFLAGS : 预处理器需要的选项 如:-I
CFLAGS:编译的时候使用的参数 –Wall –g -c
LDFLAGS :链接库使用的选项 –L -l
用户可以修改这些变量的默认值CC = gcc
3. 系统IO函数
1>. 一些概念
文件描述符
PCB
库函数与系统函数的关系
2>. open
函数原型:
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
打开方式:
必选项flags:
O_RDONLY 只读
O_WRONLY 只写
O_RDWR 读写
可选项:
O_CREAT 创建新文件
第三个参数:文件权限: 本地有一个掩码:umask 可查看 umask 022 可临时修改
文件的实际权限: 给定的权限 & 本地掩码(取反)==实际的文件权限
7 7 7
111111111(给定权限的8进制)
111111101(掩码去反的8进制)
---------
111111101
7 7 5
O_EXCL 如果同时指定了O_CREAT,并且文件已存在,则出错返回。<stdio.h>void perror(const char *s)用来将上一个函数发生错误的原因输出到标准设备(stderr)
O_TRUNC 将文件截断为0,如果文件已存在,则将其长度截断(Truncate)为0字节。
O_APPEND 追加如果文件已有内容,这次打开文件所写的数据附加到文件的末尾。
3>. read 从打开的设备或文件中读取数据。
函数原型:ssize_t read(int fd, void *buf, size_t count);
fd:文件描述符
buf:数据缓冲区
count:请求读取的字节数
返回值:
1. -1 读文件失败
2. 0 文件读完了
3. >0 读取的字节数
4>. write 向打开的设备或文件中写数据。
函数原型:ssize_t write(int fd, const void *buf, size_t count);
返回值:
-1 --> 失败
>=0 --> 写入文件的字节数
5>. lseek 修改文件偏移量(读写位置) 。文件的拓展, 最后需要做额外的写操作 。
函数原型:off_t lseek(int fd, off_t offset, int whence)
off_t offset --> 偏移量
int whence --> 偏移位置
SEEK_SET - 从文件头向后偏移
SEEK_CUR - 从当前位置向后偏移
SEEK_END - 从文件尾部向后偏移
返回值:
较文件起始位置向后的偏移量
允许超过文件结尾设置偏移量,文件会因此被拓展。
失败返回 -1
获取文件长度:lseek(fd, 0, SEEK_END); 返回值即为文件长度
6>. close
参数:open函数的返回值
返回值:
0 --> 正常关闭
-1 --> 关闭出现错误
cpu 为什么要使用虚拟地址空间与物理地址空间映射?解决了什么样的问题?
1.方便编译器和操作系统安排程序的地址分布。
程序可以使用一系列相邻的虚拟地址来访问物理内存中不相邻的大内存缓冲区。
2.方便进程之间隔离
不同进程使用的虚拟地址彼此隔离。一个进程中的代码无法更改正在由另一进程使用的物理内存。
3.方便OS使用你那可怜的内存。
程序可以使用一系列虚拟地址来访问大于可用物理内存的内存缓冲区。当物理内存的供应量变小时,
内存管理器会将物理内存页(通常大小为 4 KB)保存到磁盘文件。数据或代码页会根据需要在物理内存与磁盘之间移动。
对大文件的读写两种方式:
read write -- 每次读1个byte 效率低
getc putc -- 每次读1个byte 效率高
标准C库函数内部提供了一个缓冲区