0%

linux相关知识

一、系统知识

1. profile、bashrc、bash_profile之间的关系

  • /etc/profile: 此文件为系统的每个用户设置环境信息,当用户第一次登录时,该文件被执行. 并从/etc/profile.d目录的配置文件中搜集shell的设置。
  • /etc/bashrc: 为每一个运行bash shell的用户执行此文件.当bash shell被打开时,该文件被读取。
  • ~/.bash_profile: 每个用户都可使用该文件输入专用于自己使用的shell信息,当用户登录时,该文件仅仅执行一次!默认情况下,他设置一些环境变量,执行用户的.bashrc文件。
  • ~/.bashrc: 该文件包含专用于你的bash shell的bash信息,当登录时以及每次打开新的shell时,该该文件被读取。
  • ~/.bash_logout: 当每次退出系统(退出bash shell)时,执行该文件. 另外,/etc/profile中设定的变量(全局)的可以作用于任何用户,而~/.bashrc等中设定的变量(局部)只能继承/etc /profile中的变量,他们是”父子”关系。
  • ~/.bash_profile是交互式、login 方式进入 bash 运行的
  • ~/.bashrc 是交互式 non-login 方式进入 bash 运行的通常二者设置大致相同,所以通常前者会调用后者。

2. service的几个知识

2.1. service linux服务管理

  • service有两种管理方式,一个是用service命令,一个使用systemctl,自己感觉没啥区别,都需要写一个service文件
  • service文件位置: /usr/lib/systemd/system/
  • 服务文件内容一般如下,具体作用查看man systemd.service
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
[Unit]
Description=OpenBSD Secure Shell server
Documentation=man:sshd(8) man:sshd_config(5)
After=network.target auditd.service
ConditionPathExists=!/etc/ssh/sshd_not_to_be_run

[Service]
EnvironmentFile=-/etc/default/ssh
ExecStartPre=/usr/sbin/sshd -t
ExecStart=/usr/sbin/sshd -D $SSHD_OPTS
ExecReload=/usr/sbin/sshd -t
ExecReload=/bin/kill -HUP $MAINPID
KillMode=process
Restart=on-failure
RestartPreventExitStatus=255
Type=notify
RuntimeDirectory=sshd
RuntimeDirectoryMode=0755

[Install]
# 这个是用来配置开机启动的目标,在multi-user模式下启动服务
WantedBy=multi-user.target
Alias=sshd.service
  • 配置了WantedBy后,使用systemctl enable xxx可以设置服务在某个模式下启动,会创建一个连接到/etc/systemd/system/xxx.target.wanted/
  • 一般配置开机启动设置WantedBy为multi-user.target,然后执行上一行的命令即可
  • Type=forking会检测pidfile的进程状态

2.2. 查看服务启动日志

1
2
# -f 跟踪日志
journalctl -u [service_name] -f

3. 开机启动脚本

update-rc.d enable/disable/defaults

4. 设备event事件查看

  • 使用下面命令可以查看键盘插入或者触摸板移动等信息
1
2
# -w 跟踪
dmesg -w
  • 使用下面命令查看usb拔插事件
1
udevadm monitor --environment --udev

5. 用户组

5.1. 几个基本用法

1
2
3
4
5
6
# 将用户变更主组到group_name
usermod -g [group_name] [user_name]
# 添加用户组属性,不修改原有所属组
usermod -a -G [group_name] [user_name]
# 查看用户所属组
groups [user_name]

6. 查看系统启动时间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
########## who命令 ##########
# 查看最后一次系统启动时间
=> who -b
系统引导 2022-01-06 10:22
# 查看当前系统运行时间
=> who -r
运行级别 3 2022-01-06 10:24

########## last命令 ##########
=> last reboot
reboot system boot 4.19.181 Thu Jan 6 10:22 still running
reboot system boot 4.19.181 Sat Dec 18 20:06 - 10:20 (18+14:14)
reboot system boot 4.19.181 Sat Dec 18 19:26 - 20:04 (00:38)
reboot system boot 4.19.181 Tue Dec 7 13:49 - 20:04 (11+06:15)
reboot system boot 4.19.163 Mon Nov 22 22:41 - 13:47 (14+15:06)

wtmp begins Mon Nov 22 10:40:48 2021

########## uptime命令 ##########
# 只能看当前时间和启动了多久
=> uptime
10:37:02 up 3 days, 17:50, 0 users, load average: 5.29, 5.16, 5.04

########## /proc/uptime ##########
# 展示从系统启动到现在经过多少s
=> cat /proc/uptime
16235.69 22108.34

7. 定时任务

7.1. cron格式说明

1
2
3
4
5
6
7
8
9
*    *    *    *    *    *
┬ ┬ ┬ ┬ ┬ ┬
│ │ │ │ │ |
│ │ │ │ │ └ 星期 (0 - 7) (0或7都是星期日)
│ │ │ │ └───── 月份 (1 - 12)
│ │ │ └────────── 日期 (1 - 31)
│ │ └─────────────── 小时 (0 - 23)
│ └──────────────────── 分钟 (0 - 59)
└───────────────────────── 秒 (0 - 59, optional)

8. 进程的几种状态

top进程状态解析

9. /dev 介绍

9.1. /dev/random和/dev/urandom

  • 用于产生真随机数,其中/dev/random依赖系统中断,所以较慢,但是随机性更好
  • /dev/urandom不依赖系统中断,随机性较差
  • 它们都依赖一个系统熵
    • 熵足够,两个都是真随机
    • 熵不足,/dev/random会阻塞,/dev/urandom不会阻塞,依赖算法
  • 查看熵池大小: /proc/sys/kernel/random/poolsize
  • 查看熵池可用的数量: /proc/sys/kernel/random/entropy_avail
  • /dev/random和/dev/urandom都是字符型设备,可以当作文件直接读取
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
=> stat /dev/random
File: /dev/random
Size: 0 Blocks: 0 IO Block: 4096 character special file
Device: 0,5 Inode: 8 Links: 1 Device type: 1,8
Access: (0666/crw-rw-rw-) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2022-03-22 09:53:37.442304649 +0800
Modify: 2022-03-22 09:53:37.442304649 +0800
Change: 2022-03-22 09:53:37.442304649 +0800
Birth: -
=> stat /dev/urandom
File: /dev/urandom
Size: 0 Blocks: 0 IO Block: 4096 character special file
Device: 0,5 Inode: 9 Links: 1 Device type: 1,9
Access: (0666/crw-rw-rw-) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2022-03-22 09:53:37.442304649 +0800
Modify: 2022-03-22 09:53:37.442304649 +0800
Change: 2022-03-22 09:53:37.442304649 +0800
Birth: -

