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.管理进程标识符
int getpid(); //取得当前进程的标识符
int getppid(); //取得当前进程的父进程ID
int getpgrp(); //取得当前进程的组标识符
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.#include <unistd.h>
2.#include <sys/types.h>
3.#include <errno.h>
4.#include <sys/wait.h>
5.#include <stdlib.h>
6.#include<stdio.h>
7.int main()
8.{
9. pid_t childpid; //放子进程的id
10. int retval; //设置的子进程的返回值
11. int status; //子进程向父进程提供的退出状态
12.
13. childpid=fork();//创建新进程
14. if(childpid>=0) //创建成功
15. {
16. if (childpid==0) //说明当前进程就是子进程
17. {
18. printf("CHILD: I am the child process! \n");
19. printf("CHILD: Here's my PID: %d\n", getpid());//getpid获得当前进程id
20. printf("CHILD: My parent's PID is: % d\n", getppid());//grtppid获得父进程id
21. printf("CHILD: The value of fork return is: % d\n", childpid);//fork的返回值
22. printf("CHILD: Sleep for 1 second...\n");
23. sleep(1);//让当前进程睡眠1秒
24.
25. printf("CHILD: Enter an exit value (0~255): ");
26. scanf("%d",&retval);//设置子进程返回值
27. printf("CHILD: Goodbye! \n");
28. exit(retval); //子进程退出,退出值为刚刚设置的
29. }
30. else //当前进程为父进程
31. {
32. printf("PARENT: I am the parent process! \n");
33. printf("PARENT: Here's my PID: %d\n", getpid());//当前进程id
34. printf("PARENT: The value of my child's PID is: %d\n", childpid);//子进程id
35.
36. printf("PARENT: I will now wait for my child to exit.\n");
37. wait(&status); //等待子进程运行结束,并保存其状态
38. printf("PARENT: Child's exit code is: %d\n", WEXITSTATUS(status));//输出子进程的返回值
39.
40. printf("PARENT: Goodbye! \n");
41. exit(0); //父进程退出
42. }
43. }
44. else //创建失败
45. {
46. perror("fork error!"); /*display error messsage*/
47. exit(0);
48. }
49.}
1.#include<stdio.h>
2.#include<unistd.h>
3.#include<sys/types.h>
4.#include<sys/wait.h>
5.int main()
6.{
7. pid_t pid;
8. pid = fork();//创建新进程
9. if (pid < 0) { //进程创建失败
10. fprintf(stderr, "Fork Failed");//输出内容到文件
11. return 1;
12. }
13. else if (pid == 0) { //当前是子进程
14. execlp("/bin/ls","ls",NULL);
15. //execlp()会从PATH 环境变量所指的目录中查找符合参数file的文件名,找到后便执行该文件,然后将第二个以后的参数当做该文件的argv[0]、argv[1]……,最后一个参数必须用空指针(NULL)作结束。如果用常数0来表示一个空指针,则必须将它强制转换为一个字符指针,否则将它解释为整形参数,如果一个整形数的长度与char * 的长度不同,那么exec函数的实际参数就将出错。如果函数调用成功,进程自己的执行代码就会变成加载程序的代码,execlp()后边的代码也就不会执行了.
16. }
17. else { //父进程
18. wait (NULL);
19. //wait()系统调用挂起调用的执行进程,直到其子进程之一终止。
20. printf ("Child Complete");
21. }
22. return 0;
23.}
模拟调度算法
1.#include "stdio.h"
2.#include <stdlib.h>
3.#define getpch(type) (type*)malloc(sizeof(type))
4.
5.//模拟进程调度算法
6.struct pcb { /* 定义进程控制块PCB */
7. char name[10]; //进程名
8. char state; //进程状态:"W"-就绪态,"R"-运行态,F完成
9. int nice; //进程优先级
10. int ntime; //需要运行时间
11. int rtime; //已经运行的时间
12. struct pcb* link;
13.}*ready=NULL,*p;
14.typedef struct pcb PCB;
15.
16.char sort() /* 建立对进程进行优先级排列函数,优先数大者优先*/
17.{ //
18. PCB *first, *second;
19. int insert=0;
20. if((ready==NULL)||((p->nice)>(ready->nice)))/*优先级最大者,插入队首*/
21. {
22. p->link=ready;
23. ready=p;
24. }
25. else /* 进程比较优先级,插入适当的位置中*/
26. {
27. first=ready;
28. second=first->link;
29. while(second!=NULL)
30. {
31. if((p->nice)>(second->nice)) /*若插入进程比当前进程优先数大,*/
32. { /*插入到当前进程前面*/
33. p->link=second;
34. first->link=p;
35. second=NULL;
36. insert=1;
37. }
38. else /* 插入进程优先数最低,则插入到队尾*/
39. {
40. first=first->link;
41. second=second->link;
42. }
43. }
44. if(insert==0) first->link=p;
45. }
46.}
47.
48.char input() /* 建立进程控制块函数*/
49.{ //输入
50. int i,num;
51. printf("\n 请输入被调度的进程数目:");
52. scanf("%d",&num);
53. for(i=0;i<num;i++)
54. {
55. printf("\n 进程号No.%d:",i);
56. p=getpch(PCB);
57. printf("\n 输入进程名:");
58. scanf("%s",p->name);
59. printf(" 输入进程优先数:");
60. scanf("%d",&p->nice);
61. printf(" 输入进程运行时间:");
62. scanf("%d",&p->ntime);
63. printf("\n");
64. p->rtime=0;
65. p->state='W';
66. p->link=NULL;
67. sort(); /* 调用sort函数*/
68. }
69.}
70.
71.int space() //统计链表中结点个数
72.{
73. int l=0; PCB* pr=ready;
74. while(pr!=NULL)
75. {
76. l++;
77. pr=pr->link;
78. }
79. return(l);
80.}
81.
82.char disp(PCB * pr) /*建立进程显示函数,用于显示当前进程*/
83.{
84. printf("\n qname \t state \t nice \tndtime\truntime \n");
85. printf("%s\t",pr->name);
86. printf("%c\t",pr->state);
87. printf("%d\t",pr->nice);
88. printf("%d\t",pr->ntime);
89. printf("%d\t",pr->rtime);
90. printf("\n");
91.}
92.
93.char check() /* 建立进程查看函数 */
94.{
95. PCB* pr;
96. printf("\n **** 当前正在运行的进程是:%s",p->name); /*显示当前运行进程*/
97. disp(p); //显示进程信息
98. pr=ready;
99. if (pr!=NULL)
100. printf("\n ****当前就绪队列状态为:"); /*显示就绪队列状态*/
101. else
102. printf("\n ****当前就绪队列状态为: 空\n"); /*显示就绪队列状态为空*/
103. while(pr!=NULL)
104. {
105. disp(pr);
106. pr=pr->link;
107. }
108.}
109.
110.char destroy() /*建立进程撤消函数(进程运行结束,撤消进程)*/
111.{
112. printf(" 进程 [%s] 已完成.\n",p->name);
113. free(p);
114.}
115.
116.char running() /* 建立进程就绪函数(进程运行时间到,置就绪状态*/
117.{
118. (p->rtime)++; //运行时间增加
119. if(p->rtime==p->ntime) //达到要求的时间
120. destroy(); //完成进程并释放
121. else
122. {
123. (p->nice)--; //优先级减少
124. p->state='W'; //改状态
125. sort(); //按优先级重新排序
126. }
127.}
128.
129.int main() /*主函数*/
130.{
131. int len,h=0;
132. char ch;
133. input();
134. len=space(); //链表长度
135. while((len!=0)&&(ready!=NULL)) //只要队列不空
136. {
137. ch=getchar();
138. h++;
139. printf("\n The execute number:%d \n",h); //输出当前正在处理的进程号
140. p=ready; //换到下一个结点
141. ready=p->link; //记录下一个结点
142. p->link=NULL; //删除连接
143. p->state='R'; //更新状态
144. check(); //展示进程数
145. running(); //运行进程
146. printf("\n按任一键继续......");
147. ch=getchar();
148. }
149. printf("\n\n 所有进程已经运行完成!\n");
150. ch=getchar();
151.}