Linux开发长文
字符数量统计: 16851
瑞利判据(Rayleigh Criterion)是光学系统中用于评估光斑质量的一种方法。它用于确定两个光斑之间的最小可分辨距离,即两个光斑之间的最小距离,使得它们仍然可以被分辨为两个独立的点。
瑞利判据的公式为:
其中,
CD:最小可分辨距离
k1:瑞利常数,通常取值为1.22
λ:光波长
NA:数值孔径
瑞利判据可以用于评估光学系统的分辨率,以及确定光学系统的最小分辨距离。
高数值孔径EUV(Extreme Ultraviolet)光刻技术,是半导体制造中的一种光刻技术,用于制造微米级及更小尺寸的半导体芯片。
High NA EUV光刻技术使用极紫外光(EUV光)来照射半导体晶圆,以形成精细的电路图案。EUV光具有非常短的波长,可以穿透更深的层,从而实现更小的特征尺寸。这种技术对于制造高性能、低功耗的半导体芯片至关重要。
High NA EUV光刻技术需要非常高的数值孔径(NA),通常在0.5到1.0之间。数值孔径是指光束的会聚能力,它决定了光束在物体表面上的聚焦程度。数值孔径越高,光束在物体表面上的聚焦程度越高,从而可以实现更小的特征尺寸。
High NA EUV光刻技术是半导体制造领域的一项重要技术,它对于推动半导体行业的发展具有重要意义。然而,这种技术也面临着一些挑战,例如对光刻机的精度和稳定性的要求非常高,以及EUV光的产生和传输需要特殊的设备和工艺。
Linux广泛运行在大量计算设备上,从嵌入式设备到超算集群几乎无处不在。
特点:增量传输、断点续传、可选单向或双向同步
rsync 用法示例
rsync -avz --delete --include 'dir1/***' --include 'package.json' --exclude='*' . root@xx.xx.xx.xx:/root/remote_target/
相关内容位于
/etc/update-motd.d
目录中,以shell脚本形式提供Welcome to Ubuntu 24.04.3 LTS (GNU/Linux 6.8.0-79-generic aarch64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/pro
System information as of Thu Sep 18 01:34:49 AM UTC 2025
System load: 0.0
Usage of /: 11.7% of 57.25GB
Memory usage: 4%
Swap usage: 0%
Processes: 121
Users logged in: 0
IPv4 address for enp0s1: 192.168.64.2
IPv6 address for enp0s1: fd74:46d5:8a72:b02c:689d:40ff:fe98:b46c
* Strictly confined Kubernetes makes edge and IoT secure. Learn how MicroK8s
just raised the bar for easy, resilient and secure K8s cluster deployment.
https://ubuntu.com/engage/secure-kubernetes-at-the-edge
Expanded Security Maintenance for Applications is not enabled.
19 updates can be applied immediately.
To see these additional updates run: apt list --upgradable
Enable ESM Apps to receive additional future security updates.
See https://ubuntu.com/esm or run: sudo pro status
Last login: Wed Sep 3 03:22:35 2025 from 192.168.64.1
Linux内核提供多种同步机制:自旋锁、信号量等。需要注意的是,在用户态,锁机制是由库函数提供或是开发者自己写的,C语言本身并不提供同步机制,而操作系统提供的是用于实现这些锁的基础设施。在内核态,我们使用内核源码中已经写好的各种锁,而不是重复造轮子。今后,除非特别说明,本文所说的各种锁均指内核中的锁。
如果你想看的是用户态C语言实现Spin Lock,请阅读:
自旋锁这个中文翻译非常不好,因为自旋在中文语义默认是个高深的物理概念,让自旋锁听起来似乎很复杂。实际上SpinLock模拟的是一个小朋友,想到得到一个玩具却被别的小朋友抢先了,他只能急得原地打转,直到占用者不玩了才可能轮到他。
SpinLock的缺点也如它的名字:未能获取资源的进程会占据着CPU原地打转(不做任何有价值的事情),直到获取到等待的资源才会继续。这一期间,CPU资源被白白浪费了,而不是交给别的进程去执行有价值的任务。
此时,它的第二个缺点也可以被合理推断出来:当A等待B,B等待C,C原本在快乐地执行任务,但突然C开始等待A。永远无法解开的锁等待状态,被称为死锁。
什么时候使用spinlock:当spinlock原地转圈的时间非常短,此时上下文切换的开销更高,此时用spinlock更合适。
在面试时,考官喜欢拿乐观锁、悲观锁出考题。
乐观锁是一种乐观的并发控制策略,它假设并发冲突发生的概率很低,因此不会在获取锁时阻塞其他线程。相反,它会在操作数据时先尝试获取一个版本号,然后在进行数据操作时检查版本号是否发生了变化。如果版本号发生了变化,说明有其他线程已经修改了数据,此时需要重新获取数据并进行操作。
悲观锁是一种悲观的并发控制策略,它假设并发冲突发生的概率很高,因此会在获取锁时阻塞其他线程。只有获取到锁的线程才能进行数据操作,其他线程需要等待锁释放后才能进行操作。
内核模块入门代码:Hello, World!
hello.c
#include <linux/init.h> // 包含模块初始化和清理函数的宏
#include <linux/module.h> // 必须包含的核心头文件,用于所有模块
#include <linux/kernel.h> // 包含内核函数,如 printk()
// 模块许可证。强烈推荐设置,否则内核会发出警告。
MODULE_LICENSE("GPL");
// 模块作者信息
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple Hello World kernel module.");
MODULE_VERSION("0.1");
// 模块初始化函数
// 当使用 'insmod' 或 'modprobe' 加载模块时,这个函数会被调用
static int __init hello_init(void) {
// printk 是内核中的 printf 函数
// KERN_INFO 是日志级别,表示这是一般信息性消息
// 它会输出到内核的日志缓冲区,可以用 'dmesg' 命令查看
printk(KERN_INFO "Hello, World! The module has been loaded.\n");
return 0; // 返回 0 表示成功
}
// 模块清理函数
// 当使用 'rmmod' 卸载模块时,这个函数会被调用
static void __exit hello_exit(void) {
printk(KERN_INFO "Goodbye, World! The module has been unloaded.\n");
}
// 注册模块的初始化和清理函数
// module_init 告诉内核,hello_init 是加载时要执行的函数
module_init(hello_init);
// module_exit 告诉内核,hello_exit 是卸载时要执行的函数
module_exit(hello_exit);
Makefile
# obj-m 是一个变量,告诉 kbuild (内核构建系统) 我们要构建一个可加载的模块
# hello.o 是我们的目标文件名,- 表示它是一个模块,最终会生成 hello.ko
obj-m += hello.o
# all 是默认的目标
# make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
# -C: 切换到指定目录
# /lib/modules/$(shell uname -r)/build: 这是内核源代码或头文件所在的位置
# M=$(PWD): 告诉内核构建系统,我们的模块源代码在当前目录
# modules: 指定要执行的操作是编译模块
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
# clean 是清理的目标,用于删除编译过程中产生的文件
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
MODULE_LICENSE("GPL"); 用于访问EXPORT_SYMBOL_GPL导出的符号。简单来说,你必须加上这句话才可以使用内核中GPL协议的各种函数。由于Linux内核主体就是GPL协议,不加上这句话几乎意味着无法进行内核开发。这是由于Linux采用的GPL协议具有传染性,除了少数具体情况。
sudo insmod hello.ko
modprobe -r hello
使用
lsmod
命令可以列出当前系统已加载的内核模块。$ lsmod
Module Size Used by
tls 159744 0
qrtr 49152 2
cfg80211 1249280 0
snd_hda_codec_generic 114688 1
snd_hda_intel 57344 0
snd_intel_dspcfg 20480 1 snd_hda_intel
snd_hda_codec 208896 2 snd_hda_codec_generic,snd_hda_intel
binfmt_misc 28672 1
snd_hda_core 163840 3 snd_hda_codec_generic,snd_hda_intel,snd_hda_codec
snd_hwdep 24576 1 snd_hda_codec
snd_pcm 196608 3 snd_hda_intel,snd_hda_codec,snd_hda_core
snd_timer 53248 1 snd_pcm
snd 147456 6 snd_hda_codec_generic,snd_hwdep,snd_hda_intel,snd_hda_codec,snd_timer,snd_pcm
9pnet_virtio 20480 0
9pnet 106496 1 9pnet_virtio
soundcore 16384 1 snd
nls_iso8859_1 12288 1
input_leds 12288 0
joydev 36864 0
uas 32768 0
usb_storage 90112 1 uas
sch_fq_codel 24576 2
dm_multipath 49152 0
efi_pstore 12288 0
nfnetlink 20480 2
dmi_sysfs 24576 0
qemu_fw_cfg 24576 0
ip_tables 36864 0
x_tables 65536 1 ip_tables
autofs4 57344 2
btrfs 1929216 0
blake2b_generic 24576 0
hid_generic 12288 0
usbhid 81920 0
hid 184320 2 usbhid,hid_generic
raid10 77824 0
raid456 212992 0
async_raid6_recov 24576 1 raid456
async_memcpy 16384 2 raid456,async_raid6_recov
async_pq 16384 2 raid456,async_raid6_recov
async_xor 16384 3 async_pq,raid456,async_raid6_recov
async_tx 16384 5 async_pq,async_memcpy,async_xor,raid456,async_raid6_recov
xor 12288 2 async_xor,btrfs
xor_neon 16384 1 xor
raid6_pq 110592 4 async_pq,btrfs,raid456,async_raid6_recov
libcrc32c 12288 2 btrfs,raid456
raid1 61440 0
raid0 24576 0
crct10dif_ce 12288 1
polyval_ce 12288 0
polyval_generic 12288 1 polyval_ce
ghash_ce 24576 0
sm4 12288 0
sha3_ce 16384 0
sha2_ce 20480 0
sha256_arm64 24576 1 sha2_ce
sha1_ce 12288 0
virtio_gpu 94208 0
virtio_dma_buf 12288 1 virtio_gpu
virtio_rng 12288 0
xhci_pci 28672 0
xhci_pci_renesas 24576 1 xhci_pci
aes_neon_bs 24576 0
aes_neon_blk 28672 1 aes_neon_bs
aes_ce_blk 36864 0
aes_ce_cipher 12288 1 aes_ce_blk
lsmod
的原理是读取/proc/modules
文件的内容字段格式:
模块名称
内存占用
引用计数
依赖模块
模块状态
内存地址
$ cat /proc/modules
tls 159744 0 - Live 0x0000000000000000
qrtr 49152 2 - Live 0x0000000000000000
cfg80211 1249280 0 - Live 0x0000000000000000
snd_hda_codec_generic 114688 1 - Live 0x0000000000000000
snd_hda_intel 57344 0 - Live 0x0000000000000000
snd_intel_dspcfg 20480 1 snd_hda_intel, Live 0x0000000000000000
snd_hda_codec 208896 2 snd_hda_codec_generic,snd_hda_intel, Live 0x0000000000000000
binfmt_misc 28672 1 - Live 0x0000000000000000
snd_hda_core 163840 3 snd_hda_codec_generic,snd_hda_intel,snd_hda_codec, Live 0x0000000000000000
snd_hwdep 24576 1 snd_hda_codec, Live 0x0000000000000000
snd_pcm 196608 3 snd_hda_intel,snd_hda_codec,snd_hda_core, Live 0x0000000000000000
snd_timer 53248 1 snd_pcm, Live 0x0000000000000000
snd 147456 6 snd_hda_codec_generic,snd_hda_intel,snd_hda_codec,snd_hwdep,snd_pcm,snd_timer, Live 0x0000000000000000
9pnet_virtio 20480 0 - Live 0x0000000000000000
9pnet 106496 1 9pnet_virtio, Live 0x0000000000000000
soundcore 16384 1 snd, Live 0x0000000000000000
nls_iso8859_1 12288 1 - Live 0x0000000000000000
input_leds 12288 0 - Live 0x0000000000000000
joydev 36864 0 - Live 0x0000000000000000
uas 32768 0 - Live 0x0000000000000000
usb_storage 90112 1 uas, Live 0x0000000000000000
sch_fq_codel 24576 2 - Live 0x0000000000000000
dm_multipath 49152 0 - Live 0x0000000000000000
efi_pstore 12288 0 - Live 0x0000000000000000
nfnetlink 20480 2 - Live 0x0000000000000000
dmi_sysfs 24576 0 - Live 0x0000000000000000
qemu_fw_cfg 24576 0 - Live 0x0000000000000000
ip_tables 36864 0 - Live 0x0000000000000000
x_tables 65536 1 ip_tables, Live 0x0000000000000000
autofs4 57344 2 - Live 0x0000000000000000
btrfs 1929216 0 - Live 0x0000000000000000
blake2b_generic 24576 0 - Live 0x0000000000000000
hid_generic 12288 0 - Live 0x0000000000000000
usbhid 81920 0 - Live 0x0000000000000000
hid 184320 2 hid_generic,usbhid, Live 0x0000000000000000
raid10 77824 0 - Live 0x0000000000000000
raid456 212992 0 - Live 0x0000000000000000
async_raid6_recov 24576 1 raid456, Live 0x0000000000000000
async_memcpy 16384 2 raid456,async_raid6_recov, Live 0x0000000000000000
async_pq 16384 2 raid456,async_raid6_recov, Live 0x0000000000000000
async_xor 16384 3 raid456,async_raid6_recov,async_pq, Live 0x0000000000000000
async_tx 16384 5 raid456,async_raid6_recov,async_memcpy,async_pq,async_xor, Live 0x0000000000000000
xor 12288 2 btrfs,async_xor, Live 0x0000000000000000
xor_neon 16384 1 xor, Live 0x0000000000000000
raid6_pq 110592 4 btrfs,raid456,async_raid6_recov,async_pq, Live 0x0000000000000000
libcrc32c 12288 2 btrfs,raid456, Live 0x0000000000000000
raid1 61440 0 - Live 0x0000000000000000
raid0 24576 0 - Live 0x0000000000000000
crct10dif_ce 12288 1 - Live 0x0000000000000000
polyval_ce 12288 0 - Live 0x0000000000000000
polyval_generic 12288 1 polyval_ce, Live 0x0000000000000000
ghash_ce 24576 0 - Live 0x0000000000000000
sm4 12288 0 - Live 0x0000000000000000
sha3_ce 16384 0 - Live 0x0000000000000000
sha2_ce 20480 0 - Live 0x0000000000000000
sha256_arm64 24576 1 sha2_ce, Live 0x0000000000000000
sha1_ce 12288 0 - Live 0x0000000000000000
virtio_gpu 94208 0 - Live 0x0000000000000000
virtio_dma_buf 12288 1 virtio_gpu, Live 0x0000000000000000
virtio_rng 12288 0 - Live 0x0000000000000000
xhci_pci 28672 0 - Live 0x0000000000000000
xhci_pci_renesas 24576 1 xhci_pci, Live 0x0000000000000000
aes_neon_bs 24576 0 - Live 0x0000000000000000
aes_neon_blk 28672 1 aes_neon_bs, Live 0x0000000000000000
aes_ce_blk 36864 0 - Live 0x0000000000000000
aes_ce_cipher 12288 1 aes_ce_blk, Live 0x0000000000000000
dmesg
命令用于打印内核日志,它可以从内核日志缓冲区中读取日志信息。在内核模块中,可以使用printk
函数将日志信息输出到内核日志缓冲区中,然后使用dmesg
命令查看这些日志信息。这里我们构建一个echo服务器,向
/proc/echo_driver
写入的内容将被打印到内核日志中(使用dmesg查看)echo_proc.c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/proc_fs.h>
#include <linux/uaccess.h> // 用于 copy_from_user 和 copy_to_user
#include <linux/string.h>
#define MODULE_NAME "echo_proc"
#define PROC_ENTRY_NAME "echo_driver"
#define MAX_BUFFER_LEN 1024
static char proc_buffer[MAX_BUFFER_LEN];
static unsigned long proc_buffer_size = 0;
// 当用户读取 /proc/echo_driver 时,此函数被调用
static ssize_t proc_read(struct file *file, char __user *user_buf, size_t count, loff_t *offp)
{
ssize_t len = 0;
// 如果偏移量不为0,说明已经读完了,返回0表示文件结束
if (*offp > 0) {
return 0;
}
// 确保不会读取超出缓冲区大小的数据
if (count > proc_buffer_size) {
len = proc_buffer_size;
} else {
len = count;
}
// 将内核空间的数据复制到用户空间
if (copy_to_user(user_buf, proc_buffer, len) != 0) {
// 复制失败
return -EFAULT;
}
// 更新偏移量
*offp += len;
printk(KERN_INFO "echo_proc: Read %zu bytes from buffer\n", len);
return len;
}
// 当用户写入 /proc/echo_driver 时,此函数被调用
static ssize_t proc_write(struct file *file, const char __user *user_buf, size_t count, loff_t *offp)
{
ssize_t len = 0;
// 确保写入的数据不会超过缓冲区大小
if (count > MAX_BUFFER_LEN - 1) {
printk(KERN_WARNING "echo_proc: Write size %zu exceeds buffer limit\n", count);
len = MAX_BUFFER_LEN - 1;
} else {
len = count;
}
// 将用户空间的数据复制到内核空间
if (copy_from_user(proc_buffer, user_buf, len) != 0) {
// 复制失败
return -EFAULT;
}
// 在字符串末尾添加空字符,以便安全地作为字符串处理
proc_buffer[len] = '\0';
proc_buffer_size = len;
// 移除可能的换行符,方便读取时显示
if (len > 0 && proc_buffer[len - 1] == '\n') {
proc_buffer[len - 1] = '\0';
proc_buffer_size--;
}
printk(KERN_INFO "echo_proc: Wrote %zu bytes to buffer: %s\n", proc_buffer_size, proc_buffer);
return len; // 返回实际写入的字节数
}
// 定义文件操作结构体
static const struct proc_ops my_proc_ops = {
.proc_read = proc_read,
.proc_write = proc_write,
};
// 模块初始化函数
static int __init echo_proc_init(void)
{
struct proc_dir_entry *proc_entry;
// 在 /proc 下创建一个条目
proc_entry = proc_create(PROC_ENTRY_NAME, 0666, NULL, &my_proc_ops);
if (proc_entry == NULL) {
printk(KERN_ERR "echo_proc: Failed to create /proc/%s\n", PROC_ENTRY_NAME);
return -ENOMEM;
}
printk(KERN_INFO "echo_proc: Module loaded. /proc/%s created.\n", PROC_ENTRY_NAME);
return 0;
}
// 模块退出函数
static void __exit echo_proc_exit(void)
{
// 移除 /proc 条目
remove_proc_entry(PROC_ENTRY_NAME, NULL);
printk(KERN_INFO "echo_proc: Module unloaded. /proc/%s removed.\n", PROC_ENTRY_NAME);
}
module_init(echo_proc_init);
module_exit(echo_proc_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple echo driver using /proc");
MODULE_VERSION("0.1");
__init
宏用于标记初始化函数,这些函数在模块加载时执行,并在模块卸载时释放。使用__init
宏标记的函数在编译时会被放在一个特殊的段中,这个段在模块加载时会被映射到内存中,并在模块卸载时被释放。module_init
宏用于标记模块初始化函数,该函数在模块加载时被调用。使用module_init
宏标记的函数必须在模块加载时执行,并且必须在模块卸载时释放。printk
函数用于在内核中输出日志信息。它类似于用户空间的printf
函数,但是它的输出会被重定向到内核日志缓冲区,而不是标准输出。使用printk
函数可以方便地调试内核模块。module_param
宏用于定义模块参数,这些参数可以在模块加载时通过命令行参数进行设置。使用module_param
宏定义的参数可以在模块加载时通过命令行参数进行设置,例如:insmod hello.ko param1=10 param2="hello"
。与用户态C语言开发相似,在内核态你也可以打印日志,使用printk(print in kernel)
proc_create
函数用于创建一个proc文件,该文件可以用于在用户空间和内核空间之间进行通信。使用proc_create
函数创建的proc文件可以在/proc
目录下看到,并且可以通过cat
命令查看其内容。参数:
name
:proc文件的名称。mode
:proc文件的权限。parent
: parent目录。proc_fops
:proc文件的操作函数,用于定义如何读写proc文件。代码示例
struct proc_dir_entry *proc_entry;
proc_entry = proc_create(PROC_ENTRY_NAME, 0666, NULL, &my_proc_ops);
if (proc_entry == NULL) {
printk(KERN_ERR "echo_proc: Failed to create /proc/%s\n", PROC_ENTRY_NAME);
return -ENOMEM;
}
使用parent参数在子目录中创建文件
struct proc_dir_entry *mydir;
mydir = proc_mkdir("mydriver", NULL); // 创建 /proc/mydriver
if (mydir) {
proc_create("status", 0444, mydir, &status_proc_ops); // 创建 /proc/mydriver/status
}
proc_write
函数用于接收来自proc文件的写入。当用户在用户空间中写入proc文件时,proc_write
函数会被调用,并将用户写入的数据作为参数传递给该函数。printk
和pr_info
等pr_
开头的函数都可以用来打印内核日志。二者区别仅仅在于,pr_开头的函数是printk的封装,它从字面以上就可以知道logging level,也会提供预先配置好的打印格式。在新的内核社区代码规范中,你应当优先使用pr_开头的函数,因为它们提供了更好的可读性和可维护性。
KERN_EMERG: pr_emerg()
KERN_ALERT: pr_alert()
KERN_CRIT: pr_crit()
KERN_ERR: pr_err()
KERN_WARNING: pr_warn()
KERN_NOTICE: pr_notice()
KERN_INFO: pr_info()
KERN_DEBUG: pr_debug()
KERN_DEFAULT: printk()
KERN_CONT: pr_cont()
11.2.1. LFS: Linux From Scratch
11.2.2. BLFS: Beyond LFS
11.3.1. Partial Mirror
11.3.2. initramfs制作
Linux中有大量的发行版,比如说Ubuntu、Debian、Arch、Manjaro、Fedora、Centos、Redhat等知名产品。 许多人并不清楚这些发行版之间的关系。本章通过介绍这些发行版,让你今后对于不同的发行版都有清晰的认识和理解。
首先,请允许我引用网上广为流传的一张发行版关系树图片。
这张图片非常巨大,但我还是决定将完整的图片放在这里。你可以在这张图中找到很多非常熟悉的发行版,以及一些古老的已经不再维护的发行版。(点击跳转大图)

Debian是Linux发行版中最古老的一个。它诞生于1993年,是Linux社区中最受欢迎的发行版之一。Debian的稳定性、安全性和可定制性使其成为许多服务器和开发环境的理想选择。
Debian的包管理器APT是其最大的特点之一。APT是一个高级包管理工具,它允许用户轻松地安装、升级和删除软件包。APT还支持自动解决软件包之间的依赖关系,这使得安装和管理软件变得更加简单。
Debian的另一个特点是它的源代码可用性。Debian的源代码是开源的,任何人都可以自由地查看、修改和分发Debian的源代码。这使得Debian成为了一个理想的开发环境,许多Linux发行版都是基于Debian的。

Ubuntu是Debian的一个下游发行版。它采用了Debian的构建系统,然后做了很多修改,形成了自己的风格和发行方式。相较于Debian社区,Ubuntu是由Canonical公司维护的企业级产品,在稳定性和口碑上更适合生产环境使用。

Manjaro是一个基于Arch Linux的发行版。Manjaro的目标是让Arch Linux的用户能够更方便地使用Arch Linux,它提供了图形化的安装程序,以及大量的软件仓库,使得用户可以更方便地安装和管理软件。

Gentoo是一个基于Portage的发行版。Portage是一个包管理器,它允许用户从源代码编译软件,而不是从预编译的二进制文件中安装。Gentoo的目标是让用户能够完全控制他们的系统,包括选择要安装的软件包、编译选项等。
APT是Advanced Package Tool的缩写,它是一个用于处理Debian及其衍生发行版的软件包的工具。APT提供了软件包的安装、升级、删除、查询等功能,并且可以自动解决软件包之间的依赖关系。
APT的命令行工具是apt-get和apt-cache。apt-get用于安装、升级和删除软件包,apt-cache用于查询软件包的信息。
APT的配置文件位于/etc/apt/目录中,包括sources.list文件和preferences文件。sources.list文件列出了软件包的源,preferences文件用于指定软件包的优先级。
APT的软件包管理器是dpkg,它用于安装、升级和删除软件包。dpkg的配置文件位于/etc/dpkg/目录中,包括dpkg.conf文件和status文件。
APT的软件包格式是.deb,它是一个包含软件包元数据和二进制文件的文件。APT的软件包源可以是本地文件系统、HTTP服务器、FTP服务器等。
在Ubuntu中,你可以使用以下命令来更新软件源:
apt update
在Ubuntu中,你可以使用以下命令来升级软件包:
apt upgrade
apt install build-essential cmake git vim
在内核模块开发过程中,内核模块需要调用内核本地的函数,就必须获取内核本体的符号表。然而,不同版本的内核符号表显然是不同的,因此必须在内核模块编译时,提供
完全一致
版本的内核头文件。对于发行版提供的内核,获取内核头文件可以通过安装软件包的方式进行:
apt install linux-headers-$(uname -r)
对于自己编译的内核,内核头文件在
make
时已经生成,位于arch/x86/include/generated
(或对应CPU架构)目录中。