如果不存在,可以通过下面命令挂载

1
2
mknod /dev/random c 1 8
mknod /dev/urandom c 1 9

9.2. /dev/kmsg 对接dmesg日志

1
2
3
4
5
6
7
8
9
=> stat /dev/kmsg
File: /dev/kmsg
Size: 0 Blocks: 0 IO Block: 4096 character special file
Device: 0,6 Inode: 10 Links: 1 Device type: 1,11
Access: (0644/crw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2024-09-27 10:33:35.640000000 +0800
Modify: 2024-09-27 10:33:35.640000000 +0800
Change: 2024-09-27 10:33:35.640000000 +0800
Birth: 2024-09-27 10:33:29.151054145 +0800

想要输出到dmesg,可以直接对齐进行输出,如echo "aaa" > /dev/kmsg,这样dmesg里面可以直接看到对应日志

10. /proc介绍

  • /proc内存放了进程的信息

10.2. /proc/[pid] 进程信息

  • /proc/self为当前进程信息,可以直接在程序中进行读取

1) fd 保存当前进程打开的句柄

  • 0: 标准输入
  • 1: 标准输出
  • 2: 标准错误输出

2) maps 进程内存空间信息

  • 加载了什么库及其地址
  • 进程的基地址信息

3) limits 显示进程的一些限制

  • unlimited就是没有限制
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
=> cat /proc/self/limits
Limit Soft Limit Hard Limit Units
Max cpu time unlimited unlimited seconds
Max file size unlimited unlimited bytes
Max data size unlimited unlimited bytes
Max stack size 8388608 unlimited bytes
Max core file size unlimited unlimited bytes
Max resident set unlimited unlimited bytes
Max processes 14993 14993 processes
Max open files 1024 4096 files
Max locked memory 16777216 16777216 bytes
Max address space unlimited unlimited bytes
Max file locks unlimited unlimited locks
Max pending signals 14993 14993 signals
Max msgqueue size 819200 819200 bytes
Max nice priority 0 0
Max realtime priority 0 0
Max realtime timeout unlimited unlimited us

硬限制和软限制

  • 硬限制只能由root用户进行扩大
  • 软限制由用户自己修改,不能超过硬限制

4) cwd 当前工作目录

  • cwd是一个软连接,指向对应的工作目录
  • 查看进程当前工作目录
1
2
=> ll /proc/57733/cwd
lrwxrwxrwx 1 test test 0 Aug 16 09:29 /proc/57733/cwd -> /home/test

10.3. /proc/sys 保存内核的参数

1) 列举几个内核参数含义

  • /proc/sys/fs/file-max: 系统最多能打开的文件句柄数
  • /proc/sys/kernel/threads-max: 系统最大线程数量
  • /proc/sys/kernel/pid-max: pid的最大数

2) /proc/sys/net/ipv4/conf/interface/rp_filter

rp_filter在内核代码如何生效

  • 对应sysctl变量net.ipv4.conf.[interface].rp_filter
  • 内核取值会取all和当前进入的网卡较大的那个值
  • rp_filter设置成1则会在当前网卡收包处理时,如果发现源地址作为目的地址路由不是指向此网卡,将会丢包,具体原理看上面链接
  • 设置值的说明见 rp_filter|sysctl-explorer.net

10.4. /proc/interrupts cpu累积收到的硬中断次数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
=> sudo cat /proc/interrupts
CPU0 CPU1 CPU2 CPU3 CPU4 CPU5 CPU6 CPU7
0: 20 0 0 0 0 0 0 0 IR-IO-APIC 2-edge timer
1: 0 0 0 0 0 0 9998 0 IR-IO-APIC 1-edge i8042
8: 0 0 0 0 0 0 0 0 IR-IO-APIC 8-edge rtc0
9: 0 8 0 0 0 0 0 0 IR-IO-APIC 9-fasteoi acpi
12: 0 0 0 0 0 140 0 0 IR-IO-APIC 12-edge i8042
14: 0 55570 0 0 0 0 0 0 IR-IO-APIC 14-fasteoi INT344B:00
16: 0 0 0 0 759315 0 0 0 IR-IO-APIC 16-fasteoi idma64.0, i2c_designware.0, i801_smbus
17: 0 0 0 0 0 0 0 0 IR-IO-APIC 17-fasteoi idma64.1, i2c_designware.1
120: 0 0 0 0 0 0 0 0 DMAR-MSI 0-edge dmar0
121: 0 0 0 0 0 0 0 0 DMAR-MSI 1-edge dmar1
122: 0 0 0 0 0 0 0 0 IR-PCI-MSI 458752-edge PCIe PME, aerdrv
123: 0 0 0 0 0 0 0 0 IR-PCI-MSI 473088-edge PCIe PME, aerdrv
124: 0 0 0 0 0 0 0 0 IR-PCI-MSI 475136-edge PCIe PME, aerdrv
125: 0 0 5073 0 32 0 0 0 IR-PCI-MSI 327680-edge xhci_hcd
126: 0 0 0 17 0 0 0 0 IR-PCI-MSI 1572864-edge nvme0q0
127: 0 0 0 0 19640 0 0 0 IR-PCI-MSI 1572865-edge nvme0q1
128: 0 10107 0 0 0 0 0 0 IR-PCI-MSI 1572866-edge nvme0q2
129: 0 0 13191 0 0 0 0 0 IR-PCI-MSI 1572867-edge nvme0q3
130: 0 0 0 7165 0 0 0 0 IR-PCI-MSI 1572868-edge nvme0q4
131: 0 0 0 0 0 9568 0 0 IR-PCI-MSI 1572869-edge nvme0q5
132: 0 0 0 0 0 0 10363 0 IR-PCI-MSI 1572870-edge nvme0q6
133: 0 0 0 0 0 0 0 6400 IR-PCI-MSI 1572871-edge nvme0q7
134: 0 55570 0 0 0 0 0 0 INT344B:00 99 ETD2303:00
135: 0 0 0 0 0 0 41 0 IR-PCI-MSI 360448-edge mei_me
136: 0 0 0 0 0 0 0 357 IR-PCI-MSI 1048576-edge iwlwifi
137: 0 12 0 0 0 0 43 0 IR-PCI-MSI 524288-edge nvkm
138: 0 0 103 630743 0 4805 0 0 IR-PCI-MSI 32768-edge i915
139: 0 0 0 1144 0 0 0 0 IR-PCI-MSI 514048-edge snd_hda_intel:card0
NMI: 23 23 22 22 23 22 22 24 Non-maskable interrupts
LOC: 1949275 1734042 1936075 1538172 1801814 1862963 1854126 2040015 Local timer interrupts
SPU: 0 0 0 0 0 0 0 0 Spurious interrupts
PMI: 23 23 22 22 23 22 22 24 Performance monitoring interrupts
IWI: 12652 12187 12147 310841 13382 16848 12683 11054 IRQ work interrupts
RTR: 0 0 0 0 0 0 0 0 APIC ICR read retries
RES: 6294 10746 6760 6099 6037 6368 5831 7977 Rescheduling interrupts
CAL: 142829 133636 126459 119454 124950 121592 125146 125214 Function call interrupts
TLB: 81943 78817 75641 75416 76803 74568 81688 81265 TLB shootdowns
TRM: 0 0 0 0 0 0 0 0 Thermal event interrupts
THR: 0 0 0 0 0 0 0 0 Threshold APIC interrupts
DFR: 0 0 0 0 0 0 0 0 Deferred Error APIC interrupts
MCE: 0 0 0 0 0 0 0 0 Machine check exceptions
MCP: 6 7 7 7 7 7 7 7 Machine check polls
ERR: 0
MIS: 0
PIN: 0 0 0 0 0 0 0 0 Posted-interrupt notification event
NPI: 0 0 0 0 0 0 0 0 Nested posted-interrupt event
PIW: 0 0 0 0 0 0 0 0 Posted-interrupt wakeup event

