当前位置: 首页 > news >正文

嵌入式Linux 期末复习指南(下)

上文跳转:嵌入式Linux期末复习指南(上)

六、内存管理

 本章节部分是理解后文所有编程代码题的关键,也就是代码中必然包含一系列对于内存的操作。

1、Linux内存管理机制

此部分转到教材部分自行阅读了解即可,大部分知识均在此前的操作系统、计算机硬件等科目中学习过。

2、 内存控制

写代码不用背头文件,直接写代码就可以。

贴出每部分函数所需的头文件,供拔高/优秀同学学习记忆:

 

内存的分配与释放


头文件:

#include<stdio.h>
#include<stdlib.h>

(1)malloc 申请内存空间

void *malloc(size_t n)

其中n为指定分配的字节数,执行成功返回内存空间首地址,失败返回NULL。

注意:申请后的内存空间未初始化,需要调用memset初始化。

同时由于此函数值类型为void型指针,因此可将返回类型转换后赋值给任意类型指针。

char * buffer;
buffer = (char * )malloc(100);

(2)calloc 申请内存空间并初始化

void *calloc(size_t n,size_t size)

适合为数组申请空间,示例:

int * buffer;
buffer = (int * )malloc(10,sizeof(int));

由于其相比malloc多出了为申请好的内存初始化的操作,所以效率比malloc低。

(3)free 释放内存

void free(void *p)

在使用malloc或calloc申请内存并使用完成后需要手动释放内存空间,否则会导致内存泄漏。

 

内存映射


头文件:

#include<unistd>
#include<sys/mman.h>

由于用户层程序不能直接访问磁盘等物理设备,需要通过操作系统预留的open、close、read、write等方法走内核层间接去访问磁盘文件,为了加快文件等数据处理速度,引入内存映射概念:

内存映射就是将文件或者其他对象映射到用户空间中,通过对映射内存区域的修改,直接反应到文件磁盘上,类似“虚拟内存与物理内存”的关系。

通俗来说,传统读写方式是经过操作系统,绕路;而内存映射则是操作系统在内核与用户态之间开出一块“空地”,在这块“空地”上执行的任何操作,在操作完毕后都会直接关联到物理设备。

内存映射就是为了提高数据交换的效率。

(4)mmap 内存映射

void *mmap(void *start,size_t length,int port,int flags,int fd, off_t offsize)

 其中:

        start:映射目标内存起始地址,通常设置为Null,由系统自动选定。

        length:映射区长度,通常为文件数据类型大小*文件最大长度,见后文示例。

        port:映射区域保护方式,即映射区允许操作权限,可通过or运算符有机结合。

       (PORT_固定 后面变化 很好背)

        PORT_EXEC—可执行

        PORT_READ—可读取

        PORT_WRITE—可被写入

        PORT_NONE—不能存取

        flags:映射区域特性,死背 -> MAP_SHARED

        MAP_SHARED:映射空间共享,即与其他所有映射这个对象的进程共享映射空间。

        调用msync或者munmap函数后更新文件。

         fd:有效的文件描述词。即为文件对象。

        offsize:被映射对象内容的起点,默认为0

(5)munmap 解除映射

int munmap(void *start,size_t length)

其中:

        start:将要释放映射区的起始地址。

        length:必须为mmap中映射区长度。若小于将会造成内存泄漏。

 

注意:前文提过,在映射空间共享的情况下,进程在映射空间对共享内容的改变不会直接写回到磁盘文件中,往往调用munmap函数后才会执行msync函数实现磁盘文件内容与共享内存区的内容一致。成功返回0,失败返回-1。

(6)msync 刷新变化

msync(void *start,size_t len, int flags)

其中:

        start:映射区的开始地址。

        len:映射区的长度。

        flags:控制回写到文件的具体方式。默认 -> MS_SYNC

        MS_SYNC:等待写操作完成后才返回

 内存映射实例:

#define MAX 10000int main(){// 打开名为"test"的文件// O_RDWR:以读写模式打开// 0064:设置文件权限(八进制),表示://   - 所有者:读写(6 = 4+2)//   - 同组用户:读(4)//   - 其他用户:无权限(0)// 返回值fd是文件描述符fd = open("test",O_RDWR,0064);// 返回值array指向映射的内存起始地址array = mmap(NULL,sizeof(int)*MAX,PORT_READ | PORT_WRITE | PORT_EXEC,MAP_SHARED,fd,0);// 遍历数组所有元素for(i=0;i<MAX;++i)++array[i];   // 每个元素值加1// 解除内存映射munmap(array,sizeof(int)*MAX);// 将映射区域的修改同步到文件msync(array,sizeof(int)*MAX,MS_SYNC);// 关闭文件描述符close(fd);return 0;}

3、内存操作

头文件:

#include<unistd>
#include<sys/mman.h>

(1)内存复制

void bcopy(const void *src,void *dest,int n)
void *memcpy(void *dest,const void *src,int n)

其中:

        src:指向源地址的指针。

        dest:指向目标地址的指针。

        n:一次复制的字节长度n。

 bcopy和memcpy的区别就是源和目的指针的先后顺序推荐使用memcpy函数。

(2)内存赋值

void bzero(void *s,int n)
void *memset(void *s,int c,size_t n)

其中:

        s:内存区域指针。

        n:向内存区域前n个字节填入0(bzero) / 字节(memset)。

        c:赋值。

bzero只能将指定内存区域赋值为0,推荐使用memset可将任意值填入内存

(3)内存查找

void *memchr(const void *s,int c,size_t n)

其中:

        s:指向内存区域首地址的指针。

        c:待查找字符。

        n:搜索范围为前n个字节,找到则返回指向该字节的指针;找不到返回0。

(4)内存比较

int memcmp(const void *s1,const void *s2,size_t n)

其中:

        s1:第一个内存区域的指针。

        s2:第二个内存区域的指针。

        n:比较区间为前n个字符。

若s2大于s1则返回值>0;反之返回值<0。

 

取内存分页大小


头文件:

#include<unistd.h>

函数:

size_t getpagesize(void)

返回值为分页大小,单位字节。

 

Linux中的内存管理机制是什么?

答:①虚拟内存管理机制:分页式/分段式/段页式存储管理+虚拟内存管理

       ②线性地址空间与物理地址管理

 什么是段页式管理方式其优点是什么?

答:

  • ​分段​​:将程序逻辑划分为多个段(如代码段、数据段、堆、栈等),每个段有独立的基址和长度,便于程序模块化管理。
  • ​分页​​:在分段的基础上,将每个段进一步划分为固定大小的页(如4KB),并通过页表映射到物理内存,提高内存利用率。

    优点:分页既能提高物理内存利用率,分段又可以提供逻辑隔离​。

线性地址如何映射到物理地址? (引申段页式管理的工作流程)​

  1. ​逻辑地址 → 线性地址(分段转换)​

    • CPU访问内存时,逻辑地址(段选择符:段内偏移)通过段描述符表(GDT/LDT)转换为线性地址(虚拟地址)。
    • 例如:CS:EIP(代码段+指令指针)转换为线性地址。
  2. ​线性地址 → 物理地址(分页转换)​

    • 线性地址通过多级页表(如x86的4级页表)映射到物理页框(Page Frame)。
    • 页表由MMU(内存管理单元)和TLB(快表)加速查找。

什么是内存泄漏?如何避免内存泄漏?

答:答案在前文叙述当中。 

内存映射相比一般的文件读/写操作有什么好处?

答:答案在前文叙述当中。 

使用mmap函数设计一段程序实现内存映射。

答:答案在前文示例中。 

使用内存操作函数完成内存复制操作。

 答:

#include <stdio.h>
#include <string.h>int main() {char src[] = "Hello, World!";char dest[20];// 复制 src 的内容到 destmemcpy(dest, src, strlen(src) + 1);printf("Copied string: %s\n", dest); // 输出: Hello, World!return 0;
}

 

