• Unix环境高级编程—进程控制(三)


    一、解释器文件

    解释器文件属于文本文件,起始行形式为:

    #! pathname[optional-argument]

    我们创建一个只有一行的文件如下:

    #!/home/webber/test/echoall.c  foo

    然后通过进程fork一个子进程execl寻找到这个文件路径下,我们将看到的是/home/webber/test/echoall.c 作为第一个参数被传了进来,foo成为第二个参数,

    然后才是execl函数内指定的其他argv的值。即exec族函数的处理是把#!后面的字符串做为命令,后面加上execl参数列表中指定。

    在书中例子中,

    #!/usr/bin/awk  -f    

    -f 的选项是必须的,它告诉awk在什么地方找到awk程序。

    二、函数system

    system() executes a command specified in command by calling /bin/sh -c command, and returns after the command has been completed.
    During execution of the command, SIGCHLD will be blocked, and SIGINT and SIGQUIT will be ignored.

    system函数用于执行一个shell命令,它对forkexecwaitpid的进行封装,变为一个对外的函数接口,进行了所需的各种出错处理和各种信号处理。

    #include<stdlib.h>

    int system(const char *cmdstring) 

    返回值:因为system在实现中调用了forkexecwaitpid,所以有三种返回值。如下:

    1) fork失败或waitpid返回除EINTR之外的出错,则system返回-1,并且设置errno来指示错误类型。

    2) exec失败(表示不能执行shell),则返回值相当于shell执行exit(127),即返回定义的错误码(command not found)。

    3) 三个调用的函数都执行成功,则system的返回值是shell的终止状态。(WIFEXITED,WIFSIGNALED,WIFSTOPPED,WIFCONTINUED)

     The value returned is -1 on error (e.g.  fork(2) failed), and the return status of the command other- wise.  This latter return status is in the
    format specified in wait(2).  Thus, the exit code  of  the command  will be WEXITSTATUS(status).  In case /bin/sh could not be executed,
    the exit status will be that of a command that does exit(127).
      If the value of command is NULL, system() returns non-zero if the shell is available, and zero  if not.  system() does not affect the wait
     status of any other children.

    注意:使用system函数调用在某些情况下需要谨慎。比如,一个进程以root权限运行,当它想生产一个子进程执行以普通用户权限就能执行的任务时,绝不能用system函数去开启子进程,因为system在fork一个子进程时把root的ID也复制给了子进程,然后立即exec去执行,这样当真正的子进程以普通用户权限去执行任务时,我们geteuid()会返回0,即子进程的有效用户ID具有root权限,这是很危险的安全漏洞。对于这种情况,我们应该在fork之后,exec之前改回普通用户权限,而不能调用封装好的system函数。

    三、进程会计

    accton(8)命令开启或关闭进程会计,每当进程结束时,内核就写一个会计记录,记录命令名、所使用的CPU时间总量、用户ID和组ID、启动时间等。在Linux中,

    该记录写在/var/account/pacct文件中。会计记录所需的数据都由内核保存在进程表中,并在一个新进程fork被创建时初始化。进程终止时写一个会计记录。

    这产生两个后果:

    1).我们不能获取永远不终止的进程的会计记录。例如init、守护进程daemon

    2).在会计文件中记录的顺序对应于进程终止的顺序,而非启动顺序。

    注意:会计记录对应与进程而不是程序。在这里,我认为进程是大于程序的,可以表述为:

    首先,父进程fork了一个子进程,然后子进程exec一个程序A(通过pathname等找到程序A),同时,该子进程还可以再exec一个程序B。因此说进程大于程序,

    程序是用某一进程exec出来的。

    再回到会计记录,在上述表述中,进程会计只会记录exec出来的程序B,但CPU时间是AB之和。

    一个人在口令文件中可以有多个登录项,它们的用户ID相同,但登陆shell不同。登录名由getlogin()函数获取。 

    四、进程调度

     nice函数 属于系统调用,它的作用是通过调整nice值选择以更低的优先级运行,nice值越小,cpu占用率越高,进程越“友好”,优先级也就越高。

    Linux2.26.32内核版本中,nice值的范围为-20~19. ( Nicenesses range from -20 (most favorable scheduling) to 19 (least favorable).

        经过测试,在单核CPULinux系统中,如果两个进程并行运行,在相同nice值的情况下,我们top观察两个进程,会发现cpu占用大约各为50%

    但是如果其中一个进程调高了nice值,则系统会认为它不“友好”,从而给它分配更低的cpu占用率,它执行任务的效率会明显变低。

    五、进程时间

    times命令是内建命令。作用: 

    Print the accumulated user and system times for the shell  and  for  processes  run  from  the shell. The return status is 0.

    time命令,作用:测量一个命令的运行的三种时间,分别为:实际使用时间(real time)、用户态使用时间(the process spent in user mode)、

    内核态使用时间(the process spent in kernel mode

    times函数,返回墙上时钟时间,调用方式如下:

    clock_t  times(struct  tms  *buf);

    buf指向的tms结构,该结构定义如下:

    Struct  tms {

          clock_t tms_utime;        /*user CPU time */

          clock_t tms_stime;        /*system CPU time */

          clock_t tms_cutime;        /*user CPU time ,terminated children*/

          clock_t tms_cstime;        /*system CPU time ,terminated children*/

    }

    下面是书中用times函数以及结合了整章知识点,写了一个具有time命令功能的程序。

    其中,输入的argv[]需要有双引号。

    #include "apue.h"
    #include <sys/times.h>
    
    static void     pr_times(clock_t, struct tms *, struct tms *);
    static void     do_cmd(char *);
    
    int
    main(int argc, char *argv[])
    {
            int             i;
    
            setbuf(stdout, NULL);
            for (i = 1; i < argc; i++)
                    do_cmd(argv[i]);        /* once for each command-line arg */
            exit(0);
    }
    
    static void
    do_cmd(char *cmd)               /* execute and time the "cmd" */
    {
            struct tms      tmsstart, tmsend;
            clock_t         start, end;
            int                     status;
    
            printf("
    command: %s
    ", cmd);
    
            if ((start = times(&tmsstart)) == -1)   /* starting values */
                    err_sys("times error");
    
            if ((status = system(cmd)) < 0)                 /* execute command */
                    err_sys("system() error");
    
            if ((end = times(&tmsend)) == -1)               /* ending values */
                    err_sys("times error");
            pr_times(end-start, &tmsstart, &tmsend);
    }
    
    static void
    pr_times(clock_t real, struct tms *tmsstart, struct tms *tmsend)
    {
            static long             clktck = 0;
    
            if (clktck == 0)        /* fetch clock ticks per second first time */
                    if ((clktck = sysconf(_SC_CLK_TCK)) < 0)
                            err_sys("sysconf error");
    
            printf("  real:  %7.2f
    ", real / (double) clktck);
            printf("  user:  %7.2f
    ",
              (tmsend->tms_utime - tmsstart->tms_utime) / (double) clktck);
            printf("  sys:   %7.2f
    ",
              (tmsend->tms_stime - tmsstart->tms_stime) / (double) clktck);
            printf("  child user:  %7.2f
    ",
              (tmsend->tms_cutime - tmsstart->tms_cutime) / (double) clktck);
            printf("  child sys:   %7.2f
    ",
              (tmsend->tms_cstime - tmsstart->tms_cstime) / (double) clktck);
    }






  • 相关阅读:
    Jmeter压力测试-结果分析(三十)
    C#正则密码验证
    Docker
    c# framework 缓存依赖
    NETMQ订阅,超时断线重连
    RabbitMQ发布/订阅模式
    RAS非对称加密解密,公钥私钥
    .NETCore批量插入数据BulkLoader
    .NETcore使用CSRedisCore操作Redis
    Rabbitmq简单队列
  • 原文地址:https://www.cnblogs.com/webber1992/p/5850756.html
Copyright © 2020-2023  润新知