在调度系列文章的第一篇中我们提到调度是由好几个模块组成的,其中前面已经介绍了定时器,扫描模块的相关实现知识点了,这里就继续往后面的模块介绍吧,那么接下来,我们就讲下任务执行模块吧。如果说任务调度系统系统,也许很多同学不知道是一个什么概念,但是如果说到crontab的话,大家可能就会恍然大悟了。任务调度系统其实就是一个比cron支持功能多点和复杂点的系统,但是他们本质上是一样的,就是把任务管理起来,而其中调度系统和任务打交道的,也就是把任务调起来的模块,就是任务执行模块。而任务的存在方式也是有好多种的,用得比较多的几种分别为可执行文件(主要指的是二进制文件命令行,当然,脚本也是通过命令行调起来,这里就不去纠结这个分类是否合理了),so以及脚本。由于文章篇幅的问题,我们把这几种任务方式分成3篇博文来拆解,这次我们先来讨论下怎样调度二进制可执行文件构成的任务。

使用过命令行的同学都知道,我们使用命令行是一般是在shell中执行类似下面这样的命令

/cmd/path/cmd_bin param1 param2 ... paramN

但是问题来了,任务调度系统又不能和shell交互,要怎样调起这样的命令呢?linux为我们提供了一套可行的方式,多进程。至于怎样实现多进程的方式,我们从简单到复杂来讲吧

system()函数

在c标准库中有一个system()函数,在调用这个函数时,linux会产生一个子进程,然后由子进程来调用/bin/sh-c string来执行参数string字符串所代表的命令。命令执行完后子进程退出,随即返回原调用的进程。这个函数的原型如下

int system(const char * string); 产生一个子进程并执行参数中的命令行。参数string即要执行的命令

通过上面的分析可知,system()函数是完成能够满足我们的基本需求的,只要我们把要执行的命令拼装成一个命令行,然后传到system()函数即可。像下面例子所示

#include <stdlib.h>
int main ()
{system("ls -lrt");return 0;
}

但是,由于system()在命令执行完之前是会阻塞主进程的,这对于多个任务同时执行,而且任务都需要执行很长时间,并且我们需要知道每个任务的执行结果的情况下,system()就不能很好地满足我们的需求了。虽然不能直接使用,但是我们可以参考下system()函数是怎样实现的。

//c_standard_lib/STDLIB/SYSTEM.C/* UNIX system calls */
int _Execl(const char *, const char *, ...);
int _Fork(void);
int _Wait (int *);int (system)(const char *s){  /* send text to system command line processor */if (s){ /* not just a test */int pid = _Fork();if (pid < 0);    /* fork failed */else if (pid == 0){  /* continue here as child */_Execl("/bin/sh", "sh", "-c", s, NULL);exit(EXIT_FAILURE);}else   /* continue here as parent */while (_Wait(NULL) != pid);   /* wait for child */}return (-1);}

从上面c标准库的代码中可以看到,在实现system()函数中使用到了3个其他系统函数,分别是fork(),execl()和wait()。既然如此,我们就顺势而为,看看能不能也使用这三个函数来实现一个满足我们要求的做法。

自己动手,丰衣足食

上面我们知道了system()函数的用到了哪些系统调用,我们接下来就先熟悉下这些系统调用好了,原型如下