七、Linux进程管理

非重点,看看有印象就行,拔高同学另说。

头文件:

#include<unistd.h>
#include<sys/types.h>

(1)fork 创建子进程

pid_t fork(void)

特点:初始化一个子进程,“调用一次,返回两次”。

        ①在父进程中,fork函数返回新创建子进程的进程ID.

        ②在子进程中fork函数返回0。

        ③出现错误,返回负数。

 

在调用fork创建一个进程的时候,子进程会将现有代码完整复制一份,每份代码中执行对象(区分是爹进程还是儿子进程)通过fpid来区分:

        在爹进程里,fork返回的是儿子的身份证ID;而在儿子进程里,返回的是0。

int main()
{pid_t fpid; //fpid表示fork函数返回的值int count = 0;fpid = fork();if (fpid < 0)printf("error in fork!");//如果fpid为0代表我是儿子进程else if (fpid == 0) {printf("i am the child process, my process id is %d/n", getpid());printf("我是爹的儿子/n");count++;}//fpid不是0也不是负数,那么就是收到了儿子的身份证ID,那么就是爹else {printf("i am the parent process, my process id is %d/n", getpid());printf("我是孩子他爹/n");count++;}printf("统计结果是: %d/n", count);return 0;
}

(2)vfork 创建进程但儿子先走

pid_t vfork(void)

与fork函数最大的不同就是,fork创建出来的儿子进程跟爹进程谁先走谁后走不一定。

但是vfork创建出来的儿子进程,必须是儿子走完,调用exit()告诉爹,爹你儿子干完了,爹才能走。 并且由于是儿子,那么与爹一起共享变量(儿子花爹的钱一个意思)。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>int main(int argc, char *argv[])
{pid_t pid;pid = vfork();	// 创建进程if(pid < 0){ // 出错perror("vfork");}if(0 == pid){ // 子进程sleep(3); // 延时 3 秒,代表在干活printf("我是儿子进程\n");_exit(0); // 退出子进程,必须,然后爹进程才能干活}else if(pid > 0){ // 父进程printf("我是爹进程,必须等儿子exit()之后才走到这里\n");}return 0;
}

(3)getpid / getppid 获取进程ID / 获取爹进程ID(考试的时候记得叫父进程)

pid_t getpid(void)
pid_t getppid(void)

顾名思义,调用后返回进程和父进程ID,没啥可说的。

(4)waitpid 等待子进程ID

如果当你创建的子进程过多,爹进程不知道它的儿子们是不是活着(有没有被kill,或者儿子和老子到底谁先走啊),引出进程同步概念,这个函数就是获取各个儿子进程状态用作进程同步的。

pid_t waitpid(pid_t pid,int *status,int options)

其中:

        pid:进程PID

        status:填NULL

        options:填WNOHANG

WNOHANG:如果由pid指定的子进程没有结束,则waitpid函数不阻塞而立即返回,此时返回值为0。

 看看代码混个脸熟就行,考的几率不大。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>int main() {pid_t pid;int status;// 创建子进程pid = fork();if (pid < 0) {perror("fork failed");exit(1);} else if (pid == 0) {// 子进程执行的任务printf("Child process (PID=%d) is running...\n", getpid());sleep(5);  // 模拟耗时操作printf("Child process (PID=%d) is exiting.\n", getpid());exit(42);  // 子进程退出码为42} else {// 父进程监控子进程状态printf("Parent process (PID=%d) is monitoring child (PID=%d)...\n", getpid(), pid);while (1) {// 非阻塞方式检查子进程状态pid_t ret = waitpid(pid, &status, WNOHANG);if (ret == -1) {perror("waitpid failed");exit(1);} else if (ret == 0) {// 子进程未退出,继续监控printf("Child process is still running...\n");sleep(1);  // 避免频繁轮询} else {// 子进程已退出if (WIFEXITED(status)) {printf("Child process exited normally with code: %d\n", WEXITSTATUS(status));} else if (WIFSIGNALED(status)) {printf("Child process killed by signal: %d\n", WTERMSIG(status));}break;}}}return 0;
}

