linux and Windows Process Control

Posted by growler123 on Thu, 21 Nov 2019 22:32:25 +0100

Process Management Control

What is implemented here is a custom timer that counts the time a child process runs.The main ways of use are

timer [-t seconds] command arguments

For example, to count the run time of ls, you can enter timers directly, followed by arguments, which are the parameters of the program you want to run.For example: timer ls-al.If you want to specify how long the program will run, such as 5 seconds, you can enter timer-t 5 ls-al.It is important to note that the program does not detect abnormalities in the input, so make sure that the program input is correct.

Linux

Procedural ideas

  1. Get Time

    The time acquisition function uses gettimeofday with precision of microseconds

    struct timeval{
         long tv_sec;*//Seconds*
         long tv_usec;*//Microseconds*
    }
  2. Subprocess Creation

    1. fork() function

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

      A fork call returns -1 if it fails and a successful call:

      The fork function returns two values, zero and a positive integer.If 0, the current process is a child process; if it is a positive integer, the process is a parent process and the value is a child process pid.For a detailed explanation of process control, see: Process Control

    2. exec function

      When a child process is created with fork, it executes the same program as the parent process (but may execute different branches of code), and the child process often calls one exec function to execute another program.When a process invokes an exec function, the user space code and data of the process are completely replaced by the new program, starting with the startup routine of the new program.Calling exec does not create a new process, so the id of the process remains unchanged before and after calling exec.
      There are actually six functions that start with exec, collectively called exec functions:

      #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[]);

      These functions load a new program to execute from the startup code if the call is successful and return -1 if the call is wrong, so the exec function only returns the wrong value but not the successful one.

    3. wait and waitpid

      When a process terminates, it closes all file descriptors, frees up memory allocated in user space, but its PCB remains, in which the kernel holds some information: if it terminates normally, it saves the exit state, and if it terminates abnormally, it holds the signal that caused the process to terminate.The parent process of this process can call wait or waitpid to get this information and clean up the process completely.We know that a process's exit status can be viewed in a Shell with a special variable $? Because a Shell is its parent process and when it terminates, the Shell calls wait or waitpid to get its exit status and cleans up the process completely.
      If a process has terminated but its parent has not yet called wait or waitpid to clean it up, the state of the process is called a Zombie process.Any process that just terminates is a zombie process, and normally the zombie process is cleaned up immediately by the parent process.
      A zombie process cannot be cleaned up with the Kill Command because the kill command is only used to terminate the process, and the zombie process has been terminated.

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

    Returns the cleaned-up child process id if the call succeeds and -1 if the call fails.The parent process may call wait or waitpid:

    • Blocking (if all its subprocesses are still running)

    • Termination information with child processes returns immediately (if a child process has terminated, waiting for the parent process to read its termination information)
    • Error returns immediately (if it has no child processes)

    The difference between the two functions is:

    • If all the child processes of the parent process are still running, a call to wait will block the parent process, while a call to waitpid will return 0 immediately without blocking the parent process if WNOHANG is specified in the options parameter
    • Wat waits for the first terminated child process, and wait pid can specify which child process to wait for through the pid parameter

source code

timer source code

#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>
//The program assumes that the input is correct and no exception handling is done
//mytime [-t number] program
using namespace std;
//Call System Time
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)) //If run time is specified
    {
        step = 4;
        duration = atof(argv[2]); //No exception handling done
    }

    arg = new char *[argc - step + 1];
    for (int i = 0; i < argc - step; i++)
    {
        arg[i] = new char[100];
        strcpy(arg[i], argv[i + step]);
    }
    arg[argc - step] = NULL;

    newProcess(argv[step - 1], arg, duration);
    return 0;
}

void printTime()
{
    //Used to record when a process is running
    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) * 1000000 + (time_end.tv_usec - time_start.tv_usec);
    time_hour = time_use / (60 * 60 * (int)pow(10, 6));
    time_left = time_use % (60 * 60 * (int)pow(10, 6));
    time_min = time_left / (60 * (int)pow(10, 6));
    time_left %= (60 * (int)pow(10, 6));
    time_sec = time_left / ((int)pow(10, 6));
    time_left %= ((int)pow(10, 6));
    time_ms = time_left / 1000;
    time_left %= 1000;
    time_us = time_left;
    printf("This program runs at:%d hour, %d Minute, %d second, %d Millisecond, %d Microseconds\n", 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) //error
    {
        printf("Failed to create subprocess!");
        exit(1);
    }
    if (pid == 0) //Subprocess
    {
        execvp(child_process, argv);
    }
    else
    {
        if (abs(duration - 0) < 1e-6)
        {
            gettimeofday(&time_start, NULL);
            wait(NULL); //Waiting for child process to end
            printTime();
        }
        else
        {
            gettimeofday(&time_start, NULL);
            // printf("sleep: %lf\n", 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.\n");
                perror("kill");
            }
            else if (kill_ret_val == 0) // return 0, success
            {
                printf("process %d has been killed\n", pid);
            }
            printTime();
        }
    }
}