说明

  • 第一列是中断号,这个后面单独一个表格说明
  • 后面几列就是这个中断号在每个cpu的累积次数了
  • 最后一列是中断控制器和关联的设备

1) 中断号说明

TODO 待补充

10.5. /proc/softirqs cpu累积收到的软中断次数

1
2
3
4
5
6
7
8
9
10
11
12
=> cat /proc/softirqs
CPU0 CPU1 CPU2 CPU3 CPU4 CPU5 CPU6 CPU7
HI: 85496 91206 68246 774236 99925 84401 78588 64873
TIMER: 81679 56531 55439 194830 59389 55766 55408 55580
NET_TX: 2 0 0 1 2 1 1 0
NET_RX: 4112 3831 4385 3805 3351 4641 4935 4283
BLOCK: 115 116 82 167 158 273 70 162
IRQ_POLL: 0 0 0 0 0 0 0 0
TASKLET: 127 0 4949 1 25 2 11653 31
SCHED: 588778 555570 531161 558175 524359 507878 504310 510537
HRTIMER: 0 0 0 0 0 0 0 0
RCU: 324282 324077 318224 355089 325243 321701 319715 320503
  • 第一列是软中断,后面是累积收到的次数

TODO 说明所有的软中断是干嘛的

11. 管道

  • 进程间通信可以使用管道
  • 管道的本质是起了两个子进程,一个进程的标准输出为另一个进程的标准输入

11.1. 示例

  • 使用read让管道两边的进程阻塞
1
2
=> { echo "aaa"; read n } | { cat; read p }
aaa
  • 查看进程树
1
2
3
4
5
6
=> ps -auxf
...
test_user 2262 0.0 0.0 16840 8192 pts/2 Ss 11:41 0:01 | \_ -zsh
test_user 10342 0.0 0.0 16840 3412 pts/2 S+ 14:20 0:00 | | \_ -zsh
test_user 10343 0.0 0.0 10792 976 pts/2 S+ 14:20 0:00 | | \_ cat
...
  • 明显看到父进程起了两个子进程
  • 再查看它们的句柄
1
2
3
4
5
6
7
8
9
10
11
=> ls -l /proc/10342/fd
total 0
lrwx------ 1 test_user test_user 64 Jun 20 14:27 0 -> /dev/pts/2
l-wx------ 1 test_user test_user 64 Jun 20 14:27 1 -> 'pipe:[224646]'
lrwx------ 1 test_user test_user 64 Jun 20 14:27 10 -> /dev/pts/2
lrwx------ 1 test_user test_user 64 Jun 20 14:27 2 -> /dev/pts/2
=> ls -l /proc/10343/fd
total 0
lr-x------ 1 test_user test_user 64 Jun 20 14:27 0 -> 'pipe:[224646]'
lrwx------ 1 test_user test_user 64 Jun 20 14:27 1 -> /dev/pts/2
lrwx------ 1 test_user test_user 64 Jun 20 14:27 2 -> /dev/pts/2
  • 左边进程的标准输出到管道,右边进程的标准输入为管道

12. 文件类型

  • linux下存在很多种文件类型,一切皆文件
  • 使用ls -l可以查看文件类型

几种类型

  • -: 普通文件,文本或二进制
  • d: 目录
  • l: 链接
  • b: 块设备,磁盘就是块设备,可以向前向后读
  • c: 字符型设备,比如/dev/zero、/dev/random等,只能读取当前
  • s: 套接字文件,用于链接建立等
  • p: 管道文件,不过一般是命名管道,正常的管道一般不可见

13. 进程

13.1. 最多能创建多少线程

  • 取决于进程的虚拟内存大小和每个线程需要占用的栈大小
  • 32位系统下,4G虚拟内存,内核占用1G,剩余3G。每个线程栈大小,由ulimit -s可以查看,假设8M。那么最多创建300多个线程就会内存不足
  • 64位系统下,取决于linux系统下的线程最大数量、全局pid号数量限制、进程可以拥有的VMA数量

13.2. 最大能打开的文件句柄数

14. 可执行文件的结构

14.1. 查看可执行文件的大小

1
2
3
4
5
#include <stdio.h>

int main(int argc, char **argv) {
printf("Hello, world");
}
1
2
3
4
=> gcc -g main.cpp
=> size a.out
text data bss dec hex filename
1576 616 8 2200 898 a.out

14.2. 解析

1) bss 未初始化数据区

  • Block Started by Symbol 以符号开始的段,通常指未初始化的数据段
  • 代码中定义的未初始化的全局变量和静态变量存放到此区域
  • 此区域会被内核初始化成0,所以为什么没有初始化的全局变量和静态变量会是0的原因
1
2
3
4
5
6
7
#include <stdio.h>

int a[1000];

int main(int argc, char **argv) {
printf("Hello, world");
}
1
2
3
4
=> gcc -g main.cpp
=> size a.out
text data bss dec hex filename
1576 616 4008 6200 1838 a.out
  • 可以看到bss段增加了4 * 1000字节

