Version: Next

Linux 相关问题

Linux基础文档

Linux 根目录构成

  • 都挂在 / 路径下面
  • Linux 中一切皆 文件,Linux 会把计算机硬件都映射为一个个文件来进行管理
  • 第一部分:必须有的
目录解释
/bin系统有很多放置可执行文件的目录,但 /bin 比较特殊
它放置在单人维护模式下还能够被操作的 指令,可以被 root 与一般账号使用
catchmodchowndatemvcpbash
/boot存放开机使用到的文件
/dev任何设备与周边设备都是以文件的形态存在于这个目录中的,存取这个目录下的文件,等效于存储某个设备
如 /dev/null、/dev/zero、/dev/zero、/dev/loop
/etc系统主要配置文件目录
一般用户可读,只有 root 用户可写,不建议将可执行文件放在这里
/etc/passwd、/etc/fstab、/etc/opt
/lib放置在开机时会用到的库函数,以及 /bin 或者 /sbin 下面的指令会调用到的库函数
/lib/module/ 目录存放可抽换式驱动程序
/mnt挂载额外设备,建议挂载在这个目录
/opt给第三方协力软件放置的目录
习惯上也把第三方安装在 /usr/local 目录
/run存放开机后产生的各项信息
早期在 /var/run,最新规范在 /run
/run 可以使用内存来方针
/sbin设置系统环境的内容,此处为 root 才能使用的,开机过程中需要使用的开机、修复、还原系统指令
某些服务器软件程序,则放置在 /usr/sbin 中
本机自行安装的软件产生的系统可执行文件放在 /usr/local/sbin
/srvservice 的缩写,一些网络服务启动后,这些服务所需要去用数据用的目录
如WWW、FTP
/tmp让一般使用者或者正在执行的程序临时存东西的地方
任何人都能用,建议定期清理
规范建议每次开机时都删除
/usrUnix SoftWare Resource
规范建议开发者将各自数据合理放置到该目录下,而不要自行创建该软件自己独立的目录
类似于 windows 的 C:\Windows 和 C:\Program files 的结合体
/var常态性变动文件,包括高速缓存 cache、登录文件 log file 以及某些软件运行所产生的的文件
包括程序文件 lock file, run file 或例如 MySQL 数据库文件等
/var/lib/mysql
/var/log/ 登录文件存放目录
/var/run 某些程序、服务启动后,会将他们的 PID 存放在这里;链接到 /run
  • 第二部分:建议存在
目录解释
/home系统默认的主使用者的主文件夹,当新增一个一般用户账号时,默认使用的主文件夹
~ 代表当前用户的 home 目录
~用户名:代表某个用户的家目录
/lib<qual>用来存放与 /lib 不同格式的二进制函数库,例如 64 为函数库
/rootroot 用户的主文件夹,放在这里是因为如果进入单人维护模式而仅挂载根目录时,该目录就能够拥有 root 的主文件夹
  • 其他重要目录