pid_t fork(void);fork()系统调用会通过复制一个现有进程来创建一个全新的进程。返回值:自进程中返回0,父进程返回进程id,出错返回-1 int execl(const char *pathname, const char *arg, ... /* (char  *) NULL */);
int execlp(const char *file, const char *arg, ...  /* (char  *) NULL */);
int execle(const char *pathname, const char *arg, ...  /*, (char *) NULL, char * const envp[] */);
int execv(const char *pathname, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[],  char *const envp[]);在进程的创建上Unix采用了一个独特的方法,它将进程创建与加载一个新进程映象分离。
当我们创建了一个进程之后,通常将子进程替换成新的进程映象,这可以用exec系列的函数来进行。
当然,exec系列的函数也可以将当前进程替换掉。path参数表示你要启动程序的名称包括路径名
arg参数表示启动程序所带的参数,一般第一个参数为要执行命令名,不是带路径且arg必须以NULL结束
返回值:成功返回0,失败返回-1
上述exec系列函数底层都是通过execve系统调用实现:
int execve(const char *filename, char *const argv[],char *const envp[]);pid_t wait(int *wstatus);
pid_t waitpid(pid_t pid, int *wstatus, int options);系统调用exit后,该进程并非马上消失,而是留下一个叫僵尸进程的数据结构,僵尸进程是非常特使的一种,它放弃了几乎所有的内存空间,
没有任何可执行代码,也不能别调度,仅仅在进程列表保留位置,而且不占用任何内存空间。wait()函数用于使父进程阻塞,直到一个子进程结束或者该进程接收到了一个指定的信号为止。
如果该父进程没有子进程或者它的子进程已经结束,则wait()函数就会立即返回。waitpid()的作用和wait()一样,但它并不一定要等待第一个终止的子进程(它可以通过pid指定需要等待终止的子进程),它还有若干options,
当options==WNOHANG时,相当于提供一个非阻塞版本的 wait()功能,也能支持作业控制。
实际上,wait()函数只是 waitpid()函数的一个特例,在Linux 内部实现 wait()函数时直接调用的就是waitpid()函数

下面是一个简单的应用例子,在实际的应用中,除了这3个api的应用之外,还需要做很多其他的事情,如注册信号,写日志,管道重定向等等,有兴趣的同学可以看看apue或者《linux系统编程》等等经典著作,这里就不再啰嗦了。

