Chinaunix首页 | 论坛 | 博客

分类: LINUX

2014-02-23 17:47:54

    1、什么是进程?    
        进程是程序动态运行的结果,它不但包括程序的占用的内存,还包括程序执行期间所占用的资源,例如打开的文件描述符,内存地址空间等。
    
    2、Linux在内核当中用什么样的数据结构来表示进程?怎样才能在内核中找到它?
        内核利用task_struct结构来描述一个进程,称之为进程描述符。在Linux中有如此之多的进程描述符(多任务操作系统),以至于Linux内核采用双向链表的形式进行存放,称之为任务队列。同时,根据进程的不同状态,又分为可运行的任务队列,暂停的任务队列,可中断的等待任务队列,不可中断的任务队列,以及僵尸任务队列(猜测)。
        
Linux通过slab分配器分配task_struct结构,由于进程创建销毁频繁,看来slab分配器至少做到了一点:创建了一个task_struct 的内存池。
        怎样才能在内核中找到它?  在进程上下文中,Linux内核如何找到task_struct的。原来每个进程(进程与线程等价)在内核当中也有一个栈。内核栈的栈底会有一个thread_info的结构,它的第一个成员指向了task_stuct。以下是thread_info的结构体:
        thread_info定义在arch/x86/include/asm/thread_info.h下:
struct thread_info {
        struct task_struct      *task;                         /* main task structure,指向task_struct结构 */
        struct exec_domain      *exec_domain;         /* execution domain */
        __u32                   flags;                               /* low level flags */
        __u32                   status;                             /* thread synchronous flags */
        __u32                   cpu;                                /* current CPU */
        int                     preempt_count;                 /* 0 => preemptable, <0 => BUG */
        mm_segment_t            addr_limit;
        struct restart_block    restart_block;
        void __user             *sysenter_return;
#ifdef CONFIG_X86_32
        unsigned long           previous_esp;               /* ESP of the previous stack in case of nested (IRQ) stacks,保存上一个栈帧的ESP的值 */
        __u8                    supervisor_stack[0];
#endif
        int                     uaccess_err;
};
           以上分析了,进程描述符的组织结构,在内核变成当中我们又应该用什么方法去的进程描述符呢? current_thread_info()->task.其中在x86中current_thread_info的实现如下:
            mov $-8192, %eax
            andl %esp, %eax
            8192指明了栈的大小,在用户态下,EBP指向了栈帧的底部,ESP指向了栈帧的顶部,函数在开始执行之前总是会执行ESP - K的操作,函数中申请的局部变量占K个字节。也就是说,
在函数执行期间,栈帧大小相对固定。所以内核栈应该是这样子的,它的大小是8K,高内存地址是栈低,thread_info就保存在栈底,接下来通过ESP减操作,为局部变量申请空间。利用
current_thread_info()->task获得当前进程的描述符。

    3、Linux进程的进程状态?
        Linux是多任务抢占式操作系统中,只有处于可运行状态的进程才会被调度。进程状态,以及状态之间的转换有以下情况:            

·可运行状态(TASK_RUNNING):进程要么在CPU上执行,要么准备执行。

·可中断的等待状态(TASK_INTERRUPTIBLE) : 进程被挂起。硬件中断或信号可唤醒它

·不可中断的等待状态(TASK_UNINTERRUPTIBLE) : 信号不能唤醒。
暂停状态(TASK_STOP) : 程序受到了信号,暂停运行(CTL-Z)。

                           ·僵死状态(TASK_ZOMBIE):就是所谓的僵尸状态。
        http://blog.csdn.net/mu0206mu/article/details/7348618 对状态之间的转换做了详细的解释。注意task_struct以队列形式组织,但是也以状态进行区分(即多个队列)。
        那么可调用的函数是: set_task_state(task, state)
    
    4、进程上下文的解释?
         这是Linux内核执行程序时代表的三种执行环境。进程上下文是指:用户程序通过系统调用和出发异常,陷入内核,内核代表进程做内核部分的工作。体会到进程上下文的一次经历就是
          在进程上下文、软中断之间加锁的问题上。

    3、进程之间存在父子关系,那么new一个新的进程时,进程之间的资源是如何共享的?
         所有的进程组织成一棵树,同时由于进程描述符链表组织,利用next_task(task)、prev_task(task)、for_each_process(task)可遍历整个队列。(一般别这么做,浪费时间)。       
         要想弄清楚进程之间资源的共享情况,需要从fork开始说起,它创建了一个新的进程。创建过程如下:Linux通过clone系统调用实现fork,即fork函数根据自己的需要,向clone传递参数,表明父子进程之间需要共享的资源。然后clone又通过调用do_fork 函数完成进程创建。主要工作如下:1、为新进程创建一个内核栈、thread_info结构和task_struct,内容与当前进程的值一致,此时父子进程的进程描述符是完全一致的。2、将新的进程描述符中关于统计信息的变量都设置为0,其他大部分数据保持共享。3、更新新的进程描述符的PID以及PPID的值。4、根据传递给clone的参数标志,do_fork共享相关的变量。
        那么到底fork传递给clone函数哪些参数呢?  要好好看看clone这个函数了。

    4、Linux是如何实现线程的?
        Linux内核既没有为线程创建特殊的数据结构,也没有为之准备特殊的调度算法。Linux把所有的线程都当作是进程,所以线程就被是为是一个与其它进程共享某些资源的进程。所以每个线程都拥有唯一的属于自己的task_struct
        clone(CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND, 0)     相当于创建了线程
       clone(CLONE_SIGHAND, 0) 相当于创建了进程
    
    5、内核进程。独立运行在内核空间的进程,没有标准的进程空间,它只在内核空间运行,从来不切换到用户空间去,内核进程和普通进程一样,可以被调度,也可以被抢占。
           这些线程在系统启动时只能由另外一些内核线程启动。调用kernel_thread函数。

    如有错误之处,欢迎批评指正。
 
阅读(1037) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~