【负载均衡oj】(四) 运行模块

发布时间:2023年12月28日

一.目的

主要负责运行可执行程序。通过文件名就能运行程序。

二.runner

run运行模块: 不需要关心运行结果是否正确,只具备基本运行功能并只关心是否运行出错。

一个程序的时候,有三种IO需要关心,标准错误,标准输出,标准输入,可能不会使用到但离不开这些功能。创建出这三个文件和文件描述符,并继承给子进程。同样让子进程执行运行逻辑,父进程等待判断运行是否成功。通过信号提取运行状态

status的低16位的高8位保存子进程的返回值,高8位表示的是退出码的数字(这里不关心)

status的低16位的低8位中保存子进程异常退出的信号值,其实也只用到了其中的低7位,没用的那个是核心转储标记位

static int Run(const std::string &file_name, int cpu_limit, int mem_limit)
        {
            // 标准输入: 不处理
            // 标准输出: 程序运行完成,输出结果是什么
            // 标准错误: 运行时错误信息
            // 运行时可能会需要输入输出,同时有可能出现错误
            // 所以需要这些位置的路径
            std::string _execute = PathUtil::Exe(file_name);   // 执行程序路径
            std::string _stdin = PathUtil::Stdin(file_name);   // 新的输入路径
            std::string _stdout = PathUtil::Stdout(file_name); // 新的输出路径
            std::string _stderr = PathUtil::Stderr(file_name); // 新的错误路径

            // 打开文件描述符(主进程打开文件描述符需要关闭)
            umask(0);
            // 输入只提供创建和读的权限
            int _stdin_fd = open(_stdin.c_str(), O_CREAT | O_RDONLY, 0644);
            // 输出只提供创建和写的权限
            int _stdout_fd = open(_stdout.c_str(), O_CREAT | O_WRONLY, 0644);
            // 错误只提供创建和写的权限
            int _stderr_fd = open(_stderr.c_str(), O_CREAT | O_WRONLY, 0644);

            if (_stdin_fd < 0 || _stdout_fd < 0 || _stderr_fd < 0)
            {
                LOG(ERROR) << "程序运行时打开文件描述符失败"
                           << "\n";
                return -1; // 代表打开文件失败
            }
            // 创建子进程程序替换
            pid_t pid = fork();

            if (pid < 0)
            {
                LOG(ERROR) << "程序运行时创建子进程失败"
                           << "\n";
                close(_stdin_fd);
                close(_stdout_fd);
                close(_stderr_fd);

                return -2; // 代表创建子进程失败
            }

            else if (pid == 0) // 子进程执行程序替换执行用户的程序
            {
                // 文件描述符重定向后程序替换
                dup2(_stdin_fd, 0);
                dup2(_stdout_fd, 1);
                dup2(_stderr_fd, 2);
                
                SetProcLimit(cpu_limit, mem_limit);

                execl(_execute.c_str() /*我要执行谁*/, _execute.c_str() /*我想在命令行上如何执行该程序*/, nullptr);
                
                exit(1);
            }

            else // 主进程回收
            {
                close(_stdin_fd);
                close(_stdout_fd);
                close(_stderr_fd);

                // status的低16位的低8位中保存子进程异常退出的信号值
                int status = 0; // 将子进程的退出码保存在status内并判断状态,通过信号
                waitpid(pid, &status, 0);

                // 程序运行异常,可以通过信号判断
                LOG(INFO) << "运行完毕, info: " << (status & 0x7F) << "\n";
                return status & 0x7F;
            }
        }

添加资源限制:

可以通过下图所示函数对用户的程序进行限制。这样当到达限制条件的时候此进程就会被操作系统通过信号杀掉,经过测验,cpu触发终止发送的是24号信号,内存终止条件发送的是6号信号。

// 限制用户程序的运行时间和可使用内存大小
        static void SetProcLimit(int _cpu_limit, int _mem_limit) // 内存大小以kb为单位
        {
            // 设置cpu调度时间上限
            struct rlimit cpu_limit;
            cpu_limit.rlim_cur = _cpu_limit;    // 人为设置的上限
            cpu_limit.rlim_max = RLIM_INFINITY; // 硬件上限

            // 将参数设置进限制函数内
            setrlimit(RLIMIT_CPU, &cpu_limit);

            // 设置内存上限
            struct rlimit mem_limit;
            mem_limit.rlim_cur = _mem_limit * 1024; // 人为设置的上限
            mem_limit.rlim_max = RLIM_INFINITY;     // 硬件上限

            // 将参数设置进限制函数内
            setrlimit(RLIMIT_AS, &mem_limit);
        }

文章来源:https://blog.csdn.net/m0_62572672/article/details/135201375
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。