linux與Windows進程式控制制

来源:https://www.cnblogs.com/harrypotterjackson/archive/2019/11/22/11908825.html
-Advertisement-
Play Games

進程管理控制 這裡實現的是一個自定義timer用於統計子進程運行的時間。使用方式主要是 例如要統計 的運行時間可以直接輸入 ,其後的 是指所要運行的程式的參數。如: 。如果要指定程式運行多少時間,如5秒鐘,可以輸入 。需要註意的是,該程式對輸入沒有做異常檢測,所以要確保程式輸入正確。 Linux 程 ...


進程管理控制

這裡實現的是一個自定義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 = 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()
{
    //用以記錄進程運行的時間
    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("此程式運行的時間為:%d 小時, %d 分鐘, %d 秒, %d 毫秒, %d 微秒\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) //出錯
    {
        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\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();
        }
    }
}

測試源代碼

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

測試

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

    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;
    }
    //由於僅考慮在一天之內,不考慮小時會變成負數的情況
    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.若為空,則lpCommandLine必須指定可執行程式
                    //若路徑中存在空格,必須使用引號框定
        cWinDir,    //lpCommandLine
                    //若lpApplicationName為空,lpCommandLine長度不超過MAX_PATH
        NULL,       //指向一個SECURITY_ATTRIBUTES結構體,這個結構體決定是否返回的句柄可以被子進程繼承,進程安全性
        NULL,       //  如果lpProcessAttributes參數為空(NULL),那麼句柄不能被繼承。<同上>,線程安全性
        false,      //  指示新進程是否從調用進程處繼承了句柄。句柄可繼承性
        0,          //  指定附加的、用來控制優先類和進程的創建的標識符(優先順序)
                    //  CREATE_NEW_CONSOLE  新控制台打開子進程
                    //  CREATE_SUSPENDED    子進程創建後掛起,直到調用ResumeThread函數
        NULL,       //  指向一個新進程的環境塊。如果此參數為空,新進程使用調用進程的環境。指向環境字元串
        NULL,       //  指定子進程的工作路徑
        &si,        //  決定新進程的主窗體如何顯示的STARTUPINFO結構體
        &pi         //  接收新進程的識別信息的PROCESS_INFORMATION結構體。進程線程以及句柄
    ))
    {
    }
    else
    {
        printf("CreateProcess failed (%d).\n", GetLastError());
        return;
    }


    //wait untill the child process exits
    if (abs(duration - 0) < 1e-6)
        WaitForSingleObject(pi.hProcess, INFINITE);//這裡指定運行時間,單位毫秒
    else
        WaitForSingleObject(pi.hProcess, duration * 1000);

    GetSystemTime(&end_time);

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

測試

  1. 自行編寫程式測試

    1574352549870

  2. 系統程式測試

    1574352658762

  3. 添加至環境變數

    1574353167296

參考資料

Windows

