Linux开发长文
字符数量统计: 16851
瑞利判据(Rayleigh Criterion)是光学系统中用于评估光斑质量的一种方法。它用于确定两个光斑之间的最小可分辨距离,即两个光斑之间的最小距离,使得它们仍然可以被分辨为两个独立的点。
瑞利判据的公式为:
CD=k1λNACD = k_1 \cdot \frac{\lambda}{\text{NA}}
其中,
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函数会被调用,并将用户写入的数据作为参数传递给该函数。
printkpr_infopr_开头的函数都可以用来打印内核日志。二者区别仅仅在于,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架构)目录中。
仍在說永久 想不到是藉口 從未意會要分手