进程和程序的区别?

答:一个是动态实例(活得),一个是静态文件(死的)。

程序 (Program)​​进程 (Process)​
存储在磁盘上的 ​​静态文件​​(如 .exe.sh),包含可执行代码和数据。程序在内存中的 ​​动态执行实例​​,是操作系统分配资源的基本单位。
  • 程序​​ 像一本食谱(静态的步骤说明)。
  • ​进程​​ 像是厨师按照食谱做菜的过程(动态执行,需要占用厨房资源如灶台、食材)。
    • 同一本食谱(程序)可以同时被多个厨师(进程)使用。
    • 每个厨师(进程)的状态可能不同(有的在切菜,有的在等待烤箱)。

 进程的几种状态?为什么区分?

答:创建 → 就绪 ↔ 运行 → 终止
            ↓    ↑
          阻塞 ←─┘

为了进程管理。(书上回答更复杂,考试考了就引申展开言之有理即可)

进程控制块PCB作用,与进程有什么联系?

答: (1) 存储进程信息​

        (2) 实现进程调度​

        (3) 进程间隔离​

        (4) 进程通信与同步

        (5) 进程终止与资源回收

联系:进程依靠PCB,儿子和爹关系。

编写程序,在程序中创建一个子进程,使父进程和子进程分别打印不同内容。

答:参考前文。使用fork或者vfork创建进程,通过判断fpid值区分父子进程,打印内容。 

编写程序,使用waitpid函数不断获取某进程中子进程的状态。

答:参考前文。在创建子进程后,循环调用waitpid获取返回值判断子进程状态。 

八、信号 

进程间通讯使用信号传递消息。

 此部分上课并未提及,非重点,跳过,考了算倒霉.... ; )

过一遍书后题吧。

简述Linux操作系统中信号的处理方式

答:

​处理方式​​说明​​示例信号​
​终止(Terminate)​进程立即终止SIGKILL(强制终止)、SIGTERM(优雅终止)
​忽略(Ignore)​信号被丢弃,不采取任何操作SIGCHLD(子进程终止时默认忽略)
​捕获(Catch)​进程可以自定义信号处理函数SIGINTCtrl+C 可被捕获)
​停止(Stop)​进程暂停执行(可恢复)SIGSTOP(强制暂停)、SIGTSTP(终端暂停)
​继续(Continue)​恢复被暂停的进程SIGCONT

Linux操作系统中信号与信号量区别

答: 

​特性​​信号(Signal)​​信号量(Semaphore)​
​本质​异步事件通知机制(软件中断)同步原语(计数器)
​用途​进程控制、异常处理多进程/线程同步与互斥
​数据传递​仅信号编号(无额外数据)无数据传递,仅控制资源访问
​操作方式​kill() 发送,signal() 处理sem_wait() / sem_post()
​阻塞行为​可屏蔽(sigprocmask若资源不足,调用者阻塞
​内核支持​由内核直接处理可通过内核(System V)或用户态(POSIX)实现
​典型场景​Ctrl+C 终止进程多线程共享内存保护
 

常见混淆点​

  • ​信号 ≠ 信号量​​:
    • 信号是 ​​事件通知​​(如 SIGKILL 终止进程)。
    • 信号量是 ​​同步工具​​(如控制线程并发)。
  • ​信号量不传递数据​​:
    • 它仅是一个计数器,用于协调资源访问。
  • ​信号不可靠​​:
    • 连续发送相同信号可能丢失(除非使用实时信号 SIGRTMIN~SIGRTMAX)。

信号的本质

答: 本质上是 ​​软件模拟的中断​​。用于在进程间或内核与进程之间传递简单的控制信息。它的核心特点是 ​​轻量级、异步触发、有限信息传递​​。

 九、进程间通讯

此部分上课时让操作了,故为重点,注意理解。

管道是进程间通讯的基本手段,理解为在两个进程之间打开的一个共享文件,一端读一端写。

头文件:

#include<unistd.h>

 (1)pipe 匿名/无名管道

int pipe(int fd[2])

其中:

        fd:为文件标识符,用作区分读端和写端。fd[0]只能用于读,fd[1]只能用于写。

#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <string.h>int main() {int pipefd[2];  // pipefd[0] 读端, pipefd[1] 写端pid_t pid;char buf[1024];// 1. 创建管道if (pipe(pipefd) == -1) {perror("pipe");return 1;}// 2. 创建子进程pid = fork();if (pid == -1) {perror("儿子生不出来啦!");return 1;}if (pid == 0) {  // 子进程(读数据)close(pipefd[1]);  // 关闭写端(子进程只读)// 从管道读取数据ssize_t n = read(pipefd[0], buf, sizeof(buf));if (n == -1) {perror("出错啦!");return 1;}printf("儿子进程收到: %s\n", buf);close(pipefd[0]);  // 关闭读端return 0;} else {  // 父进程(写数据)close(pipefd[0]);  // 关闭读端(父进程只写)const char *msg = "儿子你好,我是你爹!";write(pipefd[1], msg, strlen(msg));  // 写入数据close(pipefd[1]);  // 关闭写端wait(NULL);  // 等待子进程结束printf("爹进程没啦!\n");return 0;}
}

