《Unix/Linux编程实践教程》chapter8 进程和程序:编写命令解释器sh

chapter8 进程和程序:编写命令解释器sh

章节知识总结

一个程序时存储在文件中的机器指令序列。一般它是由编译器将源代码编译成二进制格式的代码。运行一个程序意味着将这个机器指令序列载入内存然后让处理器逐条执行这些指令。

每个进程都有一个可以唯一标识它的数字,被称为进程ID。

Unix系统中的内存分为系统空间用户空间。进程存在于用户空间。

建立一个进程有点像建立一个磁盘文件,内核要找到一些用来存放程序指令和数据的空闲内存页,内核还要建立数据结构来存放相应的内存分配情况和进程属性。

一个命令解释器sh的主要处理流程可以分为三步:

  1. shell用fork建立新进程
  2. exec在新进程中运行用户指定的程序
  3. shell用wait等待新进程结束

进程通过调用fork来建立新的进程。进程调用fork(),当控制转移到内核中的fork代码后,内核做:

  1. 分配新的内存块和内核数据结构
  2. 复制原来的进程到新的进程
  3. 向运行进程集添加新的进程
  4. 将控制返回给两个进程

程序调用execvp,可以使得一个程序运行另一个程序。execvp(file,argv)载入由file指定的程序到当前进程,然后试图运行它。execvp将以NULL结尾的字符串列表传给程序。execvp在环境变量PATH所指定的路径中查找file文件。

execvp的执行流程如下:

  1. 将指定的程序复制到调用它的进程
  2. 将指定的字符串数组作为argv[]传给这个程序
  3. 运行这个程序

进程可以调用wait等待子进程结束。系统调用wait做两件事:首先,wait暂停调用它的进程直到子进程结束;然后,wait取得子进程结束时传给exit的值(最终子进程会结束任何并调用exit(n),n是0到255的一个数字)。

在这里插入图片描述

exitfork的逆操作,进程通过调用exit来停止运行。fork创建一个进程,exit删除进程。

exit(n)将子进程的弥留之言n存放在内核直到这个进程的父进程通过wait系统调用取回这个值。如果父进程没有在等这个值,那么它将被保存在内核直到父进程调用wait,那时内核将通告这个父进程子进程的结束,并转达子进程的弥留之言。

  • exit(n)的弥留之言分为三部分:高8个bit是记录退出值,低7个bit是记录信号序号,另一个bit用来指明发生错误并产生了内核映像

在这里插入图片描述

_exit()主要包括以下这些操作:

  1. 关闭所有文件描述符合目录描述符
  2. 将该进程的PID置为init进程的PID
  3. 如果父进程调用waitwaitpid来等待子进程结束,则通知父进程
  4. 向父进程发送SIGCHLD(注意:就算父进程没有调用wait,内核也会向它发送SIGCHLD消息,一般对SIGCHLD消息的默认处理方法是忽略的)

如果父进程在子进程之前退出,那么子进程将能继续运行,而不会成为“孤儿”,它们将是init进程的“子女”。

系统调用
execvp
用途在指定路径中查找并执行一个文件
头文件#include<unistd.h>
函数原型result=execvp(const char* file,const char* argv[])
参数file:要执行的文件名 ; argv:字符串数组
返回值-1:如果出错
fork
用途创建进程
头文件#include<unistd.h>
函数原型pid_t result=fork(void)
参数没有
返回值-1:如果错误;0:返回到子进程;(fork()将子进程的进程ID传给父进程)
wait
用途等待进程结束
头文件#include<sys/types.h> #include<sys/wait.h>
函数原型pid_t result=wait(int* statusptr)
参数statusptr子进程的运行结果
返回值-1:遇到错误; pid:结束进程的进程id
sh简易实现(版本一)

sh简易实现如下(下一章会有更完整的实现):

#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<sys/wait.h>

#define MAXARGS 20
#define ARGLEN 100

char* makestr(char*);
void execute(char* arglist[]);

int main(){
    char* arglist[MAXARGS];
    char argbuf[ARGLEN];
    int numarg=0;
    while(numarg<MAXARGS){
        printf("Arg[%d]?",numarg);
        if(fgets(argbuf,ARGLEN,stdin)&&*argbuf !='\n')
            arglist[numarg++]=makestr(argbuf);
        else{
            if(numarg>0){
                arglist[numarg]=NULL;
                execute(arglist);
                numarg=0;
            }
        }
    }
    return 0;
}

void execute(char* arglist[])
{
    int pid=fork();
    int existstate;
    switch(pid){
        case -1: perror("fork"); exit(1);
        case 0:
            execvp(arglist[0],arglist);
            perror("execvp failed");
            exit(1);
        default:
            while(wait(&existstate)!=pid);
            printf("child exited with status %d,%d\n",existstate>>8,existstate&0377);   //0377: 00000000 11111111
    }
}

char* makestr(char* argbuf)
{
    argbuf[strlen(argbuf)-1]='\0';
    char* newbuf=malloc(strlen(argbuf)+1);
    if(newbuf==NULL) printf("no memory");
    strcpy(newbuf,argbuf);
    return newbuf;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值