进程的数据结构和管理

    技术2024-12-19  3

    1.  进程的内核描述

    struct task_struct {

                  /*----------------------- these are hardcoded - don't touch -----------------------*/

                  long state; // 进程运行状态(-1不可运行,0可运行,>0以停止)

                  long counter; // 任务运行时间片,递减到0是说明时间片用完

                  long priority; // 任务运行优先数,刚开始是counter=priority

                  long signal; // 任务的信号位图,信号值=偏移+1

                  struct sigaction sigaction[32]; //信号执行属性结构,对应信号将要执行的操作和标志信息

                  long blocked; // 信号屏蔽码

                 /*----------------------------------- various fields--------------------------------- */

                 int exit_code; // 任务退出码,当任务结束时其父进程会读取

                 unsigned long start_code,end_code,end_data,brk,start_stack;

                 // start_code 代码段起始的线性地址

                 // end_code 代码段长度

                 // end_data 代码段长度+数据段长度

                 // brk 代码段长度+数据段长度+bss段长度

                 // start_stack 堆栈段起始线性地址

                  long pid,father,pgrp,session,leader;

                 // pid 进程号

                 // father 父进程号

                // pgrp 父进程组号

                // session 会话号

                // leader 会话首领

                unsigned short uid,euid,suid;

                // uid 用户标id

                // euid 有效用户id

                // suid 保存的用户id

                unsigned short gid,egid,sgid;

               // gid 组id

               // egid 有效组id

               // sgid 保存组id

               long alarm; // 报警定时值

               long utime,stime,cutime,cstime,start_time;

              // utime 用户态运行时间

              // stime 内核态运行时间

              // cutime 子进程用户态运行时间

              // cstime 子进程内核态运行时间

              // start_time 进程开始运行时刻

                 unsigned short used_math; // 标志,是否使用了387协处理器

    /* ----------------------------------file system info-------------------------------- */

                  int tty; // 进程使用tty的子设备号,-1表示没有使用

                  unsigned short umask; //文件创建属性屏蔽码

                 struct m_inode * pwd; // 当前工作目录的i节点

                 struct m_inode * root; // 根目录的i节点

                 struct m_inode * executable; // 可执行文件的i节点

                  unsigned long close_on_exec; // 执行时关闭文件句柄位图标志

                  struct file * filp[NR_OPEN]; // 进程使用的文件

                  /*------------------ ldt for this task 0 - zero 1 - cs 2 - ds&ss -------------------*/

                 struct desc_struct ldt[3]; // 本任务的ldt表,0-空,1-代码段,2-数据和堆栈段

                  /* ---------------------------------tss for this task ---------------------------------*/

                  struct tss_struct tss; // TSS

    };

    2. 进程0的描述符信息

    /* state etc */ { 0,15,15, /

    /* signals */ 0,{{},},0, /

    /* ec,brk... */ 0,0,0,0,0,0, /

    /* pid etc.. */ 0,-1,0,0,0, /

    /* uid etc */ 0,0,0,0,0,0, /

    /* alarm */ 0,0,0,0,0,0, /

    /* math */ 0, /

    /* fs info */ -1,0022,NULL,NULL,NULL,0, /

    /* filp */ {NULL,}, /

    { /

    {0,0}, / // ldt第0项是空

    /* ldt */ {0x9f,0xc0fa00}, / //代码段长640K,基地0,G=1,D=1,DPL=3,P=1,TYPE=0x0a

    {0x9f,0xc0f200}, / //数据段长640K,基地0,G=1, D=1, DPL=3,P=1, TYPE=0x02

    }, /

    /*tss*/ {0,PAGE_SIZE+(long)&init_task,0x10,0,0,0,0,(long)&pg_dir,/

    // esp0 = PAGE_SIZE+(long)&init_task 内核态堆栈指针初始化为页面最后

    // ss0 = 0x10 内核态堆栈的段选择符,指向系统数据段描述符,进程0的进程控制

    // 块和内核态堆栈都在system模块中

    // cr3 = (long)&pg_dir 页目录表,其实linux0.11所有进程共享一个页目录表

    0,0,0,0,0,0,0,0, /

    0,0,0x17,0x17,0x17,0x17,0x17,0x17, /

    _LDT(0),0x80000000, / // ldt表选择符指向gdt中的LDT0处

    {} /

    }

    3. 进程的内存布局

    4. 进程控制相关函数

       1).   sys_fork(), 克隆一个进程;

       2).   sys_execve(), 加载一个可执行程序(换上新的代码);

       3).   sys_exit(),进程退出。释放内存, 将所有孩子过继给进程1, 释放当前使用的文件资源, 释放TTY,如果是会话首领则结束会话。

       4).  sys_waitpid(), 等待某进程退出;

     

    5. 信号处理

        信号相当于软中断, 用于进程间通讯. CPU在执行每条指令的末端检测是否有硬件中断,而内核在进程从内核态返回时检测是否响应信号。

    struct sigaction { // 信号响应结构体

                 void (*sa_handler)(int); // 信号响应函数地址,相当与中断服务程序入口

                 sigset_t sa_mask; // 进程正在处理当前信号时,可能需要屏蔽新的信号

                                            // 将要屏蔽的信息存在这里。

                                            // 通常是把当前正在处理的信号屏蔽调。进程对正在处理

                                            // 的信号肯定是允许的,通过设置这个变量来决定是否允许

                                            // 当前信号嵌套。

                 int sa_flags; // 改变信号处理过程的信号集

                 void (*sa_restorer)(void); // 恢复函数入口地址,用于清除用户堆栈

                                                               // 这个函数由libc提供,用户无法自行设置

    };

    /*****************************************************************************/

    /* 功能:为信号注册新的响应函数  */

    /* 该响应函数是临时的,调用过一次后就恢复成SIG_DFL  */

    /* 参数:signum 指定的信号  */

    /* handler 待注册的响应函数  */

    /* restorer 恢复函数,libc提供  */

    /* 返回:signum信号原先的响应函数  */

    /*****************************************************************************/

    int sys_signal(int signum, long handler, long restorer)

    {

    struct sigaction tmp;

    // 信号值不在范围内,或者企图修改SIGKILL的响应函数,则出错

    // SIGKILL是不能被屏蔽的,收到SIGKILL的进程必须退出,所以SIGKILL的

    // 响应函数必须是SIG_DFL且不允许被修改

    if (signum<1 || signum>32 || signum==SIGKILL)

    return -1;

    //下面4句创建新的响应结构体

    tmp.sa_handler = (void (*)(int)) handler; // 填入新的响应函数句柄

    tmp.sa_mask = 0; // 响应信号时不屏蔽任何信号

    tmp.sa_flags = SA_ONESHOT | SA_NOMASK; // 新的响应函数使用一次后就恢复成

    // SIG_DFL

    tmp.sa_restorer = (void (*)(void)) restorer; // 转入恢复函数

    handler = (long) current->sigaction[signum-1].sa_handler;  // 保持旧的响应函数

    current->sigaction[signum-1] = tmp; // 装入新的响应结构

    return handler; // 返回旧函数的句柄

    }

    /*****************************************************************************/

    /* 功能:为信号转载响应机构体  */

    /* 该响应结构体永远存在,知道该信号被再次装载  */

    /* 参数:signum 指定的信号  */

    /* action 待转入的响应结构体指针,这是用户空间中的逻辑地址  */

    /* oldaction 用于返回旧的响应结构体,这是用户空间中的逻辑地址  */

    /* 返回:0 成功 –1 出错  */

    /*****************************************************************************/

    int sys_sigaction(int signum, const struct sigaction * action,

    struct sigaction * oldaction)

    {

    struct sigaction tmp;

    if (signum<1 || signum>32 || signum==SIGKILL)

    return -1;

    // 取出旧的响应结构体,放入tmp中。

    // 这里用的是“=”,因为tmp和current都在内核空间

    tmp = current->sigaction[signum-1];

    // 装入新的响应结构体action

    // 这里不能用“=”,因为新结构体是用户定义,在用户空间。action指向的是

    // 用户空间中的逻辑地址

    get_new((char *) action,

    (char *) (signum-1+current->sigaction));

    // 如果oldaction不是空,则把旧的响应结构体保存在它指向的地址

    // 同眼,oldaction也是用户空间中的逻辑地址,不能用“=”直接赋值

    if (oldaction)

    save_old((char *) &tmp,(char *) oldaction);

    // 如果允许信号在自己的信号处理函数中收到,则清空屏蔽码

    if (current->sigaction[signum-1].sa_flags & SA_NOMASK)

    current->sigaction[signum-1].sa_mask = 0;

    else // 否则屏蔽自己

    current->sigaction[signum-1].sa_mask |= (1<<(signum-1));

    return 0;

    }

    最新回复(0)