2) data 静态数据和全局初始化的数据区

  • 初始化的静态变量和全局变量会放到此区域
  • 初始化成0的变量会被编译器优化放到bss段
1
2
3
4
5
6
7
8
#include <stdio.h>

int a = 100;
int b = 0;

int main(int argc, char **argv) {
printf("Hello, world");
}
1
2
3
4
=> gcc -g main.cpp
=> size a.out
text data bss dec hex filename
1576 620 12 2208 8a0 a.out
  • 可以看到定义了两个int型变量,一个增加了data段,一个增加了bss段,各4个字节

3) text 代码段,存放代码指令

1
2
3
4
5
6
#include <stdio.h>

int main(int argc, char **argv) {
printf("Hello, world");
printf("Hello, world1");
}
1
2
3
4
=> gcc -g main.cpp
=> size a.out
text data bss dec hex filename
1605 616 8 2229 8b5 a.out
  • bss和data都没增加,text段增加了代码指令

二、有用的几个技巧

1. linux启动到文本界面和到图形界面

1
2
3
4
# 启动到文本界面
systemctl set-default multi-user.target
# 启动到图形界面
systemctl set-default graphical.target

2. 修改硬盘的uuid

1
2
3
4
# 随机生成uuid写入
uuidgen | xargs tune2fs /dev/sda5 -U
# 写入自己生成uuid
tune2fs -U c1b9d5a2-f162-11cf-9ece-0020afc76f16 /dev/sdb1

3. hostname立即生效

1
hostname "$(cat /etc/hostname)"

4. bpf 主要用于网络数据分析

原理参考 Linux超能力BPF技术介绍及学习分享

  • 新版tcpdump就是基于bpf技术实现的

5. 设置无操作用户自动退出

  • 修改/etc/profile,添加
1
2
export TMOUT=15   # 无操作15秒自动退出
readonly TMOUT # 不允许用户修改此变量

6. 系统时间和硬件时间同步

  • 使用hwclock命令
  • 下面是几种基本用法
1
2
3
4
5
6
7
# 显示硬件时间,启动就会读取的时间,和系统不一致就是每次重启时间都不对
=> sudo hwclock --show
2022-12-09 17:35:28.288827+08:00
# 硬件时间同步到系统时间,开机会自动执行
=> sudo hwclock --hctosys
# 系统时间同步到硬件时间,可以在同步系统时间后进行执行,保证重启后和当前一致
=> sudo hwclock --systohc

7. 设置时区

1
ln -sf /usr/share/zoneinfo/Asia/Beijing /etc/localtime

8. 添加一个用户

1
2
3
4
5
6
7
8
9
# 添加用户
# -d 设置home目录
# -s 设置shell
useradd test -d /home/test -s /bin/zsh
# 设置密码
passwd test
# 添加目录
mkdir /home/test
chown test:test /home/test

8.1. 给用户执行sudo的权力

  • 需要安装sudo包,然后执行visudo
  • 在打开的sudoers文件中,找到以下行:root ALL=(ALL) ALL。
  • 在该行下面添加以下内容:username ALL=(ALL) ALL,其中username是你要授予管理员权限的用户名。
  • 保存并关闭sudoers文件。

9. journalctl 检查服务日志

9.1. 选项

  • -u <unit>: 显示具体服务当前的日志

10. 命令行配置socks5代理

1
2
export http_proxy="socks5://127.0.0.1:1080"
export https_proxy="socks5://127.0.0.1:1080"

11. 串口转USB读取

CH340系列需要看看设备有没有驱动,没有需要下载

1
2
3
=> modinfo ch341
filename: /lib/modules/6.11.6-arch1-1/kernel/drivers/usb/serial/ch341.ko.zst
...

下载地址在 https://www.wch.cn/download/CH341SER_LINUX_ZIP.html

驱动没问题后,可以从dmesg中看到usb插上后映射到/dev/ttyUSBx,就可以使用minicom来进行串口读写了,打开命令如下

1
2
3
4
5
# -D /dev/ttyUSB0   打开设备是/dev/ttyUSB0
# -b 115200 设置波特率为115200
# -w 启动自动换行
# -h 使用hex显示
minicom -D /dev/ttyUSB0 -b 115200 -w

minicom快捷键

  • Ctrl+a,x: 退出
  • Ctrl+a,z: 打开帮助面板
  • Ctrl+a,c: 清屏
  • Ctrl+a,w: 启禁用自动换行
  • Ctrl+a,e: 启禁用输入显示

三、c/c++编程

1. 获取系统信息

  • sysinfo结构体定义
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// sysinfo.h
struct sysinfo {
__kernel_long_t uptime; /* Seconds since boot */
__kernel_ulong_t loads[3]; /* 1, 5, and 15 minute load averages */
__kernel_ulong_t totalram; /* Total usable main memory size */
__kernel_ulong_t freeram; /* Available memory size */
__kernel_ulong_t sharedram; /* Amount of shared memory */
__kernel_ulong_t bufferram; /* Memory used by buffers */
__kernel_ulong_t totalswap; /* Total swap space size */
__kernel_ulong_t freeswap; /* swap space still available */
__u16 procs; /* Number of current processes */
__u16 pad; /* Explicit padding for m68k */
__kernel_ulong_t totalhigh; /* Total high memory size */
__kernel_ulong_t freehigh; /* Available high memory size */
__u32 mem_unit; /* Memory unit size in bytes */
char _f[20-2*sizeof(__kernel_ulong_t)-sizeof(__u32)]; /* Padding: libc5 uses this.. */
};
  • 获取系统信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <sys/sysinfo.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>

int main() {
struct sysinfo info;
// 操作成功返回0,失败返回-1
if (sysinfo(&info)) {
printf("failed to get sysinfo, errno: %u, reason: %s\n", errno, strerror(errno));
return 1;
}
// do something
return 0;
}

2. 获取进程id

1
2
// unistd.h
__pid_t getpid (void) __THROW;

3. 获取系统时间

  • 一般获取系统时间有两个函数gettimeofday和clock_gettime
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// /usr/include/time.h
/* Get current value of clock CLOCK_ID and store it in TP. */
extern int clock_gettime (clockid_t __clock_id, struct timespec *__tp)
__THROW __nonnull((2));

