Linux 有一些区别于其他操作系统的特性。这些特性包括
第 4 点和第 5 点为系统管理员提供了极大的系统配置灵活性,允许他们从用户模式解决关键的内核错误或特定问题,而无需重启机器。例如,如果您需要在大型服务器上更改某些内容,并且不想重启,您可以准备内核以与您编写的模块进行通信。
Linux 不使用分段来区分任务,而是使用分页。(所有任务仅使用 2 个段,代码和数据/堆栈)
我们也可以说,永远不会发生任务间页错误,因为每个任务使用一组对于每个任务都不同的页表。在某些情况下,不同的任务指向相同的页表,例如共享库:这是减少内存使用所必需的;请记住,共享库仅包含代码,因为所有数据都存储在实际的任务堆栈中。
在 Linux 内核下,只存在 4 个段
[语法是 “用途 [段]”]
在 Intel 架构下,使用的段寄存器是
因此,每个任务都使用 0x23 作为代码段,0x2b 作为数据/堆栈段。
在 Linux 下,根据架构,使用 3 级页表。在 Intel 下,仅支持 2 级。Linux 还支持写时复制机制(更多信息请参见第 10 章)。
答案非常简单:任务间地址冲突不可能存在,因为它们是不可能的。“线性 -> 物理” 映射是通过“分页”完成的,因此只需要以唯一的方式分配物理页即可。
不需要。页面分配是一个动态过程。只有当任务请求页面时,我们才需要页面,因此我们从空闲内存分页中有序地选择它。当我们想释放页面时,我们只需要将其添加到空闲页面列表即可。
内核页有一个问题:它们可以以动态方式分配,但我们不能保证它们在连续区域分配中,因为线性内核空间等同于物理内核空间。
对于代码段,没有问题。引导代码在启动时分配(因此我们有固定数量的内存要分配),而在模块上,我们只需要分配一个可以包含模块代码的内存区域即可。
真正的问题是堆栈段,因为每个任务都使用一些内核堆栈页。堆栈段必须是连续的(根据堆栈定义),因此我们必须为每个任务的堆栈大小建立一个最大限制。如果我们超过此限制,将会发生糟糕的事情。我们会覆盖内核模式进程数据结构。
内核的结构帮助了我们,因为内核函数永远不会
一旦我们知道 N,并且我们知道所有内核函数的静态变量的平均值,我们就可以估计堆栈限制。
如果您想尝试解决这个问题,您可以创建一个模块,其中包含一个函数,该函数多次调用自身。在固定次数之后,内核模块将因页错误异常处理程序(通常是写入只读页)而挂起。
当 IRQ 到来时,任务切换会被延迟到稍后以获得更好的性能。一些任务作业(可能需要在 IRQ 之后立即完成,并且可能在中断时间内占用大量 CPU,例如构建 TCP/IP 数据包)会被排队,并在调度时间完成(一旦时间片结束)。
在最近的内核 (2.4.x) 中,软中断机制被赋予内核线程: “ksoftirqd_CPUn”。n 代表执行 kernel_thread 的 CPU 数量(在单处理器系统中,“ksoftirqd_CPU0” 使用 PID 3)。
“cpu_raise_softirq” 是一个例程,它将唤醒 “ksoftirqd_CPU0” 内核线程,以使其管理排队的作业。
|cpu_raise_softirq |__cpu_raise_softirq |wakeup_softirqd |wake_up_process
“__cpu_raise_softirq” 例程将在描述软中断挂起的向量中设置正确的位。
“wakeup_softirq” 使用 “wakeup_process” 来唤醒 “ksoftirqd_CPU0” 内核线程。
待办事项:描述软中断机制中涉及的数据结构。
当内核线程 “ksoftirqd_CPU0” 被唤醒后,它将执行排队的作业
“ksoftirqd_CPU0” 的代码是(主无限循环)
for (;;) { if (!softirq_pending(cpu)) schedule(); __set_current_state(TASK_RUNNING); while (softirq_pending(cpu)) { do_softirq(); if (current->need_resched) schedule } __set_current_state(TASK_INTERRUPTIBLE) }
即使 Linux 是一个单内核操作系统,也存在一些“内核线程”来执行内务处理工作。
这些任务不使用用户内存;它们共享内核内存。它们也像任何其他内核模式代码一样,在最高权限级别(i386 架构上的 RING 0)运行。
内核线程由 “kernel_thread [arch/i386/kernel/process]” 函数创建,该函数从汇编程序调用 “clone” [arch/i386/kernel/process.c] 系统调用(这是一个类似 “fork” 的系统调用)
int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags) { long retval, d0; __asm__ __volatile__( "movl %%esp,%%esi\n\t" "int $0x80\n\t" /* Linux/i386 system call */ "cmpl %%esp,%%esi\n\t" /* child or parent? */ "je 1f\n\t" /* parent - jump */ /* Load the argument into eax, and push it. That way, it does * not matter whether the called function is compiled with * -mregparm or not. */ "movl %4,%%eax\n\t" "pushl %%eax\n\t" "call *%5\n\t" /* call fn */ "movl %3,%0\n\t" /* exit */ "int $0x80\n" "1:\t" :"=&a" (retval), "=&S" (d0) :"0" (__NR_clone), "i" (__NR_exit), "r" (arg), "r" (fn), "b" (flags | CLONE_VM) : "memory"); return retval; }
一旦调用,我们就有一个新的任务(通常具有非常低的 PID 号,如 2,3 等),等待非常缓慢的资源,如交换或 usb 事件。使用非常缓慢的资源是因为否则我们将会有任务切换开销。
以下是最常见的内核线程列表(来自 “ps x” 命令)
PID COMMAND 1 init 2 keventd 3 kswapd 4 kreclaimd 5 bdflush 6 kupdated 7 kacpid 67 khubd
“init” 内核线程是启动时创建的第一个进程。它将调用所有其他用户模式任务(来自文件 /etc/inittab),如控制台守护进程、tty 守护进程和网络守护进程(“rc” 脚本)。
“kswapd” 由 “clone() [arch/i386/kernel/process.c]” 创建
初始化例程
|do_initcalls |kswapd_init |kernel_thread |syscall fork (in assembler)
do_initcalls [init/main.c]
kswapd_init [mm/vmscan.c]
kernel_thread [arch/i386/kernel/process.c]
Linux 内核模块是在内核模式下运行的代码片段(示例:fs、net 和 hw 驱动程序),您可以在运行时添加它们。
Linux 核心不能模块化:调度和中断管理或核心网络等等。
在 “/lib/modules/KERNEL_VERSION/” 下,您可以找到系统上安装的所有模块。
要加载模块,请键入以下命令
insmod MODULE_NAME parameters example: insmod ne io=0x300 irq=9
注意:如果您希望内核自动搜索某些参数(例如,当使用 PCI 驱动程序时,或者如果您在 /etc/conf.modules 文件下指定了参数),则可以使用 modprobe 代替 insmod。
要卸载模块,请键入以下命令
rmmod MODULE_NAME
一个模块始终包含
如果模块中没有这些函数,您需要添加 2 个宏来指定哪些函数将充当 init 和 exit 模块
注意:只有在内核变量已导出(使用宏 EXPORT_SYMBOL)的情况下,模块才能“看到”它。
// kernel sources side void (*foo_function_pointer)(void *); if (foo_function_pointer) (foo_function_pointer)(parameter); // module side extern void (*foo_function_pointer)(void *); void my_function(void *parameter) { //My code } int init_module() { foo_function_pointer = &my_function; } int cleanup_module() { foo_function_pointer = NULL; }
这个简单的技巧使您的内核具有非常高的灵活性,因为只有当您加载模块时,才会执行 “my_function” 例程。此例程将执行您想要执行的所有操作:例如,控制来自网络的带宽输入流量的 “rshaper” 模块就是以这种方式工作的。
请注意,整个模块机制之所以成为可能,要归功于导出到模块的一些全局变量,例如头列表(允许您根据需要扩展列表)。典型的例子是 fs、通用设备(字符、块、网络、电话)。您必须准备内核以接受您的新模块;在某些情况下,您必须创建一个基础设施(如最近创建的电话基础设施),使其尽可能标准化。
Proc fs 位于 /proc 目录中,这是一个特殊目录,允许您直接与内核对话。
Linux 使用 “proc” 目录来支持直接内核通信:这在许多情况下是必要的,例如,当您想查看主要进程数据结构或在一个接口而不是其他接口上启用 “proxy-arp” 功能时,您想更改最大线程数,或者如果您想调试某些总线状态,例如 ISA 或 PCI,以了解安装了哪些卡以及分配给它们的 I/O 地址和 IRQ。
|-- bus | |-- pci | | |-- 00 | | | |-- 00.0 | | | |-- 01.0 | | | |-- 07.0 | | | |-- 07.1 | | | |-- 07.2 | | | |-- 07.3 | | | |-- 07.4 | | | |-- 07.5 | | | |-- 09.0 | | | |-- 0a.0 | | | `-- 0f.0 | | |-- 01 | | | `-- 00.0 | | `-- devices | `-- usb |-- cmdline |-- cpuinfo |-- devices |-- dma |-- dri | `-- 0 | |-- bufs | |-- clients | |-- mem | |-- name | |-- queues | |-- vm | `-- vma |-- driver |-- execdomains |-- filesystems |-- fs |-- ide | |-- drivers | |-- hda -> ide0/hda | |-- hdc -> ide1/hdc | |-- ide0 | | |-- channel | | |-- config | | |-- hda | | | |-- cache | | | |-- capacity | | | |-- driver | | | |-- geometry | | | |-- identify | | | |-- media | | | |-- model | | | |-- settings | | | |-- smart_thresholds | | | `-- smart_values | | |-- mate | | `-- model | |-- ide1 | | |-- channel | | |-- config | | |-- hdc | | | |-- capacity | | | |-- driver | | | |-- identify | | | |-- media | | | |-- model | | | `-- settings | | |-- mate | | `-- model | `-- via |-- interrupts |-- iomem |-- ioports |-- irq | |-- 0 | |-- 1 | |-- 10 | |-- 11 | |-- 12 | |-- 13 | |-- 14 | |-- 15 | |-- 2 | |-- 3 | |-- 4 | |-- 5 | |-- 6 | |-- 7 | |-- 8 | |-- 9 | `-- prof_cpu_mask |-- kcore |-- kmsg |-- ksyms |-- loadavg |-- locks |-- meminfo |-- misc |-- modules |-- mounts |-- mtrr |-- net | |-- arp | |-- dev | |-- dev_mcast | |-- ip_fwchains | |-- ip_fwnames | |-- ip_masquerade | |-- netlink | |-- netstat | |-- packet | |-- psched | |-- raw | |-- route | |-- rt_acct | |-- rt_cache | |-- rt_cache_stat | |-- snmp | |-- sockstat | |-- softnet_stat | |-- tcp | |-- udp | |-- unix | `-- wireless |-- partitions |-- pci |-- scsi | |-- ide-scsi | | `-- 0 | `-- scsi |-- self -> 2069 |-- slabinfo |-- stat |-- swaps |-- sys | |-- abi | | |-- defhandler_coff | | |-- defhandler_elf | | |-- defhandler_lcall7 | | |-- defhandler_libcso | | |-- fake_utsname | | `-- trace | |-- debug | |-- dev | | |-- cdrom | | | |-- autoclose | | | |-- autoeject | | | |-- check_media | | | |-- debug | | | |-- info | | | `-- lock | | `-- parport | | |-- default | | | |-- spintime | | | `-- timeslice | | `-- parport0 | | |-- autoprobe | | |-- autoprobe0 | | |-- autoprobe1 | | |-- autoprobe2 | | |-- autoprobe3 | | |-- base-addr | | |-- devices | | | |-- active | | | `-- lp | | | `-- timeslice | | |-- dma | | |-- irq | | |-- modes | | `-- spintime | |-- fs | | |-- binfmt_misc | | |-- dentry-state | | |-- dir-notify-enable | | |-- dquot-nr | | |-- file-max | | |-- file-nr | | |-- inode-nr | | |-- inode-state | | |-- jbd-debug | | |-- lease-break-time | | |-- leases-enable | | |-- overflowgid | | `-- overflowuid | |-- kernel | | |-- acct | | |-- cad_pid | | |-- cap-bound | | |-- core_uses_pid | | |-- ctrl-alt-del | | |-- domainname | | |-- hostname | | |-- modprobe | | |-- msgmax | | |-- msgmnb | | |-- msgmni | | |-- osrelease | | |-- ostype | | |-- overflowgid | | |-- overflowuid | | |-- panic | | |-- printk | | |-- random | | | |-- boot_id | | | |-- entropy_avail | | | |-- poolsize | | | |-- read_wakeup_threshold | | | |-- uuid | | | `-- write_wakeup_threshold | | |-- rtsig-max | | |-- rtsig-nr | | |-- sem | | |-- shmall | | |-- shmmax | | |-- shmmni | | |-- sysrq | | |-- tainted | | |-- threads-max | | `-- version | |-- net | | |-- 802 | | |-- core | | | |-- hot_list_length | | | |-- lo_cong | | | |-- message_burst | | | |-- message_cost | | | |-- mod_cong | | | |-- netdev_max_backlog | | | |-- no_cong | | | |-- no_cong_thresh | | | |-- optmem_max | | | |-- rmem_default | | | |-- rmem_max | | | |-- wmem_default | | | `-- wmem_max | | |-- ethernet | | |-- ipv4 | | | |-- conf | | | | |-- all | | | | | |-- accept_redirects | | | | | |-- accept_source_route | | | | | |-- arp_filter | | | | | |-- bootp_relay | | | | | |-- forwarding | | | | | |-- log_martians | | | | | |-- mc_forwarding | | | | | |-- proxy_arp | | | | | |-- rp_filter | | | | | |-- secure_redirects | | | | | |-- send_redirects | | | | | |-- shared_media | | | | | `-- tag | | | | |-- default | | | | | |-- accept_redirects | | | | | |-- accept_source_route | | | | | |-- arp_filter | | | | | |-- bootp_relay | | | | | |-- forwarding | | | | | |-- log_martians | | | | | |-- mc_forwarding | | | | | |-- proxy_arp | | | | | |-- rp_filter | | | | | |-- secure_redirects | | | | | |-- send_redirects | | | | | |-- shared_media | | | | | `-- tag | | | | |-- eth0 | | | | | |-- accept_redirects | | | | | |-- accept_source_route | | | | | |-- arp_filter | | | | | |-- bootp_relay | | | | | |-- forwarding | | | | | |-- log_martians | | | | | |-- mc_forwarding | | | | | |-- proxy_arp | | | | | |-- rp_filter | | | | | |-- secure_redirects | | | | | |-- send_redirects | | | | | |-- shared_media | | | | | `-- tag | | | | |-- eth1 | | | | | |-- accept_redirects | | | | | |-- accept_source_route | | | | | |-- arp_filter | | | | | |-- bootp_relay | | | | | |-- forwarding | | | | | |-- log_martians | | | | | |-- mc_forwarding | | | | | |-- proxy_arp | | | | | |-- rp_filter | | | | | |-- secure_redirects | | | | | |-- send_redirects | | | | | |-- shared_media | | | | | `-- tag | | | | `-- lo | | | | |-- accept_redirects | | | | |-- accept_source_route | | | | |-- arp_filter | | | | |-- bootp_relay | | | | |-- forwarding | | | | |-- log_martians | | | | |-- mc_forwarding | | | | |-- proxy_arp | | | | |-- rp_filter | | | | |-- secure_redirects | | | | |-- send_redirects | | | | |-- shared_media | | | | `-- tag | | | |-- icmp_echo_ignore_all | | | |-- icmp_echo_ignore_broadcasts | | | |-- icmp_ignore_bogus_error_responses | | | |-- icmp_ratelimit | | | |-- icmp_ratemask | | | |-- inet_peer_gc_maxtime | | | |-- inet_peer_gc_mintime | | | |-- inet_peer_maxttl | | | |-- inet_peer_minttl | | | |-- inet_peer_threshold | | | |-- ip_autoconfig | | | |-- ip_conntrack_max | | | |-- ip_default_ttl | | | |-- ip_dynaddr | | | |-- ip_forward | | | |-- ip_local_port_range | | | |-- ip_no_pmtu_disc | | | |-- ip_nonlocal_bind | | | |-- ipfrag_high_thresh | | | |-- ipfrag_low_thresh | | | |-- ipfrag_time | | | |-- neigh | | | | |-- default | | | | | |-- anycast_delay | | | | | |-- app_solicit | | | | | |-- base_reachable_time | | | | | |-- delay_first_probe_time | | | | | |-- gc_interval | | | | | |-- gc_stale_time | | | | | |-- gc_thresh1 | | | | | |-- gc_thresh2 | | | | | |-- gc_thresh3 | | | | | |-- locktime | | | | | |-- mcast_solicit | | | | | |-- proxy_delay | | | | | |-- proxy_qlen | | | | | |-- retrans_time | | | | | |-- ucast_solicit | | | | | `-- unres_qlen | | | | |-- eth0 | | | | | |-- anycast_delay | | | | | |-- app_solicit | | | | | |-- base_reachable_time | | | | | |-- delay_first_probe_time | | | | | |-- gc_stale_time | | | | | |-- locktime | | | | | |-- mcast_solicit | | | | | |-- proxy_delay | | | | | |-- proxy_qlen | | | | | |-- retrans_time | | | | | |-- ucast_solicit | | | | | `-- unres_qlen | | | | |-- eth1 | | | | | |-- anycast_delay | | | | | |-- app_solicit | | | | | |-- base_reachable_time | | | | | |-- delay_first_probe_time | | | | | |-- gc_stale_time | | | | | |-- locktime | | | | | |-- mcast_solicit | | | | | |-- proxy_delay | | | | | |-- proxy_qlen | | | | | |-- retrans_time | | | | | |-- ucast_solicit | | | | | `-- unres_qlen | | | | `-- lo | | | | |-- anycast_delay | | | | |-- app_solicit | | | | |-- base_reachable_time | | | | |-- delay_first_probe_time | | | | |-- gc_stale_time | | | | |-- locktime | | | | |-- mcast_solicit | | | | |-- proxy_delay | | | | |-- proxy_qlen | | | | |-- retrans_time | | | | |-- ucast_solicit | | | | `-- unres_qlen | | | |-- route | | | | |-- error_burst | | | | |-- error_cost | | | | |-- flush | | | | |-- gc_elasticity | | | | |-- gc_interval | | | | |-- gc_min_interval | | | | |-- gc_thresh | | | | |-- gc_timeout | | | | |-- max_delay | | | | |-- max_size | | | | |-- min_adv_mss | | | | |-- min_delay | | | | |-- min_pmtu | | | | |-- mtu_expires | | | | |-- redirect_load | | | | |-- redirect_number | | | | `-- redirect_silence | | | |-- tcp_abort_on_overflow | | | |-- tcp_adv_win_scale | | | |-- tcp_app_win | | | |-- tcp_dsack | | | |-- tcp_ecn | | | |-- tcp_fack | | | |-- tcp_fin_timeout | | | |-- tcp_keepalive_intvl | | | |-- tcp_keepalive_probes | | | |-- tcp_keepalive_time | | | |-- tcp_max_orphans | | | |-- tcp_max_syn_backlog | | | |-- tcp_max_tw_buckets | | | |-- tcp_mem | | | |-- tcp_orphan_retries | | | |-- tcp_reordering | | | |-- tcp_retrans_collapse | | | |-- tcp_retries1 | | | |-- tcp_retries2 | | | |-- tcp_rfc1337 | | | |-- tcp_rmem | | | |-- tcp_sack | | | |-- tcp_stdurg | | | |-- tcp_syn_retries | | | |-- tcp_synack_retries | | | |-- tcp_syncookies | | | |-- tcp_timestamps | | | |-- tcp_tw_recycle | | | |-- tcp_window_scaling | | | `-- tcp_wmem | | `-- unix | | `-- max_dgram_qlen | |-- proc | `-- vm | |-- bdflush | |-- kswapd | |-- max-readahead | |-- min-readahead | |-- overcommit_memory | |-- page-cluster | `-- pagetable_cache |-- sysvipc | |-- msg | |-- sem | `-- shm |-- tty | |-- driver | | `-- serial | |-- drivers | |-- ldisc | `-- ldiscs |-- uptime `-- version
在该目录中,还有所有使用 PID 作为文件名的任务(您可以访问所有任务信息,如二进制文件的路径、使用的内存等等)。
有趣的是,您不仅可以查看内核值(例如,查看有关任何任务或 TCP/IP 堆栈的已启用网络选项的信息),而且您还可以修改其中的一些值,通常是 /proc/sys 目录下的那些值
/proc/sys/ acpi dev debug fs proc net vm kernel
以下是非常重要且广为人知的内核值,可以随时修改
overflowgid overflowuid random threads-max // Max number of threads, typically 16384 sysrq // kernel hack: you can view istant register values and more sem msgmnb msgmni msgmax shmmni shmall shmmax rtsig-max rtsig-nr modprobe // modprobe file location printk ctrl-alt-del cap-bound panic domainname // domain name of your Linux box hostname // host name of your Linux box version // date info about kernel compilation osrelease // kernel version (i.e. 2.4.5) ostype // Linux!
这可以被认为是最有用的 proc 子目录。它允许您更改网络内核配置的非常重要的设置。
core ipv4 ipv6 unix ethernet 802
下面列出的是常规网络设置,例如 “netdev_max_backlog”(通常为 300),这是所有网络数据包的长度。此值可能会限制您接收数据包时的网络带宽,Linux 必须等待调度时间才能刷新缓冲区(由于下半部机制),大约 1000/HZ 毫秒
300 * 100 = 30 000 packets HZ(Timeslice freq) packets/s 30 000 * 1000 = 30 M packets average (Bytes/packet) throughput Bytes/s
如果您想获得更高的吞吐量,您需要增加 netdev_max_backlog,方法是输入
echo 4000 > /proc/sys/net/core/netdev_max_backlog
注意:警告某些 HZ 值:在某些架构(如 alpha 或 arm-tbox)下,它是 1000,因此您可能具有 300 MBytes/s 的平均吞吐量。
“ip_forward”,启用或禁用 Linux 盒子的 IP 转发。这是所有设备的通用设置,您可以指定您选择的每个设备。
我认为这是最有用的 /proc 条目,因为它允许您更改某些网络设置以支持无线网络(有关更多信息,请参阅 Wireless-HOWTO)。
以下是一些您可以使用此设置的示例