• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            jake1036

            linux0.11相關進程數據結構

            1 進程結構
                union task_union{
                 struct task_struct task ;
                 char stack[PAGE_SIZE]  ;
              }
              這實際上是一個內存頁,頁的底部是進程控制塊結構。其余部分是作為進程的內核態堆棧使用。
              

            Task_struct

            內核態堆棧

            page

            Page+4K



            2 task 數組

            struct task_struct * task[NR_TASKS] = {&(init_task.task), };

            這個數組中存儲的是task_struct 結構的指針,但是實際上數組中的每一項都指著一塊內存頁。


            3 任務段數據 
              

            struct tss_struct {

                   long back_link;      /* 16 high bits zero */

                   long esp0;

                   long ss0;         /* 16 high bits zero */

                   long esp1;

                   long ss1;         /* 16 high bits zero */

                   long esp2;

                   long ss2;         /* 16 high bits zero */

                   long cr3;

                   long eip;

                   long eflags;

                   long eax,ecx,edx,ebx;

                   long esp;

                   long ebp;

                   long esi;

                   long edi;

                   long es;          /* 16 high bits zero */

                   long cs;          /* 16 high bits zero */

                   long ss;           /* 16 high bits zero */

                   long ds;          /* 16 high bits zero */

                   long fs;           /* 16 high bits zero */

                   long gs;          /* 16 high bits zero */

                   long ldt;         /* 16 high bits zero */

                   long trace_bitmap;  /* bits: trace 0, bitmap 16-31 */

                   struct i387_struct i387;        

            };


            4  進程控制塊
                

            struct task_struct {

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

                   long state;       // 進程運行狀態(-1不可運行,0可運行,>0以停止)

                   long counter;  // 任務運行時間片,遞減到0是說明時間片用完

                   long priority;  // 任務運行優先數,剛開始是counterpriority

                   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

            };


             5   linux進程結構
              
                  (1)  在linux中gdt中的每一項,都有兩個表項,一個是ldt描述符,另一個是tss描述符。
                  (2)  在task數組中占有一項,每一項是一個物理頁面,物理內存頁面底端是進程控制塊,內存頁面的其余部分是內核態堆棧。
                  (3)  task數組中的表項和gdt中的表項是一一對應的。 對于一個在task數組中的任務項是nr的任務來說,它的tss描述符在gdt中描述符
                        的位置是,gdtr + 3*8 + 16 * nr ,ldt描述符在gdt中的描述符的位置是 gdtr + 3 * 8 + 16 * nr + 8 。
                  (4) 對應于表項為nr的進程,它對應的頁目錄項是16 * nr --------16 * (nr + 1) 。
                 



            6 進程0
                進程0是一個特殊的進程,它是所有進程的祖先進程,所有其他的進程都是復制進程0或者其后代進程產生的。 但是進程0不是。
                下面主要講一下 進程0的創建順序:
                (1) 進程控制塊和頁目錄頁表的手動創建
                               
                 以下就是一個任務的初始過程
            #define INIT_TASK \
            /* 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處
                    {} \
                }
            , \
            }


              
              進程0的數據段基址為0,段限長為640KB ,代碼段基址為0,段限長為640KB。任務0的數據段和代碼段 和系統的代碼段和數據段是重合的。
              進程0的內核態堆棧和進程控制塊都是位于系統模塊內。
             
            (2)在main模塊中調用了,sched_init()函數加載了 進程0的進程0tss段描述符,ldt段描述符,并且加載TR寄存器,使它指向進程0tss段,這時候
                     進程0才完成了啟動。
               
                
            /*****************************************************************************/
            /* 功能:    1.    初始化task數組和GDT(包括設置進程1的LDT和TSS)            */
            /*            2.    加載TR和IDTR寄存器                                        */
            /*            3.    設置時鐘中斷門和系統調用中斷門                                */
            /* 參數:    (無)                                                            */
            /* 返回:    (無)                                                            */
            /*****************************************************************************/
            void sched_init(void)
            {
                
            int i;
                
            struct desc_struct * p;

                
            if (sizeof(struct sigaction) != 16)
                    panic(
            "Struct sigaction MUST be 16 bytes");
            // 在gdt中設置進程0的tss段描述符
                set_tss_desc(gdt+FIRST_TSS_ENTRY,&(init_task.task.tss));
            // 在gdt中設置進程0的ldt段描述符
                set_ldt_desc(gdt+FIRST_LDT_ENTRY,&(init_task.task.ldt));
            // 下面的循環把gdt和task中其他的項清空
                p = gdt+2+FIRST_TSS_ENTRY;
                
            for(i=1;i<NR_TASKS;i++{
                    task[i] 
            = NULL;
                    p
            ->a=p->b=0;
                    p
            ++;
                    p
            ->a=p->b=0;
                    p
            ++;
                }

            /* Clear NT, so that we won't have troubles with that later on */
                __asm__(
            "pushfl ; andl $0xffffbfff,(%esp) ; popfl");
                ltr(
            0);    // 把進程0的tss段加載到TR寄存器
                lldt(0);    // 把進程0的ldt段加載到IDTR寄存器。
                        
            // 這是將gdt中進程0的ldt描述符對應的選擇符加載到TR中。CPU將
                        
            // 選擇符加載到可見部分,將tss的基地址和段長等加載到不可見部分。
                        
            // TR寄存器只在這里明確加載一次,以后新任務ldt的加載是CPU根據
                        
            // TSS段中LDT字段自動加載。
            // 初始化8253定時器
                outb_p(0x36,0x43);        /* binary, mode 3, LSB/MSB, ch 0 */
                outb_p(LATCH 
            & 0xff , 0x40);    /* LSB */
                outb(LATCH 
            >> 8 , 0x40);    /* MSB */
                set_intr_gate(
            0x20,&timer_interrupt);        // 設置時鐘中斷門
                outb(inb_p(0x21)&~0x01,0x21);
                set_system_gate(
            0x80,&system_call);        // 設置系統調用中斷門
            }






                (3)切換回用戶態。
                
            // 把進程0從內核態切換到用戶態去執行,使用的方法是模擬中斷調用返回
            // 利用指令iret完成特權級的轉變。
            #define move_to_user_mode() \
            __asm__ (
            "movl %%esp,%%eax\n\t" \        // 當前堆棧指針保存到eax中
            "pushl $0x17\n\t" \    // 當前堆棧段選擇符0x17入棧,它指向進程0的數據段描述符// 因為進程0的代碼段、數據段、內核代碼段、數據段4者重
            // 合,所以它指向的仍然是內核模塊區域。
                "pushl %%eax\n\t" \    // 把當前堆棧指針入棧。這樣模擬外層堆棧的SS:ESP。
                                    
            // 由于進程0數據段選擇符0x17對應的還是內核模塊,和
            // 內核數據段選擇符0x10的差別僅在與對應描述符的dpl和
            // 本身rpl的不同,所以外層堆棧指針指向的還是原來的堆棧
            // 即user_stack
                "pushfl\n\t" \            // eflags入棧
                "pushl $0x0f\n\t" \        // 進程0代碼段選擇符入棧,模擬返回的CS
                "pushl $1f\n\t" \        // 下面標號1的偏移地址入棧,模擬返回的EIP
                                    
            // 也是由于4段重合,所以這里返回的CS對應的段的基地址與
                                    
            // 內核代碼段基地址一樣,都是0,故將返回的CS:EIP就是下
                                    
            // 面標號1處。
                "iret\n" \                // 中斷返回。由于當前CPL=0,將返回的CS的RPL=3,所以
                                    
            // 不僅僅要改變CS,EIP,還要發生堆棧切換(但實際上堆棧
            // 還是user_stack),同時CPL變成3。
                "1:\tmovl $0x17,%%eax\n\t" \    // 把數據段寄存器的值設為進程0的數據段
                "movw %%ax,%%ds\n\t" \
                
            "movw %%ax,%%es\n\t" \
                
            "movw %%ax,%%fs\n\t" \
                
            "movw %%ax,%%gs" \
                :::
            "ax")
             


            6 用fork創建進程
               除了進程0,所有其他的進程都是由fork()系統調用創建的,子進程是通過復制父進程的數據和代碼而產生的。
               創建結束之后,子進程與父進程的代碼和數據共享,但是子進程有自己的進程控制塊,內核堆棧和頁表。
             
              一個進程需要以下三中數據結構
               (1) 進程控制塊 task__struct 。
               (2) gdt中的tss 和ldt描述符。
               (3)頁目錄項和頁表項。
                所以fork系統調用的任務就是創建進程的上述三個部分。
                sys_fork()函數分兩步實現,第一步 首先調用,find_empty_process() 函數,第二步調用 copy_process()函數,復制進程。

              
            _sys_fork:
            // 第一步,調用find_empty_process()函數,找task[]中的空閑項。
            // 找到后數組下標放在eax中。如果沒找到直接跳轉到ret指令。
                call _find_empty_process
                testl 
            %eax,%eax
                js 1f
                push 
            %gs        // 中斷時沒有入棧的寄存器入棧,
            // 作為copy_process() 函數的參數
                pushl %esi
                pushl 
            %edi
                pushl 
            %ebp
                pushl 
            %eax
             
            // 第二步,調用copy_process() 函數復制進程。
                call _copy_process    
                addl $
            20,%esp
            1:    ret


              內存復制函數
               
            copy_mem

            /*****************************************************************************/
            /*    功能:設置新進程的LDT項(數據段描述符和代碼段描述符)中的基地址部分     */
            /*          并且復制父進程(也就是當前進程)的頁目錄和頁表,                      */
            /*          實現父子進程數據代碼共享                                              */
            /*    參數:    nr    新進程任務數組下標                                             */
            /*            p    新進程的進程控制塊                                             */
            /*    返回:    0 (成功),    -ENOMEM(出錯)                                     */
            /*****************************************************************************/

            int copy_mem(int nr,struct task_struct * p)
            {
                unsigned 
            long old_data_base,new_data_base,data_limit;
                unsigned 
            long old_code_base,new_code_base,code_limit;

                code_limit
            =get_limit(0x0f);    // 取當前進程代碼段長度
                data_limit=get_limit(0x17);    // 取當前進程數據段長度
                old_code_base = get_base(current->ldt[1]);    // 取當前進程代碼段基地址,這是線性地址
                old_data_base = get_base(current->ldt[2]);    // 取當前進程數據段基地址,這是線性地址
                
            // 0.11進程代碼段和數據段基地址必須重合
            if (old_data_base != old_code_base)
                    panic(
            "We don't support separate I&D");
                
            //0.11中數據段代碼段的基地址是重合的,都是nr*64M(nr是task[]數組下標),所以
                
            //數據段的長度肯定大于代碼段長度。而且 copy_page_tables()傳入的是data_limit,這
                
            // 把代碼和數據都包含進去了。
                if (data_limit < code_limit)
                    panic(
            "Bad data_limit");
                
            // 新進程的代碼段基地址 = 數據段基地址 = 64M*nr
                new_data_base = new_code_base = nr * 0x4000000;
                
            // 設置進程的起始線性地址
                p->start_code = new_code_base;
                
            // 設置新進程的ldt項。在copy_process()中完全復制父進程的ldt,所以
                
            // 只需重新設置ldt的基地址字段,其他字段和父進程一樣
                set_base(p->ldt[1],new_code_base);
                set_base(p
            ->ldt[2],new_data_base);
                
            // 把線性地址old_data_base處開始,一共data_limit個字節的內存對應的頁目錄、
            // 頁表復制到線性地址new_data_base。這里僅僅復制相關的頁目錄和頁表,使它們
            // 指向同一個物理頁面,實現父子進程數據代碼共享。
                if (copy_page_tables(old_data_base,new_data_base,data_limit)) {
                    free_page_tables(new_data_base,data_limit);
                    
            return -ENOMEM;
                }

                
            return 0;
            }



            復制進程


            /*****************************************************************************/
            /*    功能:復制進程,把當前進程current復制到task[nr]                             */
            /*    參數:當前進程(current)內核堆棧的所有內容                                 */
            /*          當前進程內核堆棧保存了所有寄存器的值,在程序中要把這些寄存器的值     */
            /*          全部復制給子進程,從而給子進程創造和父進程一樣的運行環境             */
            /*    返回:子進程pid                                                             */
            /*****************************************************************************/
            int copy_process(int nr,long ebp,long edi,long esi,long gs,long none,
                    
            long ebx,long ecx,long edx,
                    
            long fs,long es,long ds,
                    
            long eip,long cs,long eflags,long esp,long ss)
            {
                
            struct task_struct *p;
                
            int i;
                
            struct file *f;
            // 在主內存區申請一頁新的內存,用來放置子進程的task_struct和內核堆棧
            // get_free_page()返回的是物理地址
                p = (struct task_struct *) get_free_page();
                
            if (!p)
                    
            return -EAGAIN;
            // 設置task數組中相關項
                task[nr] = p;
            // 下面的賦值語句僅僅把父基礎的task_struct部分全部復制給子進程
            // 注意:僅僅復制task_struct部分,內核堆棧不復制,因此子程序的內核堆棧
            //          是空的,這也是我們希望的
                *= *current;    /* NOTE! this doesn't copy the supervisor stack */
            // 下面的很多賦值語句修改子進程的task_struct中若干字段
            // 這些字段跟父進程是有差別的
                p->state = TASK_UNINTERRUPTIBLE;    //子進程設為不可中斷狀態
                p->pid = last_pid;            // 設置子進程pid
                p->father = current->pid;    // 把當前進程pid舍為子進程的father
                p->counter = p->priority;    // 繼承父親的優先級
                p->signal = 0;
                p
            ->alarm = 0;
                p
            ->leader = 0;        /* process leadership doesn't inherit */
                p
            ->utime = p->stime = 0;
                p
            ->cutime = p->cstime = 0;
                p
            ->start_time = jiffies;    // 子進程開始時間
                p->tss.back_link = 0;
            // 子進程的內核堆棧指針設置為task_struct所在頁面的最高端
                p->tss.esp0 = PAGE_SIZE + (long) p;    
            // 子進程的內核堆棧選擇符為0x10,指向GDT中系統數據段。
            // 注意 雖然子進程的內核堆棧位于內核system模塊外,在主內存區,但是因為系統數據段
            //        基地址為0,限長為16M,函概了所有物理內存,故子進程內核堆棧也位于系統數
            //        段內。esp0要的是段內偏移,也是因為系統數據段基地址為0,物理地址
            //        PAGE_SIZE + (long) p 也是段內偏移。
            p->tss.ss0 = 0x10;
            // 把父進程系統調用返回地址賦給子進程當前運行的eip。這樣當子進程被調度程序選中
            // 后他從fork返回地址處開始執行。
                p->tss.eip = eip;
                p
            ->tss.eflags = eflags;
            // eax是函數返回值存放的地方,把子進程的eax設置為0,這樣fork在子進程中返回的是0。
            // 注意 子進程并沒有執行fork()函數,子進程的系統堆棧沒有進行過操作,當然不會有像
            //        父進程那樣的fork函數調用。但是當子進程開始運行時,就好像它從fork中返回。
                p->tss.eax = 0;        
                p
            ->tss.ecx = ecx;
                p
            ->tss.edx = edx;
                p
            ->tss.ebx = ebx;
                p
            ->tss.esp = esp;    // 用戶堆棧指針和父進程一樣,子進程完全復制父進程的用戶堆棧
                p->tss.ebp = ebp;
                p
            ->tss.esi = esi;
                p
            ->tss.edi = edi;
                p
            ->tss.es = es & 0xffff;
                p
            ->tss.cs = cs & 0xffff;
                p
            ->tss.ss = ss & 0xffff;
                p
            ->tss.ds = ds & 0xffff;
                p
            ->tss.fs = fs & 0xffff;
                p
            ->tss.gs = gs & 0xffff;
            // 設置子進程的ldt。從這里可以看到,task下標為nr的進程在GDT中的2項一定是
            // _LDT(nr)和_TSS(nr)。task[]中的項和GDT中的2項一一對應。
                p->tss.ldt = _LDT(nr);
                p
            ->tss.trace_bitmap = 0x80000000;
                
            if (last_task_used_math == current)
                    __asm__(
            "clts ; fnsave %0"::"m" (p->tss.i387));
            // 在copy_mem函數中設置子進程的代碼段描述符,數據段描述符,并且復制父進程的
            // 頁目錄、頁表。實現和父進程代碼數據的共享。
                if (copy_mem(nr,p)) {
                    task[nr] 
            = NULL;
                    free_page((
            long) p);
                    
            return -EAGAIN;
                }

            // 子進程繼承父進程打開的文件,所以文件引用數目要加一
                for (i=0; i<NR_OPEN;i++)
                    
            if (f=p->filp[i])
                        f
            ->f_count++;
            // 子進程繼承父進程的工作目錄、根目錄和可執行文件,所以引用數目加一
                if (current->pwd)
                    current
            ->pwd->i_count++;
                
            if (current->root)
                    current
            ->root->i_count++;
                
            if (current->executable)
                    current
            ->executable->i_count++;
            // GDT中對應位置(和nr對應)放入子進程的TSS描述符、LDT描述符
                set_tss_desc(gdt+(nr<<1)+FIRST_TSS_ENTRY,&(p->tss));
                set_ldt_desc(gdt
            +(nr<<1)+FIRST_LDT_ENTRY,&(p->ldt));
            // 最后把子進程的狀態設置為可運行狀態,這樣子進程可以被調度
                p->state = TASK_RUNNING;    /* do this last, just in case */
            // 父進程返回子進程的pid
                return last_pid;
            }




              7 進程的結束
             

            進程結束的時候,需要關閉的資源主要有:
              (1)  釋放所有的物理頁面。(子進程自己清除
              (2)  關閉所有打開的文件。(子進程自己清除
              (3)  清除task[] 中的相應的項。(父進程自己清除

              子進程通過exit()清除前面兩個選項,將自身的狀態變為TASK_ZOMBIE 。
              父進程通過調用waitpid() 將task[] 數組清空。


               一個進程的經過exit()之后,物理頁表被清除 , 頁表頁目錄項也被清除,但是它的進程控制塊和內核堆棧還在,,
              此時進程的狀態變為TASK_ZOMBIE ,不會再被處理器處理。不被處理但是還占用著task數組中的一個表項,這
             就成為了僵尸進程。

                子進程調用了exit()函數之后,就通知父進程,父進程調用waitpid() 來清除 task數組中的表項。但是很有可能,
                父進程沒有執行waitpid()操作,情況如下:
               (1) 父進程早于子進程執行exit()函數。
               (2) 子進程僵死,但是父進程沒有調用waitpid()操作。
               (3) 父進程調用了waitpid(),但是因為某種愿意沒有釋放資源。
               
               解決方法:
                如果父進程無法釋放資源,那么就讓進程1來釋放資源。
                當一個父進程早于子進程exit()的時候,它把所有的子進程過繼給父進程。

            posted on 2010-11-13 20:50 kahn 閱讀(2203) 評論(0)  編輯 收藏 引用

            亚洲人成网亚洲欧洲无码久久| 中文国产成人精品久久不卡| 性高湖久久久久久久久| 无码乱码观看精品久久| 91精品国产高清久久久久久91| 97久久超碰国产精品旧版| 久久精品国产亚洲av高清漫画 | 久久精品国产亚洲AV久| 亚洲精品成人久久久| 久久无码中文字幕东京热| 性做久久久久久久久浪潮| 一级女性全黄久久生活片免费 | 久久久久免费精品国产| 久久久久综合网久久| 久久精品国产亚洲Aⅴ香蕉| 午夜福利91久久福利| 精品久久久久成人码免费动漫| 亚洲香蕉网久久综合影视| 2021久久国自产拍精品| 国产精品99久久久久久猫咪| 久久人人爽人人爽人人片AV东京热 | 91精品国产91久久| 人妻中文久久久久| 久久99精品久久久久久动态图| 女人香蕉久久**毛片精品| 一级a性色生活片久久无少妇一级婬片免费放| 色99久久久久高潮综合影院| 伊人色综合久久天天人手人婷| 久久精品国产福利国产秒| 久久se这里只有精品| 亚洲中文久久精品无码ww16 | 亚洲精品无码久久毛片| 久久久久亚洲av无码专区喷水| 亚洲精品高清久久| 亚洲av成人无码久久精品 | 亚洲日本va中文字幕久久| 色综合久久最新中文字幕| 久久妇女高潮几次MBA| 国产日韩欧美久久| www.久久99| 色88久久久久高潮综合影院 |