// /usr/include/time.h
/* Get the current time of day, putting it into *TV.
If TZ is not null, *TZ must be a struct timezone, and both fields
will be set to zero.
Calling this function with a non-null TZ is obsolete;
use localtime etc. instead.
This function itself is semi-obsolete;
most callers should use time or clock_gettime instead. */
#ifndef __USE_TIME_BITS64
extern int gettimeofday (struct timeval *__restrict __tv,
void *__restrict __tz) __THROW __nonnull ((1));
#else
# ifdef __REDIRECT_NTH
extern int __REDIRECT_NTH (gettimeofday, (struct timeval *__restrict __tv,
void *__restrict __tz),
__gettimeofday64) __nonnull ((1));
# else
# define gettimeofday __gettimeofday64
# endif
#endif
  • clock_gettime有下面几种类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// /usr/include/bits/time.h
/* Identifier for system-wide realtime clock. */
# define CLOCK_REALTIME 0 // 从1970.01.01到现在的时间
/* Monotonic system-wide clock. */
# define CLOCK_MONOTONIC 1 // 系统启动到现在的时间
/* High-resolution timer from the CPU. */
# define CLOCK_PROCESS_CPUTIME_ID 2 // 进程所消耗的时间,sleep不再计时
/* Thread-specific CPU-time clock. */
# define CLOCK_THREAD_CPUTIME_ID 3 // 线程消耗时间,sleep不再计时
/* Monotonic system-wide clock, not adjusted for frequency scaling. */
# define CLOCK_MONOTONIC_RAW 4 // 硬件时间
/* Identifier for system-wide realtime clock, updated only on ticks. */
# define CLOCK_REALTIME_COARSE 5 // 不精确的realtime,速度更快精度更低
/* Monotonic system-wide clock, updated only on ticks. */
# define CLOCK_MONOTONIC_COARSE 6 // 不精确的MONOTONIC,速度更快精度更低
/* Monotonic system-wide clock that includes time spent in suspension. */
# define CLOCK_BOOTTIME 7
/* Like CLOCK_REALTIME but also wakes suspended system. */
# define CLOCK_REALTIME_ALARM 8
/* Like CLOCK_BOOTTIME but also wakes suspended system. */
# define CLOCK_BOOTTIME_ALARM 9
/* Like CLOCK_REALTIME but in International Atomic Time. */
# define CLOCK_TAI 11 // 原子钟的时间,与CLOCK_REALTIME类似,不可被更改,没有闰秒

4. errno

  • errno线程唯一,不用担心多线程问题
  • errno在头文件unistd.h
  • strerror()在头文件string.h

四、内存管理

1. 虚拟内存

  • 虚拟内存是每个进程有自己的虚拟地址空间,不同的进程之间的虚拟地址空间一样,但是对应的物理地址空间不一样
  • 32位下,每个进程拥有4G虚拟地址空间,其中1G为内核空间,3G为用户空间
  • 每个进程有一个页表,存放在进程的信息中,mmu处理每个进程的虚拟地址到物理地址映射时要读取不同进程的页表,通过页表寄存器指定页表地址

2. linux内存分布

64位下理论可以操作的内存范围0x0000000000000000~`0xffffffffffffffff`(16777216TB),但是linux上仅使用低47位,高17位用于扩展。内存范围如下分配

取自 https://www.kernel.org/doc/Documentation/x86/x86_64/mm.txt

Start addrEnd addrSizeVM area description
000000000000000000007fffffffffff128 TB用户地址空间,每个进程mm指向的地址不一样
0000800000000000ffff7fffffffffff~16M TB大空洞
内核地址空间:
ffff800000000000ffff87ffffffffff8 TB… guard hole, also reserved for hypervisor
ffff880000000000ffff887fffffffff0.5 TBLDT(Local Descriptor Table):局部描述符表
KPTI(Kernel page-table isolation):内核页表隔离
ffff888000000000ffffc87fffffffff64 TB线性映射的区域 (page_offset_base)
ffffc88000000000ffffc8ffffffffff0.5 TB… unused hole
ffffc90000000000ffffe8ffffffffff32 TBvmalloc和ioremap空间 (vmalloc_base)
ffffe90000000000ffffe9ffffffffff1 TB… unused hole
ffffea0000000000ffffeaffffffffff1 TBpage结构存储的位置 (vmemmap_base)
ffffeb0000000000ffffebffffffffff1 TB… unused hole
ffffec0000000000fffffbffffffffff16 TBKASAN影子内存
从这里开始,与56-bit布局相同:
fffffc0000000000fffffdffffffffff2 TB… unused hole
vaddr_end for KASLR
fffffe0000000000fffffe7fffffffff0.5 TBcpu_entry_area mapping
fffffe8000000000fffffeffffffffff0.5 TB… unused hole
ffffff0000000000ffffff7fffffffff0.5 TB%esp fixup stacks
ffffff8000000000ffffffeeffffffff444 GB… unused hole
ffffffef00000000fffffffeffffffff64 GBEFI region mapping space
ffffffff00000000ffffffff7fffffff2 GB… unused hole
ffffffff80000000ffffffff9fffffff512 MB内核代码区域,mapped to physical address 0
ffffffffa0000000fffffffffeffffff1520 MB模块加载区域
FIXADDR_STARTffffffffff5fffff~0.5 MBkernel-internal fixmap range, variable size and offset
ffffffffff600000ffffffffff600fff4 kBlegacy vsyscall ABI
ffffffffffe00000ffffffffffffffff2 MB… unused hole

五、进程

1. 进程间通信

  • 进程间通信有管道、命名管道、信号、信号量、消息队列、共享内存、套接字

1.1. 管道

1) 特点

  • 数据只能向一个方向流动
  • 只能用于父子进程或兄弟进程
  • fifo的读出和写入
  • 缓冲区有限,本身存在于内存中,分配了一个page大小

2) 创建

  • 通过shell的|创建
  • 通过代码的创建
1
int pipe(int fd[2]);
  • 管道的创建需要在父进程,然后再fork出子进程使用,或给两个兄弟子进程使用

1.2. 命名管道

1) 特点

  • 需要提供一个文件路径,两个进程通过路径进行联系,所以可以没有亲缘关系的进程通信
  • 内部没有fifo,读写会阻塞
  • 其他和管道一致

2) 创建

  • shell通过mkfifo xxx创建管道,使用>和<进行读取写入
  • 通过代码创建,正常按照文件一样打开和读写即可
1
int mkfifo(const char *pathname, mode_t mode);

1.3. 信号

1) 特点

  • 进程不知道信号什么时候来,类似于中断
  • 信号通俗讲就是系统定义的信号,也就是kill可以发出的信号

