进程和程序:编写shell——《Unix/Linux编程实践教程》读书笔记(第8章)
1、Unix shell的功能
shell是一个管理进程和运行程序的程序。所有常用的shell都有3个主要功能:
(1)运行程序;
(2)管理输入和输出
(3)可编程
shell同时也是带有变量和流程控制的编程语言。
2、Unix的进程模型
一个程序是存储在文件中的机器指令序列,一般它是由编译器将源代码编译成二进制格式的代码。运行一个程序意味着将这些机器指令序列载入内存然后让处理器(CPU)逐条执行。在Unix术语中,一个可执行程序是一些机器指令机器数据的序列。一个进程是程序运行时的内存空间和设置。数据和程序存储在磁盘文件中,程序在进程中运行。
每个进程都有一个可以唯一标识它的数字,被称为进程ID,一般简称PID;同时也有一个父进程ID(PPID)。每个进程都与一个终端相连,都一个已运行的时间,有优先级,有niceness级别,有大小。。。
Unix系统中的内存分为系统空间和用户空间。进程存在于用户空间。
3、如何执行一个程序
shell打印提示符,用户输入指令,shell就运行这个命令,然后shell再次打印提示符——如此反复。
一个shell的主循环执行下面的4步:
(1)用户键入a.out
(2)shell建立一个新的进程来运行这个出现
(3)shell将程序从磁盘载入
(4)程序在它的进程中运行知道结束
即:
while (!end_of_input) get command execute command wait for command to finish
man 3 exec
#include <unistd.h> extern char **environ; int execl(const char *path, const char *arg, ...); int execlp(const char *file, const char *arg, ...); int execle(const char *path, const char *arg, ..., char * const envp[]); int execv(const char *path, char *const argv[]); int execvp(const char *file, char *const argv[]); int execvpe(const char *file, char *const argv[], char *const envp[]); /* * The exec() family of functions replaces the current process * image with a new process image. The functions described * in this manual page are front-ends for execve(2). */
一个进程调用fork来复制自己。进程调用fork,当控制转移到内核中的fork代码后,内核做:
(1)分配新的内存块和内核数据结构
(2)复制原来的进程到新的进程
(3)向运行进程添加新的进程
(4)将控制返回给两个进程
man 2 fork
#include <unistd.h> pid_t fork(void);
进程调用wait等待子程序的结束。系统调用wait做两件事。首先,wait暂停调用它的进程直到子进程介绍。然后,wait取得子进程结束时传给exit的值。这样wait执行两个操作:通知和通信。
wait的目的之一是通知父进程子进程结束运行了。它的第二个目的是告诉父进程子进程是如何结束的。一个进程以3中方式(成功、失败或死亡)之一结束。按照Unix惯例,成功的程序调用exit(0)或者从main函数中return 0;程序遇到问题而要退出调用exit时传给它一个非零的值。
父进程调用wait时传一个整型变量地址给函数。内核将子进程的u提出状态保存在这个变量中。如果子进程调用exit退出,那么内核把exit的返回值放到这个整型变量中;如果进程是被杀死的,那么内核将信号序号存放在这个变量中。这个整数由3部分组成——高8位记录退出值,低7位记录信号序号,第7位则用来指明发生错误并产生了内核映像(core dump)。
man 2 wait
#include <sys/types.h> #include <sys/wait.h> pid_t wait(int *status); pid_t waitpid(pid_t pid, int *status, int options); int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options);
exit是fork的逆操作,进程通过调用exit来停止运行。fork创建一个进程,exit删除一个进程。基本上是这样。
exit刷新所以的流,调用atexit和on_exit注册的函数,执行当前系统定义的其他与exit相关的操作。然后调用_exit。系统函数_exit是一个内核操作,这个操作处理所有分配给这个进程的内存,关闭所有这个进程打开的文件,释放所有内核用来管理和维护这个进程的数据结构。
那些已经死亡但是没有给exit赋值的进程被称为僵尸进程。
系统调用_exit终止当前进程并执行所有必须的清理工作:
(1)关闭所有文件描述符和目录描述符
(2)将该进程的PID置为init进程的PID
(3)如果父进程调用wait或waitpid来等待子进程结束,则通知父进程
(4)向父进程发送SIGCHLD信号
下图摘自书本,为shell的fork()、exec()和exit()循环
/* * prompting shell version 02 * * Solves the ‘one-shot‘ problem of version 01 * Uses execvp(), but fork()s first so that the * shell waits around to perform another command * New problem: shell cathes signals. Run vi, press ^C. */ #include <stdio.h> #include <stdlib.h> #include <signal.h> #include <string.h> #include <unistd.h> #define MAXARGS 20 /* cmdline args */ #define ARGLEN 100 /* token length */ int main(void) { char *arglist[MAXARGS+1]; /* an array of ptrs */ int numargs; /* index into array */ char argbuf[ARGLEN]; /* read stuff here */ void execute(char **); char *makestring(char *); /* malloc etc */ numargs = 0; while (numargs < MAXARGS) { printf("Arg[%d]?", numargs); if (fgets(argbuf, ARGLEN, stdin) && *argbuf != ‘\n‘) arglist[numargs++] = makestring(argbuf); else { if (numargs > 0) /* any args */ { arglist[numargs] = NULL; /* colse list */ execute(arglist); /* do it */ numargs = 0; /* and reset */ } } } return 0; } void execute(char **arglist) /* * use fork and execvp and wiat to do it */ { pid_t pid, exitstatus; /* of child */ pid = fork(); /* make new process */ switch (pid) { case -1: perror("fork falued"); exit(1); case 0: execvp(arglist[0], arglist); /* do it */ perror("execvp failed"); exit(1); default: while (wait(&exitstatus) != pid) ; printf("child exited with status %d, %d\n", exitstatus >> 8, exitstatus & 0377); break; } } char *makestring(char *buf) /* * trim off newline and create storage for the string */ { char *cp; buf[strlen(buf)-1] = ‘\0‘; /* trim newline */ cp = malloc(strlen(buf)+1); /* get memory */ if (cp == NULL) /* or die */ { fprintf(stderr, "no memory\n"); exit(1); } strcpy(cp, buf); /* copy chars */ return cp; }
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。