目录解释
/lost + found使用标准的 ext2、ext3、ext4 文件系统格式才会产生的目录,用于文件系统发生错误时,将一些遗失的片段存到这里
/proc这个目录本身是一个 虚拟文件系统
他放置的数据都是在 内存 中的,例如系统核心、进程信息、周边设备以及网络状态等
/proc/cpuinfo、/proc/dma、/proc/interrupts、/proc/ioports、/proc/net/*
/sys与 /proc 比较像,也是虚拟文件系统,记录核心与系统硬件相关信息
包括目前已载入的核心模块与核心侦测到的硬件设备信息等
  • 关于 CentOS7 上述目录的一些链接关系
    • /bin -> /usr/bin
    • /sbin -> /usr/sbin
    • /lib -> /usr/lib
    • /lib64 -> /usr/lib64
    • /var/lock -> /run/lock
    • /var/run -> /run

Linux 进程

Linux 进程结构

代码段 + 堆栈段 + 数据段

  • 代码段:存放程序代码的数据,假设计算机中有几个进程运行相同的程序,那么他们可以 共用 相同的代码段
  • 堆栈段:存放子程序的返回地址、子程序的参数以及程序的局部变量 (不能共用)
  • 数据段:存放程序的全局变量、常数、动态分配的数据空间(不能共用)

父进程 | 子进程 | fork | exec 函数族

  • fork 用来创建新进程,该进程几乎是当前进程的一个完全拷贝
  • exec 用来启动另外的进程以取代当前运行的进程

除了 0 号进程由系统创建以外,Linux 中的进程都是由其他进程创建的

  • 创建新进程,则 父进程 为调用 fork() 函数的进程,新创建的进程为 子进程
    • 父进程在创建子进程时,子进程会把父进程的地址空间里的 数据段 进行复制,但不复制代码段
    • 在复制瞬间数据基本一致,但之后各自运行,数据段、堆栈段的数据就开始不一样了
      • 在 fork 时 Unix 体系做了优化,实际上只是 逻辑 fork ,物理上堆栈段数据段还是共享的
      • 一旦写入新数据,则立刻升级为 物理 fork,各自具有独立的堆栈段、数据段
    • 此时父子进程之间的区别仅在于 pidppid 以及资源统计量(例如挂起的信号)

exec() 也可以创建进程

  • 读取可执行文件并载入当前地址空间执行,一般称为 exec 函数族,有一系列以 exec 开头的函数,例如 execlexecve

fork 函数

Fork 函数不需要任何参数,一次调用, 返回两次,分别返回父进程和子进程

  1. 对于父进程,fork 函数返回新建子进程的 pid
  2. 对于子进程,fork 函数返回 0
  3. 如果出错,fork 函数返回 -1
    • 出错原因
      1. 系统中已经有太多的进程
      2. 调用 fork 函数的用户的进程太多

子进程还获得与父进程任何打开文件描述符相同的拷贝,意味着子进程可以读取父进程中打开的任何文件

  • 父进程和新创建的子进程之间最大的 区别 在于他们具有 不同的 PID

execve 函数

execve() 系统调用的作用是运行另一个指定程序

  • 会将新程序加载到当前进程的内存空间内,当前进程被丢弃,它的堆、栈和所有的段数据都会被新进程相应的部分代替
  • 需要提供参数,包括:文件名 filename、参数列表 argv、环境变量 envp
  • 之后,从新程序的 初始化代码main 函数开始执行,进程 PID 保持不变

fork + exec = 启动另一程序且自身保持运行

  • 如果某程序想要启动另一个程序,但自身继续存活运行,那么就好结合 fork 与 exec 的使用

例:shell 命令执行 ps 命令

  • shell 进程 fork 一个子进程
  • 利用 exec 系统调用将子进程完全替换为 ps 进程

孤儿进程与僵尸进程

image-20210820213019840

基本概念

  • 如上图所示,子进程的结束和父进程的结束是一个异步过程,即父进程无法预知子进程什么时候结束
  • 当一个子进程完成工作并终止后,父进程需要调用 wait()waitpid() 系统调用取得子进程的终止状态

孤儿进程

  • 一个父进程退出,而父进程的一个或多个子进程还在运行,那么这些子进程就成了 孤儿进程
  • 孤儿进程将被 init 进程 (进程号为 1)所收养,并由 init 进程对他们完成状态收集工作
  • init 进程 进行了接管,可以认为没什么危害

僵尸进程

  • 一个进程使用 fork 创建子进程,如果子进程退出,而父进程没用调用 wait()waitpid() 获取子进程状态信息,那么子进程的进程描述符仍然保持在系统中,称为 僵尸进程
  • 几乎放弃所有内存空间,没有任何可执行代码,不能被调度,仅在进程列表中保留一个位置,记载该进程的退出状态等信息供其他进程收集
  • 占用了 进程号,如果太多会耗尽系统资源,导致不能开出新进程
  • 僵尸进程不能用 kill 命令清除,因为它是已经退出的进程,**必须清除其 `父进程

如何杀死僵尸进程

  • 如何找到僵尸进程的父进程?
    • ps -ef | grep defunct_process_pid
  • 如何找出僵尸进程?
    • ps aux | grep Z
  • 如何杀掉僵尸进程的父进程
    • kill -s SIGCHLD pid,其中 pid 更换为父进程真实 ID

Linux 进程间通信

推荐阅读 《UNIX 环境高级编程》

  • 管道
  • 共享内存
  • 信号量
  • 套接口 Socket

管道

进程通信中最古老的方式,分为两种

  • 无名管道:用于父进程、子进程通信 —— pipe() 函数
  • 有名管道: 用于同一台计算机上 任意 两个进程间通信 —— mknod()mkfifo 函数

生成管道后,就可以使用一般文件的 I/O 函数,如 openclosereadwrite 等来对它进行操作

共享内存

运行在同一台计算机上的进程之间通信速度最快的方式,因为数据不需要在不同的进程间复制

  • 通常由一个进程创建一块共享内存区,其余进程对这块区域进行读写
  • 在 Linux 下通过 shmXXX 函数族实现利用共享内存进行的存储 (shared memory)
    • 例如 shmget 用于获得一个共享存储标识符
    • 当共享内存创建后,其余进程可以调用 shmat 将其连接到自身的地址空间中
    • void *shmat(int shmid, void *addr, int flag);
    • shmidshmget 函数返回的 共享存储标识符addrflag 参数决定了以什么方式确定连接的地址,函数的返回值是该进程数据段所连接的实际地址,进程可以对此进程进行读写操作
  • 这种方式要考虑 互斥访问 以及 数据同步 问题

信号量

又称 信号灯

  • 用来协调不同进程间的数据对象时,最主要的应用是前一节的共享内存方式的进程间通信
  • 本质上,是一个 计数器,用来记录某个资源的存取情况
  • 为了使用共享内存,一般情况下进程需要执行下列操作
    1. 测试控制该资源的信号量
    2. 若此信号量值 > 0,则允许使用资源,同时数值--
    3. 若信号量值 == 0,则资源不可用,进程进入睡眠状态,直到 值 > 0,进程被唤醒,转入步骤 1
    4. 当进程不再使用一个信号量控制的资源时,信号量++,如果此时有进程正睡眠等待此信号量,则唤醒该进程

套接字 Socket

  • Socket 是实现 Linux 系统和其他大多数操作系统中进程通信的主要方式之一
  • 对传输层的实现

用户态陷入内核态

用户态陷入指令——trap、访管指令(系统调用)

  • 陷入指令是指用户程序所依靠的指令,用于发起 系统调用,请求操作系统提供服务
  • 陷入指令的特殊之处:唯一一种,只能在用户态下执行,不能在核心态执行的指令,在发起系统调用请求,而系统调用的相应处理在核心态下进行
  • 用户程序执行陷入指令,立即产生一个 软中断,相当于将 CPU 的使用主动权交给了操作系统 内核 (从用户态切换到内核态),之后操作系统内核程序再对系统调用请求做出相应的处理
  • 处理完成后,切换回用户态

三种切入方式——中断、异常、陷入机制(系统调用)

系统调用(软中断)

  • 是用户态进程主动要求切换到内核态的一种方式,用户态进程通过 系统调用 申请使用操作系统提供的服务程序完成干工作,例如 fork() 实际上就是执行了一个创建新进程的系统调用 (内核态的 do_fork()
  • 系统调用机制:使用操作系统为用户特别开放的 软中断—— int 80H
  • 通过 中断向量表,查询 中断向量 获取 中断处理程序 的地址
  • 保护现场,处理中断请求

异常

  • 当 CPU 执行运行在用户态的程序时,发生不可知错误,此时会触发由用户态切换到内核态,执行内核中关于异常的处理程序,例如缺页异常

外部中断(硬中断)

  • 外设完成用户请求的操作后,向 CPU 发出响应的硬件中断,CPU 转而进入中断处理流程,如果当时正在执行用户态程序,则会转入内核态

Linux 查看进程状态的指令

两个家伙差不多,源自不同的规范体系

ps -ef | grep

  • 特点是能显示 PID 以及 PPID 父进程ID

ps aux | grep

  • 特点是能显示更多的存储空间消耗信息,比如内存消耗

根据 pid 查看进程状态

  • 方式一:通过包管理安装 pidstat 工具
  • 方式二:cat /proc/[pid]/status

lsof 命令

  • lsof -i :端口号 -> lsof -i :80
  • lsof -i@127.0.0.1 查看到指定主机的连接
  • lsof -i@127.0.0.1:80
  • lsof -i -sTCP:ESTABLISHED 找出任何处于已连接状态的TCP进程
    • lsof -i | grep -i ESTABLISHED
  • lsof -p [pid] 查看指定进程 ID 已打开的内容
  • lsof -iTCP 显示所有TCP进程
  • lsof -i 6 仅获取 IPv6 流量

netstat 命令

  • netstat -ntlp 查看当前所有 TCP 端口
  • netstat -tunlp | grep [端口号] 查看所有端口占用情况
    • -t:显示 TCP 相关
    • -u:显示 UDP 相关
    • -n:拒接显示别名,能显示数字的全部转化为数字
    • -l:仅列出处于 LISTEN 状态的服务
    • -p:显示建立相关连接的进程名字

/proc 路径支持的命令

  • watch -d cat /proc/interruptes
  • cat /proc/stat 提供系统 CPU 和任务统计信息
  • cat /proc/stat | grep ^cpu 提供系统 CPU 和任务统计信息,只需要保留各个 CPU的信息
  • cat /proc/interrupts | more 详细信息
  • cat /proc/interrupts | less
  • ps aux --sort -pcpu | less 根据 CPU 使用,排序,结果能精确到具体程序路径
  • ps aux --sort -pmem | less 根据 内存 使用,排序,结果能精确到具体程序路径
  • cat /proc/cpuinfo | grep "physical id" | sort |uniq |wc -l 查看物理 CPU 数量
  • cat /proc/cpuinfo | grep "cpu cores" | uniq | wc -l 查看每个物理 CPU 的核心数目
  • cat /proc/cpuinfo | grep "processor" | wc -l 查看逻辑 CPU 数目

关机重启命令

  • shutdown -h now 立即关机
  • shutdown -h 1 1分钟后关机
  • shutdown -r now 立刻重启
  • halt 关机
  • reboot 现在重启
  • sync 将内存数据同步到磁盘
提示
  1. 不管是重启系统还是关闭系统,首先要运行 sync 命令将内存数据持久化到磁盘
  2. 目前 shutdownreboothalt 等命令均已经在关机前自动执行 sync,但是还是小心驶得万年船

用户登录与注销命令

  • 用普通用户登录,然后用 su -root 切换到 root 用户,不建议直接登录 root 账户
  • logout 用来注销当前用户
    • Logout 指令在图形运行级别无效,在 运行等级3 以下有效

用户管理

添加用户

  • useradd 新用户名
    • 当创建成功后,会自动创建和用户名相同的 home 目录
    • 也可以通过 useradd -d 指定目录 新用户名 为新创建的用户指定 home 目录位置

指定和修改密码

  • passwd 用户名
  • 如果不写 用户名,默认是 修改当前登录用户的密码