Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Multithreading 调试多线程(使用克隆)程序中的分段错误_Multithreading_Debugging_Segmentation Fault_Clone_System Calls - Fatal编程技术网

Multithreading 调试多线程(使用克隆)程序中的分段错误

Multithreading 调试多线程(使用克隆)程序中的分段错误,multithreading,debugging,segmentation-fault,clone,system-calls,Multithreading,Debugging,Segmentation Fault,Clone,System Calls,我编写了一个代码来创建一些线程,每当其中一个线程完成时,就会创建一个新线程来替换它。由于我无法使用pthreads创建大量线程(>450),因此我改用克隆系统调用。(请注意,我知道拥有如此多的线程意味着什么,但这个程序只是为了强调系统)。 由于clone()要求将子线程的堆栈空间指定为参数,因此我为每个线程malloc所需的堆栈空间块,并在线程完成时释放它。当线程完成时,我向父线程发送一个信号,通知它相同的情况。 代码如下: #include <sched.h> #include &

我编写了一个代码来创建一些线程,每当其中一个线程完成时,就会创建一个新线程来替换它。由于我无法使用pthreads创建大量线程(>450),因此我改用克隆系统调用。(请注意,我知道拥有如此多的线程意味着什么,但这个程序只是为了强调系统)。
由于clone()要求将子线程的堆栈空间指定为参数,因此我为每个线程malloc所需的堆栈空间块,并在线程完成时释放它。当线程完成时,我向父线程发送一个信号,通知它相同的情况。
代码如下:

#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <errno.h>

#define NUM_THREADS 5

unsigned long long total_count=0;
int num_threads = NUM_THREADS;
static int thread_pids[NUM_THREADS];
static void *thread_stacks[NUM_THREADS];
int ppid;

int worker() {
 int i;
 union sigval s={0};
 for(i=0;i!=99999999;i++);
 if(sigqueue(ppid, SIGUSR1, s)!=0)
  fprintf(stderr, "ERROR sigqueue");
 fprintf(stderr, "Child [%d] done\n", getpid());
 return 0;
}

void sigint_handler(int signal) {
 char fname[35]="";
 FILE *fp;
 int ch;
 if(signal == SIGINT) {
  fprintf(stderr, "Caught SIGINT\n");
  sprintf(fname, "/proc/%d/status", getpid());
  fp = fopen(fname,"r");
  while((ch=fgetc(fp))!=EOF)
   fprintf(stderr, "%c", (char)ch);
  fclose(fp);
  fprintf(stderr, "No. of threads created so far = %llu\n", total_count);
  exit(0);
 } else
  fprintf(stderr, "Unhandled signal (%d) received\n", signal);
}


