【Linux】环境基础开发工具使用

在这里插入图片描述

欢迎来到Cefler的博客😁
🕌博客主页:那个传说中的man的主页
🏠个人专栏:题目解析
🌎推荐文章:题目大解析3

在这里插入图片描述


👉🏻什么是软件包?

概念

软件包是一种用于组织和分发软件的方式。它是一个包含了相关文件和元数据的压缩文件,通常以特定的格式打包。软件包可以包含可执行文件库文件配置文件文档等。

软件包的主要目的是简化软件的安装、更新和卸载过程。通过将相关文件和依赖项打包在一起,软件包可以提供一种方便的方式来分发和管理软件。用户可以通过安装软件包来将软件部署到他们的计算机上,并且可以轻松地更新或卸载软件包。

软件包管理系统是用于管理软件包的工具集合。它提供了一组命令和功能,用于安装、更新、卸载和查询软>件包。常见的软件包管理系统包括APT(Advanced Package Tool)用于Debian和Ubuntu系统、YUM(Yellowdog Updater, Modified)用于CentOS和Fedora系统、Homebrew用于macOS系统等。

通过使用软件包,开发者可以更方便地分发他们的软件,并且用户可以更轻松地安装和管理软件。软件包的使用也有助于确保软件的版本一致性和依赖项的满足性。

  • 在Linux下安装软件, 一个通常的办法是下载到程序的源代码, 并进行编译, 得到可执行程序.
  • 但是这样太麻烦了, 于是有些人把一些常用的软件提前编译好, 做成软件包(可以理解成windows上的安
    装程序)放在一个服务器上, 通过包管理器可以很方便的获取到这个编译好的软件包, 直接进行安装.
  • 软件包和软件包管理器, 就好比 “App” 和 “应用商店” 这样的关系.
  • yum(Yellow dog Updater, Modified)是Linux下非常常用的一种包管理器. 主要应用在Fedora, RedHat,
    Centos等发行版上

🗣注意:所谓安装,本质就是将可执行程序移动到某个目录下

在软件商店中,那些还没被下载的软件都放在哪里呢? 🤔

软件商店中的软件通常存储在软件仓库或应用商店的服务器上。
当用户在软件商店中选择下载或安装应用程序时,软件商店客户端会与服务器进行通信,下载相应的软件包到用户的设备上。然后,用户可以通过安装软件包来将应用程序部署到他们的设备上。

需要注意的是,不同的操作系统和平台有不同的软件商店和软件仓库。例如,Android设备使用Google Play商店,iOS设备使用App Store,Linux系统使用各种发行版的软件仓库,而Windows系统使用Microsoft Store等。每个软件商店都有自己的服务器和软件包管理系统,用于存储和分发软件。

👉🏻yum

📜yum概念
yum是一种在基于RPM的Linux发行版上用于软件包管理的工具。它是Yellowdog Updater, Modified的缩写,最初是为了简化Red Hat Linux系统上的软件包管理而开发的。

yum的主要功能是从软件源(repositories)中下载、安装、更新和卸载软件包。它可以自动解决软件包之间的依赖关系,确保所需的依赖项也被正确安装。这使得安装和更新软件包变得更加简单和高效。

以下是yum的一些关键特点和用法:

  1. 软件源(repositories):yum使用软件源来获取软件包。软件源是存储软件包的服务器,可以是官方的发行版仓库或第三方仓库。yum可以配置多个软件源,以便从不同的源获取软件包。

  2. 安装软件包:使用yum install <package_name>命令来安装指定的软件包。yum会自动解决依赖关系,并下载和安装所需的软件包及其依赖项。

  3. 更新软件包:使用yum update命令来更新系统中已安装的软件包到最新版本。yum会检查软件源中是否有可用的更新,并自动下载和安装更新的软件包。

  4. 卸载软件包:使用yum remove <package_name>命令来卸载指定的软件包。yum会删除软件包及其相关的文件和依赖项。

  5. 搜索软件包:使用yum search 命令来搜索包含指定关键字的软件包。yum会列出匹配的软件包及其描述信息。

  6. 列出已安装的软件包:使用yum list命令来列出系统中已安装的所有软件包。可以使用yum list installed命令来只列出已安装的软件包。

  7. 清理缓存:使用yum clean 命令来清理yum缓存。常见的选项包括all(清理所有缓存)和metadata(清理仅限元数据缓存)。


📜以下是yum的一些常用选项

  • install:安装软件包。使用yum install <package_name>命令来安装指定的软件包。

install -y 可以取消交互询问是否下载

  • update:更新软件包。使用yum update命令来更新系统中已安装的软件包到最新版本。

  • remove:卸载软件包。使用yum remove <package_name>命令来卸载指定的软件包。

  • search:搜索软件包。使用yum search 命令来搜索包含指定关键字的软件包。

  • info:获取软件包信息。使用yum info <package_name>命令来获取指定软件包的详细信息,包括版本、大小、依赖关系等。

  • list:列出已安装的软件包。使用yum list命令来列出系统中已安装的所有软件包。

  • upgrade:升级系统。使用yum upgrade命令来升级系统中的所有软件包到最新版本。

  • clean:清理缓存。使用yum clean 命令来清理yum缓存,常见的选项包括all(清理所有缓存)和metadata(清理仅限元数据缓存)。

  • check-update:检查可用的更新。使用yum check-update命令来检查系统中是否有可用的软件包更新。

这些是一些常用的yum选项,可以根据需要使用不同的选项来执行相应的操作。可以通过yum --help命令来获取更详细的帮助信息和其他可用选项。

centos7 yum 换源

