• linux与Windows进程控制


    阅读目录

    进程管理控制

    这里实现的是一个自定义timer用于统计子进程运行的时间。使用方式主要是

    timer [-t seconds] command arguments

    例如要统计ls的运行时间可以直接输入timer ls,其后的arguments是指所要运行的程序的参数。如:timer ls -al。如果要指定程序运行多少时间,如5秒钟,可以输入timer -t 5 ls -al。需要注意的是,该程序对输入没有做异常检测,所以要确保程序输入正确。

    Linux

    程序思路

    1. 获取时间

      时间获取函数使用gettimeofday,精度可以达到微秒

      struct timeval{
           long tv_sec;*//秒*
           long tv_usec;*//微秒*
      }
    2. 子进程创建

      1. fork()函数

        #include <sys/types.h>
        #include <unistd.h>
        pid_t fork(void);

        fork调用失败则返回-1,调用成功则:

        fork函数会有两种返回值,一是为0,一是为正整数。若为0,则说明当前进程为子进程;若为正整数,则该进程为父进程且该值为子进程pid。关于进程控制的详细说明请参考:进程控制

      2. exec函数

        用fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支),子进程往往要调用一种exec函数以执行另一个程序。当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行。调用exec并不创建新进程,所以调用exec前后该进程的id并未改变。
        其实有六种以exec开头的函数,统称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 *const argv[]);
        int execvp(const char *file, char *const argv[]);
        int execve(const char *path, char *const argv[], char *const envp[]);

        这些函数如果调用成功则加载新的程序从启动代码开始执行,不再返回,如果调用出错则返回-1,所以exec函数只有出错的返回值而没有成功的返回值。

      3. waitwaitpid

        一个进程在终止时会关闭所有文件描述符,释放在用户空间分配的内存,但它的PCB还保留着,内核在其中保存了一些信息:如果是正常终止则保存着退出状态,如果是异常终止则保存着导致该进程终止的信号是哪个。这个进程的父进程可以调用wait或waitpid获取这些信息,然后彻底清除掉这个进程。我们知道一个进程的退出状态可以在Shell中用特殊变量$?查看,因为Shell是它的父进程,当它终止时Shell调用wait或waitpid得到它的退出状态同时彻底清除掉这个进程。
        如果一个进程已经终止,但是它的父进程尚未调用wait或waitpid对它进行清理,这时的进程状态称为僵尸(Zombie)进程。任何进程在刚终止时都是僵尸进程,正常情况下,僵尸进程都立刻被父进程清理了。
        僵尸进程是不能用kill命令清除掉的,因为kill命令只是用来终止进程的,而僵尸进程已经终止了。

      #include <sys/types.h>
      #include <sys/wait.h>
      pid_t wait(int *status);
      pid_t waitpid(pid_t pid, int *status, int options);

      若调用成功则返回清理掉的子进程id,若调用出错则返回-1。父进程调用wait或waitpid时可能会:

      • 阻塞(如果它的所有子进程都还在运行

      • 带子进程的终止信息立即返回(如果一个子进程已终止,正等待父进程读取其终止信息)
      • 出错立即返回(如果它没有任何子进程)

      这两个函数的区别是:

      • 如果父进程的所有子进程都还在运行,调用wait将使父进程阻塞,而调用waitpid时如果在options参数中指定WNOHANG可以使父进程不阻塞而立即返回0
      • wait等待第一个终止的子进程,而waitpid可以通过pid参数指定等待哪一个子进程

    源代码

    timer源代码

    #include <math.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/time.h>
    #include <unistd.h>
    #include <wait.h>
    #include <ctime>
    #include <iostream>
    #include <cstring>
    //程序假定输入完全正确,没有做异常处理
    //mytime [-t number] 程序
    using namespace std;
    //调用系统时间
    struct timeval time_start;
    struct timeval time_end;
    

    void printTime();

    void newProcess(const char *child_process, char *argv[], double duration);

    int main(int argc, char const *argv[])
    {
    double duration = 0;
    char **arg;
    int step = 2;
    if (argc > 3 && (strcmp((char *)"-t", argv[1]) == 0)) //如果指定了运行时间
    {
    step = 4;
    duration = atof(argv[2]); //没有做异常处理
    }

    arg = <span class="hljs-keyword">new</span> <span class="hljs-keyword">char</span> *[argc - step + <span class="hljs-number">1</span>];
    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; argc - step; i++)
    {
        arg[i] = <span class="hljs-keyword">new</span> <span class="hljs-keyword">char</span>[<span class="hljs-number">100</span>];
        <span class="hljs-built_in">strcpy</span>(arg[i], argv[i + step]);
    }
    arg[argc - step] = <span class="hljs-literal">NULL</span>;
    
    newProcess(argv[step - <span class="hljs-number">1</span>], arg, duration);
    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
    

    }

    void printTime()
    {
    //用以记录进程运行的时间
    int time_use = 0; // us
    int time_left = 0; // us
    int time_hour = 0, time_min = 0, time_sec = 0, time_ms = 0, time_us = 0;
    gettimeofday(&time_end, NULL);

    time_use = (time_end.tv_sec - time_start.tv_sec) * <span class="hljs-number">1000000</span> + (time_end.tv_usec - time_start.tv_usec);
    time_hour = time_use / (<span class="hljs-number">60</span> * <span class="hljs-number">60</span> * (<span class="hljs-keyword">int</span>)<span class="hljs-built_in">pow</span>(<span class="hljs-number">10</span>, <span class="hljs-number">6</span>));
    time_left = time_use % (<span class="hljs-number">60</span> * <span class="hljs-number">60</span> * (<span class="hljs-keyword">int</span>)<span class="hljs-built_in">pow</span>(<span class="hljs-number">10</span>, <span class="hljs-number">6</span>));
    time_min = time_left / (<span class="hljs-number">60</span> * (<span class="hljs-keyword">int</span>)<span class="hljs-built_in">pow</span>(<span class="hljs-number">10</span>, <span class="hljs-number">6</span>));
    time_left %= (<span class="hljs-number">60</span> * (<span class="hljs-keyword">int</span>)<span class="hljs-built_in">pow</span>(<span class="hljs-number">10</span>, <span class="hljs-number">6</span>));
    time_sec = time_left / ((<span class="hljs-keyword">int</span>)<span class="hljs-built_in">pow</span>(<span class="hljs-number">10</span>, <span class="hljs-number">6</span>));
    time_left %= ((<span class="hljs-keyword">int</span>)<span class="hljs-built_in">pow</span>(<span class="hljs-number">10</span>, <span class="hljs-number">6</span>));
    time_ms = time_left / <span class="hljs-number">1000</span>;
    time_left %= <span class="hljs-number">1000</span>;
    time_us = time_left;
    <span class="hljs-built_in">printf</span>(<span class="hljs-string">"此程序运行的时间为:%d 小时, %d 分钟, %d 秒, %d 毫秒, %d 微秒
    "</span>, time_hour, time_min, time_sec, time_ms, time_us);
    

    }

    void newProcess(const char* child_process, char **argv, double duration)
    {
    pid_t pid = fork();
    if (pid < 0) //出错
    {
    printf("创建子进程失败!");
    exit(1);
    }
    if (pid == 0) //子进程
    {
    execvp(child_process, argv);
    }
    else
    {
    if (abs(duration - 0) < 1e-6)
    {
    gettimeofday(&time_start, NULL);
    wait(NULL); //等待子进程结束
    printTime();
    }
    else
    {
    gettimeofday(&time_start, NULL);
    // printf("sleep: %lf ", duration);
    waitpid(pid, NULL, WNOHANG);
    usleep(duration * 1000000); // sec to usec
    int kill_ret_val = kill(pid, SIGKILL);
    if (kill_ret_val == -1) // return -1, fail
    {
    printf("kill failed. ");
    perror("kill");
    }
    else if (kill_ret_val == 0) // return 0, success
    {
    printf("process %d has been killed ", pid);
    }
    printTime();
    }
    }
    }

    测试源代码

    #include <iostream>
    #include <ctime>
    #include <unistd.h>
    using namespace std;
    int main(int argc, char const *argv[])
    {
        for(int n = 0; n < argc; n++)
        {
            printf("arg[%d]:%s
    ",n, argv[n]);
        }
        sleep(5);
        return 0;
    }

    测试

    1. 自行编写程序测试

      1574351830016

    2. 系统程序测试

      1574351915002

    3. 将timer加入环境变量

      这里仅进行了临时变量修改。

      1574352171410

    Windows

    在Windows下进行父子进程的创建和管理在api调用上相较Linux有一定难度,但实际上在使用管理上比Linux容易的多。

    CreateProcess

    #include <Windows.h>
    BOOL CreateProcessA(
      LPCSTR                lpApplicationName,
      LPSTR                 lpCommandLine,
      LPSECURITY_ATTRIBUTES lpProcessAttributes,
      LPSECURITY_ATTRIBUTES lpThreadAttributes,
      BOOL                  bInheritHandles,
      DWORD                 dwCreationFlags,
      LPVOID                lpEnvironment,
      LPCSTR                lpCurrentDirectory,
      LPSTARTUPINFOA        lpStartupInfo,
      LPPROCESS_INFORMATION lpProcessInformation
    );

    源代码实现

    timer程序

    // 进程管理.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
    //
    

    #include <iostream>
    #include <wchar.h>
    #include <Windows.h>
    #include <tchar.h>
    using namespace std;

    void printTime(SYSTEMTIME* start, SYSTEMTIME* end);
    void newProcess(TCHAR* cWinDir, double duration);

    int _tmain(int argc, TCHAR argv[])
    {
    TCHAR
    cWinDir = new TCHAR[MAX_PATH];
    memset(cWinDir, sizeof(TCHAR) * MAX_PATH, 0);

    <span class="hljs-built_in">printf</span>(<span class="hljs-string">"argc:   %d
    "</span>, argc);
    
    <span class="hljs-keyword">int</span> step = <span class="hljs-number">1</span>;
    <span class="hljs-keyword">double</span> duration = <span class="hljs-number">0</span>;
    <span class="hljs-keyword">if</span> (argc &gt; <span class="hljs-number">1</span>)
    {
        <span class="hljs-keyword">if</span> (argv[<span class="hljs-number">1</span>][<span class="hljs-number">0</span>] == TCHAR(<span class="hljs-string">'-'</span>) &amp;&amp; argv[<span class="hljs-number">1</span>][<span class="hljs-number">1</span>] == TCHAR(<span class="hljs-string">'t'</span>) &amp;&amp; argv[<span class="hljs-number">1</span>][<span class="hljs-number">2</span>] == TCHAR(<span class="hljs-string">''</span>))
        {
            step = <span class="hljs-number">3</span>;
            duration = atof((<span class="hljs-keyword">char</span>*)argv[<span class="hljs-number">2</span>]);
        }
    }
    <span class="hljs-comment">//printf("printf content start: %ls
    ", argv[1]);</span>
    <span class="hljs-keyword">int</span> j = <span class="hljs-number">0</span>;
    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>, h = <span class="hljs-number">0</span>; i &lt; argc - step; i++)
    {
        wcscpy_s(cWinDir + j, MAX_PATH - j, argv[i + step]);
        <span class="hljs-keyword">for</span> (h = <span class="hljs-number">0</span>; argv[i + step][h] != TCHAR(<span class="hljs-string">''</span>); h++);
        j += h;
        cWinDir[j++] = <span class="hljs-string">' '</span>;
        <span class="hljs-comment">//printf("%d : %d
    ", i, j);</span>
        <span class="hljs-comment">//printf("printf content start: %ls
    ", cWinDir);</span>
    }
    cWinDir[j - <span class="hljs-number">2</span>] = TCHAR(<span class="hljs-string">''</span>);
    <span class="hljs-comment">//printf("printf content start: %ls
    ", cWinDir);</span>
    
    newProcess(cWinDir,duration);
    
    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
    

    }

    void printTime(SYSTEMTIME* start, SYSTEMTIME* end)
    {
    int hours = end->wHour - start->wHour;
    int minutes = end->wMinute - start->wMinute;
    int seconds = end->wSecond - start->wSecond;
    int ms = end->wMilliseconds - start->wMilliseconds;
    if (ms < 0)
    {
    ms += 1000;
    seconds -= 1;
    }
    if (seconds < 0)
    {
    seconds += 60;
    minutes -= 1;
    }
    if (minutes < 0)
    {
    minutes += 60;
    hours -= 1;
    }
    //由于仅考虑在一天之内,不考虑小时会变成负数的情况
    printf("runtime: %02dhours %02dminutes %02dseconds %02dmilliseconds ", hours, minutes, seconds, ms);
    }

    void newProcess(TCHAR* cWinDir, double duration)
    {
    PROCESS_INFORMATION pi;
    STARTUPINFO si;
    ZeroMemory(&si, sizeof(si));
    si.cb = sizeof(si);
    ZeroMemory(&pi, sizeof(pi));

    SYSTEMTIME start_time, end_time;
    <span class="hljs-built_in">memset</span>(&amp;start_time, <span class="hljs-keyword">sizeof</span>(SYSTEMTIME), <span class="hljs-number">0</span>);
    <span class="hljs-built_in">memset</span>(&amp;end_time, <span class="hljs-keyword">sizeof</span>(SYSTEMTIME), <span class="hljs-number">0</span>);
    GetSystemTime(&amp;start_time);
    
    <span class="hljs-keyword">if</span> (CreateProcess(
        <span class="hljs-literal">NULL</span>,       <span class="hljs-comment">//lpApplicationName.若为空,则lpCommandLine必须指定可执行程序</span>
                    <span class="hljs-comment">//若路径中存在空格,必须使用引号框定</span>
        cWinDir,    <span class="hljs-comment">//lpCommandLine</span>
                    <span class="hljs-comment">//若lpApplicationName为空,lpCommandLine长度不超过MAX_PATH</span>
        <span class="hljs-literal">NULL</span>,       <span class="hljs-comment">//指向一个SECURITY_ATTRIBUTES结构体,这个结构体决定是否返回的句柄可以被子进程继承,进程安全性</span>
        <span class="hljs-literal">NULL</span>,       <span class="hljs-comment">//  如果lpProcessAttributes参数为空(NULL),那么句柄不能被继承。&lt;同上&gt;,线程安全性</span>
        <span class="hljs-literal">false</span>,      <span class="hljs-comment">//  指示新进程是否从调用进程处继承了句柄。句柄可继承性</span>
        <span class="hljs-number">0</span>,          <span class="hljs-comment">//  指定附加的、用来控制优先类和进程的创建的标识符(优先级)</span>
                    <span class="hljs-comment">//  CREATE_NEW_CONSOLE  新控制台打开子进程</span>
                    <span class="hljs-comment">//  CREATE_SUSPENDED    子进程创建后挂起,直到调用ResumeThread函数</span>
        <span class="hljs-literal">NULL</span>,       <span class="hljs-comment">//  指向一个新进程的环境块。如果此参数为空,新进程使用调用进程的环境。指向环境字符串</span>
        <span class="hljs-literal">NULL</span>,       <span class="hljs-comment">//  指定子进程的工作路径</span>
        &amp;si,        <span class="hljs-comment">//  决定新进程的主窗体如何显示的STARTUPINFO结构体</span>
        &amp;pi         <span class="hljs-comment">//  接收新进程的识别信息的PROCESS_INFORMATION结构体。进程线程以及句柄</span>
    ))
    {
    }
    <span class="hljs-keyword">else</span>
    {
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">"CreateProcess failed (%d).
    "</span>, GetLastError());
        <span class="hljs-keyword">return</span>;
    }
    
    
    <span class="hljs-comment">//wait untill the child process exits</span>
    <span class="hljs-keyword">if</span> (<span class="hljs-built_in">abs</span>(duration - <span class="hljs-number">0</span>) &lt; <span class="hljs-number">1e-6</span>)
        WaitForSingleObject(pi.hProcess, INFINITE);<span class="hljs-comment">//这里指定运行时间,单位毫秒</span>
    <span class="hljs-keyword">else</span>
        WaitForSingleObject(pi.hProcess, duration * <span class="hljs-number">1000</span>);
    
    GetSystemTime(&amp;end_time);
    
    printTime(&amp;start_time, &amp;end_time);
    
    CloseHandle(pi.hProcess);
    CloseHandle(pi.hThread);
    

    }

    测试程序

    #include <iostream>
    #include <Windows.h>
    using namespace std;
    int main(int argc, char* argv[])
    {
        for (int n = 0; n < argc; n++)
        {
            printf("arg[%d]:%s
    ", n, argv[n]);
        }
        Sleep(5*1000);
        return 0;
    }

    测试

    1. 自行编写程序测试

      1574352549870

    2. 系统程序测试

      1574352658762

    3. 添加至环境变量

      1574353167296

    参考资料

    Windows

    Linux

  • 相关阅读:
    Spring 未初始化声明bean就ref,造成的异常
    数据库优化
    数据库删除操作尽量不要做物理删除,用状态字段来表示
    lua脚本语言
    上半年阅读书籍清单
    (转)从零实现3D图像引擎:(4)三角函数库
    (转)从零实现3D图像引擎:(1)环境配置与项目框架
    通信协议的正确处理方法
    (转)从零实现3D图像引擎:(6)向量函数库
    (转)从零实现3D图像引擎:(5)3D坐标系函数库
  • 原文地址:https://www.cnblogs.com/ylaoda/p/11909905.html
Copyright © 2020-2023  润新知