2) 使用

  • 代码注册信号
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/*
* 函数名:BR_SignalRegister
* 功 能:BugReport信号注册函数
*/
static int BR_SignalRegister(int sig) {
struct sigaction act;
memset(&act, 0, sizeof(act));
act.sa_handler = (__sighandler_t)BR_SignalHandler;
act.sa_flags = 0;
(void)sigemptyset(&act.sa_mask);

while (sigaction(sig, &act, NULL) < 0) {
if (errno != EINTR) {
(void)fprintf(stderr, "[BugReport]sigaction %s failed: %s\n",
BR_signals[sig], strerror(errno));
return -1;
}
}

return 0;
}

1.4. 信号量

1) 特点

  • 主要用于进程间共享资源访问控制机制
  • 两种类型
    • 二值信号量:只能0和1
    • 计算信号量:非负值

2) 使用

  • 创建使用
1
int semget(key_t key, int num_sems, int sem_flags)
  • 操作函数
1
2
3
4
// 改变信号量
int semop(int sem_id, struct sembuf *sem_opa, size_t num_sem_ops);
// 控制信号量
int semctl(int sem_id, int sem_num, int command, ...);

1.5. 消息队列

1) 特点

  • 就是在命名管道上加了个fifo,避免阻塞
  • 可以通过消息类型有选择的接收数据

2) 使用

1
2
3
4
5
6
// 创建或获取已存在的消息队列
int msgget(key_t key, int msgflg);
// 发送
int msgsnd(int msqid, void *msgp, int msgsz, int msgflg);
// 接收
int msgrcv(int msgid, void *msgp, int msgsz, long msgtyp, int msgflg);

1.6. 共享内存

1
int shmget(key_t key, size_t size, int shmflg);

1.7. 套接字

六、nss: Name Service Switch

Name Service Switch(NSS)是一个在Linux和其他类Unix操作系统中使用的系统组件,它提供了一种机制来管理系统中各种名称服务的配置。名称服务包括用户帐户、组、主机名、网络协议、服务和其他系统信息。NSS允许系统管理员配置系统以使用不同的名称服务,例如本地文件、NIS、LDAP等。NSS还提供了一种机制来缓存名称服务数据,以提高系统性能。在Linux系统中,NSS通常由glibc库实现。

1. 配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
=> cat /etc/nsswitch.conf
# Name Service Switch configuration file.
# See nsswitch.conf(5) for details.

passwd: files systemd
group: files [SUCCESS=merge] systemd
shadow: files systemd
gshadow: files systemd

publickey: files

hosts: mymachines resolve [!UNAVAIL=return] files myhostname dns
networks: files

protocols: files
services: files
ethers: files
rpc: files

netgroup: files
  • 上面配置的files等对应一个个插件,对应命名libnss_xxx.so
  • 修改了之后,从glibc 2.33后,会自动重新加载此文件,之前的版本只在进程起来的时候加载一次

2. 插件位置

1
2
3
4
=> ls /usr/lib/libnss_*
/usr/lib/libnss_compat.so /usr/lib/libnss_db.so.2 /usr/lib/libnss_hesiod.so /usr/lib/libnss_libvirt.so.2 /usr/lib/libnss_resolve.so.2 /usr/lib/libnss_wins.so.2
/usr/lib/libnss_compat.so.2 /usr/lib/libnss_dns.so.2 /usr/lib/libnss_hesiod.so.2 /usr/lib/libnss_myhostname.so.2 /usr/lib/libnss_systemd.so.2
/usr/lib/libnss_db.so /usr/lib/libnss_files.so.2 /usr/lib/libnss_libvirt_guest.so.2 /usr/lib/libnss_mymachines.so.2 /usr/lib/libnss_winbind.so.2

3. 自定义插件

  • 可以自定义so库,满足此命名,然后放到系统库路径后,添加到配置文件中
  • 需要满足nss对应的库的符号要求

3.1. 自定义dns解析库

  • dns解析由hosts处理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#include <stddef.h>
#include <nss.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <syslog.h>
#include <zconf.h>
#include <stdio.h>

enum nss_status _nss_hs_gethostbyname4_r(
const char *name,
struct gaih_addrtuple **pat,
char *buffer, size_t buflen,
int *errnop, int *h_errnop,
int32_t *ttlp) {

fprintf(stdout, "_nss_hs_gethostbyname4_r ns hook domain:%s pid:%d\n", name, getpid());

return NSS_STATUS_NOTFOUND;
}

enum nss_status _nss_hs_gethostbyname3_r(
const char *name,
int af,
struct hostent *host,
char *buffer, size_t buflen,
int *errnop, int *h_errnop,
int32_t *ttlp,
char **canonp) {

fprintf(stdout, "_nss_hs_gethostbyname3_r ns hook domain:%s pid:%d\n", name, getpid());

return NSS_STATUS_NOTFOUND;
}

enum nss_status _nss_hs_gethostbyname2_r(
const char *name,
int af,
struct hostent *host,
char *buffer, size_t buflen,
int *errnop, int *h_errnop) {

return _nss_hs_gethostbyname3_r(
name,
af,
host,
buffer, buflen,
errnop, h_errnop,
NULL,
NULL);
}

enum nss_status _nss_hs_gethostbyname_r(
const char *name,
struct hostent *host,
char *buffer, size_t buflen,
int *errnop, int *h_errnop) {

return _nss_hs_gethostbyname3_r(
name,
AF_UNSPEC,
host,
buffer, buflen,
errnop, h_errnop,
NULL,
NULL);
}

void _nss_hs_init(void (*cb)(size_t, struct traced_file*)) {
fprintf(stdout, "_nss_hs_init...");
}
  • 编译
1
gcc -shared -fPIC -o libnss_test.so.2 -Wl,-soname,libnss_test.so.2 test_nss.c
  • 放到/usr/lib下后,重启要使用gethostbyname的进程就好了

4. hosts dns解析服务

  • mymachines: 由systemd提供,应该是处理容器类型的dns解析
  • resolve: 由systemd提供,是systemd-resolved.service的处理服务
  • files: 由glibc提供,处理/etc/hosts
  • myhostname: 由systemd提供,处理localhost、_gateway、_outbound等域名使用,主要是查本地网卡的一些信息
  • dns: 由glibc提供,处理dns的网络请求使用

七、二进制文件格式

参考 ELF文件格式

1. 链接视图和执行视图

  • 链接视图是以节(section)为单位,用于在链接时使用,可以是静态库、编译过程文件、动态库等
  • 执行视图是以段(segment)为单位,是在执行时用到的视图。
  • 目标文件.o里的代码段.text 是section(汇编中.text同理),当多个可重定向文件最终要整合成一个可执行的文件的时候(链接过程),链接器把目标文件中相同的 section 整合成一个segment,在程序运行的时候,方便加载器的加载