Test Source Code

#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",n, argv[n]);
    }
    sleep(5);
    return 0;
}

test

  1. Write your own program tests

  2. System Program Test

  3. Add timer to environment variable

    Only temporary variable modifications have been made here.

Windows

Creating and managing parent-child processes under Windows is somewhat more difficult on api calls than on Linux, but actually easier on administration than on 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
);

Source Code Implementation

timer program

// Process Management.cpp: This file contains the "main" function.Program execution will begin and end here.
//

#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);

    printf("argc:   %d\n", argc);

    int step = 1;
    double duration = 0;
    if (argc > 1)
    {
        if (argv[1][0] == TCHAR('-') && argv[1][1] == TCHAR('t') && argv[1][2] == TCHAR('\0'))
        {
            step = 3;
            duration = atof((char*)argv[2]);
        }
    }
    //printf("printf content start: %ls\n", argv[1]);
    int j = 0;
    for (int i = 0, h = 0; i < argc - step; i++)
    {
        wcscpy_s(cWinDir + j, MAX_PATH - j, argv[i + step]);
        for (h = 0; argv[i + step][h] != TCHAR('\0'); h++);
        j += h;
        cWinDir[j++] = ' ';
        //printf("%d : %d\n", i, j);
        //printf("printf content start: %ls\n", cWinDir);
    }
    cWinDir[j - 2] = TCHAR('\0');
    //printf("printf content start: %ls\n", cWinDir);

    newProcess(cWinDir,duration);

    return 0;
}


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;
    }
    //Since only one day is considered, no consideration is given to the negative number of hours.
    printf("runtime: %02dhours %02dminutes %02dseconds %02dmilliseconds\n", 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;
    memset(&start_time, sizeof(SYSTEMTIME), 0);
    memset(&end_time, sizeof(SYSTEMTIME), 0);
    GetSystemTime(&start_time);

    if (CreateProcess(
        NULL,       //lpApplicationName. If empty, lpCommandLine must specify an executable
                    //If there are spaces in the path, you must use quotation marks to frame it
        cWinDir,    //lpCommandLine
                    //If lpApplicationName is empty, lpCommandLine length does not exceed MAX_PATH
        NULL,       //Points to a SECURITY_ATTRIBUTES structure that determines whether the returned handle can be inherited by subprocesses, and process security
        NULL,       //  If the lpProcessAttributes parameter is empty (NULL), then the handle cannot be inherited.<Ibid>, Thread Security
        false,      //  Indicates whether the new process inherits a handle from the calling process.Handle Inheritability
        0,          //  Specify additional identifiers (priority) to control the creation of priority classes and processes
                    //  CREATE_NEW_CONSOLE New Console Opens Subprocess
                    //  CREATE_SUSPENDED subprocess is created and suspended until the ResumeThread function is called
        NULL,       //  An environment block that points to a new process.If this parameter is null, the new process uses the environment in which it is called.Point to environment string
        NULL,       //  Specify the working path of the child process
        &si,        //  The STARTUPINFO structure that determines how the main form of the new process is displayed
        &pi         //  The PROCESS_INFORMATION structure that receives identification information for a new process.Process Threads and Handles
    ))
    {
    }
    else
    {
        printf("CreateProcess failed (%d).\n", GetLastError());
        return;
    }


    //wait untill the child process exits
    if (abs(duration - 0) < 1e-6)
        WaitForSingleObject(pi.hProcess, INFINITE);//This specifies the run time in milliseconds
    else
        WaitForSingleObject(pi.hProcess, duration * 1000);

    GetSystemTime(&end_time);

    printTime(&start_time, &end_time);

    CloseHandle(pi.hProcess);
    CloseHandle(pi.hThread);
}

Test program

#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", n, argv[n]);
    }
    Sleep(5*1000);
    return 0;
}

test

  1. Write your own program tests

  2. System Program Test

  3. Add to environment variable

Reference material

Windows

Linux

Topics: C++ Linux Windows shell