#include <functional>
#include <iostream>
#include <sstream>
#include <algorithm>
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <signal.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>#include <agent/global.h>
#include <agent/utility.h>int  exec( const std::string& execName, std::vector<std::string>& params)
{/***  Step 1. prepare argv*/std::vector<char*> argv;argv.resize(1024);argv[1023] = NULL;argv[0] = const_cast<char*> (execName.c_str());size_t N = params.size();N = (N > 1022) ? 1022 : N;int argc = 1;for (size_t i = 0; i < N; i++){argv[argc++] = const_cast<char*> (params.at(i).c_str());}argv[argc] = NULL;/*** Step 3. Fork and exec*/pid_t pid = fork();if (pid == 0){signal(SIGINT, SIG_IGN);// execexecvp(execName.c_str(), &argv[0]);_exit(-1); //不能用exit(),详细原因参见apue}else if (pid > 0){/* parent process */int status = 0;pid_t nwait = waitpid(pid, &status, 0);while (nwait == -1 && errno == EINTR)nwait = waitpid(pid, &status, 0);if (nwait == -1){std::cout << "waitpid: " << strerror(errno) << std::endl;return -1;}// get exit value of child processint exit_value = 0;if (WIFEXITED(status)){// normally terminatedexit_value = WEXITSTATUS(status);}else if (WIFSIGNALED(status)){// killed or abortedexit_value = 0x80 | WTERMSIG(status);}elseexit_value = 0xf0;return exit_value;}return -1;
}

怎样做到的

本来打算对上述api的实现剖析一番的,但是想到自己已经很就没有去看这么底层的东西了,而且linux内核版本更新得很快,所以就放弃了,只能奉上几个api在内核中的实现代码,有兴趣的同学自行学习好了

fork()内核实现​elixir.bootlin.comwaitpid内核实现​elixir.bootlin.comexecve内核代码​elixir.bootlin.com

c++禁止进程被结束_多进程任务实现相关推荐

  1. c++禁止进程被结束_第四章 进程管理

    4.1 进程概念 4.1.1 进程基本概念:描述和管理程序"运行过程"--进程 A.定义:进程是程序在某个数据集合上的一次运行活动.数据集合:软/硬件环境,多个进程共存/共享的环境 ...

  2. mysql killed进程不结束_优秀的数据库产品——MySQL 云数据库服务

    作为一种低成本,高性能,高可靠性和开放源代码的数据库产品,MySQL已在Internet公司中广泛使用. 例如,淘宝有数千个MySQL服务器. 尽管NoSQL在过去两年中发展迅速,新产品层出不穷,但N ...

  3. cmd 找到8080对应进程_多进程概括

    多进程图像 操作系统记录进程,并按照合理的次序交替推进(分配资源,不断调度),提高CPU利用率和程序执行速度,这就是操作系统的多进程图像. 当操作系统启动时,多进程图像就出现了. 在linux内核源码 ...

  4. 失败的windows系统服务调用readfile():管道已结束?_操作系统之进程详解(一)

    一.进程的概念和特征 进程知识拓扑图 进程的定义 1. 进程是程序的一次执行过程. 2. 进程是一个程序及其数据在处理机上顺序执行时所发生的活动. 3.进程是具有独立功能的程序在一个数据集合上运行的过 ...

  5. win python 判断 所有 子进程 结束_python 多进程 进程池子进程结束怎么获取

    匿名用户 1级 2016-10-26 回答 在利用Python进行系统管理的时候,特别是同时操作多个文件目录,或者远程控制多台主机,并行操作可以节约大量的时间.当被操作对象数目不大时,可以直接利用mu ...

  6. vc6 调试 附加到进程 列表空_今天,进程告诉我线程它它它它不想活了

    这是Java建设者的第 67 篇原创文章 上一篇文章我们解剖了进程和线程的本质,进程和线程的实现方式,这篇文章我们来探讨它们是如何通信的,进程告诉我说线程不想活了,我不管它死活,我只想知道我是谁?进程 ...

  7. Delphi写的等待进程运行结束函数

    procedure TForm1.Button1Click(Sender: TObject); var   sCommandLine: string;   bCreateProcess: boolea ...

  8. IDEA 惊天 bug:进程已结束,退出代码 1073741819

    来源 | 沉默王二 责编 | Carol 头图 | CSDN 下载自视觉中国 今天要写的文章中涉及到一串代码,关于 Undertow 的一个入门示例,贴出来大家看一下. public class Un ...

  9. 查看oracle死锁进程并结束死锁

    查看oracle死锁进程并结束死锁 摘自: http://sqcjy111.iteye.com/blog/1183928 查看锁表进程SQL语句1: select sess.sid, sess.ser ...

最新文章

  1. 查看端口号被哪个程序占用
  2. 如何查找cvpr类的论文_如何查找期刊论文?(3个实用的方法)
  3. 在Linux上使用AFL对Stagefright进行模糊测试
  4. 【重温基础】2.流程控制和错误处理
  5. 两个字符串的删除操作Python解法
  6. 矩阵每一行重复_python:19.顺时针打印矩阵
  7. android 控件xpath软件_Appium-关于appium的原生控件的 xpath 定位问题及常用方法
  8. 总结下SQLServer和Oracle转换的脚本
  9. 贺利坚老师汇编课程40笔记:指令里的数据在哪里有多长
  10. 深入研读Cache存储的计算
  11. python 模糊匹配_Case2:模糊匹配工具
  12. 替换换行符:回车换行CR/LF
  13. Blazeds文档(二)-------Blazeds体系结构(一)【转载】
  14. MATLAB判别分析-----2019/8/26
  15. Python数据分析之股票数据
  16. Take-Two同意斥资127亿美元收购Zynga,或成史上最大游戏并购交易
  17. P1685 飞跃悬崖
  18. linux floppy 虚拟,Floppylinux
  19. 台式计算机一小时功率,电脑电源功率如何选?电脑1小时能耗多少电?
  20. wordpress实时在线聊天室

热门文章

  1. 理工科毕业设计献礼,MATLAB从入门到精通之矩阵是如何实现寻访与赋值的
  2. chapter15 机器学习之大数据与mapreduce
  3. Project interpreter not specified(eclipse+pydev)
  4. Hadoop学习之HDFS架构(一)
  5. python核心编程第三版_Python之父:自学python,这3本书能节约你一大半时间编程...
  6. 《动手学深度学习》PyTorch版本
  7. 编译Tomcat9源码【转】
  8. Elasticsearch java客户端调用cat服务
  9. netflix ribbon概述
  10. Reactor by Example--转