2. elf文件

八、dbus机制

https://blog.csdn.net/alibutter/article/details/141575257

九、cgroup

1. systemd-cgtop 查看系统当前cgroup情况

和top差不多都是实时刷新

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
CGroup                                                                                     Tasks   %CPU   Memory  Input/s Output/s
/ 3134 36.8 16.4G - -
user.slice 2705 27.9 27.1G - -
user.slice/user-1000.slice 2705 27.9 27G - -
user.slice/user-1000.slice/user@1000.service 2608 27.7 26.4G - -
system.slice 199 1.9 1.3G - -
system.slice/sddm.service 8 1.5 468.3M - -
system.slice/NetworkManager.service 4 0.2 17.4M - -
user.slice/user-1000.slice/session-5.scope 63 0.1 494.4M - -
system.slice/containerd.service 68 0.1 102.1M - -
system.slice/home-wangyubo-vmware.mount 1 0.0 3.7M - -
user.slice/user-1000.slice/session-11.scope 22 0.0 10.5M - -
system.slice/dbus-broker.service 2 0.0 6.3M - -
system.slice/docker-383ac0c85f0a78abd68509f4aa44e3e83be335e91386be3074aa43190a14efd0.scope 2 0.0 12.6M - -
dev-hugepages.mount - - 164K - -
dev-mqueue.mount - - 36K - -
init.scope 1 - 6.5M - -

1.1. 快捷键

  • p: 按照CGroup路径排序
  • t: 按照task数量排序
  • c: 按照cpu排序
  • m: 按照内存排序
  • i: 按照input排序
  • +/-: 增加减少刷新间隔
  • %: cpu显示在百分比和总时长之间切换

十、linux启动引导过程

1. UEFI引导

1) 列举引导分区

PC启动会优先寻找所有硬盘的所有分区是否有esp标记,其实就是下面的EFI System,用分区工具也能看到boot,esp标记

1
2
3
4
5
6
7
8
9
10
11
12
13
14
=> sudo fdisk -l /dev/nvme0n1
Disk /dev/nvme0n1: 119.24 GiB, 128035676160 bytes, 250069680 sectors
Disk model: WDC PC SN520 SDAPNUW-128G-1006
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: gpt
Disk identifier: 26665042-C9F5-3C4E-BEAA-7215C03C71CF

Device Start End Sectors Size Type
/dev/nvme0n1p1 2048 2099199 2097152 1G EFI System
/dev/nvme0n1p2 2099200 27265023 25165824 12G Linux filesystem
/dev/nvme0n1p3 27265024 153094143 125829120 60G Linux filesystem
/dev/nvme0n1p5 153094144 250069646 96975503 46.2G Linux filesystem

找到这个标记的分区后,查找里面的EFI/xxx/grubx64.efi,显示到bootloader的引导菜单,如某一个磁盘A如下

1
2
3
4
5
6
=> find /boot/efi/EFI -name "*.efi"
/boot/efi/EFI/GRUB/grubx64.efi
/boot/efi/EFI/Microsoft/Boot/memtest.efi
/boot/efi/EFI/Microsoft/Boot/bootmgfw.efi
/boot/efi/EFI/Microsoft/Boot/bootmgr.efi
/boot/efi/EFI/Boot/bootx64.efi

会在PC的默认引导菜单(F12或其他按键进入)显示一行(多级目录不会找,所以microsoft不会显示;Boot是兼容兜底用的的也不会显示),B磁盘同样操作追加到后面。

1
2
GRUB(A硬盘)
xxx(B硬盘)

2) 选中引导分区后,加载分区里面的EFI/xxx/grubx64.efi

加载这个二进制后,基于二进制里面的代码查找grub.cfg。二进制里面会设置prefix,也就是grub.cfg所在的目录。可以用strings命令查看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# archlinux上的grub-install输出如下,默认找硬盘分区3(基于配置生成)的/boot/grub
=> strings /boot/efi/EFI/GRUB/grubx64.efi | grep -i boot
prohibited by secure boot policy
SecureBoot
UEFI Secure Boot state: %s
grub_boot_time_head
grub_efi_finish_boot_services
grub_efi_get_secureboot
grub_real_boot_time
grub_reboot
(,gpt3)/boot/grub

# ubuntu上的grub-install就不一样,默认找当前esp分区的/EFI/ubuntu/下的文件,写死的
=> strings /boot/efi/EFI/GRUB/grubx64.efi | grep -i efi
...
/EFI/ubuntu

如果引导没进去,打开了GRUB命令行,可能是找不到grub.cfg。可以查看一下prefix的值并找一下里面有没有grub.cfg,可能目录错了

1
2
grub> echo $prefix
(hd0,gpt3)/EFI/ubuntu # ubuntu的efi引导第一个prefix是这个

3) 加载$prefix/grub.cfg,进入grub引导

然后就是grub的操作了,UEFI的工作到此为止

2. legacy引导

Legacy引导是传统的BIOS引导方式,也称为BIOS引导或CSM(Compatibility Support Module)引导。与UEFI相比,legacy引导方式更为简单,但也有其局限性。

1) MBR分区表

Legacy引导使用MBR(Master Boot Record)分区表,而不是GPT。MBR有以下特点:

  • MBR位于磁盘的第一个扇区(512字节)
  • 包含引导代码和分区表(最多4个主分区)
  • 支持的最大磁盘容量为2TB
  • 分区表占用64字节,每个分区表项16字节
1
2
3
4
5
6
7
8
9
10
11
=> sudo fdisk -l /dev/sda
Disk /dev/sda: 465.76 GiB, 500107862016 bytes, 976773168 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x000a8b6a

Device Boot Start End Sectors Size Id Type
/dev/sda1 * 2048 999423 997376 487M 83 Linux
/dev/sda2 999424 976773119 975773696 465.3G 83 Linux

2) 引导过程

Legacy引导过程如下:

  1. BIOS自检(POST)
  2. BIOS查找启动设备(按照CMOS中设置的启动顺序)
  3. 读取启动设备的第一个扇区(MBR)到内存0x7C00处
  4. 跳转到0x7C00执行MBR中的引导代码
  5. MBR引导代码查找活动分区
  6. 加载活动分区的引导记录(如GRUB stage1)
  7. 执行引导加载程序(如GRUB),加载操作系统

3. 制作一个支持legacy+UEFI双引导的U盘或系统盘

制作一个同时支持Legacy和UEFI双引导的U盘或系统盘,结合两种引导方式的特点,按照下面的步骤执行。

1) 分区方案