(2)mkfifo 命名管道

作者认为掌握匿名管道就行,要是真考命名管道这么复杂的.....难评。

之前的匿名管道用于同程序下不同进程之间通讯,而命名管道又名FIFO,用作没有亲缘关系的进程间通讯,也就是可以跨程序跨进程通讯。FIFO类似文件,两端进程打开同一个文件进行读写操作。

直接上代码吧....

书上的代码太复杂了,直接贴上适合大学生的宝宝代码。

写入端代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <string.h>int main() {int fd;const char *fifo_name = "/tmp/myfifo";  // FIFO 文件路径char message[] = "Hello from FIFO writer!";// 1. 创建 FIFO(权限:用户可读可写)if (mkfifo(fifo_name, 0666) == -1) {perror("mkfifo");exit(EXIT_FAILURE);}printf("FIFO created at %s\n", fifo_name);// 2. 打开 FIFO(只写模式,会阻塞直到读取端打开)fd = open(fifo_name, O_WRONLY);if (fd == -1) {perror("open");unlink(fifo_name);  // 删除 FIFO 文件exit(EXIT_FAILURE);}// 3. 写入数据if (write(fd, message, strlen(message) + 1) == -1) {perror("write");close(fd);unlink(fifo_name);exit(EXIT_FAILURE);}printf("Writer sent: %s\n", message);close(fd);unlink(fifo_name);  // 通信完成后删除 FIFOreturn 0;
}

读取端代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>int main() {int fd;const char *fifo_name = "/tmp/myfifo";  // 必须与写入端路径一致char buf[1024];// 1. 打开 FIFO(只读模式,会阻塞直到写入端打开)fd = open(fifo_name, O_RDONLY);if (fd == -1) {perror("open");exit(EXIT_FAILURE);}// 2. 读取数据ssize_t n = read(fd, buf, sizeof(buf));if (n == -1) {perror("read");close(fd);exit(EXIT_FAILURE);}printf("Reader received: %s\n", buf);close(fd);return 0;
}

后面的消息队列、信号量巴拉巴拉跳过。

Linux操作系统中,进程间通讯方式有哪几种?分别有什么特点?

答:

​IPC 方式​​数据传输​​适用关系​​速度​​同步需求​​典型场景​
管道(Pipe)字节流父子进程Shell 命令链
信号(Signal)无数据任意进程即时进程终止、异常通知
共享内存直接内存访问任意进程最快高频数据交换
消息队列结构化消息任意进程部分任务调度
信号量无数据任意进程/线程资源互斥

还有更多,此处忽略

管道机制可以分哪几种?有什么异同点和优缺点?

