实验二 进程控制
1.实验目的:
加深对进程概念的理解,明确进程和程序的区别。
掌握Linux系统中的进程创建,管理和删除等操作。
熟悉使用Linux下的命令和工具,如man, find, grep, whereis, ps, pgrep, kill, ptree, top, vim, gcc,gdb, 管道|等。
2.基础知识:
进程的创建
Linux中,载入内存并执行程序映像的操作与创建一个新进程的操作是分离的。将程序映像载入内存,并开始运行它,这个过程称为运行一个新的程序,相应的系统调用称为exec系统调用。而创建一个新的进程的系统调用是fork系统调用。
exec系统调用
#include <unistd.h>
int execl (const char *path, const char *arg,…);
execl()将path所指路径的映像载入内存,arg是它的第一个参数。参数可变长。参数列表必须以NULL结尾。
通常execl()不会返回。成功的调用会以跳到新的程序入口点作为结束。发生错误时,execl()返回-1,并设置errno值。
例 编辑/home/kidd/hooks.txt:
int ret;
ret = execl (”/bin/vi”, ”vi”,”/home/kidd/hooks.txt”, NULL);
if (ret == -1)
perror (”execl”);
fork系统调用
#include <sys/types.h>
#include <unistd.h>
pid_t fork (void);
成功调用fork()会创建一个新的进程,它与调用fork()的进程大致相同。发生错误时,fork()返回-1,并设置errno值。
例:
pid_t pid;
pid = fork ();
if (pid > 0)
printf (”I am the parent of pid=%d!\n”, pid);
else if (!pid)
printf (”I am the baby!\n”);
else if (pid == -1)
perror (”fork”);
终止进程
exit()系统调用:
#include <stdlib.h>
void exit (int status);
进程挂起
pause() 系统调用:
int pause( void );
函数pause会把进程挂起,直到接收到信号。在信号接收后,进程会从pause函数中退出,继续运行。
wait(等待子进程中断或结束)
#include<sys/types.h>
#include<sys/wait.h>
pid_t wait (int * status);
wait()会暂时停止目前进程的执行,直到有信号来到或子进程结束。
如果在调用 wait()时子进程已经结束,则wait()会立即返回子进程结束状态值。
子进程的结束状态值会由参数status返回,而子进程的进程识别码也会一起返回。
如果不在意结束状态值,则参数status可以设成 NULL。
VIM常用命令速查
3.实验题目:
根据课堂所学内容和基础知识介绍,完成实验题目。
1、打开一个vi进程。通过ps命令以及选择合适的参数,只显示名字为vi的进程。寻找vi进程的父进程,直到init进程为止。记录过程中所有进程的ID和父进程ID。将得到的进程树和由pstree命令的得到的进程树进行比较。
首先打开终端输入vi
打开新的终端输入 ps -ef
可以看到vi进程为2690 父进程为2665
父进程搜索顺序2665->2657->1909->1348->986->1
使用pstree -p所得为
两种方法所得相同
2、编写程序,首先使用fork系统调用,创建子进程。在父进程中继续执行空循环操作;在子进程中调用exec打开vi编辑器。然后在另外一个终端中,通过ps –Al命令、ps aux或者top等命令,查看vi进程及其父进程的运行状态,理解每个参数所表达的意义。选择合适的命令参数,对所有进程按照cpu占用率排序。
首先编写程序fork.c
代码
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
int main()
{
int pid;
pid=fork();
switch(pid)
{
case -1:
printf(“fork error!\n”);
exit(1);
case 0:
execl("/usr/bin/vi",“vi”,"/home/warmllll/Desktop",NULL);
printf(“fail!\n”);
exit(1);
default:
for (int i=0;;i++){};
printf(“vi successed!\n”);
exit(1);
}
}
查阅资料可知
ps el各相关信息的意义为:
F 代表这个程序的旗标 (flag), 4 代表使用者为 superuser;
S 代表这个程序的状态 (STAT);
#UID 代表执行者身份
PID 进程的ID号!底下的 PPID 则父进程的ID;
C CPU 使用的资源百分比
RI指进程的执行优先权(Priority的简写),其值越小越早被执行;
NI 这个进程的nice值,其表示进程可被执行的优先级的修正数值。
ADDR 这个是内核函数,指出该程序在内存的那个部分。如果是个执行
SZ 使用掉的内存大小;
WCHAN 目前这个程序是否正在运作当中,若为 - 表示正在运作;
TTY 登入者的终端机位置啰;
TIME 使用掉的 CPU 时间。
CMD 所下达的指令名称
Ps aus指令
• USER:该进程属于那个使用者账号
• PID :该进程的进程ID号。
• %CPU:该进程使用掉的 CPU 资源百分比;
• %MEM:该进程所占用的物理内存百分比;
• VSZ :该进程使用掉的虚拟内存量 (Kbytes)
• RSS :该进程占用的固定的内存量 (Kbytes)
• TTY :该进程是在那个终端机上面运作,若与终端机无关,则显示 ?,另外, tty1-tty6 是本机上面的登入者程序,若为 pts/0 等等的,则表示为由网络连接进主机的程序。
• STAT:该程序目前的状态,主要的状态有:
R :该程序目前正在运作,或者是可被运作;
S :该程序目前正在睡眠当中 (可说是 idle 状态啦!),但可被某些讯号(signal) 唤醒。
T :该程序目前正在侦测或者是停止了;
Z :该程序应该已经终止,但是其父程序却无法正常的终止他,造成 zombie (疆尸) 程序的状态
• START:该进程被触发启动的时间;
• TIME :该进程实际使用 CPU 运作的时间。
• COMMAND:该程序的实际指令
使用ps -AL截图
使用Ps aux截图
使用ps auxw --sort=%cpu查看进程按cpu占有率排序
占有了全部cpu
3、使用fork系统调用,创建如下进程树,并使每个进程输出自己的ID和父进程的ID。观察进程的执行顺序和运行状态的变化。
创建程序fork2.c代码为
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <time.h>
#include <stdarg.h>
#include <sys/types.h>
#define HASPRO -10
char *a;
int main()
{
pid_t p1,p2,p3,p4,p5;
int cnt=0;
while((p1=fork()) == -1);
if(!p1)
{
while((p2=fork()) == -1);
if(!p2)
{
while ((p4=fork())==-1);
if (!p4)
{
while(1)
{ sleep(1);
printf(" p4 de pid shi %d, fu p2 de pid shi%d\n",getpid(),getppid());
}
}
else
{
while ((p5=fork())==-1);
if (!p5)
{
while(1)
{sleep(1);
printf(" p5 de pid shi%d, fu p2 de pid shi%d\n",getpid(),getppid());
}
}
else
{
;
}
}
while(1)
{
sleep(1);
printf("p2 de pid shi %d, fu p1 de pid shi %d\n",getpid(),getppid());
}
}
else
{
while ((p3=fork())==-1);
if (!p3)
{
while(1)
{sleep(1);
printf("p3 de pid shi%d, fu p1 de pid shi %d\n",getpid(),getppid());
}
}
else
{
;
}
}
while(1)
{sleep(1);
printf("p1 de pid shi %d, parent de pid shi %d\n\n",getpid(),getppid());
}
}
return 0;
}
输出为
由此可见 p1的进程号为35265 其父为1909
P2进程号为35266其父为35265
P3进程号为35267其父为35265
P4进程号为35268其父为35266
P5进程号为35269其父为35266
4、修改上述进程树中的进程,使得所有进程都循环输出自己的ID和父进程的ID。然后终止p2进程(分别采用kill -9 、自己正常退出exit()、段错误退出),观察p1、p3、p4、p5进程的运行状态和其他相关参数有何改变。
通过第三题可知p2进程号为35266
1、使用kill-9 35266结束进程p2
’
使用ps -ef查看进程
可以看到p4与p5的父进程变成了1909和p1相同。
2、自己正常退出
在p2中加入exit(0);
可以看到p4与p5的父进程变成了1909和p1相同。
两种方法所得相同
github 链接:https://github.com/warmllll/-