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 };
View Code

  一个进程的虚拟地址使用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 };
View Code

  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()操作;

 

 

 

 

 

 

 

 

 

  

  

 

posted @ 2021-07-29 16:03  笑不出花的旦旦  阅读(146)  评论(0)    收藏  举报