现实中程序编写的时候,经常会碰到一些这样需求:
调用系统命令,完成一些操作,或判定结果 或获取结果
作为启动进程,调用第三方进程,并监控进程是否退出
加载升级进程,升级进程kill调用者或调用者自行退出,完成升级
网上方法也有很多种,但各有差异
直接调用system启动
例如:system("/opt/yourapp") 此时会运行yourapp的进程
当然,如果需要后台运行,仅需后面加 & ,如:system("/opt/yourapp &")
这样system启动yourapp 不用等待退出,就可执行下一步
使用popen打开cmd
例如 FILE *pp = popen("/opt/yourapp &", "r");
使用execl 或者 execv来加载进程
如 execl("/opt/yourapp","yourapp", (char*)0);
使用 posix_spawn 打开进程
char* const args[] = {fullPath,appName,p1,p2,nullptr};
int res = posix_spawn(&pid, args[0], nullptr, nullptr, args, environ);
那么这几种有什么区别或特点呢?
| | | shell环境变量 | 特点 |
system | 执行异步执行(&) | 开启shell | 不共享shell环境变量 | 一般用于简单系统命令,例如reboot |
popen | 也可异步,但一般需要获取执行结果时 | 开启shell | 不共享shell环境变量 | 一般用于命令调用,结合fileread可以获取执行返回值,例如 ls命令返回结果 |
execl 或者 execv | 一般需要fork子进程,然后创建新进程 | | 共享 | 监控子进程。父进程不退出 |
posix_spawn | 在当前环境下,开启进程 | 可带参数 | 共享 | 加载进程。跟父进程无关 |
system跟popen相差不大,都是重开一个shell,然后运行的
所以,不继承父进程的shell 环境变量,如果需要依赖环境变量的,此时需要做调整,
如果进程要带参数,也是可以的 直接cmd带参数即可:system("/opt/yourapp -r xxxx &");
popen同时还会建立管道,此时,可根据管道获取执行的进程返回信息,例如 我们执行popen("pidof yourapp"),此时可以方便获取到 yourapp进程的pid号
int Exec(const char *cmd, vector &res)
{
res.clear();
FILE *pp = popen(cmd, "r");
if(pp == nullptr)
{
return -1;
}
char tmp[1024] = {0};
while(fgets(tmp, sizeof(tmp), pp) != NULL)
{
if(tmp[strlen(tmp) - 1] == '\n')
{
tmp[strlen(tmp) - 1] = '\0';
}
res.push_back(string(tmp));
}
auto eStatus = pclose(pp);
if (eStatus <0)
{
return res.size();
}
if (0 !&#61; WEXITSTATUS(eStatus))
{
return res.size();
}
return res.size();
}
execl 或者 execv
一般跟fork一起&#xff0c;fork本意就是复制父进程
fork 函数会新生成一个进程&#xff0c;调用 fork 函数的进程为父进程&#xff0c;新生成的进程为子进程。在父进程中返回子进程的 pid&#xff0c;在子进程中返回 0&#xff0c;失败返回-1。系统先给新的进程分配资源&#xff0c;例如存储数据和代码的空间。然后把原来的进程的所有值都复制到新的新进程中&#xff0c;只有少数值与原来的进程的值不同。相当于克隆了一个自己。
这样做的坏处&#xff1a;就是过分依赖父进程&#xff0c;如果是兄弟进程就不好用这种方式加载了
posix_spawn是比较好解决兄弟进程&#xff0c;也就是A进程 posix_spawn B进程&#xff0c;通用B进程也可以 posix_spawn A进程&#xff0c;相互之间不干扰