首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >线程和vfork

线程和vfork
EN

Unix & Linux用户
提问于 2014-10-24 07:55:51
回答 1查看 1.4K关注 0票数 4

当其中一个线程执行vfork时,我试图检查其中一个线程到底发生了什么。规范说父“控制线程”被“挂起”,直到子进程调用exec*或_exit。据我所知,协商一致意见是,它意味着整个父进程(即:它的所有线程)都被挂起。我想用一个实验来证实。到目前为止,我进行了几个实验,所有这些实验都表明其他线程正在运行。由于我没有linux经验,我怀疑我对这些实验的解释是错误的,学习这些结果的真实解释可以帮助我避免生活中的进一步误解。下面是我的感想:

实验I

代码语言:javascript
复制
#include<unistd.h>
#include<signal.h>
#include<errno.h>
#include<cstring>
#include<string>
#include<iostream>
using namespace std;
void * job(void *x){
  int pid=vfork();
  if(-1==pid){
    cerr << "failed to fork: " << strerror(errno) << endl;
    _exit(-3);
  }
  if(!pid){
    cerr << "A" << endl;
    cerr << "B" << endl;
    if(-1 == execlp("/bin/ls","ls","repro.cpp",(char*)NULL)){
      cerr << "failed to exec : " << strerror(errno) << endl;
      _exit(-4);//serious problem, can not proceed
    }
  }
  return NULL;
}
int main(){
  signal(SIGPIPE,SIG_IGN);
  signal(SIGCHLD,SIG_IGN);
  const int thread_count = 4;
  pthread_t thread[thread_count];
  int err;
  for(size_t i=0;i<thread_count;++i){
    if((err = pthread_create(thread+i,NULL,job,NULL))){
      cerr << "failed to create pthread: " << strerror(err) << endl;
      return -7;
    }
  }
  for(size_t i=0;i<thread_count;++i){
    if((err = pthread_join(thread[i],NULL))){
      cerr << "failed to join pthread: " << strerror(err) << endl;
      return -17;
    }
  }
}

有44个线程,它们都在子线程中执行vfork和exec。每个子进程执行两个输出操作,两个输出操作位于vfork和exec "A“和"B”之间。理论建议输出应该是ABABABABABA...without嵌套。但是,输出是完全混乱的:例如:

代码语言:javascript
复制
AAAA



BB
B

B

实验II

由于怀疑在vfork之后使用I/O lib可能是个坏主意,我已经用以下方法替换了job()函数:

代码语言:javascript
复制
const int S = 10000000;
int t[S];
void * job(void *x){
  int pid=vfork();
  if(-1==pid){
    cerr << "failed to fork: " << strerror(errno) << endl;
    _exit(-3);
  }
  if(!pid){
    for(int i=0;i<S;++i){
      t[i]=i;
    }
    for(int i=0;i<S;++i){
      t[i]-=i;
    }
    for(int i=0;i<S;++i){
      if(t[i]){
        cout << "INCONSISTENT STATE OF t[" << i << "] = " << t[i] << " DETECTED" << endl;
      }
    }
    if(-1 == execlp("/bin/ls","ls","repro.cpp",(char*)NULL)){
      cerr << "failed to execlp : " << strerror(errno) << endl;
      _exit(-4);
    }
  }
  return NULL;
}

这一次,我执行两个循环,以便第二个循环取消第一个循环的结果,因此在结束时,全局表t[]应该返回到初始状态(根据定义,它都是零)。如果进入子进程会冻结其他线程,使它们在当前子进程完成循环之前无法调用v叉,那么数组应该是末尾的所有零。并且我确认,当我使用fork()而不是vfork()时,上面的代码不会产生任何输出。但是,当我将fork()改为vfork()时,会向stdout报告大量的不一致之处。

实验III

这里还描述了另一个实验,https://unix.stackexchange.com/a/163761/88901 --它涉及调用睡眠,但实际上,当我用一个长for循环替换它时,结果是一样的。

EN

回答 1

Unix & Linux用户

回答已采纳

发布于 2014-10-24 10:21:59

用于vork的Linux手册页非常具体:

vfork()fork(2)的不同之处在于调用线程被挂起,直到子线程终止。

这不是整个过程,而是调用线程。这种行为不受POSIX或其他标准的保证,其他实现可能会做不同的事情(包括简单地用普通的vfork实现fork)。

(Rich还在维福克被认为危险中注意到了这种行为。)

在多线程程序中使用fork已经很难解释了,调用vfork至少也同样糟糕。您的测试中充满了未定义的行为,您甚至不允许在vfork'd子内部调用一个函数(更不用说做I/O了),除了exec-type函数和_exit (甚至不允许调用exit,并且返回会导致混乱)。

下面是您的示例,我认为它几乎没有未定义的行为,假设编译器/实现不输出函数在ints上调用原子读写。(问题之一是在vfork之后写入start --这是不允许的。)错误处理被省略,以保持其简短。

代码语言:javascript
复制
#include<unistd.h>
#include<signal.h>
#include<errno.h>
#include<atomic>
#include<cstring>
#include<string>
#include<iostream>

std::atomic<int> start;
std::atomic<int> counter;
const int thread_count = 4;

void *vforker(void *){
  std::cout << "vforker starting\n";
  int pid=vfork();
  if(pid == 0){
    start = 1;
    while (counter < (thread_count-1))
      ;
    execlp("/bin/date","date",nullptr);
  }
  std::cout << "vforker done\n";
  return nullptr;
}

void *job(void *){
  while (start == 0)
    ;
  counter++;
  return NULL;
}

int main(){
  signal(SIGPIPE,SIG_IGN);
  signal(SIGCHLD,SIG_IGN);
  pthread_t thread[thread_count];
  counter = 0;
  start   = 0;

  pthread_create(&(thread[0]), nullptr, vforker, nullptr);
  for(int i=1;i<thread_count;++i)
    pthread_create(&(thread[i]), nullptr, job, nullptr);

  for(int i=0;i<thread_count;++i)
    pthread_join(thread[i], nullptr);
}

其思想是:普通线程等待(繁忙循环)原子全局变量start在增量全局原子计数器之前成为1。执行vfork的线程将start设置为v叉子节点中的1,然后等待其他线程增加计数器(再次是繁忙循环)。

如果其他线程在vfork期间被挂起,则无法取得任何进展:挂起的线程永远不会增加counter (它们在start设置为1之前就会挂起),因此vforker线程将陷入无限繁忙的等待状态。

票数 4
EN
页面原文内容由Unix & Linux提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://unix.stackexchange.com/questions/163947

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档