【问题标题】:The GNU's clone() function got stuckGNU 的 clone() 函数卡住了
【发布时间】:2018-02-15 15:57:50
【问题描述】:

我正在 CentOS 7.3 中使用 Code::Blocks 16.01 编写程序。该程序包含主函数和由 clone() 函数创建的子进程(或所谓的线程)。我的目的是通过去掉 CLONE_FS 参数来测试 chdir() 函数是否影响主函数中的工作目录。希望它有效,但会出现新问题。请先阅读我的代码:

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <dirent.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <sched.h>
#include <fcntl.h>

#define STACK_SIZE 1024*1024*8

int thread_func(void *arg){
    int i;
    char *cdir;
    for(i = 0; i < 100; i++){
        switch(i%3){
        case 0:
            chdir("/home/centos/dirtest/dir000");
            break;
        case 1:
            chdir("/home/centos/dirtest/dir001");
            break;
        case 2:
            chdir("/home/centos/dirtest/dir002");
            break;
        }
        cdir = getcwd(NULL,0);
        fprintf(stderr,"Child Thread in # %d: %s\n",i,cdir);
    }
    free(cdir);
    return 1;
}

int main(){
     void *pstack = (void*)mmap(NULL, STACK_SIZE,
                                       PROT_READ | PROT_WRITE,
                                       MAP_PRIVATE | MAP_ANONYMOUS | MAP_ANON,
                                       -1, 0);
    if(MAP_FAILED != pstack){
        int ret,i;
        char *cdir;
        ret = clone(thread_func,
                        (void*)((unsigned char *)pstack + STACK_SIZE),
                        CLONE_VM | CLONE_FILES ,
                        NULL);
        if(ret == -1){
            fprintf(stderr,"Thread create failed\n");
            return 0;
        }
        for(i = 0; i < 100; i++){
            cdir = getcwd(NULL,0);
            fprintf(stderr,"Main Function in # %d: %s\n",i,cdir);
        }
          free(cdir);
    }
    return 1;
}

但是,当我通过命令行在终端中运行生成的 exe 文件时,它卡住了。主函数和子进程都无法完成它的“for”循环,我不得不按“Ctrl-C”终止程序。

有人能找到问题吗?

********这里是新进展的编辑********

感谢 cmets,我对 getcwd() 函数进行了更改。我还添加了 waitpid() 函数。但是,它显示失败。修改后的代码如下:

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <dirent.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/wait.h>
#include <sched.h>
#include <fcntl.h>

#define STACK_SIZE 1024*1024*8

int thread_func(void *arg){
    int i;
    char cdir[1024];
    for(i = 0; i < 100; i++){
        switch(i%3){
        case 0:
            chdir("/home/centos/dirtest/dir000");
            break;
        case 1:
            chdir("/home/centos/dirtest/dir001");
            break;
        case 2:
            chdir("/home/centos/dirtest/dir002");
            break;
        }
        getcwd(cdir,sizeof(cdir));
        fprintf(stderr,"Child Thread in # %d: %s\n",i,cdir);
    }

    return 1;
}

int main(){

     /*void *pstack = (void*)mmap(NULL, STACK_SIZE,
                                       PROT_READ | PROT_WRITE,
                                       MAP_PRIVATE | MAP_ANONYMOUS ,
                                       -1, 0);
    if(MAP_FAILED != pstack){*/
        void *pstack = malloc(STACK_SIZE);
        int ret,i;
        char cdir[1024];
        ret = clone(thread_func,
                        (void*)((char *)pstack + STACK_SIZE),
                        CLONE_VM | CLONE_FILES | CLONE_SIGHAND | CLONE_THREAD,
                        NULL);
        if(ret == -1){
            fprintf(stderr,"Thread create failed\n");
            goto mem;
        }
        for(i = 0; i < 100; i++){
            getcwd(cdir,sizeof(cdir));
            fprintf(stderr,"Main Function in # %d: %s\n",i,cdir);
        }
        ret = waitpid(ret,0,0);
        if(ret == -1){
            fprintf(stderr,"waitpid failed\n");
        }
 mem:
    //}
    free(pstack);
    return 1;
}

【问题讨论】:

  • 您的进程可以创建的线程数是否有限制?这个限制小于100吗?更严重的是,您是否释放了足够的内存?您在thread_func() 中从getcwd() 泄漏了100 个返回值中的99 个,在main() 中从getcwd() 泄漏了另外100 个返回值中的99 个。
  • 乔纳森·莱弗勒,感谢您的评论。 getcwd() 是关键问题。现在我修改了这个调用,但是又出现了一个问题:main函数虽然完成了它的循环,但是子线程没有。

标签: c linux multithreading clone


【解决方案1】:

我很确定问题之一是您有多个执行线程接触 libc,而实际上没有为它准备 libc。当线程正常生成时,使用 pthreads,它们运行一些代码,为接触 stdio 的多个线程准备 libc(您打印到stderr),为malloc 启用锁定(因为您在 getcwd 中执行 mallocs),设置 TLS 并通常确保事情不会相互碰撞。

你什么都没做。您当然可以手动使用clone 生成自己的线程,但是您有责任确保您调用的所有代码都是线程安全的。您不能只调用 libc 并希望获得最好的结果,那是行不通的。 Libc 期望要么只有一个线程在执行,要么线程是用 pthreads 创建的。

【讨论】:

  • 感谢您的评论。 getcwd 功能实际上是关键原因。现在我对代码进行了更改并编辑了原始帖子。出现了一个新问题,导致主函数无法等待子线程完成其“for”循环。
猜你喜欢
  • 1970-01-01
  • 2019-06-07
  • 1970-01-01
  • 2012-06-23
  • 2021-10-08
  • 2016-11-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多