进程和程序:编写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

一个程序如何运行另一个程序?答案是程序通过调用exec家族函数:

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).
 */

4、如何创建一个进程

一个进程调用fork来复制自己。进程调用fork,当控制转移到内核中的fork代码后,内核做:

(1)分配新的内存块和内核数据结构

(2)复制原来的进程到新的进程

(3)向运行进程添加新的进程

(4)将控制返回给两个进程

man 2 fork
#include <unistd.h>
pid_t fork(void);

5、父进程和子进程之间如何通信(父进程如何等待子进程的退出)

进程调用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);

6、进程死亡:exit和_exit

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;
}

进程和程序:编写shell——《Unix/Linux编程实践教程》读书笔记(第8章),布布扣,bubuko.com

时间: 2024-08-02 02:49:41

进程和程序:编写shell——《Unix/Linux编程实践教程》读书笔记(第8章)的相关文章

终端控制和和信号——《Unix/Linux编程实践教程》读书笔记(第6章)

1.有些程序处理从特定设备来的数据.这些与特定设备相关的程序必须控制与设备的连接.Unix系统中最常见的设备是终端. 2.终端驱动程序有很多设置.各个设置的特定值决定了终端驱动程序的模式.为用户编写的程序通常需要设置终端驱动程序为特定的模式. 3.键盘输入分为3类,终端驱动程序对这些输入做不同的处理.大多数建代表常规数据,它们从驱动程序传输到程序.有些键调用驱动程序中的编辑函数.如果按下删除键,驱动程序将前一个字符从它的行缓冲中删除,并将命令发送到终端屏幕,使之从显示器中删除字符.最后,有些键调

I/O重定向和管道——《Unix/Linux编程实践教程》读书笔记(第10章)

1.I/O重定向的概念与原因 及 标准输入.输出的标准错误的定义 所以的Unix I/O重定向都基于标准数据流的原理.三个数据了分别如下: 1)标准输入--需要处理的数据流 2)标准输出--结果数据流 3)标准错误输出--错误消息流 概念:所以的Unix工具都使用文件描述符0.1和2.标准输入文件的描述符是0,标准输出的文件描述符是1,而标准错误输出的文件描述符则是2.Unix假设文件描述符0.1.2已经被打开,可以分别进行读写操作. 通常通过shell命令行运行Unix系统工具时,stdin.

系统调用操作文件——《Unix/Linux编程实践教程》读书笔记

1.who命令通过读系统日志的内容显示当前已经登录的用户. 2.Unix系统把数据存放在文件中,可以通过以下系统调用操作文件: open(filename, how) creat(filename, mode) read(fd, buffer, amt) write(fd, buffer, amt) lseek(fd, distance, base) close(fd) 3.进程对文件的读/写都要通过文件描述符,文件描述符表示文件和进程之间的连接. 4.每次系统调用都会导致用户模式和内核模式的切

目录与文件属性——《Unix/Linux编程实践教程》读书笔记

1.ls产生一个文件名的列表,它大致是这样工作的: open directory +-> read entry - end of dir? -+ |__ display file info | close directory <--------------+ 2.目录是一种特殊的文件,它的内容是文件和目录的名字.与普通文件不同的是,目录文件永远不会空,每个目录至少包含2个特殊的项,即 "."和"..",其中 "."不是当前目录,&qu

事件驱动编程——《Unix/Linux编程实践教程》读书笔记(第7章)

1.curses库 /* 基本curses函数 */ initscr(); // 初始化curses库和tty endwin(); // 关闭curses并重置tty refresh(); // 使屏幕按照你的意图显示 move(r, c); // 移动光标到屏幕的(r, c)位置 addstr(s); // 在当前位置画字符串s addch(c); // 在当前位置画字符c clear(); // 清屏 standout(); // 启动standout模式(一般使屏幕反色) standend

Unix/Linux编程实践教程

execvp在程序中启动新程序: 用fork创建新进程: forkdemo2代码: 测试fork的时候参考<Linux权威指南>阅读笔记(3)  使用了patch: [[email protected] programming]# diff -c forkdemo2.c forkdemo2_new.c > forkdemo2.patch [[email protected] programming]# patch < forkdemo2.patch patching file fo

Unix/Linux编程实践教程(0:)

本来只打算读这本书socket等相关内容,但书写得实在好,还是决定把其余的内容都读一下. 阅读联机帮助的一个示例: open系统调用: read系统调用: Unix的time: 上面的printf可以看到,一个临时的char* 指针也可以+4,希望查看ctime函数里面是否有malloc,如果有的话由谁来释放内存???没有的话为什么可以指针操作. 为解决上述疑惑,通过查看http://www.cplusplus.com/reference/ctime/ctime/以及及http://www.cp

Unix/Linux编程实践教程(三:代码、测试)

测试logfilec.c的时候,有个sendto(sock,msg,strlen(msg),0,&addr,addrlen),编译时提示: logfilec.c:30: warning: passing argument 5 of ‘sendto’ from incompatible pointer type 但是书上是这样写的,在stackoverflow搜了一下,原来是: 需要进行一个转换.

《Linux/Unix系统编程手册》读书笔记7 (/proc文件的简介和运用)

<Linux/Unix系统编程手册>读书笔记 目录 第11章 这章主要讲了关于Linux和UNIX的系统资源的限制. 关于限制都存在一个最小值,这些最小值为<limits.h>文件中的常量. 通过cat 命令查看: [email protected]:~/Code/tlpi$ cat /usr/include/limits.h /* Copyright (C) 1991, 1992, 1996, 1997, 1998, 1999, 2000, 2005 Free Software