Linux的进程控制

研究如何建立,撤销,阻塞,或唤醒一个进程

一 创建进程

系统启动时处于内核状态

初始化最后,启动名为init的内核线程,然后保留在idle状态(空闲状态)

系统中没有进程可运行时,调度管理器运行idle进程。

 

idle进程详解

idle进程是空闲进程,是唯一不动态分配task_struct的进程,是task数组的0号元素,记作init_task

pid为0

  注意,idle进程就是0号进程,始终位于linux系统的run_queue中,也就是系统可运行队列。在前面讲到了

使用指针*next_run和*pre_run连接形成双向循环队列RUN_QUEUE

  这个双向循环队列的基准就是这个0号进程idle

  run_queue的头部元素就是init_task之后的task_struct所代表的进程,

尾部元素是init_task之前的进程。

  也就是说,每次建立新的进程,都会被插入到init_task之前。

 

init内核线程详解

  init内核线程pid为1

  负责完成一些初始化的任务

  使用/etc/inittab作为脚本创建系统中的新的进程,这些新的进程又创建各自的新进程。

 

  系统关机前还需要完成结束所有线程,是系统中所有进程的祖先。(而不是0号进程)

 

  

 

 

 

 

fork的使用

  如pid=fork(),通过系统调用创建一个新的进程

  返回值的意义:

  0,只会在子进程中出现,表示当前的进程是子进程

  >0,在父进程中,返回值是子进程的pid

  -1,创建失败

 

  内核在fork()时完成以下的操作

  

 

 ubuntu中编写代码

CTRL+alt+T

ls显示已有文件

gedit xx.c 进入xx.c的编辑界面

gcc xx.c 编译并运行xx.c

./a.out 显示输出

touch xx.c 创建一个新文件

 

  多个fork的代码

#include <unistd.h>    //ppid指当前进程的父进程pid  
#include <stdio.h>     //pid指当前进程的pid,  
int main(void)               //fpid指fork返回给当前进程的值  
{  
   int i=0;  
   printf("i son/pa ppid pid  fpid/n");  
      for(i=0;i<2;i++){  
       pid_t  fpid=fork();  
       if(fpid==0)  
           printf("%d child  %4d %4d %4d/n",i,getppid(),getpid(),fpid);  
       else  
           printf("%d parent %4d %4d %4d/n",i,getppid(),getpid(),fpid);  
   }  
   return 0;  
}  

 

 在我的机器上的运行结果为:

i son/fa ppid pid fpid

0 fa 2185 2214 2215  

1 fa 2185 2214 2216

1 child 718 2216 0      

0 child 718 2215 0    

1 fa 718 2215 2217

1 child 718 2217 0

 

emmmmmm,可能出了点问题

在父进程代码块中加入sleep(1)试一试

0 child 2434 2435 0        //第一个进程的子进程,父进程2434,自己为2435

1 child 2435 2436 0        //2435创建的2436

0 fa 2185 2434 2435      //第一个进程,祖先进程2185,自己pid为2434 ,子进程2435

1 fa 2434 2435 2436     //2435创建2436

1 child 2434 2437 0       //2434第二轮创建的2437

1 fa 2185 2434 2437      //还是第一个进程,第二轮中作为父进程创建2437

 

最终实际上只创建了4个进程。

 

二 执行进程

为了使子进程和父进程完成不同的任务。

 

 使用exec系统调用来装载新的进程映像,放弃从父进程中拷贝过来的内容。

 

只有execve是真正的系统调用,其他都是在此基础上经过包装的库函数。

 

execve对当前进程进行替换,为一个指定的程序,参数包括文件名,参数列表及环境变量。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include<unistd.h> 
char command[256];
void main()
{  
     int rtn,errorno;         /*子进程的返回数值*/
     while(1)        /* 从终端读取要执行的命令 */
     {  
            printf( ">" );
            fgets(command, 256, stdin);
            command[strlen(command)-1] = 0;
            if (fork() == 0)       /* 子进程执行此命令 */       
            { 
                    execl(“/bin/bash”,”bash”,”-c”,command);  
                    /*bash的-c选项表示命令从后续字符串中读取*/
                    /* 如exec返回,表明没有正常执行*/ perror(command);
                    exit(errorno);          
            }     
            else    /*父进程等待子进程结束 */
            {   
                    wait(&rtn);
                     printf("child process return %d\n", rtn);
             }
      }
}
               

这份代码暂时没有看懂

 

三 等待进程

父进程可以使用系统调用wait()来等待它的一个子进程结束。

参数指定了父进程等待的子进程。

 

wait()

函数原型为: pid_t   wait(*status);

 

  进程一旦调用wait,就立即阻塞自己。然后通过wait函数,不断搜寻当前进程是否有结束退出

的子进程。如果当前进程有任意一个子进程结束并退出,那么wait就搜集该子进程的信息,参数保

存到参数status中,如果没有,那么当前进程阻塞直到有一个出现为止。

 

waitpid

其功能是等待指定的子进程结束

函数原型为pid_t   waitpid(pid_t pid ,int *status,int options)

 

 

 

#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
int main( void )
{    pid_t childpid;
      int status;
      childpid = fork();
      if (childpid == -1 )
     {     perror("fork()");
           exit(EXIT_FAILURE);
     }   
else if (childpid ==0)
   {   puts("In child process");
        sleep(3); //让子进程睡眠3秒
        printf("\t child pid = %d\n", getpid());
        printf("\t child ppid = %d\n", getppid());
        exit(EXIT_SUCCESS);      }
  else 
   {   waitpid(childpid, &status, 0);
        puts("In parent process");
        printf("\t parent pid = %d\n", getpid());
        printf("\t parent ppid = %d\n", getppid());
        printf("\t child process exited with status %d \n", status);        }
  exit(EXIT_SUCCESS);    }

 

 运行结果,

in child process

    child pid=1817

    child ppid=1816

in parent process

    fa pid=1816

    fa ppid=1730

    child process exit with status 0

 

可见尽管子进程wait了,由于父进程的阻塞,还是让子进程先完成。

 

四 终止进程

  使用void exit(int status);

结束本进程,停止之后的所有操作,清除包括PCB在内的各种数据结构。

 

 

五 linux的进程调度

Linux的进程调度基于优先级

 

linux进程分为普通进程和实时进程,后者优先级更高。

普通进程一律采用基于动态优先级的轮转法。SCHED-OTHER

(感觉像是时间多的先运行,运行到时间少了优先级不如别人了再让出来,,不是的

运行到死,然后再RUN_queue挑选优先级最大的)

 

 

实时进程采用 时间片轮转 和 先进先出 策略。SCHED-RR SCHED-FIFO

进程类型用policy域表示,通过系统调用sys_sched_setscheduler()更改,确定linux进程的类型。

 

可以参考前面的task_struct中有关进程调度信息的数据结构。

 

进程的优先权

由priority和rt_priority确定。

普通进程由counter决定,实时进程取决于rt_priority。

 

priority域是调度管理器分配给进程的优先级,可以renice系统调用改变。

 rt_priority同样可以系统调用改变,且优先级比priority高。

counter是进程运行允许的时间,开始被赋予priority(优先级)的值,,是的,这也符合运行到死的规则。

 

(猜测rt_priority是一个相对优先级,,也被赋予counter作为运行时间)

 

调度管理器的功能

 

 六 Linux内核的调度算法

 

 

七 进程的虚拟内存

数据结构

 

 

 

包含指向进程页表的指针以及指向vm_area_struct链表的指针

 

vm_area_struct链表详解

可见这是个链表。

  Linux中,进程虚拟内存空间管理的基本单位是虚拟存储区域。每个区域都用一

个vm_area_struct结构来描述。

  可见描述了开始与结束区域。

  还有存取权限,以及一组内存操作函数。

 

  为了加快存取,Linux把vm_area_struct组合成AVL树。

 

  当进程请求分配虚拟内存的时候,linux不直接分配物理内存,而是创建一个

vm_area_struct,来描述此虚拟内存区域。

八 进程访问的文件

fs_struct进程所在文件系统信息

files_struct进程打开的文件的信息

 

 两个字段

 

 最多包含256个file文件

每个file结构用来描述一个被当前进程所使用的文件。

每打开一个新文件,file一个就指向这个

 

 标准文件描述符

 

 

posted @ 2019-12-08 23:13  TheDa  阅读(668)  评论(0编辑  收藏  举报