答:

​特性​​匿名管道​​命名管道(FIFO)​
​创建方式​pipe() 系统调用mkfifo() 函数或命令
​通信范围​仅限父子/兄弟进程任意进程(通过文件路径)
​生命周期​随进程结束销毁显式创建和删除(unlink()
​数据流​半双工半双工
​存储位置​内核内存内核内存(文件系统仅作为入口)
​速度​更快(直接内存访问)稍慢(需文件路径访问)
​同步机制​自动阻塞支持阻塞/非阻塞模式
​典型用途​Shell 管道(|)、父子进程通信无亲缘关系进程通信(如 C/S 架构)

编写一个简单的程序实现管道见通信。

答:见前文。 

十、网络编程基础

理解并会写TCP服务端和客户端代码即可。

头文件分组背诵,很简单看着复杂而已。

服务端代码:

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>#define PORTNUMBER 3333
#define BUFFER_SIZE 1024int main(int argc, char *argv[]) {int sockfd, new_fd;struct sockaddr_in server_addr, client_addr;socklen_t sin_size;int nbytes;char buffer[BUFFER_SIZE];// 创建socketif ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {fprintf(stderr, "报错: %s\n", strerror(errno));exit(EXIT_FAILURE);}// 配置服务器地址memset(&server_addr, 0, sizeof(server_addr));server_addr.sin_family = AF_INET;server_addr.sin_addr.s_addr = htonl(INADDR_ANY); // 监听所有网络接口server_addr.sin_port = htons(PORTNUMBER);// 绑定socket到地址if (bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {fprintf(stderr, "绑定报错: %s\n", strerror(errno));exit(EXIT_FAILURE);}// 开始监听if (listen(sockfd, 5) == -1) {fprintf(stderr, "监听报错r: %s\n", strerror(errno));exit(EXIT_FAILURE);}printf("服务器正在监听端口:%d...\n", PORTNUMBER);while (1) {// 接受客户端连接sin_size = sizeof(client_addr);if ((new_fd = accept(sockfd, (struct sockaddr *)&client_addr, &sin_size)) == -1) {fprintf(stderr, "接收错误: %s\n", strerror(errno));continue; // 继续监听而不是退出}printf("服务器接收到链接请求:%s\n", inet_ntoa(client_addr.sin_addr));// 读取客户端数据if ((nbytes = read(new_fd, buffer, BUFFER_SIZE - 1)) == -1) {fprintf(stderr, "读取数据错误: %s\n", strerror(errno));close(new_fd);continue;}buffer[nbytes] = '\0';printf("服务器接收到信息: %s\n", buffer);// 关闭连接close(new_fd);}// 关闭服务器socket (实际上不会执行到这里)close(sockfd);return EXIT_SUCCESS;
}

客户端代码:

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>#define PORT_NUMBER 3333
#define BUFFER_SIZE 1024int main(int argc, char *argv[]) {int sockfd;char buffer[BUFFER_SIZE];struct sockaddr_in server_addr;struct hostent *host;// argc就是传入参数数量 argv就是传入参数数组// 检查参数if (argc != 2) {fprintf(stderr, "Usage: %s hostname\n", argv[0]);exit(EXIT_FAILURE);}// 解析主机名if ((host = gethostbyname(argv[1])) == NULL) {fprintf(stderr, "服务器地址错误\n");exit(EXIT_FAILURE);}// 创建socketif ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {fprintf(stderr, "Socket套接字创建错误: %s\n", strerror(errno));exit(EXIT_FAILURE);}// 配置服务器地址memset(&server_addr, 0, sizeof(server_addr));server_addr.sin_family = AF_INET;server_addr.sin_port = htons(PORT_NUMBER);server_addr.sin_addr = *((struct in_addr *)host->h_addr);// 连接服务器if (connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {fprintf(stderr, "连接失败: %s\n", strerror(errno));exit(EXIT_FAILURE);}// 获取用户输入并发送printf("请输入信息:\n");fgets(buffer, BUFFER_SIZE, stdin);write(sockfd, buffer, strlen(buffer));// 关闭连接close(sockfd);return EXIT_SUCCESS;
}

