Linux系统编程(零):cmd & vim & gcc & gdb

Linux 系统编程

  • Linux 系统编程(文章链接汇总)

1. Linux(Ubuntu) cmd 指令详解

1.1 shell

1.1.1 shell 家族

  • shell:命令解释器,根据输入的命令执行相应命令
  • 查看当前系统下有哪些 shell
    $ cat /etc/shells
    
    # /etc/shells: valid login shells
    /bin/sh        # 已经被 /bin/bash 所取代
    /bin/bash      # Linux 默认的 shell
    /bin/rbash
    /bin/dash
    
  • 查看当前系统正在使用的 shell
    $ echo $SHELL
    
    /bin/bash
    

1.1.2 bash

  • bash 是一个为 GNU 计划编写的 Unix shell。它的名字是一系列缩写:Bourne-Again-SHell
  • bash 是许多 Linux 平台的内定 shell
  • 大多数时候,一个 Shell Script 通常可以在很多种 shell 上使用

1.1.3 命令和路径补齐

  • 在 bash 下敲命令时,Tab 键可以补全已经敲了一部分的文件名和目录名

1.1.4 历史记录

  • 按上、下移动光标键(或者Ctrl-p、Ctrl-n)可以一条一条浏览以前输过的命令
  • 如果能记住以前输过的某条命令中的某个关键字,可以按 Ctrl+r,然后输入关键字,随着每输入一个字母,bash 会做增量式查找
    $ history
    
    # 按下 Ctrl+r 后,输入关键字 'sq'
    $ (reverse-i-search)`sq': sqlite3
    

1.2 目录和文件

1.2.1 类 Unix 系统目录结构

  • Ubuntu 没有盘符这个概念,只有一个根目录 /,所有文件都在它下面

Linux系统编程(零):cmd & vim & gcc & gdb_第1张图片

/:根目录
/bin:
    bin 是 Binaries (二进制文件) 的缩写, 这个目录存放着最经常使用的命令
/boot:
    这里存放的是启动 Linux 时使用的一些核心文件,包括一些连接文件以及镜像文件
/dev :
    dev 是 Device(设备) 的缩写, 该目录下存放的是 Linux 的外部设备,在 Linux 中访问设备的方式和访问文件的方式是相同的
/etc:
    etc 是 Etcetera(等等) 的缩写,这个目录用来存放所有的系统管理所需要的配置文件和子目录
/home:
    用户的主目录,在 Linux 中,每个用户都有一个自己的目录,一般该目录名是以用户的账号命名的,如上图中的 alice、bob 和 eve
/lib:
    lib 是 Library() 的缩写这个目录里存放着系统最基本的动态连接共享库,其作用类似于 Windows 里的 DLL 文件。几乎所有的应用程序都需要用到这些共享库
/media:
    linux 系统会自动识别一些设备,例如U盘、光驱等等,当识别后,Linux 会把识别的设备挂载到这个目录下
/mnt:
    系统提供该目录是为了让用户临时挂载别的文件系统的,我们可以将光驱挂载在 /mnt/ 上,然后进入该目录就可以查看光驱里的内容了
/opt:
    opt 是 optional(可选) 的缩写,这是给主机额外安装软件所摆放的目录。比如你安装一个ORACLE数据库则就可以放到这个目录下。默认是空的
/proc:
    proc 是 Processes(进程) 的缩写,/proc 是一种伪文件系统(也即虚拟文件系统),存储的是当前内核运行状态的一系列特殊文件,这个目录是一个虚拟的目录,它是系统内存的映射,我们可以通过直接访问这个目录来获取系统信息
/root:
    该目录为系统管理员,也称作超级权限者的用户主目录
/sbin:
    s 就是 Super User 的意思,是 Superuser Binaries (超级用户的二进制文件) 的缩写,这里存放的是系统管理员使用的系统管理程序
/selinux:
    这个目录是 Redhat/CentOS 所特有的目录,Selinux 是一个安全机制,类似于 windows 的防火墙,但是这套机制比较复杂,这个目录就是存放selinux相关的文件的
/srv:
    该目录存放一些服务启动之后需要提取的数据
/sys:
    这是 Linux2.6 内核的一个很大的变化。该目录下安装了 2.6 内核中新出现的一个文件系统 sysfs 
/tmp:
    tmp 是 temporary(临时) 的缩写这个目录是用来存放一些临时文件的
/usr:
    usr 是 unix shared resources(共享资源) 的缩写,这是一个非常重要的目录,用户的很多应用程序和文件都放在这个目录下,类似于 windows 下的 program files 目录
/usr/bin:
    系统用户使用的应用程序
/usr/sbin:
    超级用户使用的比较高级的管理程序和系统守护程序
/usr/src:
    内核源代码默认的放置目录
/var:
    var 是 variable(变量) 的缩写,这个目录中存放着在不断扩充着的东西,我们习惯将那些经常被修改的目录放在这个目录下。包括各种日志文件
/run:
    是一个临时文件系统,存储系统启动以来的信息。当系统重启时,这个目录下的文件应该被删掉或清除。如果你的系统上有 /var/run 目录,应该让它指向 run

1.2.2 用户目录

  • 位于 /home/user,称之为用户工作目录或家目录,两种表示方式如下

    $ /home/user
    $ ~
    
    yxd@yxd-VirtualBox:~$ cd /home/
    yxd@yxd-VirtualBox:/home$ cd ~
    yxd@yxd-VirtualBox:~$
    
  • 相对、绝对路径

    # 绝对路径:从/目录开始描述的路径为绝对路径
    $ cd /home/
    
    # 从当前位置开始描述的路径为相对路径
    $ cd ../../
    
  • 每个目录下都有 . 和 …

    • . 表示当前目录
    • … 表示上一级目录,即父目录
    • 根目录下的 . 和 … 都表示当前目录

1.2.3 ls

  • ls 是英文单词 list 的简写,其功能为:列出目录的内容
    -a 列出隐藏文件,文件中以 “.” 开头的均为隐藏文件,如:~/.bashrc
    -l 列出文件的详细信息
    -R 连同子目录中的内容一起列出
    
  • 用 ls -l 命令显示的信息中,开头是由 10 个字符构成的字符串
    $ ls -l
    total 236
    -rw-r--r--  1 yxd yxd     0 11月 14 19:05 apitest
    drwxrwxr-x  2 yxd yxd  4096 7月  31 13:41 bag
    ...
    ...
    -rw-r--r--  1 yxd yxd     0 11月 13 19:46 my,db
    -rw-r--r--  1 yxd yxd 24576 11月 14 15:40 my.db
    lrwxrwxrwx  1 yxd yxd    13 9月  14 15:37 myfile -> /no/such/file
    
    • 第 1 个字符表示文件类型,可以是下述类型之一
    - 普通文件
    d 目录
    l 符号链接
    b 块设备文件
    c 字符设备文件
    s socket文件,网络套接字
    p 管道
    
    • 后面 9 个字符表示文件访问权限,分为 3 组,每组 3 位,每组 3 个字符分别表示对文件的读、写和执行权限
      • 第一组表示文件属主的权限
      • 第二组表示同组用户的权限
      • 第三组表示其他用户的权限
    r 读
    w 写
    x 可执行。对于目录,表示进入权限
    s 当文件被执行时,把该文件的 UID 或 GID 赋予执行进程的 UID(用户 ID)或 GID(组 ID)
    t 设置标志位(sticky bit)。如果有 sticky bit 的目录,在该目录下任何用户只要有适当的权限即可创建文
      件,但文件只能被超级用户、目录拥有者或文件属主删除。如果是有sticky bit的可执行文件,在该文件执行后,
      指向其正文段的指针仍留在内存。这样再次执行它时,系统就能更快地装入该文件。
    - 没有相应位置的权限
    

访问权限后面的数字表示与该文件共享 inode 的文件总数,即硬链接数

1.2.4 cd & which & pwd

  • cd:change dir 改变当前所在路径;which 查看指定命令所在路径;pwd 查看当前所在路径
    $ cd ~  # 直接输入 cd 也能直接进入 ~ 目录下
    $ cd dir1/dir2
    $ cd ..
    
    $ which ls
    
    $ pwd
    

1.2.5 mkdir & rmdir

  • mkdir:创建目录,可以一次创建多个
  • rmdir:删除空目录,可以一次删除多个(空目录:只包含 . 和 … 的目录)
    $ mkdir a
    $ mkdir a/b
    $ ls a
    b
    
    $ rmdir a/b
    $ ls a
    $ rmdir a
    
    # 后面跟着 -p,表示可以连同父目录一起创建
    $ mkdir -p a/b
    $ ls a
    b
    # 后面跟着 -p,表示可以连同空的父目录一起删除
    $ rmdir -p a/b
    

1.2.6 touch & rm & mv & cp

  • touch
    • 将每个文件的访问及修改时间都更新为目前的时间
    • 如果文件不存在,则创建一个字节数为 0 的文件
  • rm
    • 删除文件或目录
    # 删除文件
    $ rm file
    
    # 删除目录
    $ rm -rf dir
    
  • mv
    • 重命名或移动文件
    # 重命名
    $ mv file1 file2
    
    # 移动文件
    $ mv file1 ~/
    
  • cp
    • 拷贝文件或目录
    # 拷贝文件
    $ cp file1 file2
    $ cp file1 dir/
    $ cp file1 ../
    
    # 拷贝目录
    cp -r dir1 dir2 
    cp -r dir1 ~/ 
    

1.2.7 cat & more & less & head & tail

  • cat
    • 查看文件内容并输出到终端
    • 如果 cat 时没跟文件名,则读标准输入,遇到 \n 后,输出到标准输出,终端下输入 ctrl+d 表示结束
  • more
    • 查看文本文件的内容,屏幕显示完一屏就等待用户按下任意键再滚动到下一屏
    • 如果中途不想继续看下去了,可以按 ctrl+c 或 q 终止显示
  • less
    • 查看文本文件的内容,屏幕显示完一屏就等待用户按键,用户可以向上或向下查看
  • head
    • 显示指定文件的前面几行,如果没有指定文件,将从标准输入(键盘)上读取
    • 如果没有指定要显示的行数,则默认显示前 10 行
    # 显示文件 file1 的前 5 行
    $ head -5 file1
    
  • tail
    • 显示文件的最后几行,若没有指定显示的行或字符数,则默认显示末尾 10 行
    # 显示文件 file1 的后 5 行
    $ tail -5 file1 
    

1.2.8 ln

  • 链接有两种,一种被称为硬链接(Hard Link),另一种被称为符号链接(Symbolic Link)
    • 建立硬链接时,链接文件和被链接文件必须位于同一个文件系统中,并且不能建立指向目录的硬链接
    • 而对符号链接(也称软链接),则不存在这个问题
    • 默认情况下,ln 产生硬链接,ln -s 建立符号链接
    # 硬链接
    $ touch hello
    $ ln hello word_h
    
    # 软链接
    $ ln -s hello word_s
    

1.2.9 tree & wc & od & df & du

  • tree
    • 按结构树的形状显示目录和文件
    # 使用该命令前需进行安装
    $ sudo apt-get install tree
    
  • wc
    • 计算文件的 Byte 数、字数或列数
    • 若不指定文件名称、或所给予的文件名为 “-”,则从标准输入设备读取数据
    $ wc -l ./*
    # -c 或 –bytes 或 –chars   只显示 Bytes 数
    # -l 或 –lines             只显示列数
    # -w 或 –words             只显示字数
    
  • od
    • 指定数据的显示格式
    $ od -tcx file1  # 显示格式:ASCII 字符/反斜杠序列 + 十六进制数
    
    # -t 后的主要参数有
    # c ASCII 字符或反斜杠序列
    # d[SIZE] 有符号十进制数,每个整数 SIZE 字节
    # f[SIZE] 浮点数,每个整数 SIZE 字节
    # o[SIZE] 八进制(系统默认值为 02),每个整数 SIZE 字节
    # u[SIZE] 无符号十进制数,每个整数 SIZE 字节
    # x[SIZE] 十六进制数,每个整数 SIZE 字节
    
  • df
    • 查看磁盘使用情况
    $ df --block-size=GB
    $ df --block-size=MB
    
  • du
    • 查看某个目录的大小
    # 以 M 为单位
    $ du -hm /home/itcast/test
    
    # 以 B 为单位
    $ du -hb ./*
    
    # 以K为单位,4k 的整数倍
    $ du -hk ./*
    
    # 总计大小,以 M 为单位
    $ du -hc ./*
    

1.3 文件属性和用户、用户组

1.3.1 whoami

  • 查看当前登陆用户

1.3.2 chmod

  • 改变文件或目录的访问权限
文字设定法
$ chmod [who] [+|-|=] [mode] 文件名
  • 操作对象 who 可是下述字母中的任一个或者它们的组合
    u 表示 “用户(user)”,即文件或目录的所有者
    g 表示 “同组(group)用户”,即与文件属主有相同组 ID 的所有用户
    o 表示 “其他(others)用户”
    a 表示 “所有(all)用户”,它是系统默认值
    
  • 操作符号可以是
    + 添加某个权限
    - 取消某个权限
    = 赋予给定权限并取消其他所有权限(如果有的话)
    
  • 设置 mode 所表示的权限可用下述字母的任意组合
    r 可读
    w 可写
    x 可执行
    
数字设定法
$ chmod [mode] 文件名

# 0 表示没有权限
# 1 表示可执行权限
# 2 表示可写权限
# 4 表示可读权限

# 设置一个文件允许所有用户可读、可写、可执行
$ chmod 777 file1
user    group   other
r w x   r w x   r w x
4 2 1   4 2 1   4 2 1
  7       7       7

1.3.3 chown

  • 更改某个文件或目录的属主和属组
    • 例如 root 用户把自己的一个文件拷贝给用户 A, 为了让用户 A 能够存取这个文件,root 用户应该把这个文件的属主设为 A,否则,用户 A 无法存取这个文件
    • OPTION 的主要参数
      • -R 递归式地改变指定目录及其下的所有子目录和文件的拥有者
      • -v 显示chown命令所做的工作
    chown [OPTION][OWNER:GROUP] FILE…
    chown [OPTION]… –reference=RFILE FILE…
    
  • 比如把一个文件改为 itcast 用户和 nogroup 用户组所有
    • chown 需要特权用户才能执行
    • 一个文件的 owner 和 owning group 是没有关联的
      • 一个文件属于用户 A,也属于用户组 B,但并不表示用户 A 属于用户组 B
    $ sudo chown itcast:nogroup file1
    

1.3.4 chgrp

  • 改变(指定)指定文件所属的用户组
    • group 可以是用户组ID,也可以是 /etc/group 文件中用户组的组名
    • 文件名是以空格分开的要改变属组的文件列表,支持通配符
    • 如果用户不是该文件的属主或超级用户,则不能改变该文件的组
    • OPTION 的主要参数 -R:递归式地改变指定目录及其下的所有子目录和文件的属组
    chgrp [OPTION]GROUP FILE…
    chgrp [OPTION]… –reference=RFILE FILE…
    

1.4 查找与检索

1.4.1 find

  • 根据文件名查找
    • 在目录中搜索文件,path 指定目录路径,系统从这里开始沿着目录树向下查找文件
    $ find pathname -options [-print -exec -ok -name -type...]
    
    $ find . -name 'file*'  # 在当前目录及子目录下查找所有以 file 开头的文件名
    $ find / -name 'vimrc'
    $ find ~ -name '*.c'
    $ find /usr/ -name "*tmp*" -exec ls -l { } \;
    $ find ./ -name "*tmp" -ok rm { } \;
    

1.4.2 grep

  • 根据内容检索
    • 在指定文件中搜索特定的内容,并将含有这些内容的行输出到标准输出。若不指定文件名,则从标准输入读取
    • [options] 部分包含的主要参数
    -I:不区分大小写(只适用于单字符)
    -h:查询多文件时不显示文件名
    -l:查询多文件时只输出包含匹配字符的文件名
    -n:显示匹配行及行号
    -s:不显示不存在或无匹配文本的错误信息
    -v:显示不包含匹配文本的所有行
    -R:连同子目录中所有文件一起查找
    
  • 例如:到系统头文件目录下查找所有包含 printf 的文件
    $ grep 'printf' /usr/include -R
    

1.5 安装卸载软件

1.5.1 apt-get/apt

  • 更新源服务器列表
    $ sudo vi /etc/apt/sources.list
    
  • 具体指令
    $ sudo apt-get update # 更新源
    $ sudo apt-get install package # 安装包
    $ sudo apt-get remove package # 删除包
    $ sudo apt-cache search package # 搜索软件包
    $ sudo apt-cache show package # 获取包的相关信息,如说明、大小、版本等
    $ sudo apt-get install package --reinstall # 重新安装包
    $ sudo apt-get -f install # 修复安装
    $ sudo apt-get remove package --purge # 删除包,包括配置文件等
    $ sudo apt-get build-dep package # 安装相关的编译环境
    $ sudo apt-get upgrade # 更新已安装的包
    $ sudo apt-get dist-upgrade # 升级系统
    $ sudo apt-cache depends package # 了解使用该包依赖那些包
    $ sudo apt-cache rdepends package # 查看该包被哪些包依赖
    $ sudo apt-get source package # 下载该包的源代码
    $ sudo apt-get clean && sudo apt-get autoclean # 清理无用的包
    $ sudo apt-get check # 检查是否有损坏的依赖
    

1.5.2 deb 包安装

$ sudo dpkg -i xxx.deb # 安装 deb 软件包命令
$ sudo dpkg -r xxx.deb # 删除软件包命令
$ sudo dpkg -r --purge xxx.deb # 连同配置文件一起删除命令
$ sudo dpkg -info xxx.deb # 查看软件包信息命令
$ sudo dpkg -L xxx.deb # 查看文件拷贝详情命令
$ sudo dpkg -l # 查看系统中已安装软件包信息命令
$ sudo dpkg-reconfigure xxx # 重新配置软件包命令

1.5.3 源码安装

$ cd dir
$ ./configure          # 检测文件是否缺失,创建 Makefile,检测编译环境
$ make                 # 编译源码,生成库和可执行程序
$ sudo make install    # 把库和可执行程序,安装到系统路径下
$ sudo make distclean  # 删除和卸载软件

1.6 压缩包管理

1.6.1 tar

  • tar 可以为文件和目录创建档案
    • tar 命令可以为某一特定文件创建档案(备份文件),也可以在档案中改变文件,或者向档案中加入新的文件
    • 要将文件备份到一个特定的设备,只需把设备名作为备份文件名
    # 打包
    # c 创建新的档案文件
    # x 从档案文件中释放文件
    # v 详细报告 tar 处理的文件信息
    # f 使用档案文件或设备
    $ tar cvf dir.tar dir
    $ tar xvf dir.tar dir
    
    # 打 gz 压缩包
    # z 用 gzip 来压缩/解压缩文件,加上该选项后可以将档案文件进行压缩,但还原时也一定要使用该选项进行解压缩
    $ tar zcvf dir.tar.gz dir
    $ tar zxvf dir.tar.gz
    
    
    # 打 bz2 压缩包
    # j 用 bzip2 来压缩/解压缩文件,加上该选项后可以将档案文件进行压缩,但还原时也一定要使用该选项进行解压缩
    $ tar jcvf dir.tar.bz2 dir
    $ tar jxvf dir.tar.bz2
    
    # 指定目录解压缩
    $ tar zxvf dir.tar.gz -C ~/test
    

1.6.2 rar

# 打包:把 dir 压缩成 newdir.rar
$ rar a -r newdir dir

# 解包:把 newdir.rar 解压缩到当前目录
$ unrar x newdir.rar

1.6.3 zip

# 打包:把 dir 压缩成 dir.zip
$ zip -r dir.zip dir

# 解包
$ unzip dir.zip

1.7 进程管理

1.7.1 who

  • 查看当前在线上的用户情况
    • 所有的选项都是可选的,不使用任何选项时,who 命令将显示以下三项内容
      • login name:登录用户名
      • terminal line:使用终端设备
      • login time:登录到系统的时间
    $ who
    yxd      :0           2023-12-08 14:40 (:0)
    

1.7.2 ps

  • ps 命令用于监控后台进程的工作情况,因为后台进程是不和屏幕键盘这些标准输入/输出设备进行通信的
    • 最常用的四个参数是 a、u、j、x
    -e:显示所有进程
    -f:全格式
    -h:不显示标题
    -l:长格式
    -w:宽输出
    -r:只显示正在运行的进程
    -a:即 all,表示显示所有用户的进程,即包括其他用户的进程
    -u:表示以用户为主的格式来显示进程信息,包括进程的用户、进程 ID、CPU 占用率、内存占用率等
    -j:表示输出进程的详细信息,包括进程所属用户、进程 ID、父进程 ID、CPU 占用率、内存占用率等
    -x:表示显示无选项进程,即显示不属于当前终端的进程
    
    $ ps aux
    USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
    root         1  0.0  0.1 225612  9436 ?        Ss   14:39   0:01 /sbin/in
    root         2  0.0  0.0      0     0 ?        S    14:39   0:00 [kthread
    root         3  0.0  0.0      0     0 ?        I<   14:39   0:00 [rcu_gp]
    ...
    ...    
    
    $ ps ajx
    PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
       0     1     1     1 ?           -1 Ss       0   0:01 /sbin/init splash
       0     2     0     0 ?           -1 S        0   0:00 [kthreadd]
       2     3     0     0 ?           -1 I<       0   0:00 [rcu_gp]
    ...
    ...
    
    # Head 标头含义解析
    USER 用户名
    UID 用户 ID(User ID)
    PID 进程 ID(Process ID)
    PPID 父进程的进程 ID(Parent Process id)
    SID 会话 ID(Session id)
    %CPU 进程的 CPU 占用率
    %MEM 进程的内存占用率
    VSZ 进程所使用的虚存的大小(Virtual Size)
    RSS 进程使用的驻留集大小或者是实际内存的大小,Kbytes 字节
    TTY 与进程关联的终端(tty)
    STAT 进程的状态:进程状态使用字符表示的(STAT 的状态码)
        R 运行 Runnable (on run queue) 正在运行或在运行队列中等待
        S 睡眠 Sleeping 休眠中, 受阻, 在等待某个条件的形成或接受到信号
        I 空闲 Idle
        Z 僵死 Zombie(a defunct process) 进程已终止, 但进程描述符存在, 直到父进程调用 wait4() 系统调
        用后释放
        D 不可中断 Uninterruptible sleep(ususally IO) 收到信号不唤醒和不可运行, 进程必须等待直到有中
        断发生
        T 停止 Terminate 进程收到 SIGSTOP, SIGSTP, SIGTIN, SIGTOU 信号后停止运行运行
        P 等待交换页
        W 无驻留页 has no resident pages 没有足够的记忆体分页可分配
        X 死掉的进程
        < 高优先级进程高优先序的进程
        N 低优先级进程低优先序的进程
        L 内存锁页 Lock 有记忆体分页分配并缩在记忆体内
        s 进程的领导者(在它之下有子进程)
        l 多进程的(使用CLONE_THREAD, 类似NPTL pthreads)
        + 位于后台的进程组
    START 进程启动时间和日期
    TIME 进程使用的总 CPU 时间
    COMMAND 正在执行的命令行命令
    NI 优先级 (Nice)
    PRI 进程优先级编号 (Priority)
    WCHAN 进程正在睡眠的内核函数名称;该函数的名称是从 /root/system.map 文件中获得的
    FLAGS 与进程相关的数字标识
    

1.7.3 jobs

  • 用来显示当前 shell 下正在运行哪些作业(即后台作业)
    $ cat
    ^Z  # 按下Ctrl-z挂起当前进程
    [1]+  Stopped                 cat
    
    $ cat
    ^Z
    [2]+  Stopped                 cat
    
    $ jobs
    [1]-  Stopped                 cat
    [2]+  Stopped                 cat
    # 第一列中的数字表示作业序号,由当前运行的 shell 分配
    # 第二列中的 “+” 号表示相应作业的优先级比 “-” 号对应作业的优先级高
    # 第三列表明作业状态,是否为运行、中断、等待输入或停止等
    # 第四列是创建当前这个作业所对应的命令行
    

1.7.4 kill

  • 向指定进程发送信号
    $ kill [ -signal | -s signal ] pid ...
    
  • 查看信号编号
    $ kill -l [ signal ]
    
  • 给一个进程发信号,或终止一个进程的运行
    • kill 命令如果不带参数而是后面直接跟 pid,就是发给该进程 SIGTERM 信号,大部分进程收到该信号就会终止
    • 但是被挂起的进程不能处理信号,所以必须发 SIGKILL 信号,由系统强制终止进程
    $ cat
    $ # 按 ctrl+z 挂起当前进程
    [1]+ Stopped cat
    
    $ ps
    PID TTY TIME CMD
    5819 pts/1 00:00:00 bash
    5893 pts/1 00:00:00 cat
    5894 pts/1 00:00:00 ps
    
    $ kill -SIGKILL 5893  # 被挂起的进程不能处理信号,必须发 SIGKILL 信号,由系统强制终止进程
    $ # 再次按回车键
    [1]+ Killed cat
    $
    

1.7.5 env

  • 查看当前进程环境变量
    $ env
    $ vim ~/.bashrc
    
  • 配置当前用户环境变量
    • 配置系统环境变量,配置时需要有 root 权限
    $ vim /etc/profile
    $ export PATH=$PATH:新路径
    

1.8 网络管理

1.8.1 ifconfig

# 查看网卡信息
$ ifconfig

# 关闭网卡
$ sudo ifconfig eth0 down

# 开启网卡 eth0
$ sudo ifconfig eth0 up

# 给 eth0 配置临时 IP
$ sudo ifconfig eth0 IP

1.8.2 ping

  • 查看网络上的主机是否在工作
    $ ping [选项] 主机名/IP地址
    

1.8.3 netstat

  • 显示网络连接、路由表和网络接口信息,可以让用户得知目前都有哪些网络连接正在运作
    $ netstat [选项]
    

1.8.4 nslookup

  • 查询一台机器的 IP 地址和其对应的域名
    $ nslookup name
    

1.8.5 ssh

# 安装 ssh 服务器
$ sudo apt-get install openssh-server

# 远程登陆
$ ssh 用户名@IP

1.9 关机重启

1.9.1 关机 poweroff

$ poweroff

1.9.2 重启 reboot

$ reboot

1.9.3 shutdown

$ shutdown -r now 立刻重新开机
$ shutdown -h now 立刻关机

1.10 其他命令

1.10.1 man

  • 看手册 (叫做 manual 或 man page)
$ man man
$ man read     # 查看 read 命令的 man page
$ man 2 read   # 查看 read 系统函数的 man page(在第二个 section 中,表示为 read(2))
$ man -k read  # 以 read 为关键字查找相关的 man page

1.10.2 clear

  • 清屏,使光标和提示符回到屏幕第一行
    # 快捷键 ctrl+l
    $ clear
    

1.10.3 echo

  • 在显示器上显示一段文字,一般起到一个提示的作用
    • 其中选项 n 表示输出文字后不换行,字符串可以加引号,也可以不加引号
    • 用 echo 命令输出加引号的字符串时,将字符串原样输出
    • 用 echo 命令输出不加引号的字符串时,将字符串中的各个单词作为字符串输出,各字符串之间用一个空格分割
    $ echo [-n] 字符串
    

1.10.4 创建终端

创建终端标签 Ctrl + Shift + t
切换标签 Alt+n(n=1)
新开终端 Ctrl + Shift + n

2. vim 详解

2.1 vi 简介

  • vi 是 “Visual interface” 的简称,可以执行输出、删除、查找、替换、块操作等众多文本操作,而且用户可以根据自己的需要对其进行定制,vi 只是一个文本编辑程序,没有菜单,只有命令且命令繁多
  • vi 有三种基本工作模式:
    • 命令模式
    • 文本输入模式
    • 末行模式

Linux系统编程(零):cmd & vim & gcc & gdb_第2张图片

2.1.1 命令模式

  • 任何时候,不管处于何种模式,只要按一下 ESC 键,即可使 vi 进入命令模式,在 shell 环境 (提示符为 $) 下输入启动 vi 命令,进入编辑器时,也是处于该模式下
  • 在该模式下,用户可以输入各种合法的 vi 命令,用于管理自己的文档
    • 此时从键盘上输入的任何字符都被当做编辑命令来解释,若输入的字符是合法的 vi 命令,则 vi 在接受用户命令之后完成相应的动作
    • 需注意的是,所输入的命令并不在屏幕上显示出来
    • 若输入的字符不是 vi 的合法命令,vi 会响铃报警

2.1.2 文本输入模式

  • 在命令模式下
    • 输入插入命令 i、附加命令 a 、打开命令 o、修改命令 c、取代命令 r 或替换命令 s 都可以进入文本输入模式
    • 在该模式下,用户输入的任何字符都被 vi 当做文件内容保存起来,并将其显示在屏幕上
    • 在文本输入过程中,若想回到命令模式下,按键 ESC 即可

2.1.3 末行模式

  • 在命令模式下,用户按 “:” 键即可进入末行模式下,此时 vi 会在显示窗口的最后一行(通常也是屏幕的最后一行)显示一个 “:” 作为末行模式的提示符,等待用户输入命令
  • 多数文件管理命令都是在此模式下执行的 (如把编辑缓冲区的内容写到文件中等)
  • 末行命令执行完后,vi 自动回到命令模式

2.2 vim 基本操作

2.2.1 文本输入模式

i: 插入光标前一个字符
I: 插入行首
a: 插入光标后一个字符
A: 插入行未
o: 向下新开一行,插入行首
O: 向上新开一行,插入行首

2.2.2 命令模式

  • 移动光标
    h: 左移
    j: 下移
    k: 上移
    l: 右移
    M: 光标移动到中间行
    L: 光标移动到屏幕最后一行行首
    G: 移动到指定行,行号 -G
    w: 向后一次移动一个字
    b: 向前一次移动一个字
    {: 按段移动,上移
    }: 按段移动,下移
    Ctr-d: 向下翻半屏
    Ctr-u: 向上翻半屏
    Ctr-f: 向下翻一屏
    Ctr-b: 向上翻一屏
    gg: 光标移动文件开头
    G: 光标移动到文件末尾
    
  • 删除命令
    x: 删除光标后一个字符,相当于 Del
    X: 删除光标前一个字符,相当于 Backspace
    dd: 删除光标所在行,n dd 删除指定的行数 D: 删除光标后本行所有内容,包含光标所在字符
    d0: 删除光标前本行所有内容,不包含光标所在字符
    dw: 删除光标开始位置的字,包含光标所在字符
    
  • 撤销命令
    u: 一步一步撤销
    Ctr-r: 反撤销
    
  • 重复命令
    .: 重复上一次操作的命令
    
  • 复制粘贴
    yy: 复制当前行,n yy 复制 n 行
    p: 在光标所在位置向下新开辟一行,粘贴
    
  • 可视模式
    v: 按字符移动,选中文本
    V: 按行移动,选中文本可视模式可以配合 d, y, >>, << 实现对文本块的删除,复制,左右移动
    
  • 替换操作
    r: 替换当前字符
    R: 替换当前行光标后的字符
    
  • 查找命令
    /: str 查找
    n: 下一个
    N:上一个
    K:在系统函数上按 K 直接跳转到 man page
    #:在自定义函数上按 # 直接跳转到函数定义
    
  • 替换命令
    末行模式下,将光标所在行的 abc 替换成 123
    :%s/abc/123/g
    
    末行模式下,将第一行至第 10 行之间的 abc 替换成 123
    :1,10s/abc/123/g
    
  • 查看 Man Page
    光标移动到函数上,Shift-k 光标移动到函数上
    3Shift-k,查看第三章的 ManPage
    
  • 代码自动缩进排版
    gg=G
    
  • vim 里执行 shell 下命令
    末行模式里输入!,后面跟命令
    

2.3 vim 分屏操作

  • 分屏操作
    sp:上下分屏,后可跟文件名
    vsp:左右分屏,后可跟文件名
    Ctrl+w+w:在多个窗口切换
    
  • 启动分屏
    # 使用大写 O 参数进行垂直分屏
    # n 是数字,表示分屏的数量,n 要大于等于文件个数
    $ vim -On file1 file2 ...
    
    # 使用小写 o 参数进行水平分屏
    $ vim -on file1 file2 ...
    
  • 关闭分屏
    # 关闭当前窗口
    $ ctrl+w c
    
    # 关闭当前窗口,如果只剩最后一个,则退出vim
    $ ctrl+w q
    

3. gcc

3.1 gcc 编译 4 步骤

Linux系统编程(零):cmd & vim & gcc & gdb_第3张图片

3.2 gcc 编译常用参数

-v –version 查看 gcc 版本号
-I 目录 指定头文件目录,注意 -I 和目录之间空格可有可无
-c 只做预处理、编译、汇编,得到 .o 二进制文件,不进行链接
-g 编译时添加调试文件,用于 gdb 调试
-On n=0∼3 编译优化,n 越大优化得越多(嵌入式编程中使用较多,默认 n = 2)
-Wall 显示所有警告信息
-D 编译时定义宏,注意 -D 和  之间没有空格
-E 生成预处理文件
-M 生成 .c 文件与头文件依赖关系以用于 Makefile,包括系统库的头文件
-MM 生成 .c 文件与头文件依赖关系以用于 Makefile,不包括系统库的头文件

3.3 示例:-I 使用

$ vi hello.c
$ gcc hello.c -o hello
$ ls
hello  hello.c  hello.h
$ ./hello 
-----------

$ mkdir include
$ mv hello.h include/
$ ls
hello  hello.c  include
$ gcc hello.c -o hello
hello.c:1:10: fatal error: hello.h: No such file or directory
 #include "hello.h"
          ^~~~~~~~~
compilation terminated.

# 当头文件和源码不在一个目录下时,需要使用 -I 参数指定头文件所在位置
$ gcc -I./include hello.c -o hello
$ ls
hello  hello.c  include
$ ./hello 
-----------

4. 静态库和动态库

  • 所谓 “程序库”,简单说,就是包含了数据和执行码的文件
    • 其不能单独执行,可以作为其它执行程序的一部分来完成某些功能
    • 库的存在,可以使得程序模块化,可以加快程序的再编译,可以实现代码重用,可以使得程序便于升级
  • 程序库可分静态库 (static library) 和共享库 (shared object,也称动态库)
    • 静态库:是在可执行程序运行前就已经加入到执行码中,成为执行程序的一部分,适用于对空间要求较低,而时间要求较高的核心程序中
    • 动态库:是在执行程序启动时加载到执行程序中,可以被多个执行程序共享使用,适用于对时间要求较低,对空间要求较高的程序中

4.1 静态库

  • 静态库可以认为是一些目标代码的集合
    • 静态库名字以 lib 开头,以 .a 结尾,例如 libmymath.a
    • 使用 ar(archiver) 命令可以创建静态库
    • 共享库有着更大的优势,静态库已经不经常使用,但静态库使用简单,仍有使用的余地并会一直存在
  • 静态库在应用程序生成时,可以不必再编译,节省再编译时间。但在编译器越来越快的今天,这一点似乎已不重要。如果其他开发人员要使用你的程序,而你又不想给其源码,提供静态库是一种选择
  • 静态库的制作及使用流程
    # 1. 将 .c 生成 .o 文件
    $ gcc -c add.c -o add.o
    $ gcc -c sub.c -o sub.o
    $ gcc -c div1.c -o div1.o
    $ ls
    add.c  add.o  div1.c  div1.o  sub.c  sub.o
    
    # 2. 使用 ar 工具制作静态库
    $ ar rcs libmymath.a add.o sub.o div1.o
    $ ls
    add.c  add.o  div1.c  div1.o  libmymath.a  sub.c  sub.o
    
    # 3. 编译静态库到可执行文件中
    $ gcc test.c libmymath.a -o test
    
    # 当存在头文件且在 include 目录下,静态库文件在 lib 目录下时
    $ gcc test.c ./lib/libmymath.a -o test -I ./include
    

4.2 动态库

  • 地址回填
    • 源代码里的函数,相对 main 函数偏移是一定的,链接时,回填 main 函数地址后,其他源代码的函数也就得到了地址
  • 动态库里的函数会用一个 @plt 来标识,当动态库加载到内存时,再用加载进去的地址将 @plt 替换掉
  • 动态库的制作及使用流程
    # 1. 将 .c 生成 .o 文件(生成与位置无关的代码 -fPIC)
    $ gcc -c add.c -o add.o -fPIC
    $ gcc -c sub.c -o sub.o -fPIC
    $ gcc -c div1.c -o div1.o -fPIC
    $ ls
    add.c  add.o  div1.c  div1.o  sub.c  sub.o
    
    # 2. 使用 gcc -shared 制作动态库
    $ gcc -shared -o libmymath.so add.o sub.o div1.o
    $ ls
    add.c  add.o  div1.c  div1.o  libmymath.so  sub.c  sub.o
    
    # 3. 编译可执行程序时指定所使用的动态库。-l:指定库名 -L:指定库路径
    # 链接器:工作于链接阶段,工作时需要 -l 和 -L
    # 此处 -l 后的库名需要去掉前缀 lib- 和后缀 .so
    $ gcc test.c -o test -l mymath -L ./lib
    # 当存在头文件且在 include 目录下,静态库文件在 lib 目录下时
    $ gcc test.c -o test -l mymath -L ./lib -I ./include
    
    # 4. 动态链接器:工作于程序运行阶段,工作时需要提供动态库所在目录位置
    $ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/lib  # 临时生效,终端重启后环境变量失效
    $ vi ~/.bashrc  # 永久生效,将 export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:./lib 写入 .bashrc,建议使用绝对路径
    $ source .bashrc
    

4.3 项目实战

  • 1. 创建一个目录 mycal
    $ mkdir mycal
    
  • 2. 创建 4 个 c 文件和 1 个 h 头文件,分别实现加减乘除
    // add.c
    int add(int a, int b) {
        return a+b;
    }
    
    // sub.c
    int add(int a, int b) {
        return a-b;
    }
    
    // mul.c
    int add(int a, int b) {
        return a*b;
    }
    
    // dive.c
    int add(int a, int b) {
        return a/b;
    }
    
    // common.h
    #ifndef COMMON_H_
    #define COMMON_H_
    
    int add(int a, int b);
    int sub(int a, int b);
    int dive(int a, int b);
    int mul(int a, int b);
    
    #endif
    
  • 3. 制作静态库
    $ gcc -c add.c sub.c mul.c dive.c
    $ ar rcs libmycal.a add.o sub.o mul.o dive.o
    
  • 4. 制作动态库
    $ gcc -c add.c sub.c mul.c dive.c -fPIC
    $ gcc -shared -Wl, -soname, libmycal.so.1 -o libmycal.so.1.10 add.o sub.o mul.o dive.o
    
  • 5. 设置共享库加载路径
    • 打开共享库路径配置文件
    $ sudo vi /etc/ld.so.conf
    # 最后一行添加 mycal 路径
    /home/yue-VirtualBox/mycal
    
    • 更新共享库加载路径
    $ sudo ldconfig -v
    
    • 手动添加 link name
    $ ln -s libmycal.so.1.10 libmycal.so
    
  • 6. 编写测试文件 main.c,分别去链接静态库和共享库
    // main.c
    #include 
    #include "common.h"
    
    int main(void) {
        printf("%d\n", add(5, 3));
        return 0;
    }
    

5. gdb 调试工具

  • 使用 gdb 之前,要求对文件进行编译时增加 -g 参数
    • 加了这个参数过后生成的编译文件会大一些,这是因为增加了 gdb 调试内容
    • gdb 无法检查语法错误(gcc 检查),只能检查逻辑错误
  • gdb 的基本思想是:分析现象->假设错误原因->产生新的现象去验证假设

5.1 基础指令

list/l:列出源码,根据源码指定行号设置断点
break/b:b 20 在 20 行位置设置断点
run/r:全速运行程序
start:单步运行程序
next/n:下一条指令(会越过函数)
step/s:下一条指令(会进入函数)
print/p:p i 查看变量的值
delete/d:删除断点
continue/c:继续执行断点后续指令(剩下代码)
finish:结束当前函数调用
quit:退出 gdb 当前调试

5.2 高级指令

run:使用 run 查找段错误出现位置
set args:设置 main 函数命令行参数(在 start、run 之前)
run argv[1] argv[2]:设置 main 函数命令行参数(调试时命令行传参)
info b:查看断点信息表
b 20 if i = 5:设置条件断点
ptype:查看变量类型
backtrace/bt:查看函数的调用的栈帧和层级关系
frame:根据栈帧编号,切换栈帧
display:设置跟踪变量
undisplay:取消设置跟踪变量,使用跟踪变量的编号

栈帧:随着函数调用而在 stack 上开辟的一片内存空间,用于存放函数调用时产生的局部变量和临时值,随函数调用结束而自动释放内存空间

5.3 案例演示

// gdbtest.c
#include 
#include 
#include 

#define N 10

char *str = "hello ";
int var = 0;

void init_arr(int *arr, int len) {
    int i = 0;
    
    for (i = 0; i < len; i++) {
        arr[i] = rand() % 20 + 1;
    }
}

void select_sort(int *arr, int len) {
    int i, j, k, tmp;
    
    for (i = 0; i < len - 1; i++) {
        k = i;
        for (j = i + 1; j < len; j++) {
            if (arr[k] > arr[j]) {
                k = j;
            }
        }
    
        if (i != k) {
            tmp = arr[i];
            arr[i] = arr[k];
            arr[k] = tmp;
        }
    }
}

void print_arr(int *arr, int len) {
    int i;
    
    for (i = 0; i < len; i++) {
        printf("arr[%d] = %d\n", i, arr[i]);
    }
}

int main(void) {
    int arr[N];
    char *p = "hello";
    
    srand(time(NULL));
    init_arr(arr, N);
    print_arr(arr, N);
    //p[3] = 'M';
    
    select_sort(arr, N);
    printf("------after sort------\n");
    print_arr(arr, N);
    
    return 0;
}
$ gcc gdbtest.c -o gdbtest -g
$ gdb gdbtest 
GNU gdb (Ubuntu 8.1.1-0ubuntu1) 8.1.1
Copyright (C) 2018 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later //gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
//www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
//www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from gdbtest...done.
(gdb) list 1
1	#include 
2	#include 
3	#include 
4	
5	#define N 10
6	
7	char *str = "hello ";
8	int var = 0;
9	
10	void init_arr(int *arr, int len) {
(gdb) l
11		int i = 0;
12		
13		for (i = 0; i < len; i++) {
14			arr[i] = rand() % 20 + 1;
15		}
16	}
17	
18	void select_sort(int *arr, int len) {
19		int i, j, k, tmp;
20	
(gdb) b 50
Breakpoint 1 at 0x9b6: file gdbtest.c, line 50.
(gdb) r
Starting program: /home/yue/gdbtest 

Breakpoint 1, main () at gdbtest.c:50
50		init_arr(arr, N);
s
init_arr (arr=0x7fffffffdae0, len=10) at gdbtest.c:11
11		int i = 0;
(gdb) n
13		for (i = 0; i < len; i++) {
(gdb) s
14			arr[i] = rand() % 20 + 1;
(gdb) n
13		for (i = 0; i < len; i++) {
(gdb) p i 
$1 = 0
(gdb) continue 
Continuing.
arr[0] = 5
arr[1] = 13
arr[2] = 3
arr[3] = 12
arr[4] = 17
arr[5] = 19
arr[6] = 7
arr[7] = 15
arr[8] = 8
arr[9] = 16
------after sort------
arr[0] = 3
arr[1] = 5
arr[2] = 7
arr[3] = 8
arr[4] = 12
arr[5] = 13
arr[6] = 15
arr[7] = 16
arr[8] = 17
arr[9] = 19
[Inferior 1 (process 3214) exited normally]
(gdb) run   # 增加该行代码后 p[3] = 'M'; 出现段错误,直接使用 run 调试会跳转到段错误所在行
Starting program: /home/yue/gdbtest 
arr[0] = 10
arr[1] = 14
arr[2] = 2
arr[3] = 18
arr[4] = 10
arr[5] = 3
arr[6] = 18
arr[7] = 19
arr[8] = 16
arr[9] = 11

Program received signal SIGSEGV, Segmentation fault.
0x00005555555549e0 in main () at gdbtest.c:52
52		p[3] = 'M';

你可能感兴趣的:(Linux系统编程,linux,vim,运维,ubuntu,gdb,c语言,gcc)