由于uefi一定要使用gpt分区方式,而legacy重点还是在引导分区,所以我们使用gpt方式来进行分区

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
=> sudo fdisk /dev/sdX  # sdX是你的U盘或硬盘设备

# 创建GPT分区表
Command (m for help): g
Created a new GPT disklabel (GUID: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX)

# 创建EFI系统分区(ESP),512M比较合适
Command (m for help): n
Partition number (1-128, default 1):
First sector (2048-XXXXXXXX, default 2048):
Last sector, +/-sectors or +/-size{K,M,G,T,P} (2048-XXXXXXXX, default XXXXXXXX): +512M
Created a new partition 1 of type 'Linux filesystem' and of size 512 MiB.

# 设置ESP分区类型,这里是重点,一定要改这个分区的type为efi,也就是esp标记分区
Command (m for help): t
Partition number (1-128, default 1):
Partition type (type L to list all types): 1
Changed type of partition 'Linux filesystem' to 'EFI System'.

# 创建BIOS启动分区(用于Legacy引导),1M即可,不用格式化
Command (m for help): n
Partition number (2-128, default 2):
First sector (XXXXXXX-XXXXXXXX, default XXXXXXX):
Last sector, +/-sectors or +/-size{K,M,G,T,P} (XXXXXXX-XXXXXXXX, default XXXXXXXX): +1M
Created a new partition 2 of type 'Linux filesystem' and of size 1 MiB.

# 设置BIOS启动分区类型,这个是legacy启动需要识别的分区类型
Command (m for help): t
Partition number (2-128, default 2):
Partition type (type L to list all types): 4
Changed type of partition 'Linux filesystem' to 'BIOS boot'.

# 配置其他分区

# 保存并退出
Command (m for help): w
The partition table has been altered.

2) 格式化分区

1
2
# 格式化ESP分区为FAT32
sudo mkfs.fat -F32 /dev/sdX1

3) 安装GRUB2引导程序

1
2
3
4
5
6
7
8
# 挂载分区
sudo mount /dev/sdX1 /boot/efi

# 安装GRUB2 for UEFI,后面的id用于定义引导名字,在ubuntu低版本下,这个id必须用ubuntu,因为写死了
sudo grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id xxx

# 安装GRUB2 for Legacy
sudo grub-install --target=i386-pc --boot-directory=/boot /dev/sdX

5) 配置GRUB

1
2
# 生成GRUB配置文件
sudo grub-mkconfig -o /boot/grub/grub.cfg

然后基本就可以用了,剩下的就看测试效果具体排查了。部分系统可能要关闭secure boot才能启动U盘里的系统

小技巧和踩坑记

1. 报错[xxxx] pcieport 0000:00:1c.5 xxxx刷屏,无法关机

  1. 在/etc/defaults/grub里面修改,在GRUB_CMDLINE_LINUX_DEFAULT=后面追加splash pci=nomsi

2. 报错perl: warning: Falling back to the standard locale ("C").

  • 由于系统设置的语言包,系统不识别,需要安装
1
2
# 这个可以解决不识别 zh_CN.UTF-8
sudo apt get install language-pack-zh-hans

3. 分辨率怎么调都不行,总是达不到1080

  • 可以考虑下是线的问题,被一根VGA线坑了一年的人真诚提醒

4. 中文乱码

  • 部分中文出现乱码很有可能时中文字体没有安装,一般安装wqy-microhei就可以解决

5. vmware在linux下偶现虚拟机卡死

6. 调整分区大小导致无法mount分区

  • 使用分区管理器调整大小的时候,如果连续调整同时生效,很有可能出现调整失败,原分区也无法mount的情况
  • 报错提示超级块里面的大小和分区表的大小不一致

解决

  • 使用parted命令处理分区,使用resizepart将分区大小调整到原来的大小,和报错里面的一致就基本可以了

7. 查看机器内存情况

  • 下面命令查看几个插槽插了几个内存条
  • 可以看到四个插槽,插了两个DDR4的16G内存,速度3200 MT/s
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
=> sudo dmidecode | grep -A16 "Memory Device$"
Memory Device
Array Handle: 0x003B
Error Information Handle: Not Provided
Total Width: Unknown
Data Width: Unknown
Size: No Module Installed
Form Factor: Unknown
Set: None
Locator: Controller0-ChannelB-DIMM0
Bank Locator: BANK 1
Type: Unknown
Type Detail: None
--
Memory Device
Array Handle: 0x003B
Error Information Handle: Not Provided
Total Width: 64 bits
Data Width: 64 bits
Size: 16 GB
Form Factor: DIMM
Set: None
Locator: Controller0-ChannelA-DIMM1
Bank Locator: BANK 0
Type: DDR4
Type Detail: Synchronous
Speed: 3200 MT/s
--
Memory Device
Array Handle: 0x003B
Error Information Handle: Not Provided
Total Width: Unknown
Data Width: Unknown
Size: No Module Installed
Form Factor: Unknown
Set: None
Locator: Controller0-ChannelB-DIMM0
Bank Locator: BANK 1
Type: Unknown
Type Detail: None
--
Memory Device
Array Handle: 0x003B
Error Information Handle: Not Provided
Total Width: 64 bits
Data Width: 64 bits
Size: 16 GB
Form Factor: DIMM
Set: None
Locator: Controller0-ChannelB-DIMM1
Bank Locator: BANK 1
Type: DDR4
Type Detail: Synchronous
Speed: 3200 MT/s

8. UEFI启动进入grub命令行

在grub命令行执行下面命令启动

1
2
3
4
5
6
7
# 设置根目录,对应的hdx,y要用ls (hdx,y)/看是不是你对应的目录
set root=(hdx,y)
# 设置grub安装路径,也就是前缀
set prefix=(hdx,y)/boot/grub
# 挂在normal来启动normal
insmod normal
normal

进入系统后执行下面命令修复

1
2
3
4
update-grub

# uefi启动的按照下面这样设置,会生成到/boot/efi/EFI/GRUB/下两个文件
grub-install --target x86_64-efi --efi-directory=/boot/efi --bootloader-id GRUB

修复后如果还是一直进GRUB,可能是prefix的问题,在GRUB命令行下执行

1
2
grub> echo $prefix
(hd0,gpt3)/EFI/ubuntu # ubuntu的efi引导第一个prefix是这个,后面改成/boot/grub

看一下默认的prefix和当前的bootloader-id是不是不一致,不一致换对应的bootloader-id即可

1
grub-install --target x86_64-efi --efi-directory=/boot/efi --bootloader-id ubuntu