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 = 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. 自行编写程序测试

  2. 系统程序测试

  3. 将timer加入环境变量

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

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. 自行编写程序测试

  2. 系统程序测试

  3. 添加至环境变量

参考资料

Windows

Linux

原文地址:https://www.cnblogs.com/harrypotterjackson/p/11908825.html

时间: 2024-10-07 21:16:09

linux与Windows进程控制的相关文章

linux c 笔记 进程控制(三)

进程退出    进程结束表示进程即将结束运行,在linux系统中进程的退出方法分为正常退出和异常退出两种.exit函数进程有三种正常终止法及两种异常终止法.(1) 正常终止:    (a) 在main函数内执行return语句.这等效于调用 exit.    (b) 调用exit函数.此函数由ANSI C定义,其操作包括调用各终止处理程序(终止处理程序在调用atexit函数时登录),然后关闭所有标准I/O流等.因为ANSI C并不处理文件描述符.多进程(父.子进程)以及作业控制,所以这一定义对

linux c 笔记 进程控制(二)---守护进程

守护进程(Daemon),一说精灵进程,是指在后台运行的,没有控制终端与之相连的程序.它独立于控制终端周期性地执行某种任务或等待处理某些发生的事件.它是一个生存期较长的进程,守护进程常常在系统引导装入时启动,在系统关闭时终止.Linux系统有很多守护进程,大多数服务都是通过守护进程实现的,同时,守护进程还能完成许多系统任务,例如,作业规划进程crond.打印进程lqd等(这里的结尾字母d就是Daemon的意思).由于在Linux中,每一个系统与用户进行交流的界面称为终端,每一个从此终端开始运行的

linux 命令及进程控制

main.c  main.o/main.obj  main/main.exe          编译                连接 程序运行;      两步: gcc/g++  -c  main.c/main.cpp  -> main.o              gcc/g++ -o main  main.o -> main(可执行文件)     一步:  gcc -o main mian.c  -> main    工程文件:       rm  *.o     gcc  -

linux下的 进程控制 以及常见的进程控制函数

进程控制: 1. 进程创建函数: fork(); 头文件: #include<sys/types.h> #include<unistd.h> 函数原型: pid_t fork(void); 函数返回值:0:表示此进程现在是子进程: -1:表示出错: 子进程ID号:(大于零的整数):表示现在此进程时父进程,接收到的ID号是子进程的ID号: 2.fork()返回-1(也就是进程创建出错的原因) 1.系统中拥有了太多的进程:超过了系统的限制:(系统级) 2.该用户有了太多的进程,超过了C

linux c 笔记 进程控制(一)

1.进程简述 进程是一个动态的实体,操作系统资源分配的基本单位,每个进程都有一个非负整型的唯一进程 ID.因为进程 ID标识符总是唯一的,常将其用做其他标识符的一部分以保证其唯一性.    1)进程标识:    每个进程都有一个非负整型的唯一进程 ID.因为进程 ID标识符总是唯一的,常将其用做其他标识符的一部分以保证其唯一性.tmpnam 函数将进程 ID作为名字    的一部分创建一个唯一的路径名.    有某些专用的进程:进程 ID 0是调度进程,常常被称为交换进程 ( swapper )

linux c 笔记 进程控制(四)

一.更改用户 I D和组I D可以用setuid函数设置实际用户ID和有效用户ID.与此类似,可以用 setgid函数设置实际组ID和有效组ID.int setgid(gid_t gid) ;两个函数返回:若成功则为 0,若出错则为- 1关于谁能更改 ID有若干规则.现在先考虑有关改变用户 I D的规则(在这里关于用户 ID所说明的一切都适用于组 ID).(1) 若进程具有超级用户特权,则 setuid函数将实际用户 ID.有效用户 ID,以及保存的设置-用户- ID设置为uid.(2) 若进程

Windows进程控制

在Windows系统中,应用程序都以进程的形式存在于内存中.当运行一个程序的时候,操作系统就会将这个程序装入内存,并分配各种运行程序所需的资源,为进程创建主线程. 系统也提供了任务管理器,可供我们使用.管理进程的界面如下: 其中包含的内容一目了然,就没有必要讲解了.直接进入常用API. 1. 进程的创建 UINT WinExec( LPCSTR lpCmdLine,//指向一个可执行文件 UINT uCmdShow//程序运行后的窗口状态 /* SW_SHOW:表示程序运行后,窗口为显示状态 S

Linux内核开发 — 进程控制

本章主要是以代码的角度分析进程的定义.状态.数据结构等概念. 进程的定义 进程是一段运行的程序,他是一个动态的可执行实体.而程序是代码和数据的集合,代码是一个静态的实体,程序是可以供多个进程使用,比如相同的应用程序可以在不同的计算机上运行而产生多个进程. 进程四要素 进程四要素主要是针对代码中对线程.进程的区别而言: l  有一段程序供其执行 l  有进程专用的内核堆栈空间 l  有内核控制块(有一个task_struct 数据结构),拥有内核控制块,才能被内核调度 l  有独立的用户空间  

Linux进程控制(一)

1. Linux进程概述 进程是一个程序一次执行的过程,它和程序有本质区别.程序是静态的,它是一些保存在磁盘上的指令的有序集合:而进程是一个动态的概念,它是一个运行着的程序,包含了进程的动态创建.调度和消亡的过程,是Linux的基本调度单位.那么从系统的角度看如何描述并表示它的变化呢?在这里,是通过进程控制块(PCB)来描述的.进程控制块包含了进程的描述信息.控制信息以及资源信息,它是进程的一个静态描述. 内核使用进程来控制对CPU和其他系统资源的访问,并且使用进程来决定在CPU上运行哪个程序,