Linux0.11-进程控制块数据结构
- 格式:doc
- 大小:26.00 KB
- 文档页数:5
[操作系统]进程控制块
进程控制块: 是操作系统管理控制进程运⾏所哦那个的信息集合,操作系统⽤PCB来描述进程的基本情况以及运⾏变化的过程,PCB是进程存在的唯⼀标志
进程的创建:为进程创建PCB
进程的终⽌:回收他的PCB
进程的组织管理:通过对PCB的阻值管理实现
包含三⼤类信息
(⼀) 进程标识信息:如本进程的表⽰,本进程的产⽣者标识(⽗进程标识) ⽤户标识
(⼆) 处理器的状态信息保存区保存进程的运⾏现场信息
⽤户可见寄存器:⽤户程序可以使⽤的数据,地址等寄存器
控制和状态寄存器:⽐如程序计数器PC 程序状态字 PSW
栈指针:过程调⽤/系统调⽤/终端处理和返回时需要⽤到
(三)进程控制信息
调度和状态信息:⽤于操作系统调度进程并占⽤处理机使⽤
进程间通信信息:为⽀持进程通信与通信相关的各种标志信号信件等,这些信息存在接收⽅的进程控制块中
存储管理信息:包含有指向本进程映像存储空间的数据结构
进程所⽤资源:说明由进程打开、使⽤的系统资源,如打开的⽂件等。
有关数据结构链接信息:进程可以连接到⼀个进程队列中,或连接到相关的其他的其他的PCB
PCB的组织⽅式
链表:同⼀状态的进程其PCB成⼀张链表多个状态对应多个不同的链表
(各状态有不同的链表⽐如就绪链表阻塞链表)
索引表:同⼀状态归⼊⼀个index表 (由index指向PCB),多个状态对应多个不同的Index表
(各状态的进⾏形成不同的索引表:就绪索引表、阻塞索引表)。
进程控制模块分析一、模块名称:进程控制模块,在linux体系结构中属于用户接口层。
二、模块功能描述:进程控制模块,主要负责:1 进程的运行控制包括创建,注销,停止。
2 进程的信息管理,包括命名空间管理,PID管理。
3 进程的资源管理。
三、模块结构图:四、模块接口说明:1接口名: 获得进程的数字ID接口的功能描述:得到进程在给定命名空间,指定类型的数字ID号。
接口包含的函数:1)pid_t task_pid_nr_ns(structtask_struct *tskstructpid_namespace *ns)●入口参数:a)structtask_struct*tsk:需要查询的进程的描述符。
b)structpid_namespace *ns:需要查询的进程所在的命名空间。
●返回值:a)pid_t:需要查询的进程的数字pid号。
●重要的结构体说明:a)task_struct:存储的了进程的各种控制信息,详细定义参见重要数据结构。
详细介绍见模块主要结构体说明。
b)pid_t:进程的数字ID号定义为int类型。
●函数说明根据进程描述符,获得在制定命名空间的进程数字pid。
2)pid_t task_tgpid_nr_ns(structtask_struct*tskstructpid_namespace *ns)●入口参数:a)structtask_struct*tskb)structpid_namespace*ns●返回值:pid_t: 进程的数字ID号定义为int类型。
●重要的结构体说明:a)task_struct:存储的了进程的各种控制信息,详细定义参见重要数据结构。
详细介绍见模块主要结构体说明。
b)pid_t:进程的数字ID号定义为int类型。
●函数说明根据进程描述符,获得在制定命名空间的进程组的数字pid。
3)pid_t task_pgrp_nr_ns(structtask_struct *tskstructpid_namespace*ns●入口参数:a)structtask_struct *tskb)structpid_namespace *ns●返回值:pid_t●重要的结构体说明:a)task_struct:存储的了进程的各种控制信息,详细定义参见重要数据结构。
0.11核中进程的调度主要由四个部分数组成:调度初始化、调度、睡眠、唤醒。
一、调度初始化:sched_init()我们知道在内核初始化(main)过程中,要将任务0转换到用户态下执行,也就是建立Linux中的第一个用户程序。
既然任务0之前是在内核态下执行的,所以用的是系统的ldt、和tss,要在用户态下执行,就要使用用户态下的ldt、tss,那么,调度初始化的其中一个任务就是建立任务0的ldt和gdt。
下面说一下sched_init()的具体工作过程:1)在gdt中设置ldt、gdt:ldt和gdt的定义在sched.h中。
2)将eflags中的NT置位:记得在前面关于任务切换的说明中,我提到在main的中sched_init()之后,要调用move_to_user_mode()实现核心态到用户态的转换,而在move_to_user_mode()中,最后调用了iret,如果将NT为0,就不会引起进程的切换了。
3)加载tss和ldt:手动加载仅这一次,以后都是通过任务的切换CPU自动加载的。
在这里,因为是第一个用户任务,所以需要手动设置。
4)初始化8253定时器。
5)在IDT中设置时钟的中断门和系统调用的中断门。
二、调度:schedule()这个函数的主要工作就是从所有处在就绪状态的进程中选择下一个要运行的进程。
具体工作过程是这样的:它首先检查在处在可中断睡眠状态下的进程是否除了阻塞信号以外别的信号,如果存在,则将它置成就绪状态。
然后循环检查任务数组中的所有处在就绪状态的任务,挑选出剩余执行时间值最大的一个任务,利用switch_to()函数切换到该任务。
如果发现所有就绪进程的时间片都是0,则根据任务的优先级,重新计算每个任务的时间片,在重新执行循环检查所有就绪任务的执行剩余时间。
其中,这个函数的核心之一就是switch_to()函数,它能实现任务的切换,它的定义在sched.h 中,主要原理就是利用如果ljmp的操作数是任务门描述符就会引起任务的切换。
linux进程和进程结构内存分布linux中程序结构和进程结构file 可执⾏⽂件[root@centos1 c]# file getopt_longgetopt_long: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.18, not stripped size 可执⾏程序查看程序组成部分[root@centos1 c]# size getopt_longtext data bss dec hex filename2037508562601 a29 getopt_long代码段:text 主要存放指令,操作以及只读的(常量)数据(⽐如字符串常量)通常是指⽤来存放程序执⾏代码的⼀块内存区域。
这部分区域的⼤⼩在程序运⾏前就已经确定,并且内存区域通常属于只读, 某些架构也允许代码段为可写,即允许修改程序。
在代码段中,也有可能包含⼀些只读的常数变量,例如字符串常量等数据段:data 全局或者静态的已经初始化的变量数据段属于静态内存分配bss段:(bss segment)通常是指⽤来存放程序中未初始化的全局变量的⼀块内存区域。
BSS 是英⽂Block Started by Symbol 的简称。
BSS 段属于静态内存分配。
全局或者静态的未初始化的变量#include <stdio.h>#include <stdlib.h>int main(int argc,char *argv[]){printf("hello c\n");return 0;}[root@centos1 c]# size memtext data bss dec hex filename1151492161659 67b memint i=10;int main(int argc,char *argv[]){printf("hello c\n");return 0;}[root@centos1 c]# size memtext data bss dec hex filename1151496161663 67f memdata端增加了4#include <stdio.h>#include <stdlib.h>int i=10;static int j;int main(int argc,char *argv[]){printf("hello c\n");return 0;}[root@centos1 c]# size memtext data bss dec hex filename1151496241671687 membss段增加到24进程:linux操作系统最⼩的资源管理单元⼀个进程时执⾏的程序段程序在执⾏时,会动态的申请空间,执⾏⼦函数Linux对⼀个进程管理采⽤以下⽅式内核空间:PCB(进程控制块)结构体 task struct,负责管理进程的所有资源成员 mm_struct 指向这个进程相关的内存资源mm_struct指向⼀个结构体栈堆BSS段数据段代码段执⾏程序时,系统⾸先在内核空间中创建⼀个进程,为这个进程申请PCB(进程控制块 task_struct)⽤于管理整个进程的所有资源,其中mm_struct成员⽤来管理与当先进程相关的所有内存资源1.代码段,数据段,bss段,直接从磁盘拷贝到当前的内存空间,⼤⼩相等2.动态空间堆,栈空间,mmap段(映射其它库相关的信息)#include <stdio.h>#include <stdlib.h>int i=10;static int j;int main(int argc,char *argv[]){printf("hello c\n");while(1);return0;}[root@centos1 ~]# ps -ef|grep memroot 90220 May20 ? 00:00:02 [vmmemctl]root 28246279379822:20 pts/000:00:29 ./memroot 2827128249022:21 pts/100:00:00 grep mem⼀个进程的内存虚拟地址信息列表[root@centos1 ~]# cat /proc/28246/maps00400000-00401000 r-xp 0000000000:1233 /mnt/hgfs/www/web/thread/process_thread_study/c/mem 00600000-00601000 rw-p 0000000000:1233 /mnt/hgfs/www/web/thread/process_thread_study/c/mem 3339e00000-3339e20000 r-xp 00000000 fd:002490830 /lib64/ld-2.12.so333a020000-333a021000 r--p 00020000 fd:002490830 /lib64/ld-2.12.so333a021000-333a022000 rw-p 00021000 fd:002490830 /lib64/ld-2.12.so333a022000-333a023000 rw-p 0000000000:000333a200000-333a38a000 r-xp 00000000 fd:002490831 /lib64/libc-2.12.so333a38a000-333a58a000 ---p 0018a000 fd:002490831 /lib64/libc-2.12.so333a58a000-333a58e000 r--p 0018a000 fd:002490831 /lib64/libc-2.12.so333a58e000-333a590000 rw-p 0018e000 fd:002490831 /lib64/libc-2.12.so333a590000-333a594000 rw-p 0000000000:0007f605b8ff000-7f605b902000 rw-p 0000000000:0007f605b910000-7f605b912000 rw-p 0000000000:0007ffce591e000-7ffce5933000 rw-p 0000000000:000 [stack]7ffce59ac000-7ffce59ad000 r-xp 0000000000:000 [vdso]ffffffffff600000-ffffffffff601000 r-xp 0000000000:000 [vsyscall][root@centos1 ~]# pmap 2824628246: ./mem0000000000400000 4K r-x-- /mnt/hgfs/www/web/thread/process_thread_study/c/mem0000000000600000 4K rw--- /mnt/hgfs/www/web/thread/process_thread_study/c/mem0000003339e00000 128K r-x-- /lib64/ld-2.12.so000000333a020000 4K r---- /lib64/ld-2.12.so000000333a021000 4K rw--- /lib64/ld-2.12.so000000333a022000 4K rw--- [ anon ]000000333a200000 1576K r-x-- /lib64/libc-2.12.so000000333a38a000 2048K ----- /lib64/libc-2.12.so000000333a58a000 16K r---- /lib64/libc-2.12.so000000333a58e000 8K rw--- /lib64/libc-2.12.so000000333a590000 16K rw--- [ anon ]00007f605b8ff000 12K rw--- [ anon ]00007f605b910000 8K rw--- [ anon ]00007ffce591e000 84K rw--- [ stack ]00007ffce59ac000 4K r-x-- [ anon ]ffffffffff600000 4K r-x-- [ anon ]total 3924K内存映射写时申请32G平台,⼀个进程拥有⾃⼰的4G虚拟地址空间与其它进程⽆关进程和进程之间是相互独⽴的进程地址空间的申请1.代码段,数据段,BSS段,这三个部分直接从磁盘拷贝到内存,起始地址在当前32位平台linux下为0x08048000地址2.堆段,动态变化 malloc系列。
Linux0.11的地址有3类地址需要区分清楚:1.程序(进程)的虚拟地址和逻辑地址虚拟地址(virtual address)指程序产生的有段选择符和段内偏移两部分组成的地址。
一个程序的虚拟地址空间有GDT映射的全地址空间和LDT映射的局部地址空间组成。
逻辑地址(logical address)指程序产生的段内偏移地址。
应用程序只与逻辑地址打交道,分段分页对应用程序来说是透明的。
也就是说C语言中的&,汇编语言中的符号地址,C中嵌入式汇编的”m”对应的都是逻辑地址。
2.CPU的线性地址线性地址(linear address)是逻辑地址到物理地址变换的中间层,是处理器可寻址空间的地址。
程序代码产生的逻辑地址加上段基地址就产生了线性地址。
3.实际物理内存地址物理地址(physical address)是CPU外部地址总线上的寻址信号,是地址变换的最终结果,一个物理地址始终对应实际内存中的一个存储单元。
对80386保护模式来说,如果开启分页机制,线性地址经过页变换产生物理地址。
如果没有开启分页机制,线性地址直接对应物理地址。
页目录表项、页表项对应都是物理地址。
Linux 0.11的内核数据段,内核代码段基地址都是0,所以对内核来说,逻辑地址就是线性地址。
又因为1个页目录表和4个页表完全映射16M物理内存,所以线性地址也就是物理地址。
故对linux0.11内核来说,逻辑地址,线性地址,物理地址重合。
个人总结:分段和分页的区别:分段可以给每个进程提供不同的线性地址分页可以把同一线性地址空间映射到不同的物理地址逻辑地址:线性地址:物理地址:逻辑地址-》分段单元-》线性地址-》分页单元-》物理地址Linux在使用的时候避开了Intel原先的设计意图,只是对段基址设置了0,这样逻辑地址和线性地址就一样了,再经过分页机制,就最后变成了物理地址了。
Linux2.4内核使用的空间中,虚拟地址和物理地址只是简单的相差了3G与80386段相关的宏定义set_seg_desc3123715/*****************************************************************************/ /* 功能: 设置存储段描述符,把指定属性的段描述符放入gate_addr 处 */ /* 参数: gate_addr 段描述符的目的地址 */ /* type 描述符中类型域,具体见80386基础一节中的表格 */ /* dpl 描述符中特权级 */ /* base 段基地址,这是线性地址 */ /* limit 段限长 */ /* 返回: (无) */ /*****************************************************************************/ #define _set_seg_desc(gate_addr,type,dpl,base,limit) {\ // 把段描述符的第4-7字节放入gate_addr 处 *((gate_addr)+1) = ((base) & 0xff000000) | \ // base 的31..24位放入gate_addr 的31..24位 (((base) & 0x00ff0000)>>16) | \ //base 的23..16位放入gate_addr 的7..0位 ((limit) & 0xf0000) | \ //limit 的19..16位放入gate_addr 的19..16位 ((dpl)<<13) | \ // dpl 放入gate_addr 的14..13位 (0x00408000) | \ // 把P 位和D 位设置位1,G 置为 0 ((type)<<8); \ // type 放入gate_addr 的11..8位 // 把段描述符的第0-3字节放入gate_addr+1处 *(gate_addr)= (((base) & 0x0000ffff)<<16) | \ //base 的15..0放入gate+1的31..15位 ((limit) & 0x0ffff); } //limit 的15..0位放入gate+1的15..0位set_tssldt_desc/*****************************************************************************/ /* 功能: 设置系统段描述符,把指定属性的段描述符放入GDT 中 */ /* 表项n 对应的地址处, */ /* 参数: n GDT 中表项n 对应的地址 */ /* addr 系统段的基地址,这是一个线性地址 */ /* type 描述符中类型域,具体见80386基础一节中的表格 */ /* 0x89表示386TSS 段描述符,0x82表示LDT 段 */ /* 这里8是为了设置P 位为1 */0 7 15 23 31/* 返回: (无) */ /*****************************************************************************/ // %0 寄存器eax addr// %1-%6 物理地址符号项n地址-n+7的地址#define _set_tssldt_desc(n,addr,type) \__asm__ ("movw $104,%1\n\t" \ // 把TSS的限长104字节放入n地址处,// 这样ldt的限长也定为104,这没有关系,因为linux0.11// 中一个任务的ldt只有3个表项"movw %%ax,%2\n\t" \ // 把addr的15..0位(在ax中)放入n+2处"rorl $16,%%eax\n\t" \ // 把addr的高16位(eax中)放入ax中"movb %%al,%3\n\t" \ //addr的23..16位放入n+4中"movb $" type ",%4\n\t" \ // 把type字段放入n+5中"movb $0x00,%5\n\t" \ // 把G置为0,说明粒度是字节。
Linux下的进程控制块(PCB)本⽂转载⾃1. 导语进程在操作系统中都有⼀个户⼝,⽤于表⽰这个进程。
这个户⼝操作系统被称为PCB(进程控制块),在linux中具体实现是 task_struct数据结构。
2. 说明进程控制块(PCB)(系统为了管理进程设置的⼀个专门的数据结构,⽤它来记录进程的外部特征,描述进程的运动变化过程。
系统利⽤PCB来控和管理进程,所以PCB是系统感知进程存在的唯⼀标志。
进程与PCB是⼀⼀对应的)在不同的操作系统中对进程的控制和管理机制不同,PCB中的信息多少不⼀样,通常PCB应包含如下⼀些信息。
1、进程:每个进程都必须有⼀个唯⼀的标识符,可以是字符串,也可以是⼀个数字。
2、进程当前状态 status:说明进程当前所处的状态。
为了管理的⽅便,时会将相同的状态的进程组成⼀个队列,如就绪进程队列,等待进程则要根据等待的事件组成多个,如等待打印机队列、等待磁盘完成队列等等。
3、进程相应的程序和数据地址,以便把PCB与其程序和数据联系起来。
4、进程。
列出所拥有的除CPU外的,如拥有的,打开的⽂件列表等。
5、 priority:进程的优先级反映进程的紧迫程度,通常由⽤户指定和系统设置。
6、现场保护区 cpustatus:当进程因某种原因不能继续占⽤CPU时(如等待打印机),释放CPU,这时就要将CPU的各种状态信息保护起来,为将来再次得到处理机恢复CPU的各种状态,继续运⾏。
7、进程同步与通信机制⽤于实现进程间互斥、同步和通信所需的信号量等。
8、进程所在队列PCB的链接字根据进程所处的现⾏状态,进程相的PCB参加到不同队列中。
PCB链接字指出该进程所在队列中下⼀个进程PCB的⾸地址。
9、与进程有关的其他信息。
如进程记账信息,进程占⽤CPU的时间等。
在linux 中每⼀个进程都由task_struct 来定义. task_struct就是我们通常所说的PCB。
3. 源码struct task_struct{volatile long state; //说明了该进程是否可以执⾏,还是可中断等信息unsigned long flags; // flags 是进程号,在调⽤fork()时给出int sigpending; // 进程上是否有待处理的信号mm_segment_t addr_limit; //进程地址空间,区分内核进程与普通进程在内存存放的位置不同 //0-0xBFFFFFFF for user-thead //0-0xFFFFFFFF for kernel-thread //调度标志,表⽰该进程是否需要重新调度,若⾮0,则当从内核态返回到⽤户态,会发⽣调度volatile long need_resched;int lock_depth; //锁深度long nice; //进程的基本时间⽚//进程的调度策略,有三种,实时进程:SCHED_FIFO,SCHED_RR, 分时进程:SCHED_OTHERunsigned long policy;struct mm_struct *mm; //进程内存管理信息int processor;//若进程不在任何CPU上运⾏, cpus_runnable 的值是0,否则是1 这个值在运⾏队列被锁时更新unsigned long cpus_runnable, cpus_allowed;struct list_head run_list; //指向运⾏队列的指针unsigned long sleep_time; //进程的睡眠时间//⽤于将系统中所有的进程连成⼀个双向循环链表, 其根是init_taskstruct task_struct *next_task, *prev_task;struct mm_struct *active_mm;struct list_head local_pages; //指向本地页⾯unsigned int allocation_order, nr_local_pages;struct linux_binfmt *binfmt; //进程所运⾏的可执⾏⽂件的格式int exit_code, exit_signal;int pdeath_signal; //⽗进程终⽌时向⼦进程发送的信号unsigned long personality;//Linux可以运⾏由其他UNIX操作系统⽣成的符合iBCS2标准的程序int did_exec:1;pid_t pid; //进程标识符,⽤来代表⼀个进程pid_t pgrp; //进程组标识,表⽰进程所属的进程组pid_t tty_old_pgrp; //进程控制终端所在的组标识pid_t session; //进程的会话标识pid_t tgid;int leader; //表⽰进程是否为会话主管struct task_struct *p_opptr,*p_pptr,*p_cptr,*p_ysptr,*p_osptr;struct list_head thread_group; //线程链表struct task_struct *pidhash_next; //⽤于将进程链⼊HASH表struct task_struct **pidhash_pprev;wait_queue_head_t wait_chldexit; //供wait4()使⽤struct completion *vfork_done; //供vfork() 使⽤unsigned long rt_priority; //实时优先级,⽤它计算实时进程调度时的weight值//it_real_value,it_real_incr⽤于REAL定时器,单位为jiffies, 系统根据it_real_value//设置定时器的第⼀个终⽌时间. 在定时器到期时,向进程发送SIGALRM信号,同时根据//it_real_incr重置终⽌时间,it_prof_value,it_prof_incr⽤于Profile定时器,单位为jiffies。
Linux系统编程——进程控制在学习Linux系统编程总结了笔记,并分享出来。
有问题请及时联系博主:,转载请注明出处。
09-linux-day05(进程控制)⽬录:⼀、学习⽬标⼆、进程1、进程和程序2、单道和多道程序设计3、进程的状态转化4、MMU 的作⽤5、PCB的概念6、获取环境变量7、进程控制函数fork8、fork创建⼦进程9、进程控制的命令10、创建n个⼦进程11、循环创建n个⼦进程控制顺序12、⽗⼦进程共享的内容13、⽗⼦进程不共享全局变量14、execlp函数介绍15、exec函数规律16、exel实现⾃定义程序17、孤⼉进程与僵⼫进程18、wait函数简单使⽤和说明19、wait回收并且查看死亡原因20、waitpid回收⼦进程21、⽤wait回收多个⼦进程22、waitpid回收多个⼦进程⼀、学习⽬标1、了解进程相关的概念2、掌握fork/getpid/getppid函数的使⽤3、熟练掌握ps/kill命令的使⽤4、熟练掌握execl/execlp函数的使⽤5、说出什么是孤⼉进程什么是僵⼫进程6、熟练掌握wait函数的使⽤7、熟练掌握waitpid函数的使⽤⼆、进程1、进程和程序什么是程序?编译好的⼆进制⽂件。
什么是进程?运⾏着的程序。
站在程序员的⾓度:运⾏⼀系列指令的过程。
站在操作系统⾓度:分配系统资源的基本单位。
区别: 程序占⽤磁盘,不占⽤系统资源。
进程占⽤系统资源。
⼀个程序对应多个进程,⼀个进程对应⼀个程序。
程序没有⽣命周期,进程有⽣命周期。
2、单道和多道程序设计3、进程的状态转化进程的状态切换4、MMU 的作⽤》MMU作⽤:1)虚拟内存和物理内存的映射;2)修改内存访问级别⽤户空间映射到物理内存是独⽴的。
5、PCB的概念Linux内核的进程控制块是task_struct结构体。
查找结构体:> sudo grep -rn "struct task_struct {" /usr/技巧:光标停留在{上,按%,可以到结构体的结尾。
Linux0.11-进程控制块数据结构
嵌入式Linux中文站收集整理Linux0.11版本内核学习笔记,本文分析了Linux 进程控制模块的数据结构。
struct task_struct {
long state
//任务的运行状态(-1 不可运行,0 可运行(就绪),>0 已停止)。
long counter
// 任务运行时间计数(递减)(滴答数),运行时间片。
long priority
// 运行优先数。
任务开始运行时 counter=priority,
越大运行越长。
long signal
// 信号。
是位图,每个比特位代表一种信号,信号值=位偏移值+1。
struct sigaction sigacTIon[32] // 信号执行属性结构,对应信号将要执行的操作和标志信息。
long blocked
// 进程信号屏蔽码(对应信号位图)。
int exit_code
// 任务执行停止的退出码,其父进程会取。
// 代码段地址。
unsigned long start_code
unsigned long end_code
// 代码长度(字节数)。
unsigned long end_data
// 代码长度 + 数据长度(字节数)。
unsigned long brk
// 总长度(字节数)。
unsigned long start_stack。