int main(int argc, char *argv[]) {
 int rc, i; long t;
 void *chld_stack, *chld_stack2;
 siginfo_t siginfo;
 sigset_t sigset, oldsigset;

 if(argc>1) {
  num_threads = atoi(argv[1]);
  if(num_threads<1) {
   fprintf(stderr, "Number of threads must be >0\n");
   return -1;
  }
 }
 signal(SIGINT, sigint_handler);

 /* Block SIGUSR1 */
 sigemptyset(&sigset);
 sigaddset(&sigset, SIGUSR1); 
 if(sigprocmask(SIG_BLOCK, &sigset, &oldsigset)==-1)
  fprintf(stderr, "ERROR: cannot block SIGUSR1 \"%s\"\n", strerror(errno));

 printf("Number of threads = %d\n", num_threads);
 ppid = getpid();
 for(t=0,i=0;t<num_threads;t++,i++) {
  chld_stack = (void *) malloc(148*512);
  chld_stack2 = ((char *)chld_stack + 148*512 - 1);
  if(chld_stack == NULL) {
   fprintf(stderr, "ERROR[%ld]: malloc for stack-space failed\n", t);
   break;
  }
  rc = clone(worker, chld_stack2, CLONE_VM|CLONE_FS|CLONE_FILES, NULL);
  if(rc == -1) {
   fprintf(stderr, "ERROR[%ld]: return code from pthread_create() is %d\n", t, errno);
   break;
  }
  thread_pids[i]=rc;
  thread_stacks[i]=chld_stack;
  fprintf(stderr, " [index:%d] = [pid:%d] ; [stack:0x%p]\n", i, thread_pids[i], thread_stacks[i]);
  total_count++;
 }
 sigemptyset(&sigset);
 sigaddset(&sigset, SIGUSR1); 
 while(1) {
  fprintf(stderr, "Waiting for signal from childs\n");
  if(sigwaitinfo(&sigset, &siginfo) == -1)
   fprintf(stderr, "- ERROR returned by sigwaitinfo : \"%s\"\n", strerror(errno));
  fprintf(stderr, "Got some signal from pid:%d\n", siginfo.si_pid);

  /* A child finished, free the stack area allocated for it */ 
  for(i=0;i<NUM_THREADS;i++) {
   fprintf(stderr, " [index:%d] = [pid:%d] ; [stack:%p]\n", i, thread_pids[i], thread_stacks[i]);
   if(thread_pids[i]==siginfo.si_pid) {
    free(thread_stacks[i]);
    thread_stacks[i]=NULL;
    break;
   }
  }
  fprintf(stderr, "Search for child ended with i=%d\n",i);
  if(i==NUM_THREADS) 
   continue;
  /* Create a new thread in its place */
  chld_stack = (void *) malloc(148*512);
  chld_stack2 = ((char *)chld_stack + 148*512 - 1);
  if(chld_stack == NULL) {
   fprintf(stderr, "ERROR[%ld]: malloc for stack-space failed\n", t);
   break;
  }
  rc = clone(worker, chld_stack2, CLONE_VM|CLONE_FS|CLONE_FILES, NULL);
  if(rc == -1) {
   fprintf(stderr, "ERROR[%ld]: return code from clone() is %d\n", t, errno);
   break;
  }
  thread_pids[i]=rc;
  thread_stacks[i]=chld_stack;
  total_count++;
 }
 fprintf(stderr, "Broke out of infinite loop. [total_count=%llu] [i=%d]\n",total_count, i);
 return 0;
}
我尝试使用以下命令行在valgrind下运行它:

valgrind --tool=memcheck --leak-check=yes --show-reachable=yes -v --num-callers=20 --track-fds=yes ./a.out
但它在valgrind下一直运行,没有任何问题。
我对如何调试这个程序感到困惑。我觉得这可能是堆栈溢出或其他原因,但增加堆栈大小(高达74KB)并没有解决问题。

我唯一的问题是为什么和哪里有分段错误,或者如何调试这个程序。

我想我找到了答案

第一步

替换此项:

static int thread_pids[NUM_THREADS];
static void *thread_stacks[NUM_THREADS];
chld_stack2 = ((char *)chld_stack + 148*512 - 1);
据此:

static int *thread_pids;
static void **thread_stacks;
chld_stack2 = ((char *)chld_stack + 148*512);
步骤2

在主函数中添加以下内容(检查参数后):

步骤3

替换此项:

static int thread_pids[NUM_THREADS];
static void *thread_stacks[NUM_THREADS];
chld_stack2 = ((char *)chld_stack + 148*512 - 1);
据此:

static int *thread_pids;
static void **thread_stacks;
chld_stack2 = ((char *)chld_stack + 148*512);
在这两个地方你都使用它

我不知道这是否真的是你的问题,但经过测试,我没有得到任何分割错误。顺便说一句,当使用超过5个线程时,我只得到了分段错误

希望我能帮忙

编辑:使用1000个线程进行测试,运行良好

edit2:解释为什么线程PID和线程堆栈的静态分配会导致错误

最好的方法是举个例子

假设num_线程=10

以下代码中出现问题:

for(t=0,i=0;t<num_threads;t++,i++) {
...

thread_pids[i]=rc; 
thread_stacks[i]=chld_stack;

...
}

for(t=0,i=0;t找到了实际问题。
当工作线程使用sigqueue()向父进程发送信号时,父线程有时会在子线程执行return语句之前立即获取控件并释放堆栈。当同一子线程使用return语句时,由于堆栈损坏,会导致分段错误。
为了解决这个问题,我更换了

exit(0)
而不是

return 0;

老实说,我对克隆功能一无所知,但我在OpenMP中看到了这一点。你是否尝试过更改堆栈大小限制,ulimit-shi George。我在amd64上尝试过原始代码,但它没有得到分段。我很好奇你的系统出了什么问题。你能简单解释一下吗?谢谢shi George,你的解决方案对我不起作用:(顺便说一句,你能解释一下为什么这些更改对你有效吗?我注意到我没有任何问题,我没有静态地为每个线程分配内存。步骤1中使用的数据结构仅由主线程用于包含元数据。每个线程的堆栈是动态分配的。