• 进程控制


    进程控制:
    
    进程标识:
        每个进程都有一个非负整形表示的唯一进程ID。因为进程ID标识符总是唯一的,常将其用作其他标识符的
        一部分以保证其唯一性。例如,应用程序有时就把进程ID作为名字的一部分来创建一个唯一的文件名。
    
        虽然是唯一的,但是进程ID是可复用的。当一个进程终止后,其进程ID就称为复用的候选者。大多数的UNIX系统
        实现延迟复用算法,使得赋予新建进程的ID不同于最近终止进程所使用的ID。这防止了将新进程误认为将新进程
        误认为是使用同一ID的某个已终止的先前进程。
    
        系统中有一些专用进程,,但具体细节随实现而不同。ID为0的进程通常是调度进程,常常被称为交换进程。该进程
        是内核的一部分,它并不执行任何磁盘上的程序,因此也被称为系统进程。进程ID为1通常是init进程,在自举过程
        结束时由内核调用。该进程的程序文件在UNIX的早期版本中时/etc/init,在较新版本中的是/sbin/init。此进程
        负责在自举内核后启动一个UNIX系统。init通常读取与系统有关的初始化文件(/etc/rc*文件或/etc/inittab文件,
        以及在/etc/init.d中的文件),并将系统引导到一个状态(如多用户)。init进程决不会终止。它是一个普通的用户
        进程(与交换进程不同,它不是内核中的系统进程),但是它以超级用户特权运行。
    
        除了进程ID,每个进程还有一些其他标识符。下列函数返回这些标识符:
        #include <unistd.h>
        pid_t getpid(void); 返回值:调用进程的进程ID
        pid_t getppid(void);    返回值:调用进程的父进程ID
    
        uid_t getuid(void); 返回值:调用进程的实际用户ID
        uid_t getgid(void); 返回值:调用进程的有效用户ID
    
        gid_t getegid(void);    返回值:调用进程的有效组ID
    
    
    函数fork:
        一个现有的进程可以调用fork函数创建一个新进程:
        #include <unistd.h>
        pid_t fork(void);
        返回值:子进程返回0,父进程返回子进程ID,若出错,返回-1
    
        由fork创建的新进程被称为子进程。fork函数被调用一次,但返回两次。两次返回的区别是子进程的返回值是0,
        而父进程的返回值则是新建子进程的进程ID。将子进程ID返回给父进程的理由是:因为一个进程的子进程可以有多个,
        并且没有一个函数使一个进程可以获得其所有子进程的进程ID。fork使子进程得到返回值0的理由是:一个进程只会有一个父
        进程,所以子进程总是可以调用getppid以获得其父进程的进程ID(进程ID是0总是由内核交换进程使用,所以一个子进程
        的进程ID不可能是0)。
    
        例子:
            #include <unistd.h>
            #include <stdlib.h>
            #include <stdio.h>
    
            int globvar=6;
            char buf[]="a write to stdout
    ";
    
            int main(void)
            {
                int var;
                pid_t pid;
    
                var=88;
                if(write(STDOUT_FILENO,buf,sizeof(buf)-1)!=sizeof(buf)-1)
                    printf("write error
    ");
                printf("before fork
    ");
    
                if((pid=fork())<0)
                {
                    printf("fork error
    ");
                }
                else if(pid==0)
                {
                    globvar++;
                    var++;
                }
                else
                {
                    sleep(2);
                }
    
                printf("pid =%ld, glob= %d, var= %d
    ",(long)getpid(),globvar,var);
                exit(0);
            }
            执行:
            ./fork
            a write to stdout
            before fork
            pid=430,glob=7,var=89   //子进程的变量值改变了
            pid=429,glob=5,var=88   //父进程的变量值没有改变
    
            ./fork > temp.out
            cat temp.out
            a write to stdout
            before fork
            pid=430,glob=7,var=89   
            before fork
            pid=429,glob=5,var=88   
    
        一般来说,在fork之后是父进程先执行还是子进程先执行是不确定的,这取决于内核所使用的调度算法。
        如果要求父进程和子进程之间相互同步,则要求某种形式的进程间通信。
        在上述程序中,父进程使自己休眠2s,以此使子进程先执行。但并不保证2s已经足够。
        当写标准输出时,我们将buf长度减去1作为输出字节数,这是为了避免将终止null字节写出。strlen计算不包含
        终止null字节的字符串长度,而sizeof则计算包括终止null字节的缓冲区长度。两者之间的另一个差别是,使用
        strlen需进行一次函数调用,而对于sizeof而言,因为缓冲区已用已知字符串进行初始化,其长度是固定的,所以
        sizeof是在编译时计算缓冲区长度。
    
    函数vfork:
        vfork创建一个新进程。
        和fork一样都创建一个子进程,但是它并不将父进程的地址空间完全复制到子进程中,因为子进程回立即调用exec
        (或exit),于是也就不会引用该地址空间。不过在子进程调用exec或exit之前,它在父进程的空间中运行。
        与fork的区别是:
        vfork保证了子进程先运行,在它调用exec或exit之后父进程才能被调度运行,当子进程调用这两个函数中的任意一个时,
        父进程会恢复运行。(如果在调用这两个函数之前子进程依赖于父进程的进一步动作,则会导致斯锁)。
    
        其中用vfork代替了fork,删除了对于标准输出的write调用。另外,我们也不再需要让父进程调用sleep,因为我们可以
        保证,在子进程调用exec或者exit之前,内核会使父进程处于休眠状态。
    
        例子:
            #include <unistd.h>
            #include <stdlib.h>
            #include <stdio.h>
    
            int globvar=6;
    
            int main()
            {
                int var;
                pid_t pid;
    
                var=88;
                printf("before vfork
    ");
                if((pid=vfork())<0)
                {
                    printf("vfork error
    ");
                }
                else if(pid==0)
                {
                    globvar++;
                    var++;
                    _exit(0);
                }
                printf("pid =%ld ,glob=%d,var =%d
    ",(long)getpid(),globvar,var);
                exit(0);
            }
            执行:
            ./vfork
            before vfork
            pid =29039 ,glob=7,var=89
        子进程对变量做增1的操作,结果改变了父进程的变量值。因为子进程在父进程的地址空间中运行,所以这并不令人
        惊讶。但是其作用的确与fork不同。
        此程序调用的是_exit而不是exit。_exit并不执行标准I/O缓冲区的冲洗操作。如果调用的是exit而不是_exit
        则该程序的输出是不确定的。它依赖于标准I/O库的实现,我们可能会看到输出没有发生变化,或者发现没有出现
        父进程的printf输出。
    
        如果子进程调用的是exit,实现冲洗标准I/O流。如果这是函数库采取的唯一动作,那么我们会见到这样的操作的输出
        与子进程调用_exit所产生的输出完全不同,没有任何区别。如果该实现也关闭标准的I/O流,那么表示标准输出FILE
        对象的相关存储区将被清0.因为子进程借用了父进程的地址空间。所哟父进程恢复运行并调用printf时,也就不会产生
        任何输出,printf返回-1
    
    函数wait和waitpid:
        当一个进程正常或异常终止时,内核就向其父进程发送SIGCHLD信号。因为子进程终止是个异步事件,所哟这种信号也是
        内核向父进程发的异步通知。
        现在需要知道的是调用wait或waitpid的进程可能会发生什么:
        1、如果其所有子进程都还在运行,则阻塞
        2、如果一个子进程已终止,正等待父进程获取其终止状态,则取的该子进程的终止状态立即返回
        3、如果它没有任何子进程,则立即出错返回
    
        #include <sys/wait.h>
        pid_t wait(int *statloc);
        pid_t waitpid(pid_t pid,int *statloc,int options);
        返回值:成功,返回进程ID,出错,返回0或-1
    
        这两个函数的区别如下:
        1、在一个子进程终止前,wait使其调用这阻塞,而waitpid有一选项,可使调用者不阻塞
        2、waitpid并不等待在其调用之后的第一个终止子进程,它有若干个选项,可以控制它所等待的进程。
    
        如果一个进程fork一个子进程,但不要它等待子进程终止,也不希望子进程处于僵死状态直到父进程终止,实现这一
        要求的诀窍是调用fork两次:
            #include <unistd.h>
            #include <stdlib.h>
            #include <stdio.h>
            #include <sys/wait.h>
    
            int main(void)
            {
                pid_t pid;
    
                if((pid=fork())<0)
                    printf("fork error
    ");
                else if(pid==0)
                {
                    if((pid=fork())<0)
                            printf("fork error
    ");
                    else if(pid>0)
                            exit(0);
                    sleep(2);
                    printf("second child,parent pid=%ld
    ",(long)getppid());
                    exit(0);
                }
                if(waitpid(pid,NULL,0)!=pid)
                    printf("waitpid error
    ");
                exit(0);
            }
    
    函数waitid:
        此函数类似于waitpid,但是提供了很多的灵活性:
        #include <sys/wait.h>
        int waitid(idtype_t idtype,id_t id,siginfo_t *infop,int options);
        返回值:成功,返回0,出错,返回-1
    
        idtype:
        常量          说明
        P_PID           等待一特定进程:id包含要等待子进程的进程ID
        P_PGID          等待一特定进程组中的任一子进程:id包含要等待子进程的进程组ID
        P_ALL           等待任一子进程:忽略id
    
        options参数:各标志的按位或运算。
        常量          说明
        WCONTINUED      等待一进程,它以前曾被停止,此后又已继续,但其状态尚未报告
        WEXITED         等待已退出的进程
        WNOHANG         如无可用的子进程退出状态,立即返回而不是阻塞
        WNOWAIT         不破坏子进程退出状态。该子进程退出状态可由后续的wait、waitid或waitpid获得
        WSTOPPED        等待一进程,它已经停止,但其状态尚未报告
    
        infop参数是指向siginfo结构的指针。该结构包含了造成子进程状态该变有关信号的详细信息。
    
    函数wait3和wait4:
        #include <sys/typers.h>
        #include <sys/wait.h>
        #include <sys/time.h>
        #include <sys/resource.h>
    
        pid_t wait3(int *statloc,int options,struct rusage *rusage);
        pid_t wait4(pid_t pid,int *statloc,int options,struct rusage *rusage);
    
        程序输出两个字符串:一个由子进程输出,一个由父进程输出。因为输出依赖于内核使这两个进程运行的顺序及每个进程
        运行的时间长度,所以该程序包含了一个竞争条件。
            #include <unistd.h>
            #include <stdlib.h>
            #include <stdio.h>
            #include <sys/wait.h>
    
            static void charatatime(char *);
    
            int main()
            {
                pid_t pid;
    
                if((pid=fork())<0)
                    printf("fork error
    ");
                else if(pid==0)
                    charatatime("output form child
    ");
                else
                    charatatime("output form parent
    ");
                exit(0);
            }
            static void charatatime(char *str)
            {
                char *ptr;
                int c;
    
                setbuf(stdout,NULL);
                for(ptr=str;(c=*ptr++)!=0;)
                    putc(c,stdout);
            }
            程序中标准输出设置为不带缓冲的,每一次字符输出都需调用一次write。
    
        修改程序,使用TEIL和WAIT函数。
            #include <unistd.h>
            #include <stdlib.h>
            #include <stdio.h>
            #include <sys/wait.h>
    
            static void charatatime(char *);
    
            int main()
            {
                pid_t pid;
    
                TEIL_WAIT();
    
                if((pid=fork())<0)
                    printf("fork error
    ");
                else if(pid==0)
                {
                    WAIT_PARENT();
                    charatatime("output form child
    ");
                }           
                else{
                    charatatime("output form parent
    ");
                    TEIL_CHILD(pid);
                }
                exit(0);
            }
            static void charatatime(char *str)
            {
                char *ptr;
                int c;
    
                setbuf(stdout,NULL);
                for(ptr=str;(c=*ptr++)!=0;)
                    putc(c,stdout);
            }
    
    函数exec:
        用fork函数创建新的子进程后,子进程往往要调用一种exec函数以执行另一个程序。
        #include <unistd.h>
        int execl(const char *pathname,const char *arg0,...);
        int execv(const char *pathname,char *const argv[]);
        int execle(const char *pathname,const char *arg0,...);
        int execve(const char *pathname,char *const argv[],char *const envp[]);
        int execlp(const char *filename,const char *arg0,...);
        int execvp(const char *filename,char *const argv[]);
        int fexecve(int fd,char *const argv[],char *const envp[]);
    
    更改用户ID和更改组ID:
        可以使用:
        #include <unistd.h>
        int setuid(uid_t uid);
        int setgid(gid_t gid);
        两个函数的返回值:成功,返回0.失败返回-1
    
        getuid和geteuid函数只能获得实际用户ID和有效用户ID的当前值。我们没有可移植的方法
        取获得保存的设置用户ID的当前值。
    
    函数setreuid和setregid:
        交换实际用户ID和有效用户ID的值:
        #include <unistd.h>
        int setreuid(uid_t ruid,uid_t euid);
        int setregid(gid_t rgid,gid_t egid);
        两个函数返回值:成功,返回0,出错,返回-1
    
        若其中任一参数的值为-1,则表示相应的ID应当保持不变
    
    
    函数seteuid和setegid:
        只更改有效用户ID和有效组ID:
        #include <unistd.h>
        int seteuid(uid_t uid);
        int setegid(gid_t gid);
    
    解释器文件:
        最常见的解释器文件:#!/bin/sh
    
    函数system:
        在程序中执行一个命令字符串很方便。例如,假定要将时间和日期放到某一个文件中,则可使用函数实现这一点。
        调用time得到当前日历时间,接着调用localtime将日历时间变换为年、月、日、时、分、秒、周日的分解形式。
        然后调用strftime对上面的结构进行格式化处理,最后写入到文件中。但是使用下列system函数更容易:
        system("data > file");
    
        #include <stdlib.h>
        int system(const char *cmdstring);
        如果cmdstring是一个空指针,则仅当命令处理程序可用时,system返回非0值,这一特征可以确定在一个给定的
        操作系统上是否支持system函数。在UNIX中,system总是可用的。
        因为system在实现中调用了fork、exec和waitpid函数,因此有3种返回值:
        1、fork函数失败或者waitpid返回除EINTR之外的出错,则system返回-1,并且设置errno一指示错误类型。
        2、如果exec失败(表示不能执行shell),则返回值 如同shell执行了exit(127)一样
        3、否则所有3个函数(fork、exec和waitpid)都成功,那么system的返回值是shell的终止状态,其格式已在
        waitpid说明。
    
        下列程序是system函数的一种实现。它对信号没有进行处理。
            #include <sys/wait.h>
            #include <errno.h>
            #include <unistd.h>
    
            int system(const char *cmdstring)
            {
                pid_t pid;
                int status;
    
                if(cmdstring==NULL)
                    return(1);
    
                if((pid=fork())<0)
                    status=-1;
                else if(pid==0)
                {
                    execl("/bin/sh","sh","-c",cmdstring,(char *)0);
                    _exit(127);
                }
                else
                {
                    while(waitpid(pid,&status,0)<0)
                    {
                            if(errno!=EINTR)
                            {
                                    status=-1;
                                    break;
                            }
                    }
                }
                return(status);
            }
            注释:system函数(没有对信号进行处理)
        shell的-c选项告诉shell程序取下一个命令行参数(在这里是cmdstring)作为命令输入(而不是从标准输入或
        从一个给定的文件中读命令)。shell对以null字节终止的命令字符串进行语法分析,并将它们分成命令行参数,传递
        给shell的实际命令字符串可以包含任一有效的shell命令。例如,可以用<和>对输入和输出重定向。
            #include "system.h"
            //#include <sys.wait.h>
            #include <stdio.h>
            #include <stdlib.h>
    
            int main(void)
            {
                int status;
    
                if((status=system("date"))<0)
                    printf("system() error
    ");
                printf("status:%d
    ",status);
    
                if((status=system("nosuchcommand"))<0)
                    printf("system() error
    ");
                printf("status:%d
    ",status);
    
                if((status=system("who;exit 44"))<0)
                    printf("system() error
    ");
                printf("status:%d
    ",status);
    
                exit(0);
            }
            打印实际用户ID和有效用户ID:
            #include <unistd.h>
            #include <stdlib.h>
            #include <stdio.h>
            int main(void)
            {
                printf("real uid=%d,effective uid=%d
    ",getuid(),geteuid());
                exit(0);
            }
    
    进程会计:
        函数(acct)启用和禁用进程会计。唯一使用这一函数的是accton(8)命令。超级用户执行一个带路径名参数的
        accton命令启用会计处理。会计记录写到指定的文件中,在FreeBSD和Mac OS X中,该文件通常是/var/account/acct;
        在linux中,该文件是/var/account/pacct;在Solaris中,该文件是/var/adm/pacct。执行不带任何参数的accton
        命令则停止会计处理。
    
        记录样式:
        typedef u_short comp_t;
        struct acct
        {
            char ac_flag;
            char ac_stat;
    
            uid_t ac_uid;
            gid_t ac_gid;
            dev_t ac_tty;
            time_t ac_btime;
            comp_t ac_utime;
            comp_t ac_stime;
            comp_t ac_etime;
            comp_t ac_mem;
            comp_t ac_io;
    
            comp_t ac_rw;
            char ac_comm[8];
        }
        会计记录所需的各个数据(各CPU时间、传输的字符数等)都由内核保存在进程表中,并在一个新进程被创建时初始化
        (如fork之后在子进程中)。进程终止时写一个会计记录。这产生两个后果。
        1、我们不能获取永远不终止的进程的会计记录。像init这样的进程在系统生命周期中一直在运行,并不产生会计记录。
        这也同样适合于内核守护进程,它们通常不会终止。
        2、在会计文件中记录的顺序对应于进程终止的顺序,而不是它们启动的顺序。为了确定启动顺序,需要读全部会计文件,
        并按启动日历时间进行排序。这不是一种很完善的方法,因为日历时间的单位是秒,在一个给定的秒可能启动了多个进程。
        而墙上时钟时间的单位是时钟滴答(通常,每秒滴答数在60~128)。但是我们并不知道进程的终止时间,所知道的只是启动
        时间和终止顺序。这就意味着,即使墙上时钟时间比启动时间要精确得多,仍不能按照会计文件种的数据重构各进程的精确
        启动顺序。
    
    用户标识:
        任一进程都可以得到实际用户ID和有效用户ID及组ID。但是,我们有时希望找到运行该程序用户的登录名。我们可以调用
        getpwuid(getuid()),但是如果一个用户有多个登录名,这些登录名又对应着同一个用户ID,又将如何呢?(一个人在
        口令文件中可以有多个登录项,它们的用户ID相同,但登录shell不同。)系统常常记录登录时使用的名字,用getlogin
        函数可以获取此登录名:
        #include <unistd.h>
        char *getlogin(void);
            返回值:成功,返回指向登录名字符串的指针;出错,返回NULL。
        如果调用此函数的进程没有连接到用户登录时所用的终端,则函数会失败。通常称这些进程为守护进程。
    
        给出了登录名,就可用getpwnam在口令文件中查找用户的相应记录,从而确定其登录shell等。
    
    进程调度:
        UNIX系统历史上对进程提供的只是基于调度优先级的粗粒度的控制。调度策略和调度优先级时由内核确定的。进程可以通过
        调整nice值选择以更低优先级运行(通过调整nice值降低它对CPU的占有,因此进程时"友好的")。只有特权进程允许提供
        调度权限。
        nice值越小,优先级越高。NZERO是系统默认的nice值。
        注意,定义NZERO的头文件因系统而异。除了头文件之外,Linux 3.2.0 可以通过非标准的sysconf参数(_SC_NZERO)来
        访问NZERO的值。
    
        进程可以通过函数获取或更改它的nice值。使用这个函数,进程只能影响自己的nice值,不能影响任何其他进程的nice值。
        #include <unistd.h>
        int nice(int incr);
            返回值:成功,返回新的nice值NZERO;出错,返回-1.
    
        incr参数被增加到调用进程的nice值上。如果incr太大,系统直接把它降到最大合法值,不给出提示。类似地,如果incr太
        小,系统也会把它提高到最小合法值。由于-1是合法的成功返回值,在调用nice函数之前需要清除errno,在nice函数返回
        -1时,需要检查它的值。如果nice调用成功,并且返回值为-1,那么errno仍然为0。如果errno不为0,说明nice调用失
        败。
    
        getpriority函数可以像nice函数那样用于获取进程的nice值,但是getpriority还可以获取一组相关进程的nice值:
        #include <sys/resource.h>
        int getpriority(int which,id_t who);
            返回值:成功,返回-NZERO~NZERO-1之间的nice值;出错,返回-1
        which参数可以取以下3个值之一:PRIO_PROCESS表示进程,PRIO_PGRP表示进程组,PRIO_USER表示用户ID。which
        参数控制who参数是如何解释的,who参数选择感兴趣的一个或多个进程。如果who参数为0,表示调用进程、进程组或者用户
        (取决于which参数的值)。当which设为PRIO_USER并且who为0时,使用调用进程的实际用户ID。如果which参数作用于
        多个进程,则返回所有作用进程中优先级最高的(最小的nice值)。
    
        setpriority函数可用于进程、进程组和属于特定用户ID的所有进程设置优先级。
        #include <sys/resource.h>
        int setpriorty(int which,id_t who,int value);
            返回值,成功,返回0,失败,返回-1
        参数which和who与getpriority函数相同。value增加到NZERO上,然后变为新的nice值。
    
            #include <stdlib.h>
            #include <stdio.h>
            #include <errno.h>
            #include <sys/time.h>
            #include <sys/resource.h>
    
            #if defined(MACOS)
            #include <sys/syslimits.h>
            #elif defined(SOLARIS)
            #include <limits.h>
            #elif defined(BSD)
            #include <sys/param.h>
            #endif
    
            unsigned long long count;
            struct timeval end;
    
            void checktime(char *str)
            {
                struct timeval tv;
    
                gettimeofday(&tv,NULL);
                   if(tv.tv_sec>=end.tv_sec && tv.tv_usec>=end.tv_usec){
                    printf("%s count=%11d
    ",str,count);
                    exit(0);
                }
    
            }
    
            int main(int argc,char *argv[])
            {
                pid_t pid;
                char *s;
                int nzero,ret;
                int adj=0;
    
                setbuf(stdout,NULL);
            #if defined(NZERO)
                nzero=NZERO;
            #elif defined(_SC_NZERO)
                nzero=sysconf(_SC_NZERO);
            #else
            #error NZERO undefined
            #endif
             printf("NZERO =%d
    ",nzero);
                if(argc==2)
                    adj=strtol(argv[1],NULL,10);
                gettimeofday(&end,NULL);
                end.tv_sec+=10;
    
                if((pid=fork())<0)
                    printf("fork failed
    ");
                else if(pid==0)
                {
                    s="child";
                    printf("current nice value in child is %d,adjusting by %d
    ",
                    nice(0)+nzero,adj);
                    errno=0;
                    if((ret=nice(adj))==-1 && errno!=0)
                            printf("child set scheduling priority");
                    printf("now child nice value is %d
    ",ret+nzero);
                }
                else{
                    s="parent";
                    printf("current nice value in parent is %d 
    ",nice(0)+nzero);
                }
                 for(;;){
                    if(++count==0)
                            printf("%s counter wrap",s);
                    checktime(s);
                }
            }
            执行:
            ./nice
            结果:
            NZERO =20
            current nice value in parent is 20 
            current nice value in child is 20,adjusting by 0
            now child nice value is 20
            parent count=  133730266
            child count=  133272218
    
            ./nice 20
            结果:
            NZERO =20
            current nice value in parent is 20 
            current nice value in child is 20,adjusting by 20
            now child nice value is 39
            parent count=  243398034
            child count=    3540318
    
    进程时间:
        墙上时钟时间、用户CPU时间和系统CPU时间。任一进程都可调用times函数获得它自己以及终止子进程的上述值:
        #include <sys/times.h>
        clock_t times(struct tms *buf);
            返回值:成功,返回流逝的墙上时钟时间(以时钟滴答数为单位);出错,返回-1
    
        此函数填写由buf指向的tms结构,该结构定义如下:
        struct tms{
            clock_t tms_utime;//user CPU time
            clock_t tms_stime;//system CPU time
            clock_t cutime;//user CPU time,terminated children
            clock_t cstime;//system CPU time,terminated children
        }
        注意,此结构没有包含墙上时钟时间。times函数返回墙上时钟时间作为其函数值。此值时相对于过去的某一时刻度量的,
        所以不能用其绝对值而必须使用其相对值。例如,调用times,保存其返回值。在以后再次调用times,从新返回的值中
        减去以前返回的值,此差值就是墙上时钟时间。
    
            #include <unistd.h>
            #include <stdlib.h>
            #include <stdio.h>
            #include <sys/times.h>
            #include <time.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]);
                }
                exit(0);
            }
    
            static void do_cmd(char *cmd)
            {
                struct tms tmsstart,tmsend;
                clock_t start,end;
                int status;
    
                printf("
    command: %s
    ",cmd);
    
                if((start=times(&tmsstart))==-1)
                    printf("times error
    ");
    
                if((status=system(cmd))<0)
                    printf("system error
    ");
    
                if((end=times(&tmsend))==-1)
                    printf("times error
    ");
    
                pr_times(end-start,&tmsstart,&tmsend);
                printf("status: %d
    ",status);
            }
    
            static void pr_times(clock_t real,struct tms *tmsstart,struct tms *tmsend)
            {
                static long clktck=0;
    
                if(clktck==0)
                {
                    if((clktck=sysconf(_SC_CLK_TCK))<0)
                            printf("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);
            }
    
            执行:
            ./times "sleep 5" "date" "man bash > /dev/null"
            结果:
            command: sleep 5
            real:    5.00
            user:    0.00
            sys:    0.00
            child user:    0.00
            child sys:    0.00
            status: 0
    
            command: date
            Mon Mar 19 22:54:15 CST 2018
            real:    0.04
            user:    0.00
            sys:    0.00
            child user:    0.00
            child sys:    0.00
            status: 0
    
            command: man bash > /dev/null
            real:    0.76
            user:    0.00
            sys:    0.00
            child user:    0.31
            child sys:    0.15
            status: 0
    
    技术不分国界
  • 相关阅读:
    练习 : Flink 自定义 Source & Sink to Redis
    SQL 练习
    HiveSQL 常用函数说明
    Java 开发岗
    练习 : Flink 自定义 sink to hbase
    YOLOv6: a singlestage object detection framework dedicated to industrial applications.
    YOLO with Transformers and Instance Segmentation, with TensorRT acceleration
    About A paper list of some recent Transformerbased CV works
    This is an official implementation for "Swin Transformer: Hierarchical Vision Transformer using Shifted Windows".
    Coming from computer vision and new to transformers? Here are some resources that greatly accelerated my learning.
  • 原文地址:https://www.cnblogs.com/angels-yaoyao/p/12443615.html
Copyright © 2020-2023  润新知