Linux进程控制(编写命令解释器)
一、背景
之前在《计算机操作系统》这本书中已经多次接触了进程这一概念,而进程在操作系统中具体是做什么的呢?却没有实际的与进程发生过互动,因此也就没有实际的感受。对于我来说进程仅仅停留在概念的层面上:进程是程序运行时的内存空间和设置或者说进程就是程序的进行时。没有过实践可不好办,于是就打算进一步的理解进程,为进一步理解进程,就需要对进程进行控制。当然,这个报告中是一些较为简单的进行控制。
二、进程的理解
概念层面的进程这里就不说了。我讲讲实际一些的,自己理解的进程。
在命令行中输入ps,这里可以看到两个简单的进程的信息。给个参数-l就可以看到详细的进程信息。
S:status,表示这个进程的状态,S是说sleep,R是说running。
UID:进程的用户ID。
PID:进程的进程ID,这个很好理解,进程也需要唯一的数字来标识,和UID、GID引入方式类似。
PPID:父进程的ID,也就是调用这个进程的进程的ID号。从这里我就可以看到一个进程的父进程是谁。
PRI:prior,进程的优先级。
NI:进程的niceness级别,niceness级别越高这个进程就越往后排。
SZ:size,进程占用的内存大小。
WCHAN:进程睡眠的原因。刚才也说了,S列中的S是表示sleep睡眠的。这一列就说明了此进程睡眠的原因。
TIME:运行时间。
对以上这些名词也解释清楚了,可是一台计算机怎么也不至于就这么两个进程在运行吧。肯定是有很多很多的系统进程同时作用的结果。接着用-x参数就可以看到如下的系统进程。
系统进程实在太多。我只截取了前面一部分。系统进程大都没有与终端相连,他们在计算机启动的时候启动,而不需要在命令行中启动他们。
三、子进程的创建
子进程创建使用系统调用fork(),如此正在运行的进程会一分为二,原进程继续向下执行,而新创建的子进程也会从创建开始的位置继续向下执行。
而在背后内核做了许多事情,包括分配新的内存块和内核数据结构,复制原来的进程到新的进程,向运行进程集添加新的进程,将控制返回给两个进程。这样两个进程就两不相干的继续朝下运行(并非完全不相干,比如还有父子关系)。
四、父进程等待子进程运行完毕
此时使用系统调用wait(),这个时候父进程就会等待子进程直到子进程运行完毕了才会继续运行。这个说起来不直观。我写了一段等待程序,以此来理解之前的一些概念。
#include<stdio.h>
int main()
{
int newpid,newnewpid;
printf("I am a parent,my pid is:%d\n",getpid());
newpid=fork();
if(newpid==0)
{
printf("i am a son,my pid is:%d,and i wanna sleep 5s\n",getpid());
sleep(5);
newnewpid=fork();
if(newnewpid==0)
{
printf("i am a grandson,my pid is %d,and i wanna sleep 10s\n",getpid());
sleep(10);
}
else{
wait(NULL);
printf("I am a son,i wanna sleep 2s\n");
sleep(2);
}
}
else {
wait(NULL);
printf("I am a parent,i wanna sleep 4s\n");
sleep(4);
}
}
运行结果是这个样子的。实际上正如上面写的,子进程与孙子进程都睡眠了对应的时间。
另外,通过此程序也可以方便的理解之前说到的父子进程在进程ID方面的关系。
看到PID与PPID的数字,那么各个进程间的关系也就一目了然了。
五、程序调用
想在一个程序中调用另外一段程序使用库函数execvp。
六、命令解释器的实现
先来看看运行结果。
此时一直运行着a.out这一程序,而ls是a.out调用的子程序。
以下是实现的程序代码:
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<stdlib.h>
#define ARGLEN 100
#define ARGNUM 20
void execute(char *list[]);
char *makestring(char buf[]);
int main()
{
int argnum;
char argbuf[ARGLEN];
char *arglist[ARGNUM];
while(argnum<ARGNUM){
printf("arg[%d]:",argnum);
if(fgets(argbuf,ARGLEN,stdin) && *argbuf!='\n')
arglist[argnum++]=makestring(argbuf);
else if(argnum>0)
{
arglist[argnum]=NULL;
execute(arglist);
argnum=0;
}
}
}
void execute(char *list[])
{
int re_pid;
re_pid=fork();
if(re_pid==-1)
{
perror("fork error :");
exit(1);
}
else if(re_pid==0)
{
execvp(list[0],list);
exit(1);
}
else
{
wait(NULL);
}
}
char *makestring(char buf[])
{
char *cp;
void *malloc(size_t );
buf[strlen(buf)-1]='\0';
cp=malloc(strlen(buf)+1);
if(cp==NULL)
{
perror("no memory\n");
exit(1);
}
strcpy(cp,buf);
return cp;
}
七、总结
进程虽然是计算机中最重要的概念之一,但最为概念方面的进程是非常抽象与难以理解的,而自己进行简单的进程控制之后就发现进程其实并不很难理解。通过实践,学习到的概念也才会理解的更快更准。