进程控制

进程控制:

进程标识:
    每个进程都有一个非负整形表示的唯一进程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\n";

        int main(void)
        {
            int var;
            pid_t pid;

            var=88;
            if(write(STDOUT_FILENO,buf,sizeof(buf)-1)!=sizeof(buf)-1)
                printf("write error\n");
            printf("before fork\n");

            if((pid=fork())<0)
            {
                printf("fork error\n");
            }
            else if(pid==0)
            {
                globvar++;
                var++;
            }
            else
            {
                sleep(2);
            }

            printf("pid =%ld, glob= %d, var= %d\n",(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\n");
            if((pid=vfork())<0)
            {
                printf("vfork error\n");
            }
            else if(pid==0)
            {
                globvar++;
                var++;
                _exit(0);
            }
            printf("pid =%ld ,glob=%d,var =%d\n",(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\n");
            else if(pid==0)
            {
                if((pid=fork())<0)
                        printf("fork error\n");
                else if(pid>0)
                        exit(0);
                sleep(2);
                printf("second child,parent pid=%ld\n",(long)getppid());
                exit(0);
            }
            if(waitpid(pid,NULL,0)!=pid)
                printf("waitpid error\n");
            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\n");
            else if(pid==0)
                charatatime("output form child\n");
            else
                charatatime("output form parent\n");
            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\n");
            else if(pid==0)
            {
                WAIT_PARENT();
                charatatime("output form child\n");
            }           
            else{
                charatatime("output form parent\n");
                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\n");
            printf("status:%d\n",status);

            if((status=system("nosuchcommand"))<0)
                printf("system() error\n");
            printf("status:%d\n",status);

            if((status=system("who;exit 44"))<0)
                printf("system() error\n");
            printf("status:%d\n",status);

            exit(0);
        }
        打印实际用户ID和有效用户ID:
        #include <unistd.h>
        #include <stdlib.h>
        #include <stdio.h>
        int main(void)
        {
            printf("real uid=%d,effective uid=%d\n",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\n",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\n",nzero);
            if(argc==2)
                adj=strtol(argv[1],NULL,10);
            gettimeofday(&end,NULL);
            end.tv_sec+=10;

            if((pid=fork())<0)
                printf("fork failed\n");
            else if(pid==0)
            {
                s="child";
                printf("current nice value in child is %d,adjusting by %d\n",
                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\n",ret+nzero);
            }
            else{
                s="parent";
                printf("current nice value in parent is %d \n",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("\ncommand: %s\n",cmd);

            if((start=times(&tmsstart))==-1)
                printf("times error\n");

            if((status=system(cmd))<0)
                printf("system error\n");

            if((end=times(&tmsend))==-1)
                printf("times error\n");

            pr_times(end-start,&tmsstart,&tmsend);
            printf("status: %d\n",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\n");
            }

            printf("real: %7.2f\n",real/(double)clktck);
            printf("user: %7.2f\n",(tmsend->tms_utime-tmsstart->tms_utime)/(double)clktck);
            printf("sys: %7.2f\n",(tmsend->tms_stime-tmsstart->tms_stime)/(double)clktck);
            printf("child user: %7.2f\n",(tmsend->tms_cutime-tmsstart->tms_cutime)/(double)clktck);
            printf("child sys: %7.2f\n",(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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值