大纲:命令,vim,gcc,编译工具,生成代码,调试,库makefile,系统编程
文件系统:文件属性,文件内容,万物皆文件(不在内存中的是文件,鼠标键盘耳机都可以看作键盘),IO多路复用(即时聊天),进程(特点(怎么分配资源),多进程(创建和管理),),进程间通信(管道,共享内存,信号量,消息队列,信号),线程(多线程,互斥和同步,),网络(网络概念,编程,epoll,),服务器结构(进程池和线程池),sql语句,最终实现一个网盘。
1、gcc
预处理处理#开头的文件,
写代码一般就编译错误(语法错误)或者链接错误(名字冲突)
2、汇编(AT&T语言(汇编的语言有很多种,AT&T语言是其中一种,针对x86机器))
编译后所有的名字都转换成内存的地址;所有的类型的信息都变成长度信息;
pushq (push表示压栈,q表示quadra(4),表示四倍16bit就是64bit,x86的8086是16bit一个字长(每次读取内存的单位),)
%rbp(百分号说明是一个寄存器,register base pointer,表示基地址)
movq 表示拷贝
subq sub表示减, rsp减去48;
q表示四个字长,一个字长16位为两个字节,
xorl (xor表示异或,L表示两个字长32位);$表示赋值;
每四个字节为单位:
变量的名字和类型会被转化为内存的位置和内存的大小;
leaq(lea表示load effective address ,取地址)
call (调用函数);
存储程序的概念:
在需要调用mian意外的函数时先call这个函数,然后把用一个占位符占位,等链接的时候,再进入函数;
新压一个栈来
参数传递:caller的栈帧到寄存器到caller的栈帧,值传递的过程;
字符串字面常量:存于rdi区,(只读)
esi和rdi是为了调用printf
fun return 后回到main;
jmp 指针移动;(for 循环)循环的底层用goto实现
cmpl(比较);
jle(jump less equal):小于等于就跳转;编译器会优化部分代码(等价替换)
je 相等就跳转;
call ------stack------chk------fail@PLT (提示栈损毁);如果内存发生修改就证明代码错误;所以引出报错;指针内存乱指等原因;
--------------------汇编总结------------
push (压栈) pop(出栈) call(调用函数) ret(函数返回)
mov (拷贝) add(加) sub(减)lea(取地址)
xor(异或) jmp je jle (跳转)
q(8byte) l(4byte)
所有变量,数组,*p 编译完在汇编中只剩下了地址+长度
循环 for,while,和goto本质一样
函数调用 栈帧独立,传递参数值传递
AS命令,生成汇编文件
可移植GUN汇编
nm命令(列出文件中的符号信息)
常见符号类型:A(该符号的值在今后的链接中不再改变)
U(该符号未定义过,需要自其他对象文件中链接出来)
T(该符号在代码段中,通常是全局非静态函数)
gcc -c (.c 文件直接生成.o)
objdump (反编译)
反编译汇编文件可以把.o(机器语言)反编译成.s(汇编语言)
以上左侧二进制序列对应右侧汇编代码
链接(参考书《linker &loader》《程序员自我修养》)
把调用函数的名字换成地址;
ld 命令(链接文件)
或者使用gcc命令间接调用链接;
(函数 / 全局变量 定义0次/超过2次) (缺失main函数) (报错信息有ld错误)链接错误;
执行可执行程序
./ 文件名 (防止与文件名冲突)
库文件(特殊的.o文件)
静态库和动态库
静态库(打包到产品中)
动态库(运行时才加载到产品中)
ldd 查看文件依赖库
-static 选项让文件链接依赖静态库;默认动态库
静态编译占用内存较大
生成静态库:
ar命令:创建、修改库、从库中提出单个模块;
1、通过gcc -c add.c -o add.o 生成目标文件
2、打包成静态库文件
ar crsv libadd.a add.o
3、移动到系统搜索目录 /usr/lib
sudo cp libadd.a /usr/lib
4、在链接文件时加上 -ladd
静态库链接成功后生成的可执行文件不会因为原静态库的丢失而受影响;
生成动态库:
1、编译成目标文件
动态库加载于栈区和堆区之间,该区域命名为共享库映射区,该区不能生成绝对地址。
这样生成的代码叫位置无关代码
-fpic (编译时加上这个选项即可完成)
gcc add.c -o add.o -fpic (编译)
2、打包
gcc -shared add.o -o libadd.so
3、移动到系统库目录
sudo cp libadd.so /usr/lib
4、链接加上-ladd
gcc test.o -o test -ladd
ldd 文件名(查看动态库的链接情况)
删除libadd.so后可执行文件失效;重新copy后可以运行,修改链接库文件后不用链接即可在线更新;
更新出bug后进行回滚:
软连接(符号链接)类似于windows的快捷方式
用版本号来区别更新前后的文件,软连接来链接需要的文件
将动态库文件libadd.so文件移动到libadd.so.0.1后执行test显示找不到库文件;
ln -s命令可以创建软链接
创建软连接成功后用ls -al 可以查看
链接创建后可以执行文件
gcc的其他选项:
-D 选项
预处理时需要用到ifdef 为了避免重复改代码造成的风险,可以注释掉需要定义的宏,在预处理阶段加入-D命令使预处理生效注释掉的宏。
-I 选项(大写i)
增加一个搜索路径,防止重复修改文件目录,导致在源代码里面多次修改导致不可预测的风险。利用改变命令的方式来改变搜索路径。
-O(编译优化,结果不变,指令数量变少,执行速度变快(编译器作者在不影响结果的情况下修改指令顺序和数据存储位置))
可能会对调试影响(单步调试)编译优化分级来减小对代码调试的影响
-O0 不优化
-O1 发布产品
-O2 开源软件
-O3 激进优化方案
优化越深
优化越深,c于汇编的对应就乱了。
编译警告:
Linux中不能出现警告。
警告实例:在源代码中定义了 i 但是没有用到;
gdb--调试工具
不能开优化,该工具针对c代码的一行, -O0
补充调试信息, -g
以上两点要在编译时加上。
使用-O0和-g后生成可执行程序中包含了调试信息;
gbd 文件名(可执行文件名)进入gdb调式界面
list (每十行显示源代码)list后面加行号可从行号显示;l 和list一样的效果
list 加文件名 加行号 函数名
run / r运行程序
break /b(打断点)加文件名加行号 加函数(在函数定义入口打断点)
continue / c 运行到下一个断点
step / s 单步调试 vs的f11,进入函数第一行(逐步进入)
next / n vs的f10 单步调用但是跳过函数(逐步越过)
finish 直接运行这个函数调用完为止;跳出本次函数调用
info break /i b查看目前的所有断点
delete 加编号 删除断点(不加编号就删除全部)
进入循环断点时,gdb会进入这个断点循环次数,所以需要用到忽略这个断点若干次
多次命中循环体中的断点
ignore忽略断点若干次(测试循环结构)
使用i b 查看断点编号,再用ignore 加断点编号加次数设置忽略次数
已经忽略三次循环的效果
在gdb中查看监视
print / p
p为指针(内存空间以指针大小偏移,int为4byte,所以地址偏移四字节进四。内存的一个字节对应一个地址)
自动监视变量的值,display 加变量名
每查看一行就自动监视输出一次
删除display
先info display显示display的编号再undisplay 加编号删除
help 加命令 显示手册
X 在gdb中去查看内存:
FMT = 数字 字母1 字母2
数字:查看多少单位
字母1:O(八进制)x(十六进制)d(十进制) u(无符号十进制)t(二进制)
字母2:每个单位的大小 b(1byte)h(halfword,2byte)w(word,4byte) g (giant , 8 bytes)
例子:arr数组3个字节
x/3tw arr
3个单位 二进制的形式4个字节
4个单位,二进制,4字节
检查崩溃的程序
core文件-----记录程序崩溃的时刻内存的堆栈情况。
指针在解引用和初始化之前不能赋值,不然会造成野指针。
错误实例:
段错误:Segmentation fault
core dumped (core文件储存)操作系统会限制core文件的大小
ulimit -a (查看操作系统限制大小)
ulimit -c unlimited (设置core文件 不限制大小)
该指令不是永久生效,只能影响一个终端;
修改以下文件,来修改core的大小限制
切换到root用户
用echo core > /proc/sys/kernel/core_pattern
往core_pattern 写入core
之后core文件获得不限制大小的权限,即可生成core文件
gdb 可执行文件名 core(该命令用于查看core文件内容)
获得报错信息,
bt命令用于调用堆栈的情况
1、编译时加上 -g -O0
2、ulimit -c unlimited
3、执行程序
4、gdb 可执行程序 core
栈溢出导致的崩溃
命令行参数 + gdb:
用gdb启动:
set args how are you(设置追加参数,)
show args (展示追加的参数)