进程程序替换
一级目录
fork了一个子进程,父子谁先运行?不确定,由调度器说了算,
一般父子进程,谁先退出?一般都是子进程先退出,然后父进程负责进行资源回收
创建多进程
#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string>
enum
{
ok=0,
USAGE_ERR=1
};
void Task()
{
std::cout<<"我是子进程,我完成了我的任务"<<std::endl;
}
int main(int argc,char*argv[])
{
if(argc!=2)
{
std::cout<<"格式应该为.myprocess num"<<std::endl;
exit(1);
}
int num=std::stoi(argv[1]);//这个输入的是字符串
for(int i=0;i<num;i++)
{
pid_t id=fork();
if(id==0)
{
//child
Task();
exit(ok);
}
//father
int status=0;
pid_t rid=waitpid(id,&status,0);
if(rid>0)
{
printf("子进程:%d,Exit code:%d\n",rid,WEXITSTATUS(status));
}
}
return 0;
}
首先,这样创建与我们平时创建子进程相比,只多了一个
int num=std::stoi(argv[1]);//这个输入的是字符串
for(int i=0;i<num;i++)
解析一下比平时的代码多了什么,首先,在第一次for循环的时候父进程先创建了一个子进程,然后父进程等待子进程结束之后再回收子进程,然后再创建第二个子进程,但如果我想让子进程串行的话,可以改为
#include <iostream>
#include <unistd.h>
#include <vector>
#include <sys/types.h>
#include <sys/wait.h>
#include <string>
enum
{
ok=0,
USAGE_ERR=1
};
void Task()
{
std::cout<<"我是子进程,我完成了我的任务"<<std::endl;
}
int main(int argc,char*argv[])
{
if(argc!=2)
{
std::cout<<"格式应该为.myprocess num"<<std::endl;
exit(1);
}
std::vector<pid_t> subs;
int num=std::stoi(argv[1]);//这个输入的是字符串
for(int i=0;i<num;i++)
{
//创建子进程
pid_t id=fork();
if(id==0)
{
//child
Task();
exit(ok);//在这里之后子进程就结束了,变成僵尸状态
}
//这里只剩下父进程了
subs.push_back(id);
}
//father
//等待多进程
//到这里只有父进程一个执行流,但id值只有一个了,id值是最后一个子进程的pid,我们需要记录别的子进程的pid,所以要用一个vector来记录
for(auto &pid:subs)
{
int status=0;
pid_t rid=waitpid(pid,&status,0);
if(rid>0)
{
printf("子进程:%d,Exit code:%d\n",rid,WEXITSTATUS(status));
}
}
return 0;
}
写代码要养成封装的习惯哦
#include <iostream>
#include <unistd.h>
#include <vector>
#include <sys/types.h>
#include <sys/wait.h>
#include <string>
enum
{
ok = 0,
USAGE_ERR = 1
};
void Task()
{
std::cout << "我是子进程,我完成了我的任务" << std::endl;
}
void createchild(std::vector<pid_t>& subs,const int&num)
{
for (int i = 0;i < num;i++)
{
//创建子进程
pid_t id = fork();
if (id == 0)
{
//child
Task();
exit(ok);//在这里之后子进程就结束了,变成僵尸状态
}
//这里只剩下父进程了
subs.push_back(id);
}
}
void waitchild(const std::vector<pid_t>& subs)
{
for (auto& pid : subs)
{
int status = 0;
pid_t rid = waitpid(pid, &status, 0);
if (rid > 0)
{
printf("子进程:%d,Exit code:%d\n", rid, WEXITSTATUS(status));
}
}
}
int main(int argc, char* argv[])
{
if (argc != 2)
{
std::cout << "格式应该为.myprocess num" << std::endl;
exit(1);
}
std::vector<pid_t> subs;
int num = std::stoi(argv[1]);//这个输入的是字符串
createchild(subs,num);
waitchild(subs);
//father
//等待多进程
//到这里只有父进程一个执行流,但id值只有一个了,id值是最后一个子进程的pid,我们需要记录别的子进程的pid,所以要用一个vector来记录
那如果,我要在子进程中调用多个函数呢,不止Task,我还有别的要做的,这里列举多一个Hello;
这里随意列举一下,用的c++11特性的花括号初始化,
#include <iostream>
#include <unistd.h>
#include <vector>
#include <sys/types.h>
#include <sys/wait.h>
#include <string>
typedef void (*callback_t)();
////void myFunction() {
// // 一些代码
//}
//
//int main() {
// // 声明一个函数指针,语法很繁琐
// void (*funcPtr)() = &myFunction;
//
// // 调用
// (*funcPtr)();
// return 0;
//}
//// 1. 定义类型别名
//typedef void (*create_hanshu)();
//
//void myFunction() {
// // 一些代码
//}
//
//int main() {
// // 2. 使用新类型声明变量,看起来像普通变量一样
// create_hanshu func = &myFunction;
//
// // 3. 调用
// func();
// return 0;
//}
enum
{
ok = 0,
USAGE_ERR = 1
};
void Task()
{
std::cout << "我是子进程,我完成了TASK" << std::endl;
}
void Hello()
{
std::cout << "我是子进程,我完成了Hello" << std::endl;
}
void createchild(std::vector<pid_t>& subs,const int&num, const std::vector<callback_t>& subs2)
{
for (int i = 0;i < num;i++)
{
//创建子进程
pid_t id = fork();
if (id == 0)
{
//child
subs2[i]();
exit(ok);//在这里之后子进程就结束了,变成僵尸状态
}
//这里只剩下父进程了
subs.push_back(id);
}
}
void waitchild(const std::vector<pid_t>& subs)
{
for (auto& pid : subs)
{
int status = 0;
pid_t rid = waitpid(pid, &status, 0);
if (rid > 0)
{
printf("子进程:%d,Exit code:%d\n", rid, WEXITSTATUS(status));
}
}
}
int main(int argc, char* argv[])
{
if (argc != 2)
{
std::cout << "格式应该为.myprocess num" << std::endl;
exit(1);
}
std::vector<pid_t> subs;
int num = std::stoi(argv[1]);//这个输入的是字符串
std::vector<callback_t> subs2{ Hello ,Hello,Task,Task };
createchild(subs,num,subs2);
waitchild(subs);
//father
//等待多进程
//到这里只有父进程一个执行流,但id值只有一个了,id值是最后一个子进程的pid,我们需要记录别的子进程的pid,所以要用一个vector来记录
return 0;
}
一般而言,在写函数的传递参数的时候
如果是输入:const &
输出:*
输入输出:&
这样子就会创建一个子进程之后,然后子进程的状态变成僵尸状态,5个子进程都变成僵尸状态后父进程一起回收
进程程序替换
execl函数
先介绍一个函数
execl("/usr/bin/ls","-a","-l",NULL);//程序替换函数
它的作用是什么呢?

如果没有这个函数,正常打印的结果肯定是
但如果我把他加进去,会发生什么呢

哎,下面的内容怎么没实现呢,这个函数就是让你直接调到另一个文件去实现别的内容,那后面的代码怎么没有了程序替换之后execl的后序代码不执行,因为被替换了。
exec*系列的函数,成功后没有返回值
原理
你设置的文件目录的代码会覆盖execl会将物理内存中的代码段原本的代码
程序替换没有创建新进程,页表左边没变,pcb没有变化,pid没有变化,没有创建新进程
程序替换的本质:把磁盘中的代码和数据,加载到内存中(IO)
程序运行时,必须加载到内存中,是为了让CPU能够高速、高效地工作。
是谁做这个IO过程的呢?OS!OS必须提供对应的系统调用,完成这个加载的过程
加载器->exec->main函数
当然,替换的时候代码段会发生强制替换
常用例子
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
printf("我是一个子进程:%d\n",getpid());
pid_t id=fork();
if(id==0)
{
execl("/usr/bin/ls", "ls","-a","-l",NULL);//程序替换函数
exit(0);//最好设置为1
}
wait(NULL);
printf("我的进程正在执行中\n");
printf("我的进程正在执行中\n");
printf("我的进程正在执行中\n");
printf("我的进程正在执行中\n");
printf("我的进程正在执行中\n");
printf("我的进程正在执行中\n");
printf("我的进程正在执行中\n");
printf("我的进程正在执行中\n");
printf("我的进程正在执行中\n");
printf("我的进程正在执行中\n");
printf("我的进程正在执行中\n");
return 0;
}

问题1:上面不是说程序被替换了嘛?为什么下面的printf还是正常执行的?
其实是子进程被覆盖了,父进程不收影响,
问题2:那子进程后面的exit也被覆盖了呀,没有退出码,会崩溃嘛?
几乎所有的 Linux 命令行工具(如 cat、grep、ps 等)在其代码的 main 函数结束前,或者在遇到错误时,都会显式地调用 exit() 函数。ls内部是包含 exit 调用。
就算不是系统内部函数,C 语言标准规定:在 C99 及以后的标准中,如果 main 函数执行到结尾没有返回值,编译器会默认当作 return 0; 处理。
问题3:那这个子进程有必要写exit 0;嘛,不都已经被覆盖了嘛
答案:需要的,如果替换失败,就要执行exit函数了,失败时返回-1;execl返回只要失败,继续往后走,默认程序失败,可以直接设置exit为1
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)