Linux


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 當項目業務越來越複雜,打包出來的代碼會非常冗餘,文件體積會非常龐大。大體積文件會增加編譯時間,影響開發效率;如果直接上線,還會拉長請求和載入時長,影響網站體驗。作為一個追求極致體驗的攻城獅,是不能忍的。所以在多頁應用中優化打包尤為必要。那麼如何優化webpack打包呢? ...
  • 版本: 1、後端實現跨域(php) header("Access-Control-Allow-Origin: *"); // // 響應類型 // header('Access-Control-Allow-Methods:POST'); // // 響應頭設置 // header('Access-C ...
  • 本篇介紹筆者接觸的第一個後臺系統,從自身見聞出發,因此涉及的內容相對比較基礎,後臺大牛請自覺略過。 什麼是好友系統? 簡單的說,好友系統是維護用戶好友關係的系統。我們最熟悉的好友系統案例當屬QQ,實際上QQ是一款即時通訊工具,憑著好友系統沉澱了海量的好友關係鏈,從而鑄就了一個堅不可摧的商業帝國。好友 ...
  • 網站手機的模板 !想學習卻不知從何下手的小白們可以多多參考網站上的模版! ...
  • '''''' ''' 一、函數 1、函數定義 對功能或者動作的封裝 在類中定義,就是方法 在類之外定義,就是函數 2、函數寫法 1、定義或者申明函數 def 函數名(形參列表): 函數體(return) 2、調用函數 函數名(實參) 3、函數名 1、定義 是一個變數的名字(函數名也屬於變數) 2、用... ...
  • 例15 除法算式 問題描述 輸入正整數n(2≤n≤68),按從小到大輸出所有形如abcde/fghi=n的表達式。其中a~i為1~9的一個排列。 輸入格式 每行為一個正整數n (n <= 1500),輸入n=0結束。 輸出格式 輸出滿足條件的所有形如abcde/fghi=n的表達式,每個表達式占一行 ...
  • 異常處理 異常分類與體繫結構: Error 相關類型的異常是程式無法處理(大多都是修改代碼無法解決的)的異常,這類異常通常需要我們調整JVM的運行環境 Exception 相關類型的異常是程式可以處理的異常,其包含兩大子類型 編譯異常(CheckedException) 通常是語法錯誤,或是方法明確 ...
  • 概述: 在Java中存在一些基本數據類型,這些基本數據類型變數,不能像其他對象一樣調用方法,屬性.... 一些情況下帶來一些問題,包裝類就是為瞭解決這個問題而出現 包裝類可以使得這些基礎數據類型,擁有對象的能力 包裝類與基礎類型的對應關係 特點: 包裝類都是final修飾無法繼承 數字類型的父類都是 ...
一周排行
    -Advertisement-
    Play Games
  • Dapr Outbox 是1.12中的功能。 本文只介紹Dapr Outbox 執行流程,Dapr Outbox基本用法請閱讀官方文檔 。本文中appID=order-processor,topic=orders 本文前提知識:熟悉Dapr狀態管理、Dapr發佈訂閱和Outbox 模式。 Outbo ...
  • 引言 在前幾章我們深度講解了單元測試和集成測試的基礎知識,這一章我們來講解一下代碼覆蓋率,代碼覆蓋率是單元測試運行的度量值,覆蓋率通常以百分比表示,用於衡量代碼被測試覆蓋的程度,幫助開發人員評估測試用例的質量和代碼的健壯性。常見的覆蓋率包括語句覆蓋率(Line Coverage)、分支覆蓋率(Bra ...
  • 前言 本文介紹瞭如何使用S7.NET庫實現對西門子PLC DB塊數據的讀寫,記錄了使用電腦模擬,模擬PLC,自至完成測試的詳細流程,並重點介紹了在這個過程中的易錯點,供參考。 用到的軟體: 1.Windows環境下鏈路層網路訪問的行業標準工具(WinPcap_4_1_3.exe)下載鏈接:http ...
  • 從依賴倒置原則(Dependency Inversion Principle, DIP)到控制反轉(Inversion of Control, IoC)再到依賴註入(Dependency Injection, DI)的演進過程,我們可以理解為一種逐步抽象和解耦的設計思想。這種思想在C#等面向對象的編 ...
  • 關於Python中的私有屬性和私有方法 Python對於類的成員沒有嚴格的訪問控制限制,這與其他面相對對象語言有區別。關於私有屬性和私有方法,有如下要點: 1、通常我們約定,兩個下劃線開頭的屬性是私有的(private)。其他為公共的(public); 2、類內部可以訪問私有屬性(方法); 3、類外 ...
  • C++ 訪問說明符 訪問說明符是 C++ 中控制類成員(屬性和方法)可訪問性的關鍵字。它們用於封裝類數據並保護其免受意外修改或濫用。 三種訪問說明符: public:允許從類外部的任何地方訪問成員。 private:僅允許在類內部訪問成員。 protected:允許在類內部及其派生類中訪問成員。 示 ...
  • 寫這個隨筆說一下C++的static_cast和dynamic_cast用在子類與父類的指針轉換時的一些事宜。首先,【static_cast,dynamic_cast】【父類指針,子類指針】,兩兩一組,共有4種組合:用 static_cast 父類轉子類、用 static_cast 子類轉父類、使用 ...
  • /******************************************************************************************************** * * * 設計雙向鏈表的介面 * * * * Copyright (c) 2023-2 ...
  • 相信接觸過spring做開發的小伙伴們一定使用過@ComponentScan註解 @ComponentScan("com.wangm.lifecycle") public class AppConfig { } @ComponentScan指定basePackage,將包下的類按照一定規則註冊成Be ...
  • 操作系統 :CentOS 7.6_x64 opensips版本: 2.4.9 python版本:2.7.5 python作為腳本語言,使用起來很方便,查了下opensips的文檔,支持使用python腳本寫邏輯代碼。今天整理下CentOS7環境下opensips2.4.9的python模塊筆記及使用 ...