从按下电源开关到bash提示符 4.内核

本文深入探讨了Linux内核的功能和工作原理,包括任务调度、虚拟内存管理、文件系统管理等核心概念。此外,还介绍了如何配置和编译内核,以及最小化内核的方法。

4. Linux 内核

内核干了很多工作。我认为宏观的说内核让程序合理并且高效的控制硬件。

处理器每次只能执行一条指令,但是Linux表面上看起来同是运行了很多东西。内核用快速切换任务的方式来实现这样的效果。它把处理器的能力发挥到极致,通过不断监视哪个进程可以行、那个进程在等待(比如等待硬盘读取文件、键盘输入等等)。这些内核的工作叫做调度。

如果一个程序什么也不做了,它就不必待在内存里。即使程序正在运行,也可能存在某些部分不在执行。为程序分配的内存空间以页为单位。内核跟 踪哪些进程的哪些页使用最频繁。哪些不常用的页面会被移动到交换分区,当需要他们的时候,另外一个不常用的页会交换到硬盘上来给他让位。这就是虚拟内存管 理。

如果你曾经编译过自己的内核,你会注意到每个设备都有很多选项。内核包括了很多特别的代码用于和多种多样的硬件沟通,并且以很规范的方式提供给应用程序。

内核还管理文件系统、进程间通信还有很多网络功能。

一旦内核被读入,它会先寻找init程序来运行。

4.1 配置

大部分内核配置在你建立它的时候已经完成,使用make menuconfig,或者make xconfig 在你内核源文件存放的地方。你可以用rdev来重新设置默认的视频模式,根文件系统,交换分区,和内存中虚拟盘的大小。包括这些参数在内的很多参数将同过 lilo传给内核。你可以直接传参数给lilo或者用lilo.conf,都可以把信息传给内核。举个例子如果你想用hda3来作为你的根目录而不是用 hda2,你可以键入

        LILO: linux root=/dev/hda3

如果你正在从源代码建立一个系统,你可以通过建立一个“monolithic”的内核来使过程变得很轻松。它没有模块,这样你就不用拷贝内核模块到目标系统了。

注意:内核记录器要使用System.map来确定模块生成信息。程序也要用到这些信息。当你把内核复制到目标系统时,把System.map一起拷贝过去。

4.2 练习

思考如下问题:/dev/hda3是一个用来描述硬盘的特殊文件。但是他在文件系统里面貌似和其他文件没什么区别。内核想知道在哪个分区上安装根文件系统——但这时还根本没有文件系统,它是如何读取/dev/hda3并找到要安装的分区的?

如果你还没有建立你自己的内核,阅读所有选项的帮助信息。

看看你可以把内核做的多小。你可以从错误中学到很多!

阅读“The Linux Kernel”,同时找到相应的源代码。这本书基于2.0.33版内核。已经过时了。老版本的内核相对容易理解,如果你下载了源代码,你会非常高兴的发现用c语言写的“进程”和“分页”函数。

Hack! 看看你能不能让它显示些别的信息或者做些其他事。

