02-进程虚拟内存空间映射
1、Linux内核在运行在物理内存3G-4G空间(对32位系统来说),内核中的每个进程拥有0-3G的虚拟内存空间,为了方便进程在虚拟内存与物理内存之间映射,将虚拟内存空间分为几部分不同的段;
这样的内存划分也就是程序的分治思想:

在上面框图中,运行hello world程序,在虚拟内存中,分为代码段、数据段、变量和静态局部变量内存映射、bss段、引用外部库的不同段、任何内存映射文件、任何共享文件段、进程栈、进程堆;
hello world代码:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> void main(void) { char* p = (char*)malloc(16); printf("this progress pid: %d\n", getpid()); while(1){ sleep(2); } }
编译后运行,查看进程在内存中各个代码段
cat /proc/pid/maps // pid 使用上面的输出的pid或者查看进程的pid
其中各个数据列代表意思为:

补充查看结果:

1、每个进程的虚拟内存地址可以是一样的,但是实际的映射物理地址是不一样;不会引起冲突;
2、每个进程都有一个32G的虚拟地址空间;系统分配不同大虚拟内存区域;
2、在内核中进程虚拟地址的描述:
task_struct结构体描述:
1 struct task_struct 2 { 3 /* 4 1. state: 进程执行时,它会根据具体情况改变状态。进程状态是进程调度和对换的依据。Linux中的进程主要有如下状态: 5 1) TASK_RUNNING: 可运行 6 处于这种状态的进程,只有两种状态: 7 1.1) 正在运行 8 正在运行的进程就是当前进程(由current所指向的进程) 9 1.2) 正准备运行 10 准备运行的进程只要得到CPU就可以立即投入运行,CPU是这些进程唯一等待的系统资源,系统中有一个运行队列(run_queue),用来容纳所有处于可运行状态的进程,调度程序执行时,从中选择一个进程投入运行 11 12 2) TASK_INTERRUPTIBLE: 可中断的等待状态,是针对等待某事件或其他资源的睡眠进程设置的,在内核发送信号给该进程表明事件已经发生时,进程状态变为TASK_RUNNING,它只要调度器选中该进程即可恢复执行 13 14 3) TASK_UNINTERRUPTIBLE: 不可中断的等待状态 15 处于该状态的进程正在等待某个事件(event)或某个资源,它肯定位于系统中的某个等待队列(wait_queue)中,处于不可中断等待态的进程是因为硬件环境不能满足而等待,例如等待特定的系统资源,它任何情况下都不能被打断,只能用特定的方式来唤醒它,例如唤醒函数wake_up()等 16 它们不能由外部信号唤醒,只能由内核亲自唤醒 17 18 4) TASK_ZOMBIE: 僵死 19 进程虽然已经终止,但由于某种原因,父进程还没有执行wait()系统调用,终止进程的信息也还没有回收。顾名思义,处于该状态的进程就是死进程,这种进程实际上是系统中的垃圾,必须进行相应处理以释放其占用的资源。 20 21 5) TASK_STOPPED: 暂停 22 此时的进程暂时停止运行来接受某种特殊处理。通常当进程接收到SIGSTOP、SIGTSTP、SIGTTIN或 SIGTTOU信号后就处于这种状态。例如,正接受调试的进程就处于这种状态 23 24 6) TASK_TRACED 25 从本质上来说,这属于TASK_STOPPED状态,用于从停止的进程中,将当前被调试的进程与常规的进程区分开来 26 27 7) TASK_DEAD 28 父进程wait系统调用发出后,当子进程退出时,父进程负责回收子进程的全部资源,子进程进入TASK_DEAD状态 29 30 8) TASK_SWAPPING: 换入/换出 31 */ 32 volatile long state; 33 34 /* 35 2. stack 36 进程内核栈,进程通过alloc_thread_info函数分配它的内核栈,通过free_thread_info函数释放所分配的内核栈 37 */ 38 void *stack; 39 40 /* 41 3. usage 42 进程描述符使用计数,被置为2时,表示进程描述符正在被使用而且其相应的进程处于活动状态 43 */ 44 atomic_t usage; 45 46 /* 47 4. flags 48 flags是进程当前的状态标志(注意和运行状态区分) 49 1) #define PF_ALIGNWARN 0x00000001: 显示内存地址未对齐警告 50 2) #define PF_PTRACED 0x00000010: 标识是否是否调用了ptrace 51 3) #define PF_TRACESYS 0x00000020: 跟踪系统调用 52 4) #define PF_FORKNOEXEC 0x00000040: 已经完成fork,但还没有调用exec 53 5) #define PF_SUPERPRIV 0x00000100: 使用超级用户(root)权限 54 6) #define PF_DUMPCORE 0x00000200: dumped core 55 7) #define PF_SIGNALED 0x00000400: 此进程由于其他进程发送相关信号而被杀死 56 8) #define PF_STARTING 0x00000002: 当前进程正在被创建 57 9) #define PF_EXITING 0x00000004: 当前进程正在关闭 58 10) #define PF_USEDFPU 0x00100000: Process used the FPU this quantum(SMP only) 59 #define PF_DTRACE 0x00200000: delayed trace (used on m68k) 60 */ 61 unsigned int flags; 62 63 /* 64 5. ptrace 65 ptrace系统调用,成员ptrace被设置为0时表示不需要被跟踪,它的可能取值如下: 66 linux-2.6.38.8/include/linux/ptrace.h 67 1) #define PT_PTRACED 0x00000001 68 2) #define PT_DTRACE 0x00000002: delayed trace (used on m68k, i386) 69 3) #define PT_TRACESYSGOOD 0x00000004 70 4) #define PT_PTRACE_CAP 0x00000008: ptracer can follow suid-exec 71 5) #define PT_TRACE_FORK 0x00000010 72 6) #define PT_TRACE_VFORK 0x00000020 73 7) #define PT_TRACE_CLONE 0x00000040 74 8) #define PT_TRACE_EXEC 0x00000080 75 9) #define PT_TRACE_VFORK_DONE 0x00000100 76 10) #define PT_TRACE_EXIT 0x00000200 77 */ 78 unsigned int ptrace; 79 unsigned long ptrace_message; 80 siginfo_t *last_siginfo; 81 82 /* 83 6. lock_depth 84 用于表示获取大内核锁的次数,如果进程未获得过锁,则置为-1 85 */ 86 int lock_depth; 87 88 /* 89 7. oncpu 90 在SMP上帮助实现无加锁的进程切换(unlocked context switches) 91 */ 92 #ifdef CONFIG_SMP 93 #ifdef __ARCH_WANT_UNLOCKED_CTXSW 94 int oncpu; 95 #endif 96 #endif 97 98 /* 99 8. 进程调度 100 1) prio: 调度器考虑的优先级保存在prio,由于在某些情况下内核需要暂时提高进程的优先级,因此需要第三个成员来表示(除了static_prio、normal_prio之外),由于这些改变不是持久的,因此静态(static_prio)和普通(normal_prio)优先级不受影响 101 2) static_prio: 用于保存进程的"静态优先级",静态优先级是进程"启动"时分配的优先级,它可以用nice、sched_setscheduler系统调用修改,否则在进程运行期间会一直保持恒定 102 3) normal_prio: 表示基于进程的"静态优先级"和"调度策略"计算出的优先级,因此,即使普通进程和实时进程具有相同的静态优先级(static_prio),其普通优先级(normal_prio)也是不同的。进程分支时(fork),新创建的子进程会集成普通优先级 103 */ 104 int prio, static_prio, normal_prio; 105 /* 106 4) rt_priority: 表示实时进程的优先级,需要明白的是,"实时进程优先级"和"普通进程优先级"有两个独立的范畴,实时进程即使是最低优先级也高于普通进程,最低的实时优先级为0,最高的优先级为99,值越大,表明优先级越高 107 */ 108 unsigned int rt_priority; 109 /* 110 5) sched_class: 该进程所属的调度类,目前内核中有实现以下四种: 111 5.1) static const struct sched_class fair_sched_class; 112 5.2) static const struct sched_class rt_sched_class; 113 5.3) static const struct sched_class idle_sched_class; 114 5.4) static const struct sched_class stop_sched_class; 115 */ 116 const struct sched_class *sched_class; 117 /* 118 6) se: 用于普通进程的调用实体 119 调度器不限于调度进程,还可以处理更大的实体,这可以实现"组调度",可用的CPU时间可以首先在一般的进程组(例如所有进程可以按所有者分组)之间分配,接下来分配的时间在组内再次分配 120 这种一般性要求调度器不直接操作进程,而是处理"可调度实体",一个实体有sched_entity的一个实例标识 121 在最简单的情况下,调度在各个进程上执行,由于调度器设计为处理可调度的实体,在调度器看来各个进程也必须也像这样的实体,因此se在task_struct中内嵌了一个sched_entity实例,调度器可据此操作各个task_struct 122 */ 123 struct sched_entity se; 124 /* 125 7) rt: 用于实时进程的调用实体 126 */ 127 struct sched_rt_entity rt; 128 129 #ifdef CONFIG_PREEMPT_NOTIFIERS 130 /* 131 9. preempt_notifier 132 preempt_notifiers结构体链表 133 */ 134 struct hlist_head preempt_notifiers; 135 #endif 136 137 /* 138 10. fpu_counter 139 FPU使用计数 140 */ 141 unsigned char fpu_counter; 142 143 #ifdef CONFIG_BLK_DEV_IO_TRACE 144 /* 145 11. btrace_seq 146 blktrace是一个针对Linux内核中块设备I/O层的跟踪工具 147 */ 148 unsigned int btrace_seq; 149 #endif 150 151 /* 152 12. policy 153 policy表示进程的调度策略,目前主要有以下五种: 154 1) #define SCHED_NORMAL 0: 用于普通进程,它们通过完全公平调度器来处理 155 2) #define SCHED_FIFO 1: 先来先服务调度,由实时调度类处理 156 3) #define SCHED_RR 2: 时间片轮转调度,由实时调度类处理 157 4) #define SCHED_BATCH 3: 用于非交互、CPU使用密集的批处理进程,通过完全公平调度器来处理,调度决策对此类进程给与"冷处理",它们绝不会抢占CFS调度器处理的另一个进程,因此不会干扰交互式进程,如果不打算用nice降低进程的静态优先级,同时又不希望该进程影响系统的交互性,最适合用该调度策略 158 5) #define SCHED_IDLE 5: 可用于次要的进程,其相对权重总是最小的,也通过完全公平调度器来处理。要注意的是,SCHED_IDLE不负责调度空闲进程,空闲进程由内核提供单独的机制来处理 159 只有root用户能通过sched_setscheduler()系统调用来改变调度策略 160 */ 161 unsigned int policy; 162 163 /* 164 13. cpus_allowed 165 cpus_allowed是一个位域,在多处理器系统上使用,用于控制进程可以在哪里处理器上运行 166 */ 167 cpumask_t cpus_allowed; 168 169 /* 170 14. RCU同步原语 171 */ 172 #ifdef CONFIG_TREE_PREEMPT_RCU 173 int rcu_read_lock_nesting; 174 char rcu_read_unlock_special; 175 struct rcu_node *rcu_blocked_node; 176 struct list_head rcu_node_entry; 177 #endif /* #ifdef CONFIG_TREE_PREEMPT_RCU */ 178 179 #if defined(CONFIG_SCHEDSTATS) || defined(CONFIG_TASK_DELAY_ACCT) 180 /* 181 15. sched_info 182 用于调度器统计进程的运行信息 183 */ 184 struct sched_info sched_info; 185 #endif 186 187 /* 188 16. tasks 189 通过list_head将当前进程的task_struct串联进内核的进程列表中,构建;linux进程链表 190 */ 191 struct list_head tasks; 192 193 /* 194 17. pushable_tasks 195 limit pushing to one attempt 196 */ 197 struct plist_node pushable_tasks; 198 199 /* 200 18. 进程地址空间 201 1) mm: 指向进程所拥有的内存描述符 202 2) active_mm: active_mm指向进程运行时所使用的内存描述符 203 对于普通进程而言,这两个指针变量的值相同。但是,内核线程不拥有任何内存描述符,所以它们的mm成员总是为NULL。当内核线程得以运行时,它的active_mm成员被初始化为前一个运行进程的active_mm值 204 */ 205 struct mm_struct *mm, *active_mm; 206 207 /* 208 19. exit_state 209 进程退出状态码 210 */ 211 int exit_state; 212 213 /* 214 20. 判断标志 215 1) exit_code 216 exit_code用于设置进程的终止代号,这个值要么是_exit()或exit_group()系统调用参数(正常终止),要么是由内核提供的一个错误代号(异常终止) 217 2) exit_signal 218 exit_signal被置为-1时表示是某个线程组中的一员。只有当线程组的最后一个成员终止时,才会产生一个信号,以通知线程组的领头进程的父进程 219 */ 220 int exit_code, exit_signal; 221 /* 222 3) pdeath_signal 223 pdeath_signal用于判断父进程终止时发送信号 224 */ 225 int pdeath_signal; 226 /* 227 4) personality用于处理不同的ABI,它的可能取值如下: 228 enum 229 { 230 PER_LINUX = 0x0000, 231 PER_LINUX_32BIT = 0x0000 | ADDR_LIMIT_32BIT, 232 PER_LINUX_FDPIC = 0x0000 | FDPIC_FUNCPTRS, 233 PER_SVR4 = 0x0001 | STICKY_TIMEOUTS | MMAP_PAGE_ZERO, 234 PER_SVR3 = 0x0002 | STICKY_TIMEOUTS | SHORT_INODE, 235 PER_SCOSVR3 = 0x0003 | STICKY_TIMEOUTS | 236 WHOLE_SECONDS | SHORT_INODE, 237 PER_OSR5 = 0x0003 | STICKY_TIMEOUTS | WHOLE_SECONDS, 238 PER_WYSEV386 = 0x0004 | STICKY_TIMEOUTS | SHORT_INODE, 239 PER_ISCR4 = 0x0005 | STICKY_TIMEOUTS, 240 PER_BSD = 0x0006, 241 PER_SUNOS = 0x0006 | STICKY_TIMEOUTS, 242 PER_XENIX = 0x0007 | STICKY_TIMEOUTS | SHORT_INODE, 243 PER_LINUX32 = 0x0008, 244 PER_LINUX32_3GB = 0x0008 | ADDR_LIMIT_3GB, 245 PER_IRIX32 = 0x0009 | STICKY_TIMEOUTS, 246 PER_IRIXN32 = 0x000a | STICKY_TIMEOUTS, 247 PER_IRIX64 = 0x000b | STICKY_TIMEOUTS, 248 PER_RISCOS = 0x000c, 249 PER_SOLARIS = 0x000d | STICKY_TIMEOUTS, 250 PER_UW7 = 0x000e | STICKY_TIMEOUTS | MMAP_PAGE_ZERO, 251 PER_OSF4 = 0x000f, 252 PER_HPUX = 0x0010, 253 PER_MASK = 0x00ff, 254 }; 255 */ 256 unsigned int personality; 257 /* 258 5) did_exec 259 did_exec用于记录进程代码是否被execve()函数所执行 260 */ 261 unsigned did_exec:1; 262 /* 263 6) in_execve 264 in_execve用于通知LSM是否被do_execve()函数所调用 265 */ 266 unsigned in_execve:1; 267 /* 268 7) in_iowait 269 in_iowait用于判断是否进行iowait计数 270 */ 271 unsigned in_iowait:1; 272 273 /* 274 8) sched_reset_on_fork 275 sched_reset_on_fork用于判断是否恢复默认的优先级或调度策略 276 */ 277 unsigned sched_reset_on_fork:1; 278 279 /* 280 21. 进程标识符(PID) 281 在CONFIG_BASE_SMALL配置为0的情况下,PID的取值范围是0到32767,即系统中的进程数最大为32768个 282 #define PID_MAX_DEFAULT (CONFIG_BASE_SMALL ? 0x1000 : 0x8000) 283 在Linux系统中,一个线程组中的所有线程使用和该线程组的领头线程(该组中的第一个轻量级进程)相同的PID,并被存放在tgid成员中。只有线程组的领头线程的pid成员才会被设置为与tgid相同的值。注意,getpid()系统调用 284 返回的是当前进程的tgid值而不是pid值。 285 */ 286 pid_t pid; 287 pid_t tgid; 288 289 #ifdef CONFIG_CC_STACKPROTECTOR 290 /* 291 22. stack_canary 292 防止内核堆栈溢出,在GCC编译内核时,需要加上-fstack-protector选项 293 */ 294 unsigned long stack_canary; 295 #endif 296 297 /* 298 23. 表示进程亲属关系的成员 299 1) real_parent: 指向其父进程,如果创建它的父进程不再存在,则指向PID为1的init进程 300 2) parent: 指向其父进程,当它终止时,必须向它的父进程发送信号。它的值通常与real_parent相同 301 */ 302 struct task_struct *real_parent; 303 struct task_struct *parent; 304 /* 305 3) children: 表示链表的头部,链表中的所有元素都是它的子进程(子进程链表) 306 4) sibling: 用于把当前进程插入到兄弟链表中(连接到父进程的子进程链表(兄弟链表)) 307 5) group_leader: 指向其所在进程组的领头进程 308 */ 309 struct list_head children; 310 struct list_head sibling; 311 struct task_struct *group_leader; 312 313 struct list_head ptraced; 314 struct list_head ptrace_entry; 315 struct bts_context *bts; 316 317 /* 318 24. pids 319 PID散列表和链表 320 */ 321 struct pid_link pids[PIDTYPE_MAX]; 322 /* 323 25. thread_group 324 线程组中所有进程的链表 325 */ 326 struct list_head thread_group; 327 328 /* 329 26. do_fork函数 330 1) vfork_done 331 在执行do_fork()时,如果给定特别标志,则vfork_done会指向一个特殊地址 332 2) set_child_tid、clear_child_tid 333 如果copy_process函数的clone_flags参数的值被置为CLONE_CHILD_SETTID或CLONE_CHILD_CLEARTID,则会把child_tidptr参数的值分别复制到set_child_tid和clear_child_tid成员。这些标志说明必须改变子 334 进程用户态地址空间的child_tidptr所指向的变量的值。 335 */ 336 struct completion *vfork_done; 337 int __user *set_child_tid; 338 int __user *clear_child_tid; 339 340 /* 341 27. 记录进程的I/O计数(时间) 342 1) utime 343 用于记录进程在"用户态"下所经过的节拍数(定时器) 344 2) stime 345 用于记录进程在"内核态"下所经过的节拍数(定时器) 346 3) utimescaled 347 用于记录进程在"用户态"的运行时间,但它们以处理器的频率为刻度 348 4) stimescaled 349 用于记录进程在"内核态"的运行时间,但它们以处理器的频率为刻度 350 */ 351 cputime_t utime, stime, utimescaled, stimescaled; 352 /* 353 5) gtime 354 以节拍计数的虚拟机运行时间(guest time) 355 */ 356 cputime_t gtime; 357 /* 358 6) prev_utime、prev_stime是先前的运行时间 359 */ 360 cputime_t prev_utime, prev_stime; 361 /* 362 7) nvcsw 363 自愿(voluntary)上下文切换计数 364 8) nivcsw 365 非自愿(involuntary)上下文切换计数 366 */ 367 unsigned long nvcsw, nivcsw; 368 /* 369 9) start_time 370 进程创建时间 371 10) real_start_time 372 进程睡眠时间,还包含了进程睡眠时间,常用于/proc/pid/stat, 373 */ 374 struct timespec start_time; 375 struct timespec real_start_time; 376 /* 377 11) cputime_expires 378 用来统计进程或进程组被跟踪的处理器时间,其中的三个成员对应着cpu_timers[3]的三个链表 379 */ 380 struct task_cputime cputime_expires; 381 struct list_head cpu_timers[3]; 382 #ifdef CONFIG_DETECT_HUNG_TASK 383 /* 384 12) last_switch_count 385 nvcsw和nivcsw的总和 386 */ 387 unsigned long last_switch_count; 388 #endif 389 struct task_io_accounting ioac; 390 #if defined(CONFIG_TASK_XACCT) 391 u64 acct_rss_mem1; 392 u64 acct_vm_mem1; 393 cputime_t acct_timexpd; 394 #endif 395 396 /* 397 28. 缺页统计 398 */ 399 unsigned long min_flt, maj_flt; 400 401 /* 402 29. 进程权能 403 */ 404 const struct cred *real_cred; 405 const struct cred *cred; 406 struct mutex cred_guard_mutex; 407 struct cred *replacement_session_keyring; 408 409 /* 410 30. comm[TASK_COMM_LEN] 411 相应的程序名 412 */ 413 char comm[TASK_COMM_LEN]; 414 415 /* 416 31. 文件 417 1) fs 418 用来表示进程与文件系统的联系,包括当前目录和根目录 419 2) files 420 表示进程当前打开的文件 421 */ 422 int link_count, total_link_count; 423 struct fs_struct *fs; 424 struct files_struct *files; 425 426 #ifdef CONFIG_SYSVIPC 427 /* 428 32. sysvsem 429 进程通信(SYSVIPC) 430 */ 431 struct sysv_sem sysvsem; 432 #endif 433 434 /* 435 33. 处理器特有数据 436 */ 437 struct thread_struct thread; 438 439 /* 440 34. nsproxy 441 命名空间 442 */ 443 struct nsproxy *nsproxy; 444 445 /* 446 35. 信号处理 447 1) signal: 指向进程的信号描述符 448 2) sighand: 指向进程的信号处理程序描述符 449 */ 450 struct signal_struct *signal; 451 struct sighand_struct *sighand; 452 /* 453 3) blocked: 表示被阻塞信号的掩码 454 4) real_blocked: 表示临时掩码 455 */ 456 sigset_t blocked, real_blocked; 457 sigset_t saved_sigmask; 458 /* 459 5) pending: 存放私有挂起信号的数据结构 460 */ 461 struct sigpending pending; 462 /* 463 6) sas_ss_sp: 信号处理程序备用堆栈的地址 464 7) sas_ss_size: 表示堆栈的大小 465 */ 466 unsigned long sas_ss_sp; 467 size_t sas_ss_size; 468 /* 469 8) notifier 470 设备驱动程序常用notifier指向的函数来阻塞进程的某些信号 471 9) otifier_data 472 指的是notifier所指向的函数可能使用的数据。 473 10) otifier_mask 474 标识这些信号的位掩码 475 */ 476 int (*notifier)(void *priv); 477 void *notifier_data; 478 sigset_t *notifier_mask; 479 480 /* 481 36. 进程审计 482 */ 483 struct audit_context *audit_context; 484 #ifdef CONFIG_AUDITSYSCALL 485 uid_t loginuid; 486 unsigned int sessionid; 487 #endif 488 489 /* 490 37. secure computing 491 */ 492 seccomp_t seccomp; 493 494 /* 495 38. 用于copy_process函数使用CLONE_PARENT标记时 496 */ 497 u32 parent_exec_id; 498 u32 self_exec_id; 499 500 /* 501 39. alloc_lock 502 用于保护资源分配或释放的自旋锁 503 */ 504 spinlock_t alloc_lock; 505 506 /* 507 40. 中断 508 */ 509 #ifdef CONFIG_GENERIC_HARDIRQS 510 struct irqaction *irqaction; 511 #endif 512 #ifdef CONFIG_TRACE_IRQFLAGS 513 unsigned int irq_events; 514 int hardirqs_enabled; 515 unsigned long hardirq_enable_ip; 516 unsigned int hardirq_enable_event; 517 unsigned long hardirq_disable_ip; 518 unsigned int hardirq_disable_event; 519 int softirqs_enabled; 520 unsigned long softirq_disable_ip; 521 unsigned int softirq_disable_event; 522 unsigned long softirq_enable_ip; 523 unsigned int softirq_enable_event; 524 int hardirq_context; 525 int softirq_context; 526 #endif 527 528 /* 529 41. pi_lock 530 task_rq_lock函数所使用的锁 531 */ 532 spinlock_t pi_lock; 533 534 #ifdef CONFIG_RT_MUTEXES 535 /* 536 42. 基于PI协议的等待互斥锁,其中PI指的是priority inheritance/9优先级继承) 537 */ 538 struct plist_head pi_waiters; 539 struct rt_mutex_waiter *pi_blocked_on; 540 #endif 541 542 #ifdef CONFIG_DEBUG_MUTEXES 543 /* 544 43. blocked_on 545 死锁检测 546 */ 547 struct mutex_waiter *blocked_on; 548 #endif 549 550 /* 551 44. lockdep, 552 */ 553 #ifdef CONFIG_LOCKDEP 554 # define MAX_LOCK_DEPTH 48UL 555 u64 curr_chain_key; 556 int lockdep_depth; 557 unsigned int lockdep_recursion; 558 struct held_lock held_locks[MAX_LOCK_DEPTH]; 559 gfp_t lockdep_reclaim_gfp; 560 #endif 561 562 /* 563 45. journal_info 564 JFS文件系统 565 */ 566 void *journal_info; 567 568 /* 569 46. 块设备链表 570 */ 571 struct bio *bio_list, **bio_tail; 572 573 /* 574 47. reclaim_state 575 内存回收 576 */ 577 struct reclaim_state *reclaim_state; 578 579 /* 580 48. backing_dev_info 581 存放块设备I/O数据流量信息 582 */ 583 struct backing_dev_info *backing_dev_info; 584 585 /* 586 49. io_context 587 I/O调度器所使用的信息 588 */ 589 struct io_context *io_context; 590 591 /* 592 50. CPUSET功能 593 */ 594 #ifdef CONFIG_CPUSETS 595 nodemask_t mems_allowed; 596 int cpuset_mem_spread_rotor; 597 #endif 598 599 /* 600 51. Control Groups 601 */ 602 #ifdef CONFIG_CGROUPS 603 struct css_set *cgroups; 604 struct list_head cg_list; 605 #endif 606 607 /* 608 52. robust_list 609 futex同步机制 610 */ 611 #ifdef CONFIG_FUTEX 612 struct robust_list_head __user *robust_list; 613 #ifdef CONFIG_COMPAT 614 struct compat_robust_list_head __user *compat_robust_list; 615 #endif 616 struct list_head pi_state_list; 617 struct futex_pi_state *pi_state_cache; 618 #endif 619 #ifdef CONFIG_PERF_EVENTS 620 struct perf_event_context *perf_event_ctxp; 621 struct mutex perf_event_mutex; 622 struct list_head perf_event_list; 623 #endif 624 625 /* 626 53. 非一致内存访问(NUMA Non-Uniform Memory Access) 627 */ 628 #ifdef CONFIG_NUMA 629 struct mempolicy *mempolicy; /* Protected by alloc_lock */ 630 short il_next; 631 #endif 632 633 /* 634 54. fs_excl 635 文件系统互斥资源 636 */ 637 atomic_t fs_excl; 638 639 /* 640 55. rcu 641 RCU链表 642 */ 643 struct rcu_head rcu; 644 645 /* 646 56. splice_pipe 647 管道 648 */ 649 struct pipe_inode_info *splice_pipe; 650 651 /* 652 57. delays 653 延迟计数 654 */ 655 #ifdef CONFIG_TASK_DELAY_ACCT 656 struct task_delay_info *delays; 657 #endif 658 659 /* 660 58. make_it_fail 661 fault injection 662 */ 663 #ifdef CONFIG_FAULT_INJECTION 664 int make_it_fail; 665 #endif 666 667 /* 668 59. dirties 669 FLoating proportions 670 */ 671 struct prop_local_single dirties; 672 673 /* 674 60. Infrastructure for displayinglatency 675 */ 676 #ifdef CONFIG_LATENCYTOP 677 int latency_record_count; 678 struct latency_record latency_record[LT_SAVECOUNT]; 679 #endif 680 681 /* 682 61. time slack values,常用于poll和select函数 683 */ 684 unsigned long timer_slack_ns; 685 unsigned long default_timer_slack_ns; 686 687 /* 688 62. scm_work_list 689 socket控制消息(control message) 690 */ 691 struct list_head *scm_work_list; 692 693 /* 694 63. ftrace跟踪器 695 */ 696 #ifdef CONFIG_FUNCTION_GRAPH_TRACER 697 int curr_ret_stack; 698 struct ftrace_ret_stack *ret_stack; 699 unsigned long long ftrace_timestamp; 700 atomic_t trace_overrun; 701 atomic_t tracing_graph_pause; 702 #endif 703 #ifdef CONFIG_TRACING 704 unsigned long trace; 705 unsigned long trace_recursion; 706 #endif 707 };
一个进程的虚拟地址使用mm_struct结构体来描述,在进程描述符上的task_struct结构体中包含mm_struct结构体,描述进程的虚拟地址空间;
mm_struct结构体如下面描述:代表当前进程的虚拟地址空间
1 struct mm_struct 2 { 3 struct vm_area_struct *mmap; //list of VMA 4 rb_root_t mm_rb; //指向vma段红黑树的指针 5 struct vm_area_struct *mmap_cache; //last find_vma result 存储上一次查询的操作的结果 6 pgd_t *pgd; //进程页目录的起始地址 7 atomic_t mm_users; //how many users with user space 8 atomic_t mm_count; //how many reference to "struct mm_struct" 9 int map_count; //Number of VMA 10 struct rw_semaphore mmap_sem; //对mmap操作的互赤信号量 11 spinlock_t page_table_lock; //Protects task page tables and mm->rss 12 struct list_head mmlist; //list of all active mm's. These are globally together off init_mm.mmlist,and are protected by mmlist_lock 13 unsigned long start_code,end_code,start_data,end_data; 14 unsigned long start_brk,brk,start_stack; 15 unsigned long arg_start,arg_end,env_start,env_end; 16 unsigned long rss,total_vm,locked_vm; //rss进程内容驻留在物理内存的页面地址 17 unsigned long def_flags; 18 unsigned long cpu_vm_mask; 19 unsigned long swap_address; //页面换出过程用到交换空间地址 20 21 unsigned dumpable:1; 22 //Architecture-specific MM context 23 mm_context_t context; //存放着当前进程使用的段起始地址 24 };
1、红黑树对VMA进行管理,方便搜索;
2、页目录表;虚拟地址,物理地址、线程访问同一个内存空间
3、虚拟内存区域:vm_area_struct
struct vm area_struct:用来描述一个虚拟内存区域(VMA)。
内核将每个内存区域作为一个单独的内存对象管理,每个内存区域都有一致的属性,比如权限等。所以我们程序的代码段、数据段和bss段在内核里都分别有一个struct vm_area_struct结构体来描述
struct vm_area_struct { struct mm_struct *vm_mm; //The address space we belong to unsigned long vm_start; // 进程在虚拟空间的起始地址 unsigned long vm_end; // 虚拟空间的结束地址 struct vm_area_struct *vm_next; pgprot_t vm_page_prot; //Access permission of this VMA unsigned long vm_flags; // 虚拟内存操作的标志位 rb_node_t vm_rb; //rb Tree struct vm_area_struct *vm_next_share; struct vm_area_struct *vm_pprev_share; struct vm_operations_struct *vm_ops; //Function pointer to deal with this struct unsigned long vm_pgoff; struct file *vm_file; // 所映射在那个文件,排除匿名映射 unsigned long vm_raend; void *vm_private_data; };
https://www.cnblogs.com/hzhida/archive/2012/03/14/2395650.html
总体映射机制:

4、用户空间的虚拟地址映射
映射文件或者设备映射到进程地址空间;
void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset); int munmap( void * addr, size_t len )
在用户空间,调用mmap(),内核中会在mm_struct结构体中添加一个vm_area_struct结构体;
5、线程之间共享内存地址的实现机制:
在linux中,如果clone()进程时,设置CLONE_VM标志,我们把这样的进程称作为线程。线程之间共享同样的虚拟内存空间。
fork()函数利用copy_mm()函数复制父进程的mm_struct,也就是current->mm域给其子进程。
kernel/fork.c
static int copy_mm(unsigned long clone flags, struct task struct *tsk)
6、mmap()内核源码:
mmap()可以将一个文件或者设备映射到当前进程的地址空间,映射进来后,可以直接操作地址空间,进行文件或者设备操作;相比较于write()或者read()来说,效率会高很多;这样就实现了对物理内存的直接访问。传统的read()和write()需要对物理空间虚拟空间做转换以及数据拷贝;
用户空间的mmap()通过系统调用,调用do_mmap();
do_mmap()中,1、首先内核创建一个新的VMA并初始化,然后加入进程的虚拟地空间;2、然后调用底层的mmap函数(对应不同驱动的mmap函数)建立VMA和实际物理地址的联系;
建立VMA和物理地址的映射的工作由remap_pfn_range来完成,函数原型
int remap_pfn_range(struct vm_area_struct *vma, unsigned long virt_addr, unsigned long pfn, unsigned long size, pgprot_t prot); // 负责为一段新的物理地址建立页表映射
vma:需要建立映射的VMA;
virt_addr:需要建立VMA的起始地址;
pfn : 页帧号,对应虚拟地址应当被映射的物理地址,物理地址有移PAGE_SHIFT位(12位);
size:需要建立VMA的大小;
prot: 使用在vma->vma_page_prot中找到的值;
即:基于物理内存接口实现了向软件访问的接口;取代了传统的read()和write()操作;

浙公网安备 33010602011771号