system 和 exec 的区别

在 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 创建了子进程。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值