⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 p6-13-20.c

📁 SUN Solaris8平台下进程间通信
💻 C
字号:
#include <termios.h>#include <unistd.h>#include <sys/wait.h>#include <stdio.h>#include "err_exit.h" /* 一个 process 代表一个进程.  */ typedef struct process {     struct process *next;       /* next process in pipeline */     char **argv;                /* for exec */     pid_t pid;                  /* process ID */     char completed;             /* true if process has completed */     char stopped;               /* true if process has stopped */     int status;                 /* reported status value */ } process;/* 一个job 代表由管道连接的若干个进程组成的一个作业.  */typedef struct job{    struct job *next;           /* next active job */    char *command;              /* command line, used for messages */    process *first_process;     /* list of processes in this job */    pid_t pgid;                 /* process group ID */    char notified;              /* true if user told about stopped job */    struct termios tmodes;      /* saved terminal modes */    int jstdin, jstdout, jstderr;  /* standard i/o channels */} job;void put_job_in_background(job *, int);void put_job_in_foreground(job *, int);void wait_for_job (job *);void format_job_info (job *, const char *);/* 活跃的作业组成链表。这是其表头 */    job *first_job = NULL;/* 查找用PGID指明的活跃作业.  */job * find_job(pid_t pgid){    job *j;    for (j = first_job; j; j = j->next)        if (j->pgid == pgid)            return j;    return NULL;}/* 若该作业中的所有进程均已暂停或完成,返回真值。 */int job_is_stopped (job *j){    process *p;    for (p = j->first_process; p; p = p->next)        if (!p->completed && !p->stopped)           return 0;    return 1;}/* 若该作业中的所有进程均已完成,返回真值。*/int job_is_completed (job *j){    process *p;    for (p = j->first_process; p; p = p->next)        if (!p->completed)           return 0;    return 1;}pid_t shell_pgid;     struct termios shell_tmodes;int shell_terminal;int shell_is_interactive;/* 保证该shell 在开始工作之前是交互运行的前台作业 */void init_shell(void){    /* 检查是否交互运行,即判定与STDIN_FILENO相连的是否终端 */    shell_terminal = STDIN_FILENO;    shell_is_interactive = isatty (shell_terminal);    if (shell_is_interactive) {  /* 是交互运行shell */        /* 检查是否在前台运行,如果进程组ID不同于控制终端的进程组ID则是在后台运行,         因而必须发送停止信号,此过程循环直至用户将自己放置于前台 */        while (tcgetpgrp (shell_terminal) != (shell_pgid = getpgrp ()))             kill (- shell_pgid, SIGTTIN);        /* 忽略交互和作业控制信号 */        signal (SIGINT, SIG_IGN);        signal (SIGQUIT, SIG_IGN);        signal (SIGTSTP, SIG_IGN);        signal (SIGTTIN, SIG_IGN);        signal (SIGTTOU, SIG_IGN);        signal (SIGCHLD, SIG_IGN);        /* 设置shell的进程组 */        shell_pgid = getpid ();        if (setpgid (shell_pgid, shell_pgid) < 0)            error_exit ("Couldn't put the shell in its own process group");        /* 抢夺控制终端 */        tcsetpgrp (shell_terminal, shell_pgid);        /* 保存shell的缺省终端属性以便退出时恢复 */        tcgetattr (shell_terminal, &shell_tmodes);    }}void launch_process (process *p, pid_t pgid, int infile,                      int outfile, int errfile, int foreground){    pid_t pid;    if (shell_is_interactive) {        /* 放置该进程至进程组。因为竞争的缘故,子进程和shell二者都必须这样做 */        pid = getpid();        if (pgid == 0)          /*  pgid 为0表示这是进程组的第一个进程 */                   pgid = pid;         /*  使它成为进程组组长 */        setpgid(pid, pgid);        /*若是前台进程,给进程组控制终端。*/         if (foreground)            tcsetpgrp (shell_terminal, pgid);        /* 设置作业控制信号的句柄回到缺省 */        signal (SIGINT, SIG_DFL);        signal (SIGQUIT, SIG_DFL);        signal (SIGTSTP, SIG_DFL);        signal (SIGTTIN, SIG_DFL);        signal (SIGTTOU, SIG_DFL);        signal (SIGCHLD, SIG_DFL);    }    /* 设置新进程的标准I/O通道 */    if (infile != STDIN_FILENO) {       /* 管道输入情形  */        dup2 (infile, STDIN_FILENO);    /* 复制管道描述字至标准输入描述字 */        close (infile);                 /* 关闭管道描述字,以下类似 */    }    if (outfile != STDOUT_FILENO) {        dup2 (outfile, STDOUT_FILENO);        close (outfile);    }    if (errfile != STDERR_FILENO) {        dup2 (errfile, STDERR_FILENO);        close (errfile);    }    /* 执行新进程 */    execvp (p->argv[0], p->argv);    err_exit("execvp");}void launch_job (job *j, int foreground){    process *p;    pid_t pid;    int mypipe[2], infile, outfile;    infile = j->jstdin;      /* 第一个进程的标准输入由命令扫描程序指定 */    for (p = j->first_process; p; p = p->next) {         /* 有下一个进程,则是用管道连接的。建立管道            使得这个进程的输出成为下一个进程的输入 */        if (p->next) {                   if (pipe (mypipe) < 0)                 err_exit ("pipe");            outfile = mypipe[1];        }        else            outfile = j->jstdout;        /* Fork 子进程 */        if ((pid = fork ()) < 0)                  err_exit ("fork");        if (pid == 0)          /* 子进程 */             launch_process (p, j->pgid, infile,                                outfile, j->jstderr, foreground);        else{                    /* 父进程 */              /* 放置该进程至进程组。因为竞争的缘故,子进程和shell二者都必须这样做 */            p->pid = pid;            if (shell_is_interactive) {                if (!j->pgid)                    j->pgid = pid;      /* 记录作业的进程组ID */                setpgid (pid, j->pgid);            }        }         /* 建立管道之后的清理工作 */        if (infile != j->jstdin)            close(infile);        if (outfile != j->jstdout)            close(outfile);        infile = mypipe[0];    /* 下一个进程的标准输入是这个进程的管道输出 */         }    format_job_info(j, "launched");  /* 格式化输出作业状态信息 */    if (!shell_is_interactive)        wait_for_job(j);    else if (foreground)        put_job_in_foreground (j, 0);    else        put_job_in_background (j, 0);}void put_job_in_foreground (job *j, int cont){    /*  放置该作业于前台.  */    tcsetpgrp (shell_terminal, j->pgid);    /*  如果有必要,向该作业发送一继续信号.  */    if (cont) {        tcsetattr (shell_terminal, TCSADRAIN, &j->tmodes);        if (kill (- j->pgid, SIGCONT) < 0)            perror ("kill (SIGCONT)");    }    /*  等待它的报告.  */    wait_for_job(j);    /*  使shell重新回到前台.  */    tcsetpgrp (shell_terminal, shell_pgid);    /*  恢复shell的终端方式.  */    tcgetattr(shell_terminal, &j->tmodes);    tcsetattr(shell_terminal, TCSADRAIN, &shell_tmodes);}/* 放置一个作业至后台。如果cont 参数为真,则向进程组发送一个SIGCONT信号以唤醒它 */void put_job_in_background (job *j, int cont){    /* 若有必要,向该作业发送继续信号。*/    if (cont)       if (kill(-j->pgid, SIGCONT) < 0)          perror("kill (SIGCONT)");}/* 保存由waitpid 返回的进程pid的状态。如果一切正常,返回0;否则非0.  */int mark_process_status (pid_t pid, int status){    job *j;    process *p;    if (pid > 0) {         /*  更新该进程的记录.  */        for (j = first_job; j; j = j->next)            for (p = j->first_process; p; p = p->next)               if (p->pid == pid) {                  p->status = status;                  if (WIFSTOPPED (status))  /* 记录停止状态 */                     p->stopped = 1;                  else{                     p->completed = 1;                     if (WIFSIGNALED (status))                        fprintf (stderr, "%d: Terminated by signal %d.\n",                                 (int) pid, WTERMSIG (p->status));                  }                  return 0;               }        fprintf (stderr, "No child process %d.\n", pid);        return -1;    }    else if (pid == 0 || errno == ECHILD)   /* 没有进程准备报告 */        return -1;    else {  /* 其它错误 */        perror ("waitpid");        return -1;    }}/* 检查状态信息可用的进程,不阻塞。 */void update_status (void){    int status;    pid_t pid;    do       pid = waitpid (WAIT_ANY, &status, WUNTRACED|WNOHANG);    while (!mark_process_status (pid, status));}/*  检查状态信息可用的进程, 阻塞直至给定作业中的所有进程均已报告。 */void wait_for_job (job *j){    int status;    pid_t pid;    do        pid = waitpid (WAIT_ANY, &status, WUNTRACED);    while (!mark_process_status (pid, status)              && !job_is_stopped (j) && !job_is_completed (j));}/* 格式化给用户查看的作业状态信息。*/void format_job_info (job *j, const char *status){    fprintf (stderr, "%ld (%s): %s\n", (long)j->pgid, status, j->command);}/* 提请用户注意暂停或终止的作业。从活跃作业表中删除已终止的作业。*/void do_job_notification (void){    job *j, *jlast, *jnext;    process *p;    /* 更新子进程的状态信息 */    update_status ();    jlast = NULL;    for (j = first_job; j; j = jnext) {        jnext = j->next;         /* 如果所有进程均已完成,告诉用户该作业已完成,然后,从活跃作业表中删除它*/        if (job_is_completed (j)) {            format_job_info (j, "completed");            if (jlast)               jlast->next = jnext;            else                first_job = jnext;            free_job (j);        }        /* 提请用户注意暂停的作业,标记它们使得以后不再做这一动作。  */        else if (job_is_stopped(j) && !j->notified) {            format_job_info (j, "stopped");            j->notified = 1;            jlast = j;        }        /* 对仍在运行的作业无任何动作。*/        else            jlast = j;    }}/* 标记被暂停的作业J已重新开始运行.  */void mark_job_as_running (job *j){    process *p;    for (p = j->first_process; p; p = p->next)         p->stopped = 0;    j->notified = 0;}/*  继续作业J.  */void continue_job (job *j, int foreground){    mark_job_as_running (j);    if (foreground)        put_job_in_foreground (j, 1);    else        put_job_in_background (j, 1);}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -