Linux基础开发工具
1.软件包管理器1.1什么是软件包在Linux下安装软件一个通常的办法是下载到程序的源代码并进行编译得到可执行程序。但是这样太麻烦了于是一些人把一些常用的软件提前编译好做出软件包可以理解成windows上的安装程序放在一个服务器上通过包管理器可以很方便的获取到这个编译好的软件包直接进行安装。软件包和软件包管理器就好比“APP”和“应用商店”这样的关系。yumYellow dog Updater,Modified是Linux下非常常用的一种包管理器。主要应用在FedoraRedHatCentos等发行版上。Ubuntu主要使用aptAdvanced Package Tool作为其包管理器。apt同样提供了自动解决依赖关系、下载和安装软件包的功能。安装其实就是拷贝必须使用root权限才能安装安装到系统里面只要安装一次任何人都能使用。1.2Linux软件生态Linux下载软件的过程Ubuntu、Centos、other操作系统的好坏评估——生态问题为什么会有人免费在特定社区提供软件、发布还提供云服务器让你下载一款操作系统的配套软件也算生态的一环。软件包依赖问题国内镜像源1.3yum具体操作1.3.1查看软件包通过yum list命令可以列出当前一共有那些软件包由于包的数目可能非常多我们需要grep命令来筛选出我们关注的包yum list | grep sl注意事项软件包名称主版本号.次版本号.源程序发行号-软件包的发行号.主机平台.cpu架构。x86_64 后缀表示64位系统的安装包“i686”后缀表示32位系统的安装包选择包时要和系统匹配。“el7”表示操作系统发行版的版本。“el7”表示的是centos7/redhat7。最后一列base表示“软件源”的名称类似“小米应用商店”“华为应用商店”这样的概念。1.3.2安装软件#Centos yum install -y sl #ubuntu apt install -y slyum/apt会自动找到有那些软件包需要下载这时按“y”确认安装出现“complete”字样或中间没有报错说明安装完成1.3.3卸载软件#Centos yum remove -y sl #ubuntu apt remove -y sl2.编辑器vim2.1 Linux编辑器-vim的使用vi/vim的区别简单一点来说它们都是多模式编辑器不同的是vim是vi的升级版本他不仅兼容vi的所有指令而且还有一些新的特性在里面。2.2 vim的基本概念vim的三种模式主要的三种分别是命令模式command mode、插入模式insert mode和底行模式last line mode。正常/普通/命令模式Normal mode 控制屏幕光标的移动字符、字或行的删除移动复制以及进入插入模式或者底行模式插入模式insert mode 只有在插入模式下才可以做文字输入按ESC键可回到命令行模式。底行模式last line mode文件保存或退出也可以进行文件替换找字符串列出行号等操作。在命令模式下 shift ; 其实就是 : 即可进入该模式。要查看你的所有模式打开vim底行模式直接输入:help vim-modes一共有12中模式six BASIC modes 和six ADDITIONAL modes2.3 vim的基本操作进入vim在系统提示符号输入vim以及文件名就进入vim全屏编辑画面vim code.c进入vim后是处于正常模式切换到插入模式才能够输入文字正常模式切换到插入模式 输入a / i / o插入模式切换正常模式 按ESC正常模式切换底行模式输入“ shift ; ”其实就是 :退出vim及保存文件在正常模式下按 : 冒号键进入底行模式:w 保存当前文件:wq 报存并退出vim:q! 输入q! 不保存强制退出vim2.4 vim正常模式命令集插入模式按 i 切换进入插入模式按“ i ” 进入插入模式后是从光标当前位置开始输入文件。按 a 进入插入模式后是从目前光标的下一个位置开始输入文字按 o 进入插入模式后是插入新的一行从行首开始输入文字。移动光标下面#代表任意数字vim可以直接用键盘上的光标来上下左右移动但正规的vim是用小写英文字母 h j k l分别控制光标左、下、上、右移一格按G 移动到文章的最后按$ : 移动到光标所在的“行尾”按^ : 移动到光标所在行的“行首”按w光标跳到下个字的开头按e光标跳到下个字的字尾按b光标回到上个字的开头按#l光标移到该行的第#个位置如5l15l。按gg进入到文本开始按shiftg进入文本末端按ctrlb屏幕往“后”移动一页按ctrlf屏幕往“前”移动一页按ctrlu屏幕往“后”移动半页按ctrld屏幕往“前”移动半页删除文字x 每按一次删除光标所在位置的一个字符#x例如6x表示删除光标所在位置的后面包含自己在内6个字符X大写的X每按一次删除光标所在位置的前面的一个字符#X例如10X表示删除光标所在位置的前面10个字符dd删除光标所在行#dd从光标所在行开始删除#行复制yw将光标所在之处到字尾的字符复制到缓冲区中。#yw复制#个字到缓冲区中yy复制光标所在行到缓冲区中#yy例如6yy表示拷贝从光标所在的该行“往下数”6行文字。p将缓冲区内的字符贴到光标所在位置。替换r替换光标所在处的字符R替换光标所在处的字符直到按下ESC键停止撤销上一次操作u如果误操作执行一个命令按 u 回到上一个操作多次按 u 可以执行多次恢复ctrl r 撤销的恢复更改 直接进入插入模式cw更改光标所在处的字到字尾处c#w例如c3w表示更改3个字跳至指定行ctrl g 列出光标所在行的行号#G例如15G表示移动光标至文章的第15行行首。2.5 vim底行模式命令集使用底行模式前先按ESC键确定处于正常模式再按进入底行模式列出行号 set nu 输入set nu后会在文件中的每一行前面列出行号跳到文件的某一行 # #表示一个数字在冒号后面输入一个数字再按回车就会跳到该行如输入数字15再回车就会跳到文章的15行。查找字符 /关键字先按 / 键再输入要寻找的字符如果第一次找的关键字不是想要的可以一直按n往后找到你要的关键字为止。?关键字先按?键在输入要寻找的字符如果第一次找的关键字不是想要的可以一直按n往前找到你要的关键字为止。!指令不退出vim就可以执行指令批量化替换%s / dst / src / src替换dst:vs 文件 打开另一个文件 例如vs test.cctrl ww在两个文件中光标切换2.6使用vim的小技巧vim code.c 行号 打开vim后光标在当前行号在命令行中输入 !v 使用最近的v开头的命令2.7简单的vim配置配置文件的位置在目录/etc/下面有个名为vimrc的文件这是系统中公共的vim配置文件对所有用户都有效而在每个用户的主目录下都可以自己建立私有的配置文件命名为.vimrc。打开自己目录下的.vimrc文件执行vim .vimrc在里面配置文件。注释vim中的语句使用 “自己配置vim非常麻烦在gitee上VimForCpp: 快速将vim打造成c IDE这个里面连接可以直接配置vim。但是这个配置只能在centos7上使用3.编译器gcc/ggcc格式 gcc [选项] 要编译的文件 [选项] [目标文件]3.1gcc编译过程3.1.1 预处理进行宏替换预处理功能主要包含宏定义文件包含条件编译去注释等。预处理指令是以#开头的代码行gcc -E code.c -o code.i选项“-E”该选项作用时让gcc在预处理结束后停止编译过程选项“-o”是指目标文件“.i”文件为已经过预处理的C原始程序3.1.2编译生成汇编在这个阶段gcc首先要检查代码的规范性、是否有语法错误以确定代码的实际要做的工作在检查无误后gcc把代码翻译成汇编语言。用户可以使用“-S”选项来进行查看该选项只进行编译而不进行汇编生成汇编代码。gcc -S code.i -o code.s3.1.3汇编生成机器可识别代码汇编阶段把编译生成的“.s”文件转成目标文件使用选项“-c”就可看到汇编代码已转化为“.o”的二进制目标代码了gcc -c code.s -o code.o3.1.4链接生成可执行文件或库文件在成功编译后就进入了链接阶段gcc code.o -o code3.2gcc其他常用选项-E 只激活预处理这个不生成文件需要把它重定向到一个输出文件里面-S 编译到汇编语言不进行汇编和链接-c 编译到目标代码-o 文件输出到文件-static 此选项对生成的文件采用静态链接-g 生成调试信息GNU调试器可利用该信息-shared 此选项将尽量使用动态库生成文件比较小。-O0-O1-O2-O3编译器的优化选项的4个级别-O0表示没有优化-O1表示为缺省值-O3优化级别最高-w 不生成任何警告信息-Wall 生成所有警告信息3.2.1如何理解条件编译下面-DM 就相当于把 define M100 插入到我们的代码中条件编译的用途1. 软件进行专业度收费情况进行区分业务使用条件编译可以进行代码动态裁减2. 内核源代码也是采用条件编译进行代码裁减3. 开发工具应用软件3.3 动态链接和静态链接我们在实际开发中不可能将所有代码放在一个源文件中所有会出现多个源文件而且多个源文件之间不是独立的而会存在多种依赖关系如一个源文件可能要调用另一个源文件中定义的函数但是每个源文件都是独立编译的即每个*.c文件会形成一个*.o文件为了满足前面说的依赖关系则需要将这些源文件产生的目标文件进行链接从而形成一个可执行程序。这个链接的过程就是静态链接。静态链接的缺点很明显浪费空间因为每个可执行程序中对所有需要的目标文件都要有一份副本所以多个程序对同一个目标文件都有依赖如多个程序中都调用了printf()函数则这多个程序中都含有printf.o所以同一个目标文件都在内存存在多个副本更新比较困难因为每当库函数的代码修改了这个时候就需要重新进行编译链接形成可执行程序。但是静态链接的优点是在可执行程序中已经具备了所有执行程序所需要的任何东西在执行的时候运行速度快动态链接的出现解决了静态链接中提到的问题。动态链接的基本思想是把程序按照模块拆分成各个相对独立部分在程序运行时才能将它们链接在一起形成一个完整的程序而不是像静态链接一样把所有程序模块都链接成一个单独的可执行文件。动态链接其实远比静态链接要常用的多。比如我们看下面code这个可执行程序依赖的动态库会发现用到了一个c动态链接库ldd命令用于打印程序或库文件所依赖的共享库列表[wxwVM-0-15-centos lesson8]$ ldd code linux-vdso.so.1 (0x00007ffec26d6000) libc.so.6 /lib64/libc.so.6 (0x00007f2a80e72000) /lib64/ld-linux-x86-64.so.2 (0x00007f2a81240000)这里涉及到一个重要的概念库我们的c程序中并没有定义“printf”的函数实现且在预编译中包含的“stdio.h”中也只有该函数的声明而没有定义函数的实现那么是在哪里实现“printf”函数的呢最后的答案是系统把这些函数实现都被放到名为libc.so.6的库文件中了在没有特别指定时gcc会到系统默认的搜索路径“/usr/lib”下进行查找也就是链接到libc.so.6库函数中去这样就能实现函数“printf”了而这也是链接的作用3.4 静态库和动态库静态库是指编译链接时把库文件的代码全部加入到可执行文件中因此生成的文件较大但在运行时也就不需要库文件了。其后缀一般名为“.a”动态库与之相反在编译链接时并没有把库文件的代码加入到可执行文件中而是在程序执行时由运行时链接文件加载库这样可以节省系统的开销。动态库一般后缀名为“.so”如前面所述的libc.so.6就是动态库。gcc在编译时默认使用动态库。完成链接后gcc就可以生成可执行文件。gcc默认生成的二进制程序是动态链接的这个可以通过file命令验证。Linux下动态库后缀是.so静态库后缀是.aWindows下动态库后缀是.dll静态库后缀是.lib一般云服务器是没有安装C/C的静态库的可以采用下面方法安装yum install glibc-static libstdc-static -ygcc code.c -o code-static -static可以看到静态链接比动态链接大的多动态库在内存中只出现一份sudo提权指令可以看到sudo时会报错所以我们要在root权限下打开/etc/sudoers配置文件vim /etc/sudoers在配置文件的100行左右找到下图所示的在root后面添加当前自己用户的名称4.自动化构建-make/Makefile会不会写makefile从侧面说明了一个人是否具备完成大型工程的能力一个工程的源文件不计其数其安类型、功能、模块分别放在若干目录中makefile定义了一系列的规则来指定那些文件需要先编译那些文件需要后编译那些文件需要重新编译甚至进行更复杂的功能操作makefile带来的好处就是——“自动化编译”一旦写好只需要一个make指令这个工程完全自动编译极大的提高了软件开发的效率。make是一个命令工具是一个解释makefile中指令的命令工具一般来说大多数的IDE都有这个命令比如Delphi的makeVisual C的nmakeLinux下GNU的make。可见makefile都成为了一种在工程方面的编译方法。make是一个命令makefile是一个文件两个搭配使用完成项目自动化构建。4.1基本使用写了一个.c文件并且创建了Makefile文件用vim打开Makefile在命令行中执行make可见生成了code文件依赖关系上面文件code依赖code.c依赖方法gcc -o code code.c就是对应的依赖关系项目清理像clean这种没有被第一个目标文件直接或间接关联那么它后面所定义的命令将不会被自动执行不过我们可以显示要make执行即命令——“make clean”以此来清除所有的目标文件以便重新编译。一般我们这种clean的目标文件我们将它设置为伪目标用 .PHONY 修饰伪目标的特性是总被执行。make clean后code文件删除为什么这里make只能运行一次呢因为已经编译好了节省编译时间。Makefile1 code:code.c 2 gcc -o code code.c 3 4 .PHONY:clean 5 clean: 6 rm -f code 7make命令扫描Makefile文件的时候从上到下扫描默认形成第一个目标文件。我们把Makefile中的顺序调换一下1 clean: 2 rm -f code 3 4 .PHONY:code 5 code:code.c 6 gcc -o code code.c这个时候make code就可以执行很多次了为什么会这样呢因为用 .PHONY 修饰伪目标的特性是总被执行对应的依赖方法和依赖关系。那么什么叫不被执行呢默认的老代码不做重新编译make怎么知道现在的.c和可执行文件的新旧问题呢我们stat一下code.c文件可以看到他有Access、Modify、Change三种时间文件 内容 属性Modify内容变更时间更新Change属性变更时间更新Access常指的是文件最后一次被访问的时间。在早期的Linux版本中每当文件被访问时其atime都会更新。但是这种机制会导致大量的IO操作。我们打开文件在文件中加入内容可以看到三个时间都修改了因为我修改了内容导致文件的大小发生了改变属性也被修改了我将文件所有者的x权限加上发行Change时间修改但是前两个时间没有变我们查看这个文件发现Access时间也修改了但是过于高频的更新Access时间会出现很多的IO操作所有Linux现在查看若干次文件才会累计更新一次Access。Linux 默认采用relatime挂载策略而非 “累计次数”仅当文件访问时间atime比上次更新时间晚超过一定阈值默认 30 秒时才会更新 atime目的是减少 IO 开销。回到上面的问题因为.PHONY:让make忽略源文件和可执行文件的Modify时间对比所有可以再次执行。补充touch指令更改文件的时间。4.2推导过程1 code:code.o 2 gcc code.o -o code 3 code.o:code.s 4 gcc -c code.s -o code.o 5 code.s:code.i 6 gcc -S code.i -o code.s 7 code.i:code.c 8 gcc -E code.c -o code.i 9 10 .PHONY:clean 11 clean: 12 rm -f *.i *.s *.o code依赖方法依次入栈出栈完成编译make是如何工作的在默认的方式下我们只输入make命令make会在当前目录下找名字叫“Makefile”或“makefile”的文件如果找到它会找文件中的第一个目标文件target在上面的例子中他会找到code这个文件并把这个文件作为最终的目标文件。如果code文件不存在或者code所依赖后面的code.o文件的文件修改时间要比code这个文件新那么它会执行后面所定义的命令来生成code这个文件。通过文件时间戳判断是否重新构建如果code所依赖的code.o文件不存在那么make会在当前文件中找目标为code.o文件的依赖性如果找到再根据规则生成code.o文件在寻找的过程中如果出现错误比如说最后依赖的文件找不到了make会直接退出并且报错。make只管文件的依赖性4.3扩展语法1 BINcode #定义变量 2 3 .PHONY:test 4 test: 5 echo $(BIN) #引用这个变量echo打印make之后会回显 echo code这句指令在echo前面加就不会回显了1 BINcode #定义变量 2 CCgcc 3 SRCcode.c 4 FLAGS-o 5 RMrm -f 6 相当于 7 $(BIN):$(SRC) #code:code.c 8 $(CC) $(SRC) $(FLAGS) $(BIN) # gcc code.c -o code 9 10 .PHONY: 11 clean: 12 S(RM) $(BIN) #rm -f code 13 14 .PHONY:test 15 test: 16 echo $(BIN) //不会回显命令 17 echo $(CC) 18 echo $(SRC) 19 echo $(FLAGS) 20 echo $(RM)1 BINcode #定义变量 2 CCgcc 3 SRCcode.c 4 FLAGS-o 5 RMrm -f 6 7 $(BIN):$(SRC) 8 $(CC) $(FLAGS) $ $^ #$代表目标文件名$^代表依赖文件列表 9 echo linking ... $^ to $ 10 11 .PHONY: 12 clean: 13 $(RM) $(BIN) 14 echo remove ... $(BIN)上述写的是面对一个文件进行编译链接如果我们有多个文件要编译链接该如何解决呢1 BINcode #定义变量 2 CCgcc 3 SRCcode.c 4 OBJcode.o 5 FLAGS-o 6 CFLAGS-c 7 RMrm -f 8 9 $(BIN):$(OBJ) 10 $(CC) $(FLAGS) $ $^ #$代表目标文件名$^代表依赖文件列表 11 echo linking ... $^ to $ 12 13 %.o:%.c #相当于通配符把当前路径下所有的.o/.c依次展开 14 $(CC) $(CFLAGS) $ #gcc -c code1.c 15 echo compling ... $ to $ #这$是目标文件这里是.o文件上面是BIN文件 16 17 .PHONY: 18 clean: 19 $(RM) $(BIN) $(OBJ) 20 echo remove ... $(BIN)1 BINcode #定义变量 2 CCgcc 3 #SRC$(shell ls *.c) 4 SRC$(wildcard *.c) #显示当前目录下所有的.c文件名 5 OBJ$(SRC:.c.o) #SRC内部的文件名.c-文件名.o 6 FLAGS-o 7 CFLAGS-c 8 RMrm -f 9 10 $(BIN):$(OBJ) 11 $(CC) $(FLAGS) $ $^ #$代表目标文件名$^代表依赖文件列表 12 echo linking ... $^ to $ 13 14 %.o:%.c #相当于通配符把当前路径下所有的.o/.c依次展开 15 $(CC) $(CFLAGS) $ #gcc -c code1.c 16 echo compling ... $ to $ 17 18 .PHONY: 19 clean: 20 $(RM) $(BIN) $(OBJ) 21 echo remove ... $(BIN)5.Linux第一个系统程序——进度条5.1 回车与换行回车就是回到开头\r换行(\n)就是换到下一行在C语言中\r 和 \n统一成了 \n。5.2 行缓冲区#includestdio.h 2 3 int main() 4 { 5 printf(hello world!\n); 6 sleep(3); 7 return 0; 8 }运行这个代码我们发现了先打印了hello world再休眠3秒屏幕录制 2026-01-10 165437将代码修改把\n去掉1 #includestdio.h 2 3 int main() 4 { 5 printf(hello world!); 6 sleep(3); 7 return 0; 8 }这个时候我们发现是先休眠三秒再打印了hello world为什么会这样呢去掉\nC 语言中 “行缓冲区标准输出缓存” 的机制导致的核心是printf的输出内容默认会先存到 “缓冲区”而非直接显示到屏幕触发 “缓冲区刷新” 的条件不同就会出现两种不同现象第一个代码带了\n是换行符它会触发 “行缓冲刷新”执行printf时内容先写入缓冲区遇到\n缓冲区会立即把内容输出到屏幕打印出hello bite!之后执行sleep(3)程序休眠 3 秒再结束。第二个代码不带\n先休眠再打印printf(hello bite!);没有\n且程序没触发其他 “缓冲区刷新条件”执行printf时内容只写入了缓冲区没输出到屏幕直接执行sleep(3)程序先休眠 3 秒当main函数结束return 0时程序会自动刷新缓冲区此时才把内容输出到屏幕打印出hello bite!。1 #includestdio.h 2 3 int main() 4 { 5 printf(hello world!); 6 fflush(stdout); 7 sleep(3); 8 return 0; 9 }fflush用来刷新缓冲区这个时候就发现和第一个代码效果一样。5.3 倒计时程序1 #includestdio.h 2 #includeunistd.h 3 4 int main() 5 { 6 int i 10; 7 while(i 0) 8 { 9 printf(%-2d\r,i); //左对齐\r回车 10 fflush(stdout); 11 sleep(1); 12 i--; 13 } 14 printf(\n); 15 return 0; 16 }5.4进度条代码process.h1 #pragma once 2 3 #includestdio.h 4 #includestring.h 5 #includeunistd.h 6 void process_v1(); 7 ~processbar.c1 #includeprocess.h 2 3 #define NUM 101 4 #define STYLE # 5 6 void process_v1() 7 { 8 char buffer[NUM]; 9 memset(buffer, 0, sizeof(buffer)); 10 const char* lable |/-\\; 11 int len strlen(lable); 12 int cnt 0; 13 while(cnt 100) 14 { 15 printf([%-100s][%d%%][%c]\r,buffer,cnt,lable[cnt%len]); //%% 输出% 或者 \% 16 buffer[cnt] STYLE; 17 fflush(stdout); 18 cnt; 19 usleep(50000); 20 } 21 printf(\n); 22 }Makefile1 SRC$(wildcard *.c) 2 OBJ$(SRC:.c.o) 3 BINprocessbar 4 5 $(BIN):$(OBJ) 6 gcc -o $ $^ 7 %.o:%.c 8 gcc -c $ 9 10 .PHONY: 11 clean: 12 rm -f $(BIN) $(OBJ)这个进度条代码是简易的进度条代码可是这个进度条没办法使用下面这个进度条代码可以模拟下载时的进度条的代码main.c这个DownLoad函数current来定义当前下载的量FlushProcess只为进度条的显示1 #includeprocess.h 2 3 #define total 1024.0 4 #define speed 1.0 5 6 void DownLoad() 7 { 8 double current 0; 9 while(current total) 10 { 11 FlushProcess(total,current); 12 usleep(3000);//充当下载数据 13 current speed; 14 } 15 printf(\ndownload %.2lfMB Done\n,current); 16 } 17 18 int main() 19 { 20 DownLoad(); 21 DownLoad(); 22 23 return 0; 24 } 25processbar.c这个函数只为了进度条的显示1 #includeprocess.h 2 3 #define NUM 101 4 #define STYLE # 5 6 void FlushProcess(double total,double current) 7 { 8 char buffer[NUM]; 9 memset(buffer, 0, sizeof(buffer)); 10 const char* lable |/-\\; 11 int len strlen(lable); 12 13 static int cnt 0; 14 //这个函数不需要自己循环,当前的进度对应的符号取整。 15 int num (int)(current*100/total); 16 int i 0; 17 for(; inum; i) 18 { 19 buffer[i] STYLE; 20 } 21 double rate current/total; 22 cnt % len; 23 printf([%-100s][%.2lf%%][%c]\r,buffer,rate*100,lable[cnt]); 24 cnt; 25 fflush(stdout); 26 }6.版本控制器Git我们在编写各种文件时为了防止文档丢失、更改失误失误后能恢复到原来的版本不得不复制出一个副本比如“报告-v1”“报告-v2”“报告-确定版”……每个版本有各自的内容但最终会只有一份报告需要被我们使用。但在此之前的工作都需要这些不同版本的报告于是每次都是复制粘贴副本产出的文件就越来越多了文件多不是问题问题是随着版本数量的不断增多这些版本各自修改的内容会忘记。文档如此我们写的项目代码也是如此。6.1 版本控制器为了能够更方便我们管理这些不同版本的文件便有了版本控制器。所谓版本控制器就是能让你了解一个文件的历史以及它的发展过程的系统。通俗的讲就是一个可以记录工程的每一次改动和版本迭代的一个管理系统同时也方便多人协调作业。目前最主流的版本控制器就是Git。Git可以控制电脑上所有格式文件例如doc、excel、dwg、dgn、rvt等等。对于我们开发人员来说Git最重要的就是可以帮助我们管理软件开发项目中的源代码文件同⽣活中的许多伟⼤事物⼀样Git 诞⽣于⼀个极富纷争⼤举创新的年代。Linux 内核开源项⽬有着为数众多的参与者。 绝⼤多数的 Linux 内核维护⼯作都花在了提交补丁和保存归档的繁琐事务上1991−2002年间。 到 2002 年整个项⽬组开始启⽤⼀个专有的分布式版本控制系统 BitKeeper 来管理和维护代码。到了 2005 年开发 BitKeeper 的商业公司同 Linux 内核开源社区的合作关系结束他们收回了 Linux内核社区免费使⽤ BitKeeper 的权⼒。 这就迫使 Linux 开源社区特别是 Linux 的缔造者 LinusTorvalds基于使⽤ BitKeeper 时的经验教训开发出⾃⼰的版本系统。 他们对新的系统制订了若⼲⽬标同⽣活中的许多伟⼤事物⼀样Git 诞⽣于⼀个极富纷争⼤举创新的年代。Linux 内核开源项⽬有着为数众多的参与者。 绝⼤多数的 Linux 内核维护⼯作都花在了提交补丁和保存归档的繁琐事务上1991−2002年间。 到 2002 年整个项⽬组开始启⽤⼀个专有的分布式版本控制系统 BitKeeper 来管理和维护代码。到了 2005 年开发 BitKeeper 的商业公司同 Linux 内核开源社区的合作关系结束他们收回了 Linux内核社区免费使⽤ BitKeeper 的权⼒。 这就迫使 Linux 开源社区特别是 Linux 的缔造者 LinusTorvalds基于使⽤ BitKeeper 时的经验教训开发出⾃⼰的版本系统。 他们对新的系统制订了若⼲⽬标同⽣活中的许多伟⼤事物⼀样Git 诞⽣于⼀个极富纷争⼤举创新的年代。Linux 内核开源项⽬有着为数众多的参与者。 绝⼤多数的 Linux 内核维护⼯作都花在了提交补丁和保存归档的繁琐事务上1991−2002年间。 到 2002 年整个项⽬组开始启⽤⼀个专有的分布式版本控制系统 BitKeeper 来管理和维护代码。到了 2005 年开发 BitKeeper 的商业公司同 Linux 内核开源社区的合作关系结束他们收回了 Linux内核社区免费使⽤ BitKeeper 的权⼒。 这就迫使 Linux 开源社区特别是 Linux 的缔造者 LinusTorvalds基于使⽤ BitKeeper 时的经验教训开发出⾃⼰的版本系统。 他们对新的系统制订了若⼲⽬标6.2 git简史同⽣活中的许多伟⼤事物⼀样Git 诞⽣于⼀个极富纷争⼤举创新的年代。Linux 内核开源项⽬有着为数众多的参与者。 绝⼤多数的 Linux 内核维护⼯作都花在了提交补丁和保存归档的繁琐事务上1991−2002年间。 到 2002 年整个项⽬组开始启⽤⼀个专有的分布式版本控制系统 BitKeeper 来管理和维护代码。到了 2005 年开发 BitKeeper 的商业公司同 Linux 内核开源社区的合作关系结束他们收回了 Linux内核社区免费使⽤ BitKeeper 的权⼒。 这就迫使 Linux 开源社区特别是 Linux 的缔造者 Linus Torvalds基于使⽤ BitKeeper 时的经验教训开发出⾃⼰的版本系统。 他们对新的系统制订了若⼲⽬标•速度•简单的设计•对⾮线性开发模式的强⼒⽀持允许成千上万个并⾏开发的分⽀•完全分布式•有能⼒⾼效管理类似 Linux 内核⼀样的超⼤规模项⽬速度和数据量⾃诞⽣于 2005 年以来Git ⽇臻成熟完善在⾼度易⽤的同时仍然保留着初期设定的⽬标。 它的速度⻜快极其适合管理⼤项⽬有着令⼈难以置信的⾮线性分⽀管理系统6.3 git的使用先创建一个git仓库git在提交时只会提交变化的部分Git 提交基于快照机制完整保存当前工作区状态而非仅提交变化部分SVN 为增量提交只是内部通过哈希和指针优化存储外观上呈现 “增量效果”Git 可管理任意格式文件如文档、图片等并非 “只管理源文件”.gitignore仅用于忽略不需要跟踪的文件不限制文件类型。下面是完整的代码提交需要注意的是第一次提交git的时候要设置姓名和邮箱地址git版本管理只进行管理源文件.gitignore需要忽略特定后缀的文件列表7. 调试器 gdb/cgdb的使用测试代码如下#include stdio.h int Sum(int s, int e) { int result 0; for(int i s; i e; i) { result i; } return result; } int main() { int start 1; int end 100; printf(I will begin\n); int n Sum(start, end); printf(running done, result is: [%d-%d]%d\n, start, end, n); return 0; }程序的发布方式有两种debug和release模式Linux gcc/g 编译出来的二进制程序默认是release模式。要使用gdb调试必须在源代码生成二进制程序的时候加上-g选项如果没有添加程序无法被编译。Makefile 1 test:test.c 2 gcc -o $ $^ -stdc99 -g //标准为c99-g生成debug模式 3 4 .PHONY:clean 5 clean: 6 rm -f test ~7.1 常见使用开始gdb 文件退出ctrld 或 quit调试命令7.1.1 watch执行时监视一个表达式的值。如果监视的表达式在程序运行期间的值发生了变化GDB会暂停程序的执行并通知使用者。如果有一些变量不应该修改但是你怀疑它修改导致出了问题你可以watch它如果变化了就会通知你。监视变量的变化7.1.2 set var 确定问题原因更改一下标志位假设我们想得到-result1 #include stdio.h 2 int flag 0; //故意错误 3 int Sum(int s, int e) 4 { 5 int result 0; 6 for(int i s; i e; i) 7 { 8 result i; 9 } 10 return result*flag; 11 } 12 int main() 13 { 14 int start 1; 15 int end 100; 16 printf(I will begin\n); 17 int n Sum(start, end); 18 printf(running done, result is: [%d-%d]%d\n, start, end, n); 19 return 0; 20 }能通过set var来更改当前变量的值。7.1.3给已经存在的断点新增条件条件断点添加常见两种方式1.新增 2.给已有断点追加新增b 行号/文件名:行号/函数名 if i30 条件给已有断点追加condition 2 i30 其中2是已有断点编号没有ifcgdb分屏操作ESC进入代码屏i 回到gdb屏。