在 linux 中,使用 system 和 exec 都可以执行一个程序或者执行一个命令。两者的区别如下:
system 中创建了一个子进程,在子进程中执行用户的命令,子进程执行完毕之后,system 会返回。exec 不会创建子进程,而是直接用 exec 要执行的进程来代替当前的进程,并且如果在执行过程中没有出现错误,那么 exec 是不会返回的,调用 exec 的进程永远也回不来了。
1 exec
使用如下两个代码来演示 exec 的执行过程。
如下代码使用 gcc hello.c -o hello 来编译出可执行文件 hello。这段代码中每隔一秒中打印一次 hello,打印 3 次之后退出。
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>int main() {int i = 0;while (i < 3) {printf("hello\n");sleep(1);i++;}return 0;
}
下边的代码中,main 函数中首先创建了一个线程,线程中每隔 1 秒打印一次 in thread。打印 5 次之后,调用 execv 来执行一个程序,第一次执行的程序是 hello1,是一个不存在的可执行文件,这种情况下, execv 会返回错误。第二次调用 execv 来执行 hello,hello 得到执行。
#include <iostream>
#include <thread>
#include <unistd.h>void thread_func() {while (1) {std::cout << "in thread\n";sleep(1);}
}int main() {std::thread t(thread_func);sleep(5);std::cout << "before wrong exec\n";int ret = execv("./hello1", NULL);if (ret < 0) {perror("exec error:");}std::cout << "after wrong exec\n";std::cout << "before right exec\n";execv("./hello", NULL);std::cout << "after right exec\n";return 0;
}
程序运行结果如下,从打印信息来看,可以得出如下 3 点:
(1)调用 exec 正确执行的时候,exec 执行的可执行文件覆盖了整个程序的镜像,不仅仅是 main 函数所在的主线程不执行了,main 函数中创建的子线程也不运行了,调用 exec 之后 in thread 不再打印
(2)exec 执行的程序 hello,打印 3 次 hello 之后退出,这个时候 exec 也不会返回了,after right exec 没有打印
(3)当 exec 找不到可执行文件时,会返回错误,并且设置了 errno,信息是 No such file or directory
2 system
将 execv 替换程 system,代码如下。
#include <iostream>
#include <thread>
#include <unistd.h>void thread_func() {while (1) {std::cout << "in thread\n";sleep(1);}
}int main() {std::thread t(thread_func);t.detach();sleep(5);std::cout << "before wrong system\n";int ret = system("./hello1");if (ret < 0) {perror("1 system error:");}std::cout << "after wrong system\n";std::cout << "before right system\n";ret = system("./hello");if (ret < 0) {perror("2 system error:");}std::cout << "after right system\n";return 0;
}
执行结果如下,从执行结果可以看出来:
(1)system 执行的时候只是阻塞当前这个线程的执行,并没有阻塞整个进程的执行, 在 system 执行的时候,main 函数中创建的 thread 还在运行,in thread 还在打印
(2)system 执行完毕之后,会返回,代码中的 after right system 打印了出来
3 popen
在软件开发中,有的时候有这样一个需求,就是在代码中执行一个命令,并且在代码中还可以获取到执行这个命令时的打印信息。比如在控制台,我们可以很容易的执行 free 命令来查看系统内存使用情况,如果我们想在代码中执行 free 命令,并且获取 free 命令的显示信息,最后把 free 显示的信息打印出来,应该怎么实现呢 ?可以通过 popen 来实现。
如下代码可以实现上述功能。popen 结合 fgets 可以获取到命令执行的打印信息,使用完毕之后要通过 pclose() 关闭句柄。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>int main() {FILE *fp;char buffer[1024];sleep(2);fp = popen("free", "r");if (fp == NULL) {printf("Error: Failed to run command\n");return EXIT_FAILURE;}while (fgets(buffer, sizeof(buffer), fp) != NULL) {printf("result:\n");printf("%s", buffer);}pclose(fp);return EXIT_SUCCESS;
}
为了使用 strace 来追踪 popen 相关的系统调用,在调用 popen 之前睡了 2s。通过如下截图可以看到,popen 的时候使用了管道,也通过 vfork 创建了子进程。