zoukankan      html  css  js  c++  java
  • [APUE]第八章 进程控制

    第八章 进程控制

    引言:

     

    如何创建进程?执行程序?进程终止?

    讲述进程属性的各种ID---实际、有效、保存的用户和ID,他们是如何受到进程控制原语的影响。

    解释器文件和system函数,进程会计机制。

    进程标识符

    每一个进程都有一个非负整数表示的唯一进程ID,这个进程ID是唯一的,他的使用机制和文件描述符是不相同的。文件打开的都是最小的整数(未打开),进程ID是采用延迟重用算法。

    init 0表示关机 init 6表示重新启动

    ID位0的是一个交换进程,该进程是内核的一部分。

    ID为1的是init进程

    pid_t getpid() ;

    pid_t getppid();

    uid_t getuid();

    uid_t geteuid();

    gid_t getgid();

    gid_t getegid();

    fork

    创建进程fork,返回两次,子进程返回0,父进程返回子进程的ID,

    pid_t fork(); //出错返回-1

    fork的一个特性是父进程的所有打开的文件描述符都被复制到子进程中,父子进程的每一个相同的打开文件描述符共享一个文件表项。重定向父进程对的标准输出的时候,子进程的标准输出也重定向。

    子进程会复制父进程的数据空间。

    子进程会继承父进程的

    实际用户ID、有效用户ID、实际组ID、有效组ID

    附加组ID

    会话ID

    控制终端

    设置用户ID标志和设置组DI标志

    当前工作目录、根目录

    文件模式的创建屏蔽字

    子进程不会继承父进程的

    进程ID

    父进程设置的文件锁

    vfork

    pid_t vfork()

    在调用exec或者exit之前,子进程在父进程的地址空间运行,子进程修改的变量会修改父进程的值。还有一个特点是vfork保障子进程先运行,在他调用exec或者exit之后父进程才可能被调度运行。

    exit函数

    五种进程正常终止

    1  main函数中return返回

                                         2       调用exit()

                                         3       调用_Exit()、_exit()

                                         4       最后一个线程返回

                                         5       最后一个线程调用phread_exit()

    异常终止:

    1          调用abort

    2          进程收到某些信号,使进程退出

    3          最后一个线程对取消做出反应 pthread_cancel()

    不管进程如何终止,都会关闭文件描述符和释放他使用的存储器

    退出状态:是return value、exit(value) 中的value

    终止状态:是wait(int &value)中的value

    关于父子进程谁先终止的问题

    如果父进程先终止,那么子进程的父进程就变成init(pid是1)的进程,子进程终止的时候,init为它收尸。不会僵死占用空间。

    如果子进程先终止,那么父进程必须为子进程收尸(wait),不然子进程就会一直占用空间,导致出现僵死进程。

    子进程终止的时候,会向父进程发送SIGCHLD信号。这个时候可以对子进程进行处理。

    wait和waitpid可能的情况

    如果子进程正在运行,则阻塞

    如果没有子进程就直接出错返回

    如果子进程已终止,正等待父进程获取其终止状态,则取得该子进程的终止状态时立即返回。

    pid_t wait(int &status)
    pid_t waitpid(pid_t pid, int *status, intoptions)

    如果pid == 0 等待其组ID等于调用进程组ID的任意子进程

             pid== -1 等待任意子进程

             pid》0 等待pid位pid的子进程

             pid《0 等待pid为|pid|的子进程。

    exec*函数

    #include <unistd.h>
    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 *constargv[]);
    int execvp(const char *file, char *constargv[]);
    int execve(const char *path, char *constargv[], char *const envp[]);

    最终调用的是execve,其他都是在这个函数的基础上进行封装的。

    更改用户ID和组ID

    int setuid(uid_t uid)
    int setgid(gid_t gid)

    如果进程具有超级用户权限,那么设置成功。

    如果进程不具有超级用户权限,并且uid等于实际用户ID或者设置用户ID,那么仅仅能把有效用户ID设置为实际用户ID

    如果上面都不满足就直接返回-1.

    以上规则也适用于组ID


    man命令切换的原理

    man程序可能必须执行几个其它命令来处理包含要显示的手册页的文件。为了避免被欺骗以致运行错误的命令或覆写错误的文件,man命令必须在两个权限集里交换:运行man命令的用户和拥有man可执行文件的用户。下面的步骤会发生:


    1、假设man程序文件被用户名man拥有并设置了设置用户ID,当我们exec它时,我们有:真实用户ID=我们的用户ID;有效用户ID=man;保存的设置用户ID=man。 

    2、man程序访问所需的配置文件和手册页。这些文件被用户名man拥有,但是因为有效用户ID是man,所以文件访问被允许。 

    3、在man为我们执行任何一个程序时,它调用setuid(getuid())。因为我们不是超级用户进程,所以这只改变有效用户ID。我们有:真实用户ID=我们的用户ID(不变);有效用户ID=我们的用户ID;保存的设置用户ID=man(不变)。现在man进程用我们的用户ID作为它的有效用户 ID运行。这意味着我们只能访问我们有普通权限的文件。我们没有更多的权限。它可以为我们执行任何过滤器。 

    4、当过滤完成时,man调用setuid(euid),这里euid是用户名man的数值用户ID。(这是我们为什么需要保存的设置用户ID。)现在我们有:真实用户ID=我们的用户ID(不变);有效有用ID=man;保存的设置用户ID=man(不变)。 

    5、man程序现在可以在它的文件上进行操作,因为它的用效用户ID是man。 

    解释器文件、

    system函数、

    system调用fork execl waitpid这些函数其中fork、waitpid出错返回-1 execl出错返回127

    正常返回是执行shell的终止状态

    进程会计

    调用accton + filename 文件名称

    就会把进程执行完毕的一些信息(使用的CPU、用户ID和组ID、启动时间)记录到文件里面

    下面这个程序就是解析这个文件的。程序是固定的

    /*
     *=====================================================================================
     *
     *      Filename:  accton.cpp
     *
     *   Description:  accton
     *
     *       Version:  1.0
     *       Created:  2012年05月04日 21时13分45秒
     *      Revision:  none
     *       Compiler:  gcc
     *
     *        Author:  LeoK,
     *  Organization: 
     *
     *=====================================================================================
     */
    #include <stdlib.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/acct.h>
    #include <unistd.h>
    #include <errno.h>
    //#include "errorout.h"
    #define     err_sys  perror
    #define     err_quit perror
    #ifdef HAS_SA_STAT
    #define FMT "%-*.*s e = %6ld, chars =%7ld, stat = %3u; %c %c %c\n"
    #else
    #define FMT "%-*.*s e = %6ld, chars =%7ld, %c %c %c\n"
    #endif
     
    #ifndef HAS_ACORE
    #define ARORE 0
    #endif
     
    #ifndef HAS_AXSIG
    #define AXSIG 0
    #endif
     
    static unsigned long compt2ulong(comp_tcomptime)
    {  
     
             unsignedlong   val;
             int             exp;
     
             val= comptime & 0x1fff;                       //13-bit fraction
             exp= (comptime >> 13) & 7;                     //3 -bit exponent (0-7)
             while(exp-- >0)
                       val*=8;
             returnval;
    }
    int main(int argc,char *argv[]){
             structacct     acdata;
             FILE            *fp;
     
             if(argc!= 2)
                       err_quit("usage:pracct filename");
             if((fp=  fopen(argv[1],"r")) == NULL)
                       err_sys("can'topen accton " );
             while(fread(&acdata,sizeof(acdata), 1,fp) ==1){
                       printf(FMT,(int)sizeof(acdata.ac_comm)
                                         ,(int)sizeof(acdata.ac_comm), acdata.ac_comm
                                         ,compt2ulong(acdata.ac_etime), compt2ulong(acdata.ac_io)
    #ifdef HAS_SA_STAT
                                         ,(unsignedchar) acdata,ac_stat
    #endif
                                         ,acdata.ac_flag& ACORE ? 'D' : ' '
                                         ,acdata.ac_flag& AXSIG ? 'X' : ' '
                                         ,acdata.ac_flag& AFORK ? 'F' : ' '
                                         ,acdata.ac_flag& ASU   ? 'S' : ' '
                                  );
     
             }
             if(ferror(fp))
                       err_sys("readerror");
             exit(0);
    }

    调用execl不会增加进程会计的个数

  • 相关阅读:
    部署Tomcat ----【javaweb-02】
    初步了解web ----【javaweb-01】
    JDBC-02
    JDBC-01
    利用Maven进行导jar包
    ContainerBase.addChild: start 错误问题
    MySQL数据库02
    MySQL数据库01
    简单认识并使用JavaScript【供后端人员作为了解】
    JSP+SSH+Mysql+DBCP实现的租车系统
  • 原文地址:https://www.cnblogs.com/javawebsoa/p/3087426.html
Copyright © 2011-2022 走看看