• 【Linux】进程控制与进程调度


    Linux进程介绍 

    进程的基本概念

    Linux是多用户、多任务的操作系统。在这样的环境中,各种计算机资源的分配和管理都是以进程为单位进行的。

    Linux操作系统包括三种不同类型的进程:

    1)交互进程:一种由Shell启动的进程。交互进程既可在前台进行,也可在后台运行。前者称为前台进程,后者为后台进程。前台进程可与用户通过Shell进行交互。

    2)批处理进程:与终端没有联系,是系列进程,即多个进程按照指定的方式执行。

    3)守护进程:运行在后台的一种特殊进程,在系统启动时启动,并在后台运行。守护进程本身不在屏幕上显示任何信息,但会在后台悄悄地为用户服务,例如运行的网络服务程序。

    描述进程的数据结构

    在操作系统中,进程是指运行中的程序实体。此外还包括这个运行的程序所占据的所有系统资源,如CPU、设备、内存、网络资源等。Linux中,可以用ps命令得到当前系统中进程的列表。

    在Linux中每个进程在被创建时都会分配一个结构体 task_struct,即进程控制块PCB。

    task_struct结构体虽然庞大而复杂,但其中信息可以归为以下几类:

    1)标识信息:唯一标识一个进程

    2)状态信息

            1.运行:进程处于运行状态或者准备运行状态。

            2.等待:进程在等待一个事件或资源。又分为两类,可中段(可被信号中断),不可中断(由于硬件原因而等待)

            3.停止:进程处于停止状态。

            4.跟踪:进程正在被跟踪。

            5.僵死:进程已终止。

            6.死亡:表示进程处于退出过程,它所占的所有资源都会被回收。

    3)调度信息:调度进程所需要的信息,如:进程类型、优先级、允许进程执行的时间片。

    Linux进程调度介绍

    进程调度就是进程程序按照一定的策略,动态地把CPU分配给处于就绪队列中的某个进程。

    Linux操作系统中存在两类进程,即普通进程和实时进程。任何实时进程的优先级都高于普通进程的优先级。

    普通进程用nice值得表示优先级,取值范围为[-20,19],默认为0。越低的nice值代表越高的优先级,越高优先级的普通进程有越高的执行时间。

    实时优先级是可配置的,默认范围是[0,99]。与nice值相反,越高的实时优先级数值代表越高的优先级。 

    进程控制函数介绍

    在Linux操作系统中,fork()函数用来创建一个新的进程,exec函数用来启动另外的程序以取代当前运行的进程。

    1.创建进程

    在Linux中,创建进程的常见方法是使用fork函数从已经存在的进程(父进程)中创建一个新进程(子进程)。子进程是父进程的副本,子进程和父进程使用相同的代码包;子进程复制父进程的数据与堆栈空间,并继承父进程的用户代码等。由于子进程完全复制了父进程 ,因此父子进程会运行同一个程序。

    int fork();

    返回值意义如下:

    大于等于0:正确返回

            1)等于0,当前是子进程,从子进程返回进程ID值

            2)大于0,表示当前进程是父进程,从父进程返回进程ID值

    小   于    0 :错误返回,表示进程创建失败;原因:进程数达到上限/内存不足

    子进程虽然继承了父类的一切数据,当子进程一旦开始运行,就会和父进程分开。子进程拥有自己的ID、资源、计时器等,与父进程之间不再共享数据。

    2.管理进程标识符

    1. int getpid();    //取得当前进程的标识符
    2. int getppid(); //取得当前进程的父进程ID
    3. int getpgrp(); //取得当前进程的组标识符
    4. int getpgid(int pid); //将当前进程的组标识符改为当前进程的ID

    3.加载新的进程映像

    execlp函数使用 - erictanghu - 博客园 (cnblogs.com)

    execlp()会从PATH 环境变量所指的目录中查找符合参数file的文件名,找到后便执行该文件,然后将第二个以后的参数当做该文件的argv[0]、argv[1]……最后一个参数必须用空指针(NULL)作结束。如果用常数0来表示一个空指针,则必须将它强制转换为一个字符指针,否则将它解释为整形参数,如果一个整形数的长度与char * 的长度不同,那么exec函数的实际参数就将出错。如果函数调用成功,进程自己的执行代码就会变成加载程序的代码,execlp()后边的代码也就不会执行了.

    4.wait函数

    wait()和waitpid()的一些理解_wait(null)与wait(0)区别-CSDN博客

    wait()系统调用挂起调用的执行进程,直到其子进程之一终止。

    5.终止进程执行

    void exit(int status);

    一个进程自我终止后,将释放所占资源并通知父进程可以删除它,此时它处于僵死状态。参数status是调用进程终止执行时传递给其父进程的值。

    实践

    1. 1.#include 
    2. 2.#include 
    3. 3.#include 
    4. 4.#include 
    5. 5.#include 
    6. 6.#include
    7. 7.int main()
    8. 8.{
    9. 9.   pid_t childpid; //放子进程的id
    10. 10.   int retval;     //设置的子进程的返回值
    11. 11.   int status;     //子进程向父进程提供的退出状态
    12. 12.
    13. 13.   childpid=fork();//创建新进程
    14. 14.   if(childpid>=0//创建成功
    15. 15.   {
    16. 16.      if (childpid==0//说明当前进程就是子进程
    17. 17.      {
    18. 18.        printf("CHILD: I am the child process! \n");
    19. 19.        printf("CHILD: Here's my PID: %d\n"getpid());//getpid获得当前进程id
    20. 20.        printf("CHILD: My parent's PID is: % d\n"getppid());//grtppid获得父进程id
    21. 21.        printf("CHILD: The value of fork return is: % d\n", childpid);//fork的返回值
    22. 22.        printf("CHILD: Sleep for 1 second...\n");
    23. 23.        sleep(1);//让当前进程睡眠1秒
    24. 24.
    25. 25.        printf("CHILD: Enter an exit value (0~255): ");
    26. 26.        scanf("%d",&retval);//设置子进程返回值
    27. 27.        printf("CHILD: Goodbye! \n"); 
    28. 28.        exit(retval); //子进程退出,退出值为刚刚设置的
    29. 29.      }
    30. 30.      else  //当前进程为父进程
    31. 31.      {
    32. 32.        printf("PARENT: I am the parent process! \n");
    33. 33.        printf("PARENT: Here's my PID: %d\n"getpid());//当前进程id
    34. 34.        printf("PARENT: The value of my child's PID is: %d\n", childpid);//子进程id
    35. 35.
    36. 36.        printf("PARENT: I will now wait for my child to exit.\n");
    37. 37.        wait(&status); //等待子进程运行结束,并保存其状态
    38. 38.        printf("PARENT: Child's exit code is: %d\n"WEXITSTATUS(status));//输出子进程的返回值
    39. 39.
    40. 40.        printf("PARENT: Goodbye! \n"); 
    41. 41.        exit(0); //父进程退出
    42. 42.      }
    43. 43.  }
    44. 44.  else //创建失败
    45. 45.  {
    46. 46.     perror("fork error!"); /*display error messsage*/
    47. 47.     exit(0);
    48. 48.   }
    49. 49.}
    1. 1.#include
    2. 2.#include
    3. 3.#include
    4. 4.#include
    5. 5.int main()
    6. 6.{
    7. 7.   pid_t  pid;
    8. 8. pid = fork();//创建新进程
    9. 9. if (pid < 0) { //进程创建失败
    10. 10.  fprintf(stderr, "Fork Failed");//输出内容到文件
    11. 11.  return 1;
    12. 12. }
    13. 13. else if (pid == 0) { //当前是子进程
    14. 14.                execlp("/bin/ls","ls",NULL);
    15. 15.  //execlp()会从PATH 环境变量所指的目录中查找符合参数file的文件名,找到后便执行该文件,然后将第二个以后的参数当做该文件的argv[0]、argv[1]……,最后一个参数必须用空指针(NULL)作结束。如果用常数0来表示一个空指针,则必须将它强制转换为一个字符指针,否则将它解释为整形参数,如果一个整形数的长度与char * 的长度不同,那么exec函数的实际参数就将出错。如果函数调用成功,进程自己的执行代码就会变成加载程序的代码,execlp()后边的代码也就不会执行了.
    16. 16. }
    17. 17. else { //父进程
    18. 18.  wait (NULL);
    19. 19.  //wait()系统调用挂起调用的执行进程,直到其子进程之一终止。
    20. 20.  printf ("Child Complete");
    21. 21. }
    22. 22.        return 0;
    23. 23.}

    模拟调度算法 

    1. 1.#include "stdio.h" 
    2. 2.#include  
    3. 3.#define getpch(type) (type*)malloc(sizeof(type)) 
    4. 4.
    5. 5.//模拟进程调度算法
    6. 6.struct pcb { /* 定义进程控制块PCB */ 
    7. 7.  char name[10]; //进程名
    8. 8.  char state;    //进程状态:"W"-就绪态,"R"-运行态,F完成
    9. 9.  int nice;      //进程优先级
    10. 10.  int ntime;     //需要运行时间
    11. 11.  int rtime;     //已经运行的时间
    12. 12.  struct pcb* link;  
    13. 13.}*ready=NULL,*p; 
    14. 14.typedef struct pcb PCB; 
    15. 15.
    16. 16.char sort() /* 建立对进程进行优先级排列函数,优先数大者优先*/ 
    17. 17.{ //
    18. 18.  PCB *first, *second; 
    19. 19.  int insert=0
    20. 20.  if((ready==NULL)||((p->nice)>(ready->nice)))/*优先级最大者,插入队首*/ 
    21. 21.  { 
    22. 22.    p->link=ready; 
    23. 23.    ready=p; 
    24. 24.  } 
    25. 25.  else /* 进程比较优先级,插入适当的位置中*/ 
    26. 26.  { 
    27. 27.    first=ready; 
    28. 28.    second=first->link; 
    29. 29.    while(second!=NULL
    30. 30.    { 
    31. 31.      if((p->nice)>(second->nice)) /*若插入进程比当前进程优先数大,*/ 
    32. 32.      { /*插入到当前进程前面*/ 
    33. 33.        p->link=second; 
    34. 34.        first->link=p; 
    35. 35.        second=NULL
    36. 36.        insert=1
    37. 37.      } 
    38. 38.      else /* 插入进程优先数最低,则插入到队尾*/ 
    39. 39.      { 
    40. 40.        first=first->link; 
    41. 41.        second=second->link; 
    42. 42.      } 
    43. 43.    } 
    44. 44.    if(insert==0) first->link=p; 
    45. 45.  } 
    46. 46.
    47. 47.
    48. 48.char input() /* 建立进程控制块函数*/ 
    49. 49.{ //输入
    50. 50.  int i,num; 
    51. 51.  printf("\n 请输入被调度的进程数目:"); 
    52. 52.  scanf("%d",&num); 
    53. 53.  for(i=0;i
    54. 54.  { 
    55. 55.    printf("\n 进程号No.%d:",i); 
    56. 56.    p=getpch(PCB); 
    57. 57.    printf("\n 输入进程名:"); 
    58. 58.    scanf("%s",p->name); 
    59. 59.    printf(" 输入进程优先数:"); 
    60. 60.    scanf("%d",&p->nice); 
    61. 61.    printf(" 输入进程运行时间:"); 
    62. 62.    scanf("%d",&p->ntime); 
    63. 63.    printf("\n"); 
    64. 64.    p->rtime=0;
    65. 65.    p->state='W'
    66. 66.    p->link=NULL
    67. 67.    sort(); /* 调用sort函数*/ 
    68. 68.  } 
    69. 69.
    70. 70.
    71. 71.int space() //统计链表中结点个数
    72. 72.{ 
    73. 73.  int l=0; PCB* pr=ready; 
    74. 74.  while(pr!=NULL
    75. 75.  { 
    76. 76.    l++; 
    77. 77.    pr=pr->link; 
    78. 78.  } 
    79. 79.  return(l); 
    80. 80.
    81. 81.
    82. 82.char disp(PCB * pr) /*建立进程显示函数,用于显示当前进程*/ 
    83. 83.{ 
    84. 84.  printf("\n qname \t state \t nice \tndtime\truntime \n"); 
    85. 85.  printf("%s\t",pr->name); 
    86. 86.  printf("%c\t",pr->state); 
    87. 87.  printf("%d\t",pr->nice); 
    88. 88.  printf("%d\t",pr->ntime); 
    89. 89.  printf("%d\t",pr->rtime); 
    90. 90.  printf("\n"); 
    91. 91.}
    92. 92.
    93. 93.char check() /* 建立进程查看函数 */ 
    94. 94.{ 
    95. 95.  PCB* pr; 
    96. 96.  printf("\n **** 当前正在运行的进程是:%s",p->name); /*显示当前运行进程*/ 
    97. 97.  disp(p); //显示进程信息
    98. 98.  pr=ready; 
    99. 99.  if (pr!=NULL
    100. 100.    printf("\n ****当前就绪队列状态为:"); /*显示就绪队列状态*/
    101. 101.  else 
    102. 102.    printf("\n ****当前就绪队列状态为: 空\n"); /*显示就绪队列状态为空*/
    103. 103.  while(pr!=NULL
    104. 104.  { 
    105. 105.    disp(pr); 
    106. 106.    pr=pr->link; 
    107. 107.  } 
    108. 108.
    109. 109.
    110. 110.char destroy() /*建立进程撤消函数(进程运行结束,撤消进程)*/ 
    111. 111.{ 
    112. 112.  printf(" 进程 [%s] 已完成.\n",p->name); 
    113. 113.  free(p); 
    114. 114.}
    115. 115. 
    116. 116.char running() /* 建立进程就绪函数(进程运行时间到,置就绪状态*/ 
    117. 117.{ 
    118. 118.  (p->rtime)++; //运行时间增加
    119. 119.  if(p->rtime==p->ntime) //达到要求的时间
    120. 120.  destroy(); //完成进程并释放
    121. 121.  else 
    122. 122.  { 
    123. 123.    (p->nice)--; //优先级减少
    124. 124.    p->state='W'//改状态
    125. 125.    sort(); //按优先级重新排序
    126. 126.  } 
    127. 127.
    128. 128.
    129. 129.int main() /*主函数*/ 
    130. 130.{ 
    131. 131.  int len,h=0
    132. 132.  char ch; 
    133. 133.  input(); 
    134. 134.  len=space(); //链表长度
    135. 135.  while((len!=0)&&(ready!=NULL)) //只要队列不空
    136. 136.  { 
    137. 137.    ch=getchar(); 
    138. 138.    h++; 
    139. 139.    printf("\n The execute number:%d \n",h); //输出当前正在处理的进程号
    140. 140.    p=ready; //换到下一个结点
    141. 141.    ready=p->link; //记录下一个结点
    142. 142.    p->link=NULL//删除连接
    143. 143.    p->state='R'//更新状态
    144. 144.    check(); //展示进程数
    145. 145.    running(); //运行进程
    146. 146.    printf("\n按任一键继续......"); 
    147. 147.    ch=getchar(); 
    148. 148.  } 
    149. 149.  printf("\n\n 所有进程已经运行完成!\n"); 
    150. 150.  ch=getchar(); 
    151. 151.}

  • 相关阅读:
    contos7中mongodb数据库无法备份与还原,数据库工具的安装
    CSS 几种常见的选择器
    springboot+vue音乐歌曲分享试听网站java
    Java高级
    银河麒麟4.0.2安装带有opengl的Qt5.12.9
    在qt的设计师界面没有QVTKOpenGLWidget这个类,只有QOpenGLWidget,那么我们如何得到QVTKOpenGLWidget呢?
    Unity边缘检测的数学原理和shader实现
    助推专精特新企业数字化的低代码
    linux常用命令
    使用docker搭建drogon windows10,linux,mac下开发环境
  • 原文地址:https://blog.csdn.net/m0_74183164/article/details/136656022