有时候,我们需要给Yum换源。
🤖有时候需要给yum更换软件源的原因有以下几点

  1. 加快下载速度:默认的软件源可能位于国外服务器,如果您的网络连接到该服务器的速度较慢,那么下载软件包可能会很慢。通过更换为国内的镜像源,可以加快下载速度,提高软件包的获取效率。

  2. 提高稳定性和可靠性:默认的软件源可能会因为服务器故障、网络问题或其他原因而不可用。通过选择可靠的镜像源,可以提高软件包的可用性和稳定性,确保您能够顺利地下载和安装软件包。

  3. 获取特定版本的软件包:默认的软件源可能只提供最新版本的软件包,而您可能需要获取特定版本的软件包。通过更换为其他源,您可以找到并下载所需版本的软件包。

  4. 解决依赖关系问题:有时候默认的软件源可能无法满足软件包之间的依赖关系,导致安装或更新软件包时出现问题。通过更换为其他源,您可能能够找到满足依赖关系的软件包,从而解决依赖问题。


🍙如何换源?
要在CentOS 7上更换yum软件源,可以按照以下步骤进行操作:

  1. 打开终端或命令行界面。

  2. 以root用户或具有sudo权限的用户身份登录。

  3. 备份当前的yum源配置文件,以防需要恢复:

   cp /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.bak
  1. 使用文本编辑器(如vi或nano)打开yum源配置文件:
   vi /etc/yum.repos.d/CentOS-Base.repo
  1. 在打开的文件中,注释掉(在行首添加#)所有以baseurl开头的行和以mirrorlist开头的行。

  2. 在文件中找到以baseurl开头的行和以mirrorlist开头的行,将其替换为新的yum源地址。可以从国内的镜像站点或其他可靠的源获取新的yum源地址。以下是一些常用的国内yum源地址:

  • 阿里云:http://mirrors.aliyun.com/centos/$ releasever/os/$basearch/
  • 清华大学:https://mirrors.tuna.tsinghua.edu.cn/centos/$ releasever/os/$basearch/
  • 163镜像站:http://mirrors.163.com/centos/$ releasever/os/$basearch/

例如,将baseurl行替换为阿里云的yum源地址:

   baseurl=http://mirrors.aliyun.com/centos/$releasever/os/$basearch/
  1. 保存并关闭文件。

  2. 清理yum缓存,以便下次使用新的yum源:

   yum clean all
  1. 生成新的yum缓存:
   yum makecache

👉🏻Vim编辑器

编辑器和IDE概念

🍏编辑器
编辑器是一种专门用于编辑文本文件的工具。它通常提供基本的文本编辑功能,如插入删除复制粘贴等。编辑器可以用于编辑各种类型的文本文件,包括代码文件、配置文件和普通文本文件等。编辑器的设计目标是提供简洁、轻量级和高度可定制的编辑环境。一些常见的文本编辑器包括Vim、Emacs、Sublime Text和Notepad++等。
🍏IDE
IDE是一个综合性的开发工具,旨在为开发人员提供一个集成的开发环境。IDE不仅提供了文本编辑功能,还集成了许多其他开发相关的功能,如调试器、编译器、自动完成、版本控制、项目管理和构建工具等。IDE通过将这些功能整合到一个统一的界面中,提供了更强大、高效和便捷的开发体验。常见的IDE包括Visual Studio、Eclipse、IntelliJ IDEA和PyCharm等。

vim基本操作

进入vim,在系统提示符号输入vim及文件名称后,就进入vim全屏幕编辑画面:

  • vim test.c
    不过有一点要特别注意,就是你进入vim之后,是处于[正常模式],你要切换到[插入模式]才能够输入文字。

vim 文件名 +n :可指定打开文件后,光标位于第n行

🦪 [正常模式]切换至[插入模式]

  • 输入a
  • 输入i
  • 输入o
    [插入模式]切换至[正常模式]
    目前处于[插入模式],就只能一直输入文字,如果发现输错了字,想用光标键往回移动,将该字删除,可
    以先按一下「ESC」键转到[正常模式]再删除文字。当然,也可以直接删除。
    [正常模式]切换至[末行模式]
  • 「shift + ;」, 其实就是输入「:」
    退出vim及保存文件,在[正常模式]下,按一下「:」冒号键进入「Last line mode」,例如:
  • : w (保存当前文件)
  • : wq (输入「wq」,存盘并退出vim)
  • : q! (输入q!,不存盘强制退出vim)

正常模式操作

  • 插入模式
    按「i」切换进入插入模式「insert mode」,按“i”进入插入模式后是从光标当前位置开始输入文件;
    按「a」进入插入模式后,是从目前光标所在位置的下一个位置开始输入文字;
    按「o」进入插入模式后,是插入新的一行,从行首开始输入文字。
  • 从插入模式切换为命令模式
    按「ESC」键。
  • 移动光标
    vim可以直接用键盘上的光标来上下左右移动,但正规的vim是用小写英文字母「h」、「j」、「k」、「l」,分别控制光标移一格
    按「G」:移动到文章的最后
    按「 $ 」:移动到光标所在行的“行尾”
    按「^」:移动到光标所在行的“行首”
    按「w」:光标跳到下个字(单词)的开头
    按「e」:光标跳到下个字(单词)的字尾
    按「b」:光标回到上个字(单词)的开头
    按「#l」:光标移到该行的第#个位置,如:5l,56l
    按[gg]:进入到文本开始
    按[shift+g]:进入文本末端

n+shift+g == G:定位到代码的任意一行|最后一行

按「ctrl」+「b」:屏幕往“后”移动一页
按「ctrl」+「f」:屏幕往“前”移动一页
按「ctrl」+「u」:屏幕往“后”移动半页
按「ctrl」+「d」:屏幕往“前”移动半页

  • 删除文字
    「x」:每按一次,删除光标所在位置的一个字符
    「#x」:例如,「6x」表示删除光标所在位置的“后面(包含自己在内)”6个字符
    「X」== shift+x:大写的X,每按一次,删除光标所在位置的“前面”一个字符
    「#X」:例如,「20X」表示删除光标所在位置的“前面”20个字符
    「dd」:删除(剪切)光标所在行
    「#dd」:从光标所在行开始删除#行
  • 复制
    「yw」:将光标所在之处到字尾的字符复制到缓冲区中。
    「#yw」:复制#个字到缓冲区
    「yy」:复制光标所在行到缓冲区。
    「#yy」:例如,「6yy」表示拷贝从光标所在的该行“往下数”6行文字。
    「p」:将缓冲区内的字符贴到光标所在位置。注意:所有与“y”有关的复制命令都必须与“p”配合才能完
    成复制与粘贴功能。
  • 替换
    「r」:替换光标所在处的字符。
    「n+r」:替换光标所在处后的多个字符。
    「R」== shift +r:进入替换模式,替换光标所到之处的字符,直到按下「ESC」键为止。
  • 撤销上一次操作
    「u」:如果您误执行一个命令,可以马上按下「u」,回到上一个操作。按多次“u”可以执行多次回
    复。
    「ctrl + r」: 撤销的恢复
  • 更改
    「cw」:更改光标所在处的字到字尾处
    「c#w」:例如,「c3w」表示更改3个字
  • 跳至指定的行
    「ctrl」+「g」列出光标所在行的行号。
    「#G」:例如,「15G」,表示移动光标至文章的第15行行首。

❄️

  • gg:定位到代码第一行
  • n+shift+g == G:定位到代码的任意一行|最后一行
  • shift+4 == ¥ :特定一行的结尾
  • shift+6 == ^ :特定一行的开始
  • shift + ` ==~:更改大小写
  • shift +3 == # :高亮显示所有相关字的字符,n可同单词之间跳转

vim末行模式操作

  • 列出行号
    「set nu」: 输入「set nu」后,会在文件中的每一行前面列出行号。

  • 跳到文件中的某一行
    「#」:「#」号表示一个数字,在冒号后输入一个数字,再按回车键就会跳到该行了,如输入数字15,
    再回车,就会跳到文章的第15行。

  • 查找字符
    「/关键字」: 先按「/」键,再输入您想寻找的字符,如果第一次找的关键字不是您想要的,可以一直按
    「n」会往后寻找到您要的关键字为止。
    「?关键字」:先按「?」键,再输入您想寻找的字符,如果第一次找的关键字不是您想要的,可以一直
    按「n」会往前寻找到您要的关键字为止。
    问题:/ 和 ?查找有和区别?操作实验一下

  • 保存文件
    「w」: 在冒号输入字母「w」就可以将文件保存起来

  • 离开vim
    「q」:按「q」就是退出,如果无法离开vim,可以在「q」后跟一个「!」强制离开vim。
    「wq」:一般建议离开时,搭配「w」一起使用,这样在退出的时候还可以保存文件。

  • set /nu/nonu :显示行号/不显示行号

  • :!cmd 直接在不退出vim的前提下,进行查看,编译、运行的操作

  • /搜索关键字,n表示下一个

👉🏻Vim中的注释

如何批量注释?
步骤如下:
1.ctrl+v:进入视图模式
2.运用j、k:划分待会需要注释的区间
3.shift+i == I: 进入insert模式,此时光标会移动到区间首行
4.//:对当前行进行注释
5.Esc:退出insert模式后,刚刚划分的注释区间将被注释

批量化删除注释
1.ctrl+v:进入视图模式
2.运用j、k:划分待会需要删除注释的区间
3.d:即可删除
注:可以运用h、l,更改删除的宽度

n+j/k:可直接确定区分行数,就不用一个一个慢慢按了

👉🏻Vim支持多文件编辑

在底行模式下:

vs 文件名 //可打开/创建 文件
  • ctrl +ww:可在文件中切换光标

🗣注意光标所在处,即是当下所在编辑的文件

👉🏻在sudoers名单(信任名单)上添加用户

步骤如下:👇🏻
1.将账号切换成root账号
2.

vim /etc/sudoers  //打开sudoers文件

3.光标定位到100行左右,将以下命令yy复制粘贴,将root名改为所添加用户名,然后在底行模式下wq!,保存退出即可

 root    ALL=(ALL)       ALL

👉🏻Vim 的配置

在Linux下,Vim的配置是通过编辑位于✨用户主目录下.vimrc文件来实现的。该文件包含了各种设置和自定义命令,以满足个人偏好和需求。下面是一些常见的Vim配置选项:

  1. 设置行号
    在.vimrc中添加以下行,启用显示行号:

    set number
    
  2. 启用语法高亮
    语法高亮可以帮助您更清晰地看到代码结构。在.vimrc中添加以下行,启用语法高亮:

    syntax on
    
  3. 设置缩进
    缩进对于代码的可读性很重要。您可以在.vimrc中设置默认的缩进方式和缩进大小。例如,将缩进方式设置为空格,并将缩进大小设置为4个空格:

    set expandtab
    set tabstop=4
    
  4. 设置自动补全
    您可以启用自动补全功能,以便在输入代码时自动提供建议。例如,使用插件"vim-autocomplpop",在.vimrc中添加以下行:

    let g:acp_enableAtStartup = 1
    
  5. 设置主题
    您可以选择适合自己的Vim主题。首先,将主题文件(通常以.vim结尾)放入~/.vim/colors/目录中。然后,在.vimrc中添加以下行,设置使用的主题:

    colorscheme <theme_name>
    
  6. 自定义键映射
    您可以自定义按键映射,以便为常用操作创建快捷键。例如,将F2键映射为保存当前文件的快捷键,在.vimrc中添加以下行:

    nnoremap <F2> :w<CR>
    
  7. 安装插件管理器
    使用插件管理器可以方便地安装、更新和删除Vim插件。常见的插件管理器有Vundle、Pathogen和vim-plug等。

vim 一键配置写好的脚本

快速将vim打造成c++ IDE
前提先在yum下下好curl:

yum install curl

而后复制以下链接可下载脚本:

curl -sLf https://gitee.com/HGtz2222/VimForCpp/raw/master/install.sh -o ./install.sh && bash ./install.sh

下载成功后如下
在这里插入图片描述

👉🏻Linux编译器-gcc/g++使用

在linux编译下:

  • gcc:只能编译c文件
  • g++:可以编译c++文件,同时也可以编译c文件

centos7 yum下载g++

yum install gcc gcc-c++

背景介绍

当我们在Linux系统上编译和运行程序时,通常会经历以下几个步骤:预处理、编译、汇编、链接和执行。让我逐一解释这些步骤的含义。

  1. 预处理(Preprocessing):
    在预处理阶段,编译器会对源代码进行处理。它会处理以#开头的预处理指令(条件编译),并将宏定义替换、删除注释、包含其他文件等操作。预处理生成的结果称为预处理后的源代码。

  2. 编译(Compilation):
    编译阶段是将预处理后的源代码转换为汇编代码的过程。在这个阶段,编译器会将高级语言(如C或C++)的源代码翻译成低级的中间表示形式,即汇编代码。

  3. 汇编(Assembly):
    在汇编阶段,汇编器将汇编代码转换为机器码指令。汇编器会将每条汇编语句翻译成与特定硬件平台相关的二进制指令

  4. 链接(Linking):
    链接阶段是将多个目标文件(.o/.obj)和库文件合并成一个可执行文件的过程。在这个阶段,链接器会解析函数和变量之间的引用关系,并将它们连接起来,生成最终的可执行文件

  5. 执行(Execution):
    执行阶段是将生成的可执行文件加载到内存中,并按照程序的逻辑顺序执行。此时,计算机会按照指令一条一条地执行程序,完成所需的任务。

需要注意的是,这些步骤并不是严格线性的,而是在编译过程中交叉进行的。通常,编译器会自动处理这些步骤,我们只需要运行相应的编译命令即可。


gcc

🔥gcc常用选项

  1. -c: 编译源文件为目标文件而不进行链接
  2. -o filename: 将编译出来的目标文件放在指定的文件中
  3. -Wall: 显示所有警告信息
  4. -g: 在可执行文件中增加调试信息
  5. -O level: 优化等级,可取-O, -O1, -O2, -O3等
  6. -I dir: 指定头文件搜索路径
  7. -L dir: 指定库文件搜索路径
  8. -l library: 指定要链接的库名
  9. -D macro: 定义宏
  10. -U macro: 取消已定义的宏
  11. -E: 只运行预处理器
  12. -S: 只编译生成汇编代码
  13. -shared: 生成动态链接库
  14. -fPIC: 生成位置无关代码
  15. -pthread: 指定链接pthread库

静态库和动态库

在Linux中,静态库(Static Library)和动态库(Dynamic Library)是常用的代码重用方式。它们都是将一组函数符号打包成一个文件,供其他程序使用。

  1. 静态库:
    是c/c++或者第三方提供的所有方法的集合,以拷贝的方式,将所需的代码拷贝到可执行程序当中
    静态库是将一组函数和符号的实现编译成一个独立的可执行文件,当需要使用这些函数和符号时,链接器会将静态库的代码复制到最终的可执行文件中。优点是简单、方便,不依赖于外部环境;缺点是每个使用该库的可执行文件都会包含一份完整的库代码,导致可执行文件变大。

静态库的文件扩展名通常为.a(如libexample.a)。使用静态库的步骤如下:

  • 编译静态库:

    gcc -c library.c  // 编译源文件生成目标文件
    ar rcs libexample.a library.o  // 使用ar命令将目标文件打包成静态库
    
  • 编译可执行文件并链接静态库:

    gcc -o executable main.c -L/path/to/library -lexample  // 编译可执行文件并链接静态库
    

    这里-L选项指定了库文件的路径,-l选项指定了要链接的静态库。

  1. 动态库:
    是c/c++或者第三方提供的所有方法的集合,以动态链接的方式将所有程序链接起来,但其本质上也是拷贝,只是将要连接的库中的方法函数的地址拷贝到可执行程序的特定位置
    动态库是在程序运行时被加载到内存中的共享库,它可以被多个可执行文件共享使用。优点是节省内存空间,可执行文件较小;缺点是需要依赖于外部环境,如果系统中没有相应的动态库,程序将无法正常运行。

可执行文件与编译器中的链接器在还没加载到内存时,就会先建立联系,链接器会告诉可执行文件动态库,即共享库的位置,等到所有代码加载到内存当中时,要调用动态库中的某一方法,就会依据链接器给的地址寻到对应的动态库。
若共享库一旦被销毁,其共享的所有可执行文件都会受到影响

动态库的文件扩展名通常为.so(如libexample.so)。使用动态库的步骤如下:

  • 编译动态库:

    gcc -shared -o libexample.so library.c  // 编译源文件生成动态库
    
  • 编译可执行文件并链接动态库:

    gcc -o executable main.c -L/path/to/library -lexample  // 编译可执行文件并链接动态库
    

    这里-L选项指定了库文件的路径,-l选项指定了要链接的动态库。

除了上述方法,还可以使用dlopendlsym等函数动态加载和使用动态库。

总结来说,静态库适合于独立的、不需要频繁更新的代码,而动态库适合于共享的、可能需要更新的代码。
🗣:在Windows下,静态库一般表示为.dll,动态库表示为.lib

  • .i文件:只执行过预处理生成的文件
  • .s文件:经过预处理、汇编生成的文件(里面是汇编转成了二进制文件)
  • .o文件:目标文件,还未经过链接
    .s和.o文件都是可执行文件

静态链接和动态链接

在Linux系统中,静态链接和动态链接是用于将程序与库文件进行连接的两种方式。

静态链接是指在编译过程中,将程序所需要的库文件的代码复制到可执行文件中,形成一个独立的、完整的可执行文件。这意味着程序不再依赖于外部的库文件,可以在任何没有相关库文件的系统上运行。静态链接的优点是可移植性好不受外部环境的影响且执行速度较快缺点是可执行文件的体积较大,并且每个使用相同库的程序都会有一份该库的副本,造成资源浪费

动态链接是指在编译过程中,程序只包含对库文件的引用,而不将库文件的代码复制到可执行文件中。在程序运行时,系统会根据需要加载并链接相应的库文件。这样可以节省内存空间,并且当库文件更新时,所有使用该库的程序都能受益于更新。动态链接的优点是节省内存减小可执行文件的体积,并且方便库文件的更新和维护。缺点是可能会存在版本兼容性问题,如果系统上没有相应的库文件,则无法运行程序。

选择静态链接还是动态链接取决于具体情况。如果程序需要在多个系统上运行,或者需要独立部署,那么静态链接是一个较好的选择。如果程序依赖于一些常用的库文件,并且希望能够方便地更新和维护这些库文件,那么动态链接是一个更合适的选择。

  • gcc 在编译时默认使用动态库。完成了链接之后,gcc 就可以生成可执行文件
  • gcc默认生成的二进制程序,是动态链接的,这点可以通过 file 命令验证

所以在我们平常使用的开发环境中,其默认要为我们做的事需要有:

  • 下载开发环境include ,lib
  • 设置好合理的查找路径
  • 规定好我们形成的可执行程序的链接方式

linux下安装静态库

我们知道,linux下是默认带有动态库的,所以静态库需要我们自己去安装。
c静态库

yum install glibc-static

在这里插入图片描述
c++静态库

sudo yum install -y libstdc++-static

在这里插入图片描述

👉🏻Linux项目自动化构建工具-make/Makefile

概念

Make是一种被广泛使用的构建工具,主要用于自动化构建和管理软件项目。它通过读取Makefile文件中的规则和指令来执行编译链接测试等操作,从而简化了项目构建的过程

Makefile是Make工具所使用的配置文件,其中包含了一系列规则和指令,用于指导Make进行构建过程。Makefile采用了一种基于依赖关系的编程模型,通过定义目标(target)和依赖关系(dependencies)来描述源文件之间的关系,以及如何生成目标文件。

一个典型的Makefile文件包含了以下几个重要的部分:

  1. 变量定义:可以定义一些变量,用于存储编译选项、文件路径等信息,提高可维护性和灵活性。

  2. 规则定义:包括目标、依赖关系和执行命令。每个规则指定了生成某个目标文件所需的依赖文件,以及生成目标文件的命令。

  3. 默认规则:如果没有明确指定目标,默认规则会被执行。通常情况下,这个规则用来构建整个项目。

在Makefile中,目标和依赖关系之间通过规则连接起来。当我们运行make命令时,Make会检查目标文件和依赖文件的时间戳,确定哪些文件需要重新编译,然后按照依赖关系执行相应的命令,生成目标文件。

通过使用Make和Makefile,可以实现项目的自动化构建和管理,提高开发效率和可维护性。它是Linux开发中非常重要且常用的工具之一。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

依赖关系和依赖方法

依赖关系指的是源文件之间的相互关系,即一个文件是否依赖于其他文件。如果一个文件发生了变化,那么依赖于它的文件也需要重新编译。Makefile使用依赖关系来确保只有需要重新编译的文件才会被重新编译,从而提高编译效率。

依赖方法是指在Makefile中定义依赖关系的方式。常见的依赖方法有两种:

  1. 显式规则:使用显式规则可以明确指定目标文件所依赖的源文件和命令。格式如下:
target: dependencies
    command

其中,target 是要生成的目标文件,dependenciestarget 所依赖的源文件,command 是编译或链接命令。

例如,假设我们有一个名为 program 的目标文件,依赖于 file1.cfile2.c 两个源文件,可以使用以下规则:

program: file1.c file2.c
    gcc -o program file1.c file2.c
  1. 隐式规则:使用隐式规则可以根据文件的后缀名自动生成编译命令。Makefile会根据规则自动推导出依赖关系。例如,对于 .c 文件,Makefile会使用 gcc 命令进行编译。
program: file1.c file2.c

在这个例子中,Makefile会自动推导出编译命令:

gcc -o program file1.c
gcc -o program file2.c

通过定义正确的依赖关系和依赖方法,Makefile可以自动构建程序,并根据源文件的变化来判断是否需要重新编译。这样可以提高开发效率,并确保只有必要的文件被重新编译。

make编译与源文件变化的关系

我们知道一个文件有一个ACM属性
在这里插入图片描述
Modify的时间更改了,即源文件内容修改后,此时去make 编译,make才会允许再编译
而这里我们要引入一个新概念:.PHONY修饰的伪目标
伪目标不受任何情况拦截,其依赖方法总是被执行
在这里插入图片描述
这里被.PHONY修饰的clean命令,无论源文件是否有被修改,都可以执行clean命令

@修饰依赖方法

@修饰方法,会隐藏该方法的编译过程
在这里插入图片描述
在这里插入图片描述

makefile中的特殊字符——$

在Makefile中,$符号是用于引用变量的特殊字符。它可以用来表示已定义的变量的值函数的返回值以及其他一些特殊用途。

下面是一些常见的 $ 的用法:

  1. 引用变量:可以使用 $ 来引用已定义的变量。例如,如果有一个变量 SRC 定义为 source.c,可以通过 $SRC 来引用该变量的值。
SRC = source.c
$(CC) -c $(SRC)
  1. 特殊变量:Makefile中有一些特殊的预定义变量,如 $@$^ 等。前者代表是:左侧内容,后者是:右侧内容
program: source.c header.h
    $(CC) -o $@ $^
  1. 函数调用:可以使用 $()${} 来调用函数,并使用函数的返回值。Makefile提供了一些内置函数,如 $(shell)$(wildcard)$(subst) 等。可以根据需要使用相应的函数。
files = $(shell ls *.c)
objs = $(patsubst %.c, %.o, $(files))
  1. 命令替换:可以使用 $() 或 ````符号来执行命令,并将命令的输出作为变量的值。这在需要根据运行时的结果来设置变量时非常有用。
timestamp = $(shell date +%Y-%m-%d-%H:%M:%S)

请注意,$符号在Makefile中是特殊字符,如果想要表示 $ 字符本身,需要使用 $$ 进行转义。

LOG_FILE = $$(date +%Y-%m-%d).log

通过灵活地使用 $ 符号,可以更好地处理变量、函数和命令的值,并使Makefile更加强大和可扩展。

👉🏻输出缓冲区

🍋概念
在 Linux 系统中,输出缓冲区是一个临时的存储区域,用于暂时存储应用程序产生的输出,直到它们被发送到目标设备或显示到屏幕上。

输出缓冲区位于内核和实际设备之间。当应用程序产生输出时,数据会首先被写入输出缓冲区中,然后由内核负责将缓冲区中的内容发送到目标设备。这种缓冲机制可以提高输出的效率,避免频繁的系统调用和设备访问

Linux 系统中的输出缓冲区有多种类型,包括标准输出缓冲区(stdout)、标准错误缓冲区(stderr)以及文件和设备特定的缓冲区。其中,stdout 和 stderr 是常用的两个缓冲区。

默认情况下,stdout 是行缓冲的,也就是说当我们在应用程序中使用 printf 或 cout 等函数输出字符时,输出会被缓冲并在换行符出现时立即刷新,或者当输出缓冲区被填满时才触发刷新操作。例如:

printf("Hello, World!\n"); // \n刷新 stdout 缓冲区

而 stderr 是无缓冲的,它会直接将输出内容发送到目标设备,不进行缓冲。这样可以确保错误消息尽快显示,以便更及时地进行错误处理。

除了标准输出和标准错误外,应用程序还可以使用文件操作函数(如 fwrite、fprintf)来操作文件缓冲区。对于特定设备的输出,例如串口或网络套接字,也会有相应的缓冲机制。

需要注意的是,在某些情况下,当应用程序异常终止或程序结束时,输出缓冲区中的内容可能不会被刷新到目标设备上,导致部分输出丢失。为了避免这种情况,可以手动调用 fflush 函数来刷新输出缓冲区。

总结起来,输出缓冲区在 Linux 系统中起到了重要的作用,它提供了一个缓冲机制,可以提高输出效率并避免频繁的系统调用和设备访问。不同类型的输出缓冲区有不同的刷新机制,并且可以通过手动刷新来确保输出内容尽快显示。

打印倒计时

 #include <stdio.h>
  2 #include <unistd.h>
  3 int main()
  4 {
  5   int count = 9;
  6   while(count)
  7   {
  8     printf("%d\r",count);
  9     fflush(stdout);//刷新输出缓冲区
 10     count--;
 11     sleep(1);
 12                                                                                                               
 13   }
 14   return 0;
 15 }

实现一个进度条

main.c

 1 #include "process.h"                                                                                                                            "
  2 #define TARGET_SIZE 1024*1024 // 目标文件大小                                                                                                                              
  3 #define DSIZE 1024*10 //加载速度                                                                                                                              
  4 void download(callback_t cb)                                                                                                                              
  5 {                                                                                                                              
  6  int target = TARGET_SIZE;                                                                                                                              
  7  int total = 0;                                                                                                                              
  8                                                                                                                               
  9  while(total<=target)                                                                                                                              
 10  {                                                                                                                              
 11    usleep(STIME);                                                                                                                                                                       
 12   total+=DSIZE;//每次的加载量                                                                                                                              
 13    double rate = total*100.0/target;                                                                                                                              
 14    cb(rate);                                                                                                                              
 15  }                                                                                                                              
 16  printf("\n");                                                                                                                              
 17 }                                                                                                                              
 18 int main()                                                                                                                              
 19 {                                                                                                                              
 20  // process_v1();                                                                                                                              
 21  download(process_v2);                                                                                                                              
 22                                                                                                                               
 23   return 0;                                                                                                                              
 24 }       

process.h

#pragma once
  3 #include <stdio.h>
  4 #include<unistd.h>
  5 #include <string.h>
  6 
  7 #define SIZE 101
  8 #define MAX_RATE 100
  9 #define STYLE '#'
 10 #define STIME 1000*40
 11 
 12 typedef void (*callback_t)(int);//这里定义了一个函数指针类型,如果没有typedef,后者只是一个函数指针,不能用作类型使用
 13 
 14 
 15 void process_v1();
 16 void process_v2(int);

process.c

 1 #include "process.h"                                                                                                                                                                    
  2 const char *str = "|/\\";
  3 
  4 void process_v1()
  5 {
  6   int rate = 0;
  7   char bar[SIZE];
  8   memset(bar,'\0',sizeof(bar));
  9   int num = strlen(str);
 10 
 11   while(rate <=MAX_RATE)
 12   {
 13     printf("[%-100s][%d%%][%c]\r",bar,rate,str[rate%num]);
 14     fflush(stdout);
 15     usleep(STIME);
 16     bar[rate++] = STYLE;
 17 
 18   }
 19   printf("\n");
 20 
 21 }
 22 void process_v2(int rate)
 23 {
 24 
 25     static char bar[SIZE]= {0};
 26     int num = strlen(str);
 27     if(rate <= MAX_RATE && rate >=0)
 28     {
 29         printf("[%-100s][%d%%][%c]\r", bar, rate, str[rate%num]);
 30         fflush(stdout);
 31         bar[rate] = STYLE;
 32     }
 33     if(rate == MAX_RATE) memset(bar, '\0', sizeof(bar));
 34 }

👉🏻使用 git

git 发展史以及分布式管理概念

Git 是一个分布式版本控制系统,它于2005年由Linus Torvalds创建,并迅速成为世界上最受欢迎的版本控制系统之一。以下是 Git 的发展史:

  1. 2005年:Git 诞生。Linus Torvalds 创建了 Git,最初是为了更好地管理 Linux 内核的开发。

  2. 2005年至2008年:Git 的普及。Git 开始在全球范围内得到认可和采用,成为了众多开源项目和企业中的首选版本控制系统。

  3. 2008年:GitHub 的创立。GitHub 是一个基于 Git 的代码托管平台,成为了开源社区的重要中心,推动了 Git 的广泛应用和协作开发方式的改变。

  4. 2010年:Git 1.7 发布。此版本引入了一些新特性,例如子模块(submodule)和嵌套工作树(sparse checkout),进一步丰富了 Git 的功能。

  5. 2012年:Git 2.0 发布。此版本引入了一些重要的变化,包括清理命令行界面、简化配置和更好的前向兼容性等。

  6. 2014年:Git LFS 发布。Git Large File Storage(LFS)是 Git 的一个扩展,用于管理大型二进制文件,如图像、视频和其他媒体文件。

  7. 2016年:Git 2.10 发布。此版本带来了一些效率和性能改进,包括增量重写(incremental rewrite)和性能优化等。

  8. 至今:Git 持续发展。Git 在过去几年中持续演进,不断推出新的功能和改进,如改进的分支策略、签名提交、自动GC优化等。


版本管理器是一种用于跟踪和管理代码或文档等文件的工具或系统。它们帮助团队协作开发,记录每个文件的历史变更,并提供回滚、并行开发、分支管理等功能。

版本管理器的主要目标是追踪文件的修改历史状态变更,并让团队成员能够高效地协同工作。版本管理器记录每次文件的修改,包括修改内容、作者、时间等信息,并为每个修改创建一个唯一的标识符(commit ID)。这使得开发者可以随时回溯到先前的版本,并比较、合并不同版本之间的差异。

版本管理器还支持分支管理,允许团队成员在相同代码库上独立工作,创建新的分支进行开发,并最终将其合并到主分支(通常是 master 或 main)上。这样可以避免团队成员之间的冲突,并提供更高效的代码管理和协作方式。

Git 是一种分布式版本管理器,具有强大的功能和灵活性,可以在本地存储完整的代码仓库副本,并与其他开发者进行同步。它提供了高效的分支管理、快速的合并和补丁管理等功能,成为广泛使用的版本管理工具。
git 既是一个客户端,又是服务器.
github/gitee是网站,基于git软件搭建的网站—让版本管理可视化

git 命令

🥄安装git

yum install -y git

🫗常见git命令

  1. git init:在当前目录初始化一个新的 Git 仓库。

  2. git clone <repository>:克隆(下载)一个远程仓库到本地。

  3. git add <file>:将文件添加到暂存区。git add .可以提交当前文件下所有已改动的文件

  4. git commit -m "<message>":提交暂存区的文件到版本库,并附上提交信息。

  5. git status:查看工作区、暂存区和版本库的状态。

  6. git diff:查看工作区与暂存区的差异。

  7. git log:查看提交历史记录。

  8. git pull:从远程仓库拉取最新内容并合并到本地分支。

  9. git push:将本地分支的提交推送到远程仓库。

  10. git branch:查看分支列表。

  11. git branch <branch_name>:创建新分支。

  12. git checkout <branch_name>:切换到指定分支。

  13. git merge <branch_name>:将指定分支合并到当前分支。

  14. git remote add <name> <url>:添加一个远程仓库。

  15. git remote -v:查看远程仓库的详细信息。

  16. git stash:将当前未提交的修改保存到堆栈中,以便后续恢复。

  17. git tag <tag_name>:给当前提交打标签。

这只是一部分常用的 Git 命令,Git 还有许多其他命令和选项可以进一步探索和使用。如果需要了解这些命令的详细用法,可以使用 git help <command> 或者在互联网上查找相关的文档资料。

在commit后,此时文件已经传输到本地仓库,要与远程仓库同步还要再Push

git commit 出现 please tell me who you are 解决方法

在Git控制台输入以下命令:

git config --global user.email "邮箱"
git config --global user.name "用户名"

.gitignore

.gitignore 文件是用来指定在 Git 仓库中需要被忽略的文件或目录。当你在项目中使用 Git 进行版本控制时,有些文件或目录可能不希望被纳入版本控制,例如编译生成的临时文件、日志文件、缓存文件等。通过使用 .gitignore 文件,你可以告诉 Git 忽略这些文件,使它们不会出现在你的版本历史中。

以下是一些关于 .gitignore 文件的重要信息和用法:

  1. 文件位置.gitignore 文件通常放置在项目根目录下。在这个文件中列出的规则将适用于整个仓库及其子目录。

  2. 规则格式:每行一个规则,以斜杠(/)开头表示目录,其他情况下表示文件。你可以使用通配符来匹配多个文件或目录,如 *.log 匹配所有扩展名为 .log 的文件,build/ 匹配 build 目录及其内容。

  3. 注释:可以使用 # 符号添加注释,注释后的内容将被忽略。

  4. 模式匹配:支持简单的通配符模式,如 *(匹配任意字符序列),?(匹配单个字符),[abc](匹配字符 abc),[0-9](匹配数字)等。

  5. 感叹号(!):如果某个文件被忽略,但你希望将其包含在版本控制中,可以在规则前添加感叹号。

以下是一个示例 .gitignore 文件的内容:

# 忽略编译生成的文件
build/
bin/

# 忽略日志文件
*.log

# 忽略临时文件
tmp/

# 不忽略 README.md 文件
!README.md

这个 .gitignore 文件指示 Git 忽略 build/bin/ 目录下的所有内容,所有扩展名为 .log 的日志文件,以及根目录下的 tmp/ 目录。但会保留 README.md 文件。

.gitignore 文件对于确保不必要的文件不会误入版本控制非常有用,并保持 Git 仓库整洁和高效。

👉🏻gdb调试

常见用法

在这里插入图片描述

GDB是一个功能强大的调试器,它可以用于在Linux系统中调试应用程序,帮助开发人员查找和修复程序中的错误。下面是在Linux下使用GDB进行调试的一般步骤

  1. 编译应用程序时添加-g选项。例如,使用GCC编译时将代码转换为可执行文件可同时添加-g选项:
gcc -g main.c -o main
  1. 使用GDB启动应用程序,在命令行中输入以下命令:
gdb ./main
  1. 设置断点。在GDB提示符下,输入break\b命令设置断点,以便在指定的位置停止执行程序:
break main.c:10

这将在main.c文件的第10行设置断点。你也可以使用其他语法来设置断点,如按函数名文件名/行号等。

  1. 运行程序。在GDB提示符下,输入以下命令以运行程序:
run

执行此命令后,程序将开始运行并在遇到断点时停止。

  1. 查看变量的值和程序状态。在程序运行时,可以使用GDB命令查看变量的值和程序状态,例如:
  • print variable:打印变量的当前值
  • info locals:列出当前作用域中的局部变量

info b:查看断点信息

  • backtrace:打印当前调用栈
  • step/s:单步执行程序,并进入到函数内部
  • next/n:单步执行程序,但不进入函数内部
  • continue:继续执行程序直到下一个断点或程序结束
  1. 修改变量的值。你可以使用GDB命令修改变量的值,例如:
set variable = value

这将把变量的值设置为指定的值。

  1. 结束GDB调试。当你完成调试时,可以使用以下命令退出GDB:
quit

想要在linux下使用gdb调试需要在编译gcc的时候,加上-g增加调试信息

8.删除断点
在GDB中,要删除断点,可以使用delete命令。下面是一些关于删除断点的常用选项和示例:

  1. 删除所有断点:
delete

这将删除目前设置的所有断点。

  1. 删除指定编号的断点:
delete <breakpoint_number>

例如,要删除断点号为1的断点:

delete 1
  1. 删除指定源文件中的所有断点:
delete breakpoints <filename>

例如,要删除文件main.c中的所有断点:

delete breakpoints main.c
  1. 删除所有条件断点:
delete breakpoints if <condition>

例如,要删除所有带有条件的断点:

delete breakpoints if 1

其中<condition>是条件表达式,表示只删除满足此条件的断点。

  1. 删除所有临时断点:
delete breakpoints tbreak

这将删除所有临时断点,即只会在一次触发后自动删除的断点。

在GDB中,还有其他更高级和灵活的方式来管理断点,如禁用断点、启用断点等。你可以使用help delete命令获取更多关于删除断点的详细信息。

🥐启用和禁用断点
启用断点:

enable 断点编号

禁用断点

disable 断点编号

gdb——list用法

在使用GDB进行调试时,list(或 l)命令用于显示当前执行位置周围的源代码。下面是一些关于list命令的常用选项和示例:

  1. 在当前行显示源代码:
list

这将在当前执行位置的前后各10行显示源代码。

  1. 显示指定行号范围内的源代码:
list <start_line>, <end_line>

例如,要显示第5行到第15行的源代码:

list 5, 15
  1. 使用函数名显示源代码:
list <function_name>

例如,要显示名为main的函数的源代码:

list main
  1. 继续显示下一段源代码:
list

在首次使用list命令后,再次输入list命令会继续显示源代码的下一段。

  1. 向前或向后显示源代码:
list +<num_lines>
list -<num_lines>

例如,要向前显示5行源代码:

list +5

要向后显示5行源代码:

list -5
  1. 显示其他源文件的源代码:
list <filename>:<line>

例如,要显示文件main.c中的第20行源代码:

list main.c:20

readelf

readelf是一个用于读取和显示ELF(Executable and Linkable Format)文件的命令行工具。ELF是一种常见的二进制文件格式,用于可执行文件、共享对象和目标文件等。

你可以在Linux系统上使用readelf来获取有关ELF文件的各种信息,包括文件头、段表、符号表、重定位表和动态链接信息等。下面是一些使用readelf的常用命令选项和示例:

  1. 显示ELF文件的文件头信息:
readelf -h <filename>

例如:

readelf -h main
  1. 显示ELF文件的段表信息:
readelf -S <filename>

例如:

readelf -S main
  1. 显示ELF文件的符号表信息:
readelf -s <filename>

例如:

readelf -s main
  1. 显示ELF文件的重定位表信息:
readelf -r <filename>

例如:

readelf -r main
  1. 显示ELF文件的动态链接信息:
readelf -d <filename>

例如:

readelf -d main

这些只是readelf的一些常用选项和示例,还有其他命令选项可供使用,你可以通过man readelf命令查看readelf的完整文档并了解更多详细信息。


如上便是本期的所有内容了,如果喜欢并觉得有帮助的话,希望可以博个点赞+收藏+关注🌹🌹🌹❤️ 🧡 💛,学海无涯苦作舟,愿与君一起共勉成长
在这里插入图片描述
在这里插入图片描述

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值