十一、X86内核和嵌入式内核

比较阴间,书上没有这部分内容但是上课和上机实验有。

这部分内容以上机实验指导书内容为准,掌握内核编译的流程即可,详细代码忽略。

优秀选手要掌握代码,此处就不作说明。

大致流程:

        ①解压Linux内核文件

        ②cd切换工作目录

        ③make distclean 清理中间文件

        ④选择参考配置文件

        ⑤make distclean 配置内核

        ⑥make bzImage 编译内核

        ⑦make modules 编译内核模块

        ⑧make modules_install 安装内核模块

        ⑨init ramdisk / mkinitrd initrd-2.6.29 2.6.29 制作“引导”文件

        ⑩安装内核,将上面的“引导文件”和内核文件拷贝到/boot,修改grud配置文件添加启动代码,重启虚拟机完成编译。

grud加入代码:

title my linux(2.6.29) /*选择项名字*/root (hd0,0)kernel /vmlinuz-2.6.29 ro root=LABEL=/ rhgb quietinitrd /initrd-2.6.29

 

嵌入式内核编译区别:

make menuconfig ARCH=arm CROSS_COMPILE=arm-linux

与前面X86内核配置相比,添加了ARCH=arm表示现在编译的是arm架构的内核

make uImage ARCH=arm CROSS_COMPILE=arm-linux


标准参考:

(以下资料来源于B站up:林鹤鸣ID(ID号:102399253),特此感谢。)

x86内核编译


嵌入式内核编译


内核代码参考

 

十二、学有余力混眼熟

1、信号处理

2、共享内存

3、MakeFile

 

到此全部结束,诸君考试顺利!

        

 

http://www.lqws.cn/news/84637.html

相关文章:

  • Mysql水平分表(基于Mycat)及常用分片规则
  • Spring 5 响应式编程:构建高性能全栈应用的关键
  • hooks组件-useState
  • 吴恩达机器学习笔记(1)—引言
  • 设计模式——访问者设计模式(行为型)
  • wow Warlock shushia [Dreadsteed]
  • 地图 APP 和购物 APP 是最急切上 AI的地方
  • Artificial Analysis2025年Q1人工智能发展六大趋势总结
  • ThreadLocal ,底层原理,强引用,弱引用,内存泄漏
  • Vue3(watch,watchEffect,标签中ref的使用,TS,props,生命周期)
  • FastAPI+Pyomo实现线性回归解决饮食问题
  • 函数调用的机器级实现(二):栈帧的访问与切换机制
  • 极客时间:用 FAISS、LangChain 和 Google Colab 模拟 LLM 的短期与长期记忆
  • 【springcloud】快速搭建一套分布式服务springcloudalibaba(四)
  • python爬虫:Ruia的详细使用(一个基于asyncio和aiohttp的异步爬虫框架)
  • Langchian - 自定义提示词模板 提取结构化的数据
  • 【redis实战篇】第七天
  • 在 Linux 服务器上无需 sudo 权限解压/打包 .7z 的方法(实用命令)
  • 小团队如何落地 Scrum 模型:从 0 到 1 的实战指南
  • rabbitmq Direct交换机简介
  • C++——AVL平衡树
  • Java递归编程中的StackOverflowError问题分析与解决方案
  • 题目 3230: 蓝桥杯2024年第十五届省赛真题-星际旅行
  • 数字孪生智慧水利解决方案:数字化场景、智慧化模拟、精准化决策,构建数字孪生流域为核心的智慧水利体系
  • 【笔记】Windows 部署 Suna 开源项目完整流程记录
  • 前端面试宝典---前端水印
  • Linux中的System V通信标准-共享内存、消息队列以及信号量
  • API 版本控制:使用 ABP vNext 实现版本化 API 系统
  • SpringBoot统一功能处理
  • linux驱动 - 5: simple usb device驱动