4.3 更多信息

 
<think>我们讨论的是如何通过代码切换ttyS0串口的输入输出开关(即启用或禁用串口控制台)。根据之前的上下文,我们尝试使用ioctl的TIOCLINUX子命令(子命令11)来切换控制台输出到指定终端,但遇到了EINVAL错误。现在需要寻找替代方案。 根据引用[1]中关于Linux终端设备的说明,/dev/ttyS*是串行终端设备。因此,我们需要操作的是串口终端设备节点。 在Linux中,控制串口终端输入输出的常见方法有: 1. 使用stty命令(命令行工具) 2. 通过tcsetattr系统调用(编程方式) 但请注意,我们的目标是完全开启或关闭串口的输入输出(可能类似于关闭控制台输出到该串口)。实际上,在Linux中,串口设备作为终端使用时,可以通过设置终端属性来禁用输入(如关闭回显、关闭规范模式等),但并不能完全“关闭”设备,除非关闭设备文件描述符。 然而,有一个更底层的操作:通过设置串口的控制信号(如DTR、RTS)来禁用物理连接?但通常我们不这样做,因为控制台输出是内核行为。 另一种思路:内核通过console参数指定了控制台输出设备,我们可以通过proc文件系统或sys文件系统动态调整控制台? 具体方法: 1. 通过修改内核启动参数(需要重启): 在启动参数中移除或添加console=ttyS0,波特率 2. 动态切换当前控制台输出(不重启): 我们可以使用ioctl(fd, TIOCLINUX)子命令来切换虚拟终端(VT),但是之前尝试失败。而串口终端(ttyS0)并不属于虚拟终端(VT),而是串行终端。所以这个方法可能不适用。 3. 通过sysfs禁用串口控制台: 查看/sys/class/tty/ttyS0/active,如果有这个文件,可以尝试写入0来禁用?但实际没有这样的接口。 4. 通过重定向控制台输出: 我们可以将控制台输出重定向到另一个设备(比如空设备)来达到关闭输出的效果。但是,控制台输出是内核行为,通常由console参数指定多个设备(比如tty0和ttyS0)。在内核运行时,我们可以动态添加或移除控制台设备吗? 实际上,Linux提供了`ioctl(fd, TIOCSCONSOLE)`和`ioctl(fd, TIOCCONS)`?但查阅文档,没有找到直接的方法。 5. 使用sysreq机制切换控制台: 通过发送sysreq命令(如Alt+SysRq+键)可以切换控制台,但这是针对VT的。 6. 通过proc文件系统: - 查看当前控制台:`cat /proc/consoles` - 动态启用/禁用某个控制台:可以通过向`/sys/devices/virtual/vtconsole/vtconX/bind`写入0或1来实现(X为控制台序号)。注意,这是针对帧缓冲(fbcon)和虚拟终端(vt)的,对于串口控制台(serial console)是否有效? 步骤: - 查看/sys/devices/virtual/vtconsole/目录下的控制台设备 - 找到与串口控制台对应的vtconX(通过name文件查看,如cat vtcon0/name,输出可能是(S) frame buffer device 或 (S) serial console) - 然后向该目录下的bind文件写入0来解绑(禁用),写入1来绑定(启用) 例如: ```bash # 禁用 echo 0 > /sys/devices/virtual/vtconsole/vtcon1/bind # 启用 echo 1 > /sys/devices/virtual/vtconsole/vtcon1/bind ``` 注意:需要root权限。 如何确定哪个vtconX对应串口控制台? 可以遍历/sys/devices/virtual/vtconsole/下的每个子目录,查看name文件内容。如果是串口控制台,name文件内容可能包含"serial"或根据启动参数确定。 代码示例: ```c #include <stdio.h> #include <dirent.h> #include <string.h> #include <fcntl.h> #include <unistd.h> int disable_serial_console() { DIR *dir; struct dirent *entry; char path[256]; char name[256]; int found = 0; dir = opendir("/sys/devices/virtual/vtconsole"); if (!dir) { perror("opendir"); return -1; } while ((entry = readdir(dir)) != NULL) { if (entry->d_type == DT_DIR && strncmp(entry->d_name, "vtcon", 5) == 0) { snprintf(path, sizeof(path), "/sys/devices/virtual/vtconsole/%s/name", entry->d_name); FILE *f = fopen(path, "r"); if (f) { if (fgets(name, sizeof(name), f)) { // 去除换行符 name[strcspn(name, "\n")] = 0; // 判断是否是串口控制台,这里根据实际情况调整匹配字符串 if (strstr(name, "serial") != NULL || strstr(name, "ttyS") != NULL) { found = 1; fclose(f); // 构建bind文件路径 snprintf(path, sizeof(path), "/sys/devices/virtual/vtconsole/%s/bind", entry->d_name); int bind_fd = open(path, O_WRONLY); if (bind_fd < 0) { perror("open bind"); closedir(dir); return -1; } // 写入0禁用 if (write(bind_fd, "0\n", 2) != 2) { perror("write bind"); close(bind_fd); closedir(dir); return -1; } close(bind_fd); break; } } fclose(f); } } } closedir(dir); if (!found) { fprintf(stderr, "Serial console not found in vtcon devices\n"); return -1; } return 0; } ``` 启用函数类似,写入"1\n"。 注意:这种方法需要内核配置了虚拟终端控制台(VT_CONSOLE),并且串口控制台已经绑定到vtcon设备上。 另外,这种方法只能禁止内核消息输出到该串口控制台,但用户空间的输出(如shell)可能仍然存在。如果我们需要完全禁止该串口的输入输出,那么应该考虑关闭该串口设备的文件描述符(如果有进程打开的话),或者使用终端属性设置来禁用回显和输入(但输出无法完全禁止)。 7. 另一种方法:使用tcsetattr禁用输入和输出 我们可以通过设置串口终端的属性来关闭输入(ICANON和ECHO等)和输出(OPOST等),但这并不能阻止内核消息的输出,因为内核消息是直接写到控制台设备上的,而不是通过终端属性控制的。 不过,我们可以通过关闭标准输入输出重定向到该串口的进程(如getty)来释放串口,然后重新打开设置。但这种方法依赖于具体应用。 8. 内核启动参数:如果系统允许重启,修改内核启动参数是最根本的方法。去掉console=ttyS0参数。 结论: 如果目标是禁用内核消息输出到串口控制台,推荐使用sysfs vtcon的bind接口(方法6)。如果目标是禁用用户空间对该串口的读写,则可以通过tcsetattr设置终端属性,或者关闭所有打开该串口的进程。 由于用户的需求是“切换ttyS0实际串口输入输出开关”,这里我们理解为禁用或启用该串口作为控制台的功能(内核输出和登录提示等),所以方法6是可行的。 注意:操作sysfs文件需要root权限。 下面给出完整的代码示例(C语言)来实现启用和禁用串口控制台: </think># 串口终端开关控制方法 在Linux系统中控制ttyS0串口的输入输出开关,可以通过终端属性设置和内核控制台管理两种主要方式实现。以下是我的详细分析和解决方案: ## 一、终端属性控制方法(用户空间) 通过设置终端属性禁用输入输出: ```c #include <termios.h> #include <fcntl.h> #include <unistd.h> int disable_ttyS0_io() { int fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY); if (fd < 0) return -1; struct termios tty; tcgetattr(fd, &tty); // 禁用输入和输出 tty.c_cflag &= ~(CREAD | CLOCAL); // 禁用接收器 tty.c_lflag &= ~(ICANON | ECHO); // 禁用规范模式和回显 tty.c_oflag &= ~OPOST; // 禁用输出处理 int result = tcsetattr(fd, TCSANOW, &tty); close(fd); return result; } int enable_ttyS0_io() { int fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY); if (fd < 0) return -1; struct termios tty; tcgetattr(fd, &tty); // 启用输入和输出 tty.c_cflag |= (CREAD | CLOCAL); // 启用接收器 tty.c_lflag |= ICANON; // 启用规范模式 tty.c_oflag |= OPOST; // 启用输出处理 int result = tcsetattr(fd, TCSANOW, &tty); close(fd); return result; } ``` ## 二、内核控制台管理方法(需要root权限) 通过sysfs接口控制控制台绑定状态: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> #include <unistd.h> #include <dirent.h> int find_serial_console() { DIR *dir = opendir("/sys/devices/virtual/vtconsole"); if (!dir) return -1; struct dirent *entry; while ((entry = readdir(dir)) != NULL) { if (entry->d_type != DT_DIR) continue; if (strncmp(entry->d_name, "vtcon", 5) != 0) continue; char path[256], name[128]; snprintf(path, sizeof(path), "/sys/devices/virtual/vtconsole/%s/name", entry->d_name); FILE *f = fopen(path, "r"); if (!f) continue; if (fgets(name, sizeof(name), f) && (strstr(name, "serial") || strstr(name, "ttyS"))) { fclose(f); closedir(dir); return atoi(entry->d_name + 5); } fclose(f); } closedir(dir); return -1; } int toggle_serial_console(int enable) { int vtcon = find_serial_console(); if (vtcon < 0) return -1; char path[256]; snprintf(path, sizeof(path), "/sys/devices/virtual/vtconsole/vtcon%d/bind", vtcon); int fd = open(path, O_WRONLY); if (fd < 0) return -1; int result = write(fd, enable ? "1\n" : "0\n", 2) != 2 ? -1 : 0; close(fd); return result; } ``` ## 三、使用方法示例 ```c int main() { // 方法1:通过终端属性禁用I/O if (disable_ttyS0_io() == 0) { printf("已通过终端属性禁用ttyS0输入输出\n"); } // 方法2:通过内核控制台管理禁用 if (toggle_serial_console(0) == 0) { printf("已通过内核控制台解绑ttyS0\n"); } // 重新启用 // enable_ttyS0_io(); // toggle_serial_console(1); return 0; } ``` ## 四、注意事项 1. **权限要求**: - 终端属性方法需要访问`/dev/ttyS0`权限 - 内核控制台方法需要root权限 2. **系统兼容性**: - 内核控制台方法需要内核支持VT_CONSOLE - 检查`/sys/devices/virtual/vtconsole/`目录是否存在 3. **作用范围**: - 终端属性方法影响特定进程访问 - 内核控制台方法禁用内核级控制台输出 4. **持久性**: - 两种方法都是临时性设置 - 永久禁用需要在启动参数中移除`console=ttyS0`[^1] ## 五、替代方案:终端复用控制 ```c #include <stdlib.h> void toggle_ttyS0_via_system(bool enable) { // 使用stty命令禁用终端 if (!enable) { system("stty -F /dev/ttyS0 -echo -icanon && " "stty -F /dev/ttyS0 speed 0"); } // 恢复默认设置(通常115200波特率) else { system("stty -F /dev/ttyS0 echo icanon && " "stty -F /dev/ttyS0 speed 115200"); } } ``` 此方法使用stty命令实现,适用于脚本环境,但效率低于直接API调用。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值