KernelAnalysis
Roberto Arcomano
Copyright (C) 2000,2001,2002 Roberto Arcomano.
Директория /usr/src/linux подразумевается как стартовая
В данном документе для рассмотрения того , как функции вызывают друг друга ,
используется т.н. префикс "InterCallings Analysis "(ICA)" , например , команда the sleep_on : color=blue>
|sleep_on
|init_waitqueue_entry --
|__add_wait_queue | enqueuing request
|list_add |
|__list_add --
|schedule --- waiting for request to be executed
|__remove_wait_queue --
|list_del | dequeuing request
|__list_del --
В примере function1 -> function2 < function1 > есть указатель на другую функцию .
Когда мы пишем function: , то это как правило метка .
Итак , что же такое ядро ? Это сердцевина компьютерной системы , которая позволяет конечным пользователям
использовать ресурсы компьютера , которая - как в случае с виндами - может включать в себя графическое управление .
В чем разница между пользовательским режимом и режимом ядра ?
1. Kernel Mode: в этом случае машина оперирует с критическими
структурами данных - памятью , маппмнгом , IRQ , DMA и т.д.
2. User Mode: пользовательские приложения
Процессоры Intel могут находиться в 4-х состояниях - 0,1,2,3 , и 0 - это Kernel Mode.
Линукс использует лишь 2 уровня из 4-х .
Когда происходит переключение из одного режима в другой - как правило :
1. При вызове System Call: вызывается код в Kernel Mode
2. При генерации прерывания или исключения
Системные функции могут быть вызваны при :
когда происходит вызов I/O device или file
когда нужно вычислить pid, изменить scheduling policy
вызов fork
выполнение команды
Ниже представлен список System Calls для 2.4.17, [ arch/i386/kernel/entry.S ]
long SYMBOL_NAME(sys_ni_syscall)/* 0 - old "setup()" system call*/
.long SYMBOL_NAME(sys_exit)
.long SYMBOL_NAME(sys_fork)
.long SYMBOL_NAME(sys_read)
.long SYMBOL_NAME(sys_write)
.long SYMBOL_NAME(sys_open) /* 5 */
.long SYMBOL_NAME(sys_close)
.long SYMBOL_NAME(sys_waitpid)
.long SYMBOL_NAME(sys_creat)
.long SYMBOL_NAME(sys_link)
.long SYMBOL_NAME(sys_unlink) /* 10 */
.long SYMBOL_NAME(sys_execve)
.long SYMBOL_NAME(sys_chdir)
.long SYMBOL_NAME(sys_time)
.long SYMBOL_NAME(sys_mknod)
.long SYMBOL_NAME(sys_chmod) /* 15 */
.long SYMBOL_NAME(sys_lchown16)
long SYMBOL_NAME(sys_ni_syscall)/* old break syscall holder */
.long SYMBOL_NAME(sys_stat)
.long SYMBOL_NAME(sys_lseek)
.long SYMBOL_NAME(sys_getpid) /* 20 */
.long SYMBOL_NAME(sys_mount)
.long SYMBOL_NAME(sys_oldumount)
.long SYMBOL_NAME(sys_setuid16)
.long SYMBOL_NAME(sys_getuid16)
.long SYMBOL_NAME(sys_stime) /* 25 */
.long SYMBOL_NAME(sys_ptrace)
.long SYMBOL_NAME(sys_alarm)
.long SYMBOL_NAME(sys_fstat)
.long SYMBOL_NAME(sys_pause)
.long SYMBOL_NAME(sys_utime) /* 30 */
.long SYMBOL_NAME(sys_ni_syscall)/* old stty syscall holder */
.long SYMBOL_NAME(sys_ni_syscall)/* old gtty syscall holder */
.long SYMBOL_NAME(sys_access)
.long SYMBOL_NAME(sys_nice)
.long SYMBOL_NAME(sys_ni_syscall) /* 35 */
/* old ftime syscall holder */
.long SYMBOL_NAME(sys_sync)
.long SYMBOL_NAME(sys_kill)
.long SYMBOL_NAME(sys_rename)
.long SYMBOL_NAME(sys_mkdir)
.long SYMBOL_NAME(sys_rmdir) /* 40 */
.long SYMBOL_NAME(sys_dup)
.long SYMBOL_NAME(sys_pipe)
.long SYMBOL_NAME(sys_times)
/* old prof syscall holder */
.long SYMBOL_NAME(sys_ni_syscall)
.long SYMBOL_NAME(sys_brk) /* 45 */
.long SYMBOL_NAME(sys_setgid16)
.long SYMBOL_NAME(sys_getgid16)
.long SYMBOL_NAME(sys_signal)
.long SYMBOL_NAME(sys_geteuid16)
.long SYMBOL_NAME(sys_getegid16) /* 50 */
.long SYMBOL_NAME(sys_acct)
/* recycled never used phys() */
.long SYMBOL_NAME(sys_umount)
/* old lock syscall holder */
.long SYMBOL_NAME(sys_ni_syscall)
.long SYMBOL_NAME(sys_ioctl)
.long SYMBOL_NAME(sys_fcntl) /* 55 */
/* old mpx syscall holder */
.long SYMBOL_NAME(sys_ni_syscall)
.long SYMBOL_NAME(sys_setpgid)
/* old ulimit syscall holder */
.long SYMBOL_NAME(sys_ni_syscall)
.long SYMBOL_NAME(sys_olduname)
.long SYMBOL_NAME(sys_umask) /* 60 */
.long SYMBOL_NAME(sys_chroot)
.long SYMBOL_NAME(sys_ustat)
.long SYMBOL_NAME(sys_dup2)
.long SYMBOL_NAME(sys_getppid)
.long SYMBOL_NAME(sys_getpgrp) /* 65 */
.long SYMBOL_NAME(sys_setsid)
.long SYMBOL_NAME(sys_sigaction)
.long SYMBOL_NAME(sys_sgetmask)
.long SYMBOL_NAME(sys_ssetmask)
.long SYMBOL_NAME(sys_setreuid16) /* 70 */
.long SYMBOL_NAME(sys_setregid16)
.long SYMBOL_NAME(sys_sigsuspend)
.long SYMBOL_NAME(sys_sigpending)
.long SYMBOL_NAME(sys_sethostname)
.long SYMBOL_NAME(sys_setrlimit) /* 75 */
.long SYMBOL_NAME(sys_old_getrlimit)
.long SYMBOL_NAME(sys_getrusage)
.long SYMBOL_NAME(sys_gettimeofday)
.long SYMBOL_NAME(sys_settimeofday)
.long SYMBOL_NAME(sys_getgroups16) /* 80 */
.long SYMBOL_NAME(sys_setgroups16)
.long SYMBOL_NAME(old_select)
.long SYMBOL_NAME(sys_symlink)
.long SYMBOL_NAME(sys_lstat)
.long SYMBOL_NAME(sys_readlink) /* 85 */
.long SYMBOL_NAME(sys_uselib)
.long SYMBOL_NAME(sys_swapon)
.long SYMBOL_NAME(sys_reboot)
.long SYMBOL_NAME(old_readdir)
.long SYMBOL_NAME(old_mmap) /* 90 */
.long SYMBOL_NAME(sys_munmap)
.long SYMBOL_NAME(sys_truncate)
.long SYMBOL_NAME(sys_ftruncate)
.long SYMBOL_NAME(sys_fchmod)
.long SYMBOL_NAME(sys_fchown16) /* 95 */
.long SYMBOL_NAME(sys_getpriority)
.long SYMBOL_NAME(sys_setpriority)
/* old profil syscall holder */
.long SYMBOL_NAME(sys_ni_syscall)
.long SYMBOL_NAME(sys_statfs)
.long SYMBOL_NAME(sys_fstatfs) /* 100 */
.long SYMBOL_NAME(sys_ioperm)
.long SYMBOL_NAME(sys_socketcall)
.long SYMBOL_NAME(sys_syslog)
.long SYMBOL_NAME(sys_setitimer)
.long SYMBOL_NAME(sys_getitimer) /* 105 */
.long SYMBOL_NAME(sys_newstat)
.long SYMBOL_NAME(sys_newlstat)
.long SYMBOL_NAME(sys_newfstat)
.long SYMBOL_NAME(sys_uname)
.long SYMBOL_NAME(sys_iopl) /* 110 */
.long SYMBOL_NAME(sys_vhangup)
.long SYMBOL_NAME(sys_ni_syscall)/* old "idle" system call */
.long SYMBOL_NAME(sys_vm86old)
.long SYMBOL_NAME(sys_wait4)
.long SYMBOL_NAME(sys_swapoff) /* 115 */
.long SYMBOL_NAME(sys_sysinfo)
.long SYMBOL_NAME(sys_ipc)
.long SYMBOL_NAME(sys_fsync)
.long SYMBOL_NAME(sys_sigreturn)
.long SYMBOL_NAME(sys_clone) /* 120 */
.long SYMBOL_NAME(sys_setdomainname)
.long SYMBOL_NAME(sys_newuname)
.long SYMBOL_NAME(sys_modify_ldt)
.long SYMBOL_NAME(sys_adjtimex)
.long SYMBOL_NAME(sys_mprotect) /* 125 */
.long SYMBOL_NAME(sys_sigprocmask)
.long SYMBOL_NAME(sys_create_module)
.long SYMBOL_NAME(sys_init_module)
.long SYMBOL_NAME(sys_delete_module)
.long SYMBOL_NAME(sys_get_kernel_syms) /* 130 */
.long SYMBOL_NAME(sys_quotactl)
.long SYMBOL_NAME(sys_getpgid)
.long SYMBOL_NAME(sys_fchdir)
.long SYMBOL_NAME(sys_bdflush)
.long SYMBOL_NAME(sys_sysfs) /* 135 */
.long SYMBOL_NAME(sys_personality)
.long SYMBOL_NAME(sys_ni_syscall) /* for afs_syscall */
.long SYMBOL_NAME(sys_setfsuid16)
.long SYMBOL_NAME(sys_setfsgid16)
.long SYMBOL_NAME(sys_llseek) /* 140 */
.long SYMBOL_NAME(sys_getdents)
.long SYMBOL_NAME(sys_select)
.long SYMBOL_NAME(sys_flock)
.long SYMBOL_NAME(sys_msync)
.long SYMBOL_NAME(sys_readv) /* 145 */
.long SYMBOL_NAME(sys_writev)
.long SYMBOL_NAME(sys_getsid)
.long SYMBOL_NAME(sys_fdatasync)
.long SYMBOL_NAME(sys_sysctl)
.long SYMBOL_NAME(sys_mlock) /* 150 */
.long SYMBOL_NAME(sys_munlock)
.long SYMBOL_NAME(sys_mlockall)
.long SYMBOL_NAME(sys_munlockall)
.long SYMBOL_NAME(sys_sched_setparam)
.long SYMBOL_NAME(sys_sched_getparam) /* 155 */
.long SYMBOL_NAME(sys_sched_setscheduler)
.long SYMBOL_NAME(sys_sched_getscheduler)
.long SYMBOL_NAME(sys_sched_yield)
.long SYMBOL_NAME(sys_sched_get_priority_max)
.long SYMBOL_NAME(sys_sched_get_priority_min) /* 160 */
.long SYMBOL_NAME(sys_sched_rr_get_interval)
.long SYMBOL_NAME(sys_nanosleep)
.long SYMBOL_NAME(sys_mremap)
.long SYMBOL_NAME(sys_setresuid16)
.long SYMBOL_NAME(sys_getresuid16) /* 165 */
.long SYMBOL_NAME(sys_vm86)
.long SYMBOL_NAME(sys_query_module)
.long SYMBOL_NAME(sys_poll)
.long SYMBOL_NAME(sys_nfsservctl)
.long SYMBOL_NAME(sys_setresgid16) /* 170 */
.long SYMBOL_NAME(sys_getresgid16)
.long SYMBOL_NAME(sys_prctl)
.long SYMBOL_NAME(sys_rt_sigreturn)
.long SYMBOL_NAME(sys_rt_sigaction)
.long SYMBOL_NAME(sys_rt_sigprocmask) /* 175 */
.long SYMBOL_NAME(sys_rt_sigpending)
.long SYMBOL_NAME(sys_rt_sigtimedwait)
.long SYMBOL_NAME(sys_rt_sigqueueinfo)
.long SYMBOL_NAME(sys_rt_sigsuspend)
.long SYMBOL_NAME(sys_pread) /* 180 */
.long SYMBOL_NAME(sys_pwrite)
.long SYMBOL_NAME(sys_chown16)
.long SYMBOL_NAME(sys_getcwd)
.long SYMBOL_NAME(sys_capget)
.long SYMBOL_NAME(sys_capset) /* 185 */
.long SYMBOL_NAME(sys_sigaltstack)
.long SYMBOL_NAME(sys_sendfile)
.long SYMBOL_NAME(sys_ni_syscall) /* streams1 */
.long SYMBOL_NAME(sys_ni_syscall) /* streams2 */
.long SYMBOL_NAME(sys_vfork) /* 190 */
.long SYMBOL_NAME(sys_getrlimit)
.long SYMBOL_NAME(sys_mmap2)
.long SYMBOL_NAME(sys_truncate64)
.long SYMBOL_NAME(sys_ftruncate64)
.long SYMBOL_NAME(sys_stat64) /* 195 */
.long SYMBOL_NAME(sys_lstat64)
.long SYMBOL_NAME(sys_fstat64)
.long SYMBOL_NAME(sys_lchown)
.long SYMBOL_NAME(sys_getuid)
.long SYMBOL_NAME(sys_getgid) /* 200 */
.long SYMBOL_NAME(sys_geteuid)
.long SYMBOL_NAME(sys_getegid)
.long SYMBOL_NAME(sys_setreuid)
.long SYMBOL_NAME(sys_setregid)
.long SYMBOL_NAME(sys_getgroups) /* 205 */
.long SYMBOL_NAME(sys_setgroups)
.long SYMBOL_NAME(sys_fchown)
.long SYMBOL_NAME(sys_setresuid)
.long SYMBOL_NAME(sys_getresuid)
.long SYMBOL_NAME(sys_setresgid) /* 210 */
.long SYMBOL_NAME(sys_getresgid)
.long SYMBOL_NAME(sys_chown)
.long SYMBOL_NAME(sys_setuid)
.long SYMBOL_NAME(sys_setgid)
.long SYMBOL_NAME(sys_setfsuid) /* 215 */
.long SYMBOL_NAME(sys_setfsgid)
.long SYMBOL_NAME(sys_pivot_root)
.long SYMBOL_NAME(sys_mincore)
.long SYMBOL_NAME(sys_madvise)
.long SYMBOL_NAME(sys_getdents64) /* 220 */
.long SYMBOL_NAME(sys_fcntl64)
.long SYMBOL_NAME(sys_ni_syscall) /* reserved for TUX */
.long SYMBOL_NAME(sys_ni_syscall) /* Reserved for Security */
.long SYMBOL_NAME(sys_gettid)
.long SYMBOL_NAME(sys_readahead) /* 225 */
При возникновении IRQ Event вызывается IRQ Handler , и любая задача прерывается ,
после чего управление возвращается точно в точку прерывания :
Running Task
|-----------| (3)
NORMAL | | | [break execution] IRQ Handler
EXECUTION (1)| | | ------------->|---------|
| \|/ | | | does |
IRQ (2)---->| .. |-----> | some |
| | |<----- | work |
BACK TO | | | | | ..(4). |
NORMAL (6)| \|/ | <-------------|_________|
EXECUTION |___________| [return to code]
(5)
USER MODE KERNEL MODE
Специальные прерывания таймера возникают при :
1. Системных предупреждениях
2. Вызываются системным шедулятором
Ключевым понятием для современных ОС является понятие "Task" или приложение .
Приложения выполняются в режиме многозадачности . Переключение между приложениями
происходит автоматически через промежуток времени , называемый "timeslice" ,
которое обычно равно 10 ms . При этом приложение использует переменную , называемую "state" ,
которая может быть в 2-х состояниях - READY или BLOCKED.
Виртуальная память и сегментация позволяют решить проблемы с размещением приложениий
независимо от имеющихся ресурсов . Сегмент - это как-бы образ приложения в памяти.
Память разбивается на страницы размером по 4 кб , и процесс загружается в одну или несколько
таких страниц .
На следующей схеме показано , как процесс размещается в памяти :
| .. |
|____________________|
----->| Page 1 |
| |____________________|
| | .. |
____________________ | |____________________|
| | |---->| Page 2 |
| Segment X | ----| |____________________|
| | | | .. |
|____________________| | |____________________|
| | .. |
| |____________________|
|---->| Page 3 |
|____________________|
| .. |
Сегмент разбивается на 3 куска , каждый из которых загружается в отдельную страницу .
Пэйджинг имеет иерархическую структуру :
| | | |
| | Offset2 | Value |
| | /|\| |
Offset1 | |----- | | |
/|\ | | | | | |
| | | | \|/| |
| | | ------>| |
\|/ | | | |
Base Paging Address ---->| | | |
| ....... | | ....... |
| | | |
Linux Startup
Ядро стартует с метки ''startup_32:''
|startup_32:
|start_kernel
|lock_kernel
|trap_init
|init_IRQ
|sched_init
|softirq_init
|time_init
|console_init
|#ifdef CONFIG_MODULES
|init_modules
|#endif
|kmem_cache_init
|sti
|calibrate_delay
|mem_init
|kmem_cache_sizes_init
|pgtable_cache_init
|fork_init
|proc_caches_init
|vfs_caches_init
|buffer_init
|page_cache_init
|signals_init
|#ifdef CONFIG_PROC_FS
|proc_root_init
|#endif
|#if defined(CONFIG_SYSVIPC)
|ipc_init
|#endif
|check_bugs
|smp_init
|rest_init
|kernel_thread
|unlock_kernel
|cpu_idle
· startup_32 [arch/i386/kernel/head.S]
· start_kernel [init/main.c]
· lock_kernel [include/asm/smplock.h]
· trap_init [arch/i386/kernel/traps.c]
· init_IRQ [arch/i386/kernel/i8259.c]
· sched_init [kernel/sched.c]
· softirq_init [kernel/softirq.c]
· time_init [arch/i386/kernel/time.c]
· console_init [drivers/char/tty_io.c]
· init_modules [kernel/module.c]
· kmem_cache_init [mm/slab.c]
· sti [include/asm/system.h]
· calibrate_delay [init/main.c]
· mem_init [arch/i386/mm/init.c]
· kmem_cache_sizes_init [mm/slab.c]
· pgtable_cache_init [arch/i386/mm/init.c]
· fork_init [kernel/fork.c]
· proc_caches_init
· vfs_caches_init [fs/dcache.c]
· buffer_init [fs/buffer.c]
· page_cache_init [mm/filemap.c]
· signals_init [kernel/signal.c]
· proc_root_init [fs/proc/root.c]
· ipc_init [ipc/util.c]
· check_bugs [include/asm/bugs.h]
· smp_init [init/main.c]
· rest_init
· kernel_thread [arch/i386/kernel/process.c]
· unlock_kernel [include/asm/smplock.h]
· cpu_idle [arch/i386/kernel/process.c]
Фактически функция start_kernel никогда не кончается
Функция ''init'' :
|init
|lock_kernel
|do_basic_setup
|mtrr_init
|sysctl_init
|pci_init
|sock_init
|start_context_thread
|do_init_calls
|(*call())-> kswapd_init
|prepare_namespace
|free_initmem
|unlock_kernel
|execve
В линуксе 4 типа сегментов :
1. Kernel Code [0x10]
2. Kernel Data / Stack [0x18]
3. User Code [0x23]
4. User Data / Stack [0x2b]
Сегментные регистры :
· CS - Code Segment
· DS - Data Segment
· SS - Stack Segment
· ES - Alternative Segment
Под линуксом возможны 3 уровня пэйджинга , в зависимости от архитектуры .
На интеловской платформе 2 уровня пэйджинга .
В линуксе память процесса , несмотря на адресную "разбросанность" страниц , его составляющих ,
в дефрагментации не нуждается .
Существует проблема со стеком ядра . Каждый процесс внутри ядра использует общий для всех стек ядра.
И размер этого стека в каждой задаче должен быть ограничен .
Функции ядра не могут быть рекурсивны и вызываются фиксированное число раз .
При вызове IRQ происходит переключение задач . Некоторые процессы , такие например , которые
отвечают за TCP/IP , могут в этот момент выстраиваться шедулятором в очередь .
Этот механизм в ядре 2.4 называется 'ksoftirqd_CPUn' , где n - это константа kernel_thread.
Схема механизма Softirq :
|cpu_raise_softirq
|__cpu_raise_softirq
|wakeup_softirqd
|wake_up_process
· cpu_raise_softirq [kernel/softirq.c]
· __cpu_raise_softirq [include/linux/interrupt.h]
· wakeup_softirq [kernel/softirq.c]
· wake_up_process [kernel/sched.c]
При вызове треда ядра под названием '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)
}
· ksoftirqd [kernel/softirq.c]
Kernel Threads
В монолитном линукс-ядре нужно выделить несколько ''kernel threads''.
Они используюи KERNEL-память и у них 0-я привилегия .
Они создаются функцией ''kernel_thread [arch/i386/kernel/process]''
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;
}
Т.о. сгенерированная задача будет ожидать критичное по скорости выполнения событие
типа свопа или usb-шное событие .
Ниже приведен список наиболее важных kernel threads (''ps x''):
PID COMMAND
1 init
2 keventd
3 kswapd
4 kreclaimd
5 bdflush
6 kupdated
7 kacpid
67 khubd
Они , в свою очередь , могут вызывать другие пользовательские задачи (из /etc/inittab)
типа консольных демонов , tty-демонов,сетевых демонов (скрипт ''rc'' ).
Пример Kernel Threads: kswapd [mm/vmscan.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]
Kernel Modules
Linux Kernel modules - это код (например: fs, net, hw - драйвера) , выполняющийся в пространстве ядра ,
который подключается после загрузки , по команде пользователя .
Такие вещи , как шедулятор , управление прерываниями или сетевое ядро , не может быть загружено
из модулей , поэтому оно жестко вкомпилировано в ядро .
Модули , инсталлированные в вашей системе , можно найти в "/lib/modules/KERNEL_VERSION/"
Для загрузки модуля , наберите команду :
insmod MODULE_NAME parameters
пример: insmod ne io=0x300 irq=9
Для загрузки можно использовать другую команду - modprobe , которая сама отыщет нужные параметры ,
например в /etc/conf.modules.
Для выгрузки модуля :
rmmod MODULE_NAME
Модуль всегда включает в себя
1. Функцию "init_module"
2. Функцию "cleanup_module"
Если таких функций в модуле нет ,
тогда там должны быть по крайней мере 2 макроса :
1. module_init(FUNCTION_NAME)
2. module_exit(FUNCTION_NAME)
Модуль видит переменную ядра с помощь макроса EXPORT_SYMBOL.
При разработке модулей нужно иметь в виду следующее : на стороне ядра мы имеем :
void (*foo_function_pointer)(void *);
if (foo_function_pointer)
(foo_function_pointer)(parameter);
// на стороне модуля
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;
}
Каталог /proc .
/proc - каталог , который позволяет напрямую "общаться" с ядром .
Линукс использует его для поддержки т.н. direct kernel communications:
можно например посмотреть основные структуры процессов , изменить максимальное число трэдов ,
отдебажить состояние шины данных типа ISA или PCI , узнать о том , какие карты проинсталированы
и какие им назначены порты и прерывания .
|-- 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
При этом можно не только получить информацию о переменных ядра , но и модифицировать некоторые
из них - см. /proc/sys каталог :
/proc/sys/
acpi
dev
debug
fs
proc
net
vm
kernel
Ниже перечислены некоторые значения каталога /proc/sys/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/sys/net
Этот каталог позволяет изменить сетевые настройки ядра :
core
ipv4
ipv6
unix
ethernet
802
/proc/sys/net/core
Ниже идет перечисление таких сетевых параметров , как "netdev_max_backlog" (300), длина пакета.
Это может изменить производительность ваше системы по получению пакетов.
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
/proc/sys/net/ipv4
"ip_forward" - разрешает или запрещает ip forwarding .
/proc/sys/net/ipv4/conf/interface
Позволяет поддерживать wireless networks
"proxy_arp" - arp feature.
"send_redirects" - посылка ICMP_REDIRECT
Multitasking
Выполняемый процесс может быть в одном из следующих состояний : [include/linux.h]):
1. TASK_RUNNING
2. TASK_INTERRUPTIBLE - в ожидании прерывания
3. TASK_UNINTERRUPTIBLE - в очереди
4. TASK_ZOMBIE - процесс , потерявшиий родителя
5. TASK_STOPPED - процесс под дебагом
Graphical Interaction
______________ CPU Available ______________
| | ----------------> | |
| TASK_RUNNING | | Real Running |
|______________| <---------------- |______________|
CPU Busy
| /|\
Waiting for | | Resource
Resource | | Available
\|/ |
______________________
| |
| TASK_INTERRUPTIBLE / |
| TASK-UNINTERRUPTIBLE |
|______________________|
Timeslice
Каждые 10 ms генерится IRQ0 comes, которое играет ведущую роль в многозадачности.
Этот сигнал приходит от PIC 8259 :
_____ ______ ______
| CPU |<------| 8259 |------| 8253 |
|_____| IRQ0 |______| |___/|\|
|_____ CLK 1.193.180 MHz
// From include/asm/param.h
#ifndef HZ
#define HZ 100
#endif
// From include/asm/timex.h
#define CLOCK_TICK_RATE 1193180 /* Underlying HZ */
// From include/linux/timex.h
#define LATCH ((CLOCK_TICK_RATE + HZ/2) / HZ) /* For divider */
// From arch/i386/kernel/i8259.c
outb_p(0x34,0x43); /* binary, mode 2, LSB/MSB, ch 0 */
outb_p(LATCH & 0xff , 0x40); /* LSB */
outb(LATCH >> 8 , 0x40); /* MSB */
PIT,или Programmable Interval Timer , запрограммирован с LATCH = (1193180/HZ) = 11931.8 .
LATCH = 11931.8 дает на выходе 8253 частоту 1193180 / 11931.8 = 100 Hz, поэтому период = 10ms.
Это частота с которой шедулятор моет "перебирать" процессы , стоящие в очереди .
Linux Timer IRQ ICA
Linux Timer IRQ
IRQ 0 [Timer]
|
\|/
|IRQ0x00_interrupt // wrapper IRQ handler
|SAVE_ALL ---
|do_IRQ | wrapper routines
|handle_IRQ_event ---
|handler() -> timer_interrupt
|do_timer_interrupt
|do_timer
|jiffies++;
|update_process_times
|if (--counter <= 0) {
|counter = 0;
|need_resched = 1;
|}
|do_softirq
|while (need_resched) { // if necessary
|schedule // reschedule
|handle_softirq
|}
|RESTORE_ALL
· IRQ0x00_interrupt, SAVE_ALL [include/asm/hw_irq.h]
· do_IRQ, handle_IRQ_event [arch/i386/kernel/irq.c]
timer_interrupt, do_timer_interrupt [arch/i386/kernel/time.c]
· do_timer, update_process_times [kernel/timer.c]
· do_softirq [kernel/soft_irq.c]
RESTORE_ALL, while loop [arch/i386/kernel/entry.S]
У каждой задачи есть счетчик , и когда он уменьшается до нуля , автоматически происходит
переключение на другую задачу .
Scheduler
scheduler - это код , который выбирает , какая задача будет выполняться в данный момент .
|schedule
|do_softirq // manages post-IRQ work
|for each task
|calculate counter
|prepare_to__switch // does anything
|switch_mm // change Memory context (change CR3 value)
|switch_to (assembler)
|SAVE ESP
|RESTORE future_ESP
|SAVE EIP
|push future_EIP *** push parameter as we did a call
|jmp __switch_to (it does some TSS work)
|__switch_to()
..
|ret *** ret from call using future_EIP in place of call address
new_task
Когда приходит прерывание от устройства, линукс выполняет переключение на задачу , которая начинает
опрашивать это устройство . Для того , этот опрос не пожирал все ресурсы , начиная с версии 1.0 реализован
механизм "bottom half" (BH).
В более поздних версиях bh заменен на более динамичную очередь .
Схема работы BH :
1. Декларирование
2. Маркировка
3. Выполнение
Декларация:
#define DECLARE_TASK_QUEUE(q) LIST_HEAD(q)
#define LIST_HEAD(name) \
struct list_head name = LIST_HEAD_INIT(name)
struct list_head {
struct list_head *next, *prev;
};
#define LIST_HEAD_INIT(name) { &(name), &(name) }
''DECLARE_TASK_QUEUE'' [include/linux/tqueue.h, include/linux/list.h]
"DECLARE_TASK_QUEUE(q)" - это макрос для декларирования структуры "q" , управляющей очередью заданий .
Маркировка
Функция "mark_bh" [include/linux/interrupt.h]
|mark_bh(NUMBER)
|tasklet_hi_schedule(bh_task_vec + NUMBER)
|insert into tasklet_hi_vec
|__cpu_raise_softirq(HI_SOFTIRQ)
|soft_active |= (1 << HI_SOFTIRQ)
''mark_bh''[include/linux/interrupt.h]
Выполнение - функция "do_IRQ" [arch/i386/kernel/irq.c]
|do_softirq
|h->action(h)-> softirq_vec[TASKLET_SOFTIRQ]->action -> tasklet_action
|tasklet_vec[0].list->func
Список низко-уровневых подпрограмм :
set_intr_gate
set_trap_gate
set_task_gate (not used).
(*interrupt)[NR_IRQS](void) = { IRQ0x00_interrupt, IRQ0x01_interrupt, ..}
Переключение задач
Оно происходит в следующих случаях :
· TimeSlice
когда приложение начинает опрашивать какой-то ресурс ,
оно засыпает
когда процесс в ожидании пайпа от другого процесса ,
нужно переключиться на этот другой процесс
#define switch_to(prev,next,last) do { \
asm volatile("pushl %%esi\n\t" \
"pushl %%edi\n\t" \
"pushl %%ebp\n\t" \
"movl %%esp,%0\n\t" /* save ESP */ \
"movl %3,%%esp\n\t" /* restore ESP */ \
"movl $1f,%1\n\t" /* save EIP */ \
"pushl %4\n\t" /* restore EIP */ \
"jmp __switch_to\n" \
"1:\t" \
"popl %%ebp\n\t" \
"popl %%edi\n\t" \
"popl %%esi\n\t" \
:"=m" (prev->thread.esp),"=m" (prev->thread.eip), \
"=b" (last) \
:"m" (next->thread.esp),"m" (next->thread.eip), \
"a" (prev), "d" (next), \
"b" (prev)); \
} while (0)
Переключение происходит после метки 1 :
U S E R M O D E K E R N E L M O D E
| | | | | | | |
| | | | Timer | | | |
| | | Normal | IRQ | | | |
| | | Exec |------>|Timer_Int.| | |
| | | | | | .. | | |
| | | \|/ | |schedule()| | Task1 Ret|
| | | | |_switch_to|<-- | Address |
|__________| |__________| | | | | |
| | |S | |
Task1 Data/Stack Task1 Code | | |w | |
| | T|i | |
| | a|t | |
| | | | | | s|c | |
| | | | Timer | | k|h | |
| | | Normal | IRQ | | |i | |
| | | Exec |------>|Timer_Int.| |n | |
| | | | | | .. | |g | |
| | | \|/ | |schedule()| | | Task2 Ret|
| | | | |_switch_to|<-- | Address |
|__________| |__________| |__________| |__________|
Task2 Data/Stack Task2 Code Kernel Code Kernel Data/Stack
. Fork
Fork - генерация нового процесса . Имеется родительский процесс , структуры данных которого используются
для наследуемого процесса
| |
| .. |
Task Parent | |
| | | |
| fork |---------->| CREATE |
| | /| NEW |
|_________| / | TASK |
/ | |
--- / | |
--- / | .. |
/ | |
Task Child /
| | /
| fork |<-/
| |
|_________|
Порождаемый процес отличается от родителя следующим :
1 PID
2. child-процесс возвращает 0, в то время как родительский вернет PID
3. память child-процесса помечена как ''READ + EXECUTE'', не "WRITE''
Fork ICA
|sys_fork
|do_fork
|alloc_task_struct
|__get_free_pages
|p->state = TASK_UNINTERRUPTIBLE
|copy_flags
|p->pid = get_pid
|copy_files
|copy_fs
|copy_sighand
|copy_mm // should manage CopyOnWrite (I part)
|allocate_mm
|mm_init
|pgd_alloc -> get_pgd_fast
|get_pgd_slow
|dup_mmap
|copy_page_range
|ptep_set_wrprotect
|clear_bit // set page to read-only
|copy_segments // For LDT
|copy_thread
|childregs->eax = 0
|p->thread.esp = childregs // child fork returns 0
|p->thread.eip = ret_from_fork // child starts from fork exit
|retval = p->pid // parent fork returns child pid
|SET_LINKS // insertion of task into the list pointers
|nr_threads++ // Global variable
|wake_up_process(p) // Now we can wake up just created child
|return retval
fork ICA
· sys_fork [arch/i386/kernel/process.c]
· do_fork [kernel/fork.c]
· alloc_task_struct [include/asm/processor.c]
· __get_free_pages [mm/page_alloc.c]
· get_pid [kernel/fork.c]
· copy_files
· copy_fs
· copy_sighand
· copy_mm
· allocate_mm
· mm_init
· pgd_alloc -> get_pgd_fast [include/asm/pgalloc.h]
· get_pgd_slow
· dup_mmap [kernel/fork.c]
· copy_page_range [mm/memory.c]
· ptep_set_wrprotect [include/asm/pgtable.h]
· clear_bit [include/asm/bitops.h]
· copy_segments [arch/i386/kernel/process.c]
· copy_thread
· SET_LINKS [include/linux/sched.h]
· wake_up_process [kernel/sched.c]
Copy on Write
При попытке записать данные в память child-процесса возникает Page Fault,
при этом будет выделена новая страница памяти
| Page
| Fault
| Exception
-----------> |do_page_fault
|handle_mm_fault
|handle_pte_fault
|do_wp_page
|alloc_page // Allocate a new page
|break_cow
|copy_cow_page // Copy old page to new one
|establish_pte // reconfig Page Table pointers
|set_pte
Page Fault ICA
· do_page_fault [arch/i386/mm/fault.c]
· handle_mm_fault [mm/memory.c]
· handle_pte_fault
· do_wp_page
· alloc_page [include/linux/mm.h]
· break_cow [mm/memory.c]
· copy_cow_page
· establish_pte
· set_pte [include/asm/pgtable-3level.h]
Linux Memory Management
Linux при управлении памятью использует комбинацию из сегментации + пэйджинга .
Linux использует 4 сегмента :
· 2 сегмента (код и стек) для KERNEL SPACE
· 2 сегмента (код и стек) для USER SPACE
4 GB--->| | |
| Kernel | | Kernel Space (Code + Data/Stack)
| | __|
3 GB--->|----------------| __
| | |
| | |
2 GB--->| | |
| Tasks | | User Space (Code + Data/Stack)
| | |
1 GB--->| | |
| | |
|________________| __|
0x00000000
Пэйджинг в 386-м линуксе 2-уровневый :
------------------------------------------------------------
L I N E A R A D D R E S S
----------------------------------------------------------------
\___/ \___/ \_____/
PD offset PF offset Frame offset
[10 bits] [10 bits] [12 bits]
| | |
| | ----------- |
| | | Value |----------|---------
| | | | |---------| /|\ | |
| | | | | | | | |
| | | | | | | Frame offset |
| | | | | | \|/ |
| | | | |---------|<------ |
| | | | | | | |
| | | | | | | x 4096 |
| | | PF offset|_________|------- |
| | | /|\ | | |
PD offset |_________|----- | | | _________|
/|\ | | | | | | |
| | | | \|/ | | \|/
_____ | | | ------>|_________| PHYSICAL ADDRESS
| | \|/ | | x 4096 | |
| CR3 |-------->| | | |
|_____| | ....... | | ....... |
| | | |
Page Directory Page File
У различных процессов фактически сегментные адреса будут одинаковые ,
различаться будет лишь содержимое регистра CR3 .
В User mode одновременно может быть запущено не более 768 процессов(768*4MB = 3GB).
В Kernel Mode это справедливо для 256 процессов .
________________ _____
|Other KernelData|___ | | |
|----------------| | |__| |
| Kernel |\ |____| Real Other |
3 GB --->|----------------| \ | Kernel Data |
| |\ \ | |
| __|_\_\____|__ Real |
| Tasks | \ \ | Tasks |
| __|___\_\__|__ Space |
| | \ \ | |
| | \ \|----------------|
| | \ |Real KernelSpace|
|________________| \|________________|
Logical Addresses Physical Addresses
Работа с памятью начинается при загрузке в kmem_cache_init (launched by start_kernel [init/main.c]
|kmem_cache_init
|kmem_cache_estimate
kmem_cache_init [mm/slab.c]
kmem_cache_estimate
Далее - mem_init [init/main.c])
|mem_init
|free_all_bootmem
|free_all_bootmem_core
mem_init [arch/i386/mm/init.c]
free_all_bootmem [mm/bootmem.c]
free_all_bootmem_core
Пример выделения памяти в run-time :
|copy_mm
|allocate_mm = kmem_cache_alloc
|__kmem_cache_alloc
|kmem_cache_alloc_one
|alloc_new_slab
|kmem_cache_grow
|kmem_getpages
|__get_free_pages
|alloc_pages
|alloc_pages_pgdat
|__alloc_pages
|rmqueue
|reclaim_pages
Linux Networking
Для каждой разновидности NIC имеется свой драйвер . Внутри них вызывается
стандартный роутинг - "netif_rx [net/core/dev.c]".
Что происходит , когда приходит TCP-пакет :
|netif_rx
|__skb_queue_tail
|qlen++
|* simple pointer insertion *
|cpu_raise_softirq
|softirq_active(cpu) |= (1 << NET_RX_SOFTIRQ)
// set bit NET_RX_SOFTIRQ in the BH vector
NET_RX_SOFTIRQ генерирует вызов ''net_rx_action [net/core/dev.c]''
|net_rx_action
|skb = __skb_dequeue (the exact opposite of __skb_queue_tail)
|for (ptype = first_protocol; ptype < max_protocol; ptype++)
|if (skb->protocol == ptype)
|ptype->func -> ip_rcv
**** NOW WE KNOW THAT PACKET IS IP ****
|ip_rcv
|NF_HOOK (ip_rcv_finish)
|ip_route_input
|skb->dst->input -> ip_local_deliver
|ip_defrag // reassembles IP fragments
|NF_HOOK (ip_local_deliver_finish)
|ipprot->handler -> tcp_v4_rcv
**** NOW WE KNOW THAT PACKET IS TCP ****
|tcp_v4_rcv
|sk = __tcp_v4_lookup
|tcp_v4_do_rcv
|switch(sk->state)
Packet can be sent to the task which uses relative socket ***
|case TCP_ESTABLISHED:
|tcp_rcv_established
|__skb_queue_tail // enqueue packet to socket
|sk->data_ready -> sock_def_readable
|wake_up_interruptible
*** Packet has still to be handshaked by 3-way TCP handshake ***
|case TCP_LISTEN:
|tcp_v4_hnd_req
|tcp_v4_search_req
|tcp_check_req
|syn_recv_sock -> tcp_v4_syn_recv_sock
|__tcp_v4_lookup_established
|tcp_rcv_state_process
*** 3-Way TCP Handshake ***
|switch(sk->state)
|case TCP_LISTEN: // We received SYN
|conn_request -> tcp_v4_conn_request
|tcp_v4_send_synack // Send SYN + ACK
|tcp_v4_synq_add // set SYN state
|case TCP_SYN_SENT: // we received SYN + ACK
|tcp_rcv_synsent_state_process
tcp_set_state(TCP_ESTABLISHED)
|tcp_send_ack
|tcp_transmit_skb
|queue_xmit -> ip_queue_xmit
|ip_queue_xmit2
|skb->dst->output
|case TCP_SYN_RECV: // We received ACK
|if (ACK)
|tcp_set_state(TCP_ESTABLISHED)
Т.о. вначале мы определяем тип протокола (сначала IP , потом TCP)
Функция NF_HOOK (function) управляет сетевым фильтром.
После чего идет реализация классического TCP-рукопожатия - 3-way TCP Handshake :
SERVER (LISTENING) CLIENT (CONNECTING)
SYN
<-------------------
SYN + ACK
------------------->
ACK
<-------------------
3-Way TCP handshake
В случае успеха вызываем "tcp_rcv_established[net/ipv4/tcp_input.c]" .
Стек используется для
1. хранения глобальных переменных
2. хранения локальных переменных
3. возвращения адресов
Например , при вызове функции c n-параметрами :
|int foo_function (parameter_1, parameter_2, ..., parameter_n) {
|variable_1 declaration;
|variable_2 declaration;
..
|variable_n declaration;
|// Body function
|dynamic variable_1 declaration;
|dynamic variable_2 declaration;
..
|dynamic variable_n declaration;
|// Code in Code Segment
|return (ret-type) value
|}
мы имеем :
| |
| 1. parameter_1 pushed | \
S | 2. parameter_2 pushed | | Before
T | ................... | | the calling
A | n. parameter_n pushed | /
C | ** Return address ** | -- Calling
K | 1. local variable_1 | \
| 2. local variable_2 | | After
| ................. | | the calling
| n. local variable_n | /
| |
... ... Free
... ... stack
| |
H | n. dynamic variable_n | \
E | ................... | | Allocated by
A | 2. dynamic variable_2 | | malloc & kmalloc
P | 1. dynamic variable_1 | /
|_______________________|
Чем различаются понятия - приложение - и - процесс ?
Приложение - это исполняемый код .
Процесс - образ памяти , которую использует приложение .
Синонимом для - процесса - являются понятия - задача - или - тред .
Copy_on_write - механизм для уменьшения используемой памяти .
Например , когда какая-то задача порождает другой процесс , он выполняется внутри
ее страниц памяти с правами read-only , и если только возникает необходимость для
записи данных в память , тогда страницы памяти родителя будут скопированы с правами write .
Схема механизма прерываний :
|<--> IRQ(0) [Timer]
|<--> IRQ(1) [Device 1]
| ..
|<--> IRQ(n) [Device n]
_____________________________|
/|\ /|\ /|\
| | |
\|/ \|/ \|/
Task(1) Task(2) .. Task(N)
Под линуксом , любой ресурс часто бывает задействован между несколькими обьектами ,
и нужна очередь для распределения доступа к этому ресурсу .
Эта очередь называется "wait queue" ":
*** wait queue structure [include/linux/wait.h] ***
struct __wait_queue {
unsigned int flags;
struct task_struct * task;
struct list_head task_list;
}
struct list_head {
struct list_head *next, *prev;
};
Графическая схема :
*** wait queue element ***
/|\
|
<--[prev *, flags, task *, next *]-->
*** wait queue list ***
/|\ /|\ /|\ /|\
| | | |
<--[task1]--> <--[task2]--> <--[task3]--> .... <--[taskN]
| |
_____________________________________________________|
*** wait queue head ***
task1 <--[prev *, lock, next *]--> taskN
"wait queue head" указывает на первый и последний элементы в очереди "wait queue list".
Когда новый элемент добавляется , вызывается "__add_wait_queue" , затем "list_add":
*** function list_add [include/linux/list.h] ***
// classic double link list insert
static __inline__ void __list_add (struct list_head * new, \
struct list_head * prev, \
struct list_head * next) {
next->prev = new;
new->next = next;
new->prev = prev;
prev->next = new;
}
Линукс написан на чистом ''C'' , и существуют следующие типы переменных :
1. Локальные переменные
2. Переменные модуля
3. Глобальные статические переменные ,
видимые во всех модулях
|
Natalia | GOOD 2013-04-09 19:53:49 | |
|