【问题标题】:readline clash with child printf?readline 与子 printf 冲突?
【发布时间】:2010-09-28 19:49:15
【问题描述】:

在 Linux 中,readline() 在无限循环中重复读取 文本\n。然而,一旦子进程开始打印到屏幕上,r​​eadline 就不再读取新行。即使我反复按回车,readline() 也不会返回。

有人知道怎么回事吗?

根据要求提供代码示例:

char* input;
int cpid;
while(1)
{
     input = readline(">>>");
     strcpy(tempInput, input); //...does some stuff w/ tempInput
     //....
     cpid = fork();
     if(cpid == 0){
           printf("..."); printf("...");
           execl("ls", "ls", "-l", (char*) NULL); //sample execl parameters
     }
     else{
           //do some stuff...
           printf("...");
     }
     free(input);
}

    //readline(">>>") works the first time and doesn't return on subsequent calls

代码挂起位置的堆栈跟踪(永远):

Thread [1] (Suspended : Signal : SIGINT:Interrupt)  
    __read_nocancel() at ../sysdeps/unix/syscall-template.S:82 0x7ffff78f0490   
    rl_getc() at 0x7ffff7bc3727 
    rl_read_key() at 0x7ffff7bc3c90 
    readline_internal_char() at 0x7ffff7baf25f  
    readline() at 0x7ffff7baf795    
    main() at /home/.../.../xxx.c:95 0x4010a1   

编辑:对于经验丰富的 unix 开发人员来说,这可能听起来像是完全的技术问题,但是子进程是否可能以某种方式“捕获”了标准输入并且无法以某种方式释放它?

【问题讨论】:

  • 能否提供代码示例?
  • 分叉后你没有任何对readline的调用。
  • 孩子什么都没读(只是添加了一些显示 exec 调用的代码)。

标签: c linux gcc fork readline


【解决方案1】:

即使在 exec 之后,您的子进程仍会将其标准输入连接到您的终端。要么使用wait() 等待子进程完成,要么重新打开子进程的标准输入为/dev/null

close(STDIN_FILENO) && open("/dev/null", "O_WRONLY");

【讨论】:

    【解决方案2】:

    在你 fork() 之后,你的子进程执行 2 个 printf 调用,然后继续执行 while(1) 循环,所以你正在做的是在每个换行符之后创建 1 个额外的进程。

    您需要做的是在打印 2 个省略号后立即杀死孩子 (...)

    将您的代码更改为:

      if(cpid == 0){
            printf("..."); printf("..."); exec(...); exit(0); 
        }
    

    请记住,只有在 exec() 失败时才会调用 exit()。

    编辑:

    如果您打算接受某种命令行解释器的输入,那么 readline 不是一个很好的选择。它的官方手册页说:

    BUGS:
    
        It’s too big and too slow.
    

    我可以建议另一种获取输入字符串的方法:

    char c;
    inputString = calloc(0,sizeof(char));
    inputLength = 0;
    
    c = getchar();
        while(c!='\n')
        {
            inputString = realloc(inputString,(inputLength+1)*sizeof(char));
            inputString[inputLength++] = c;
            c = getchar();
        }
        inputString[inputLength] = '\0';
    

    现在您在 inputString 中获得了字符串,在 inputLength 中获得了它的长度。你可以这样做:

    execlp(inputString,inputString,0);
    

    执行所需的功能。

    【讨论】:

    • 对不起,我忘了在子分支中包含一个相当重要的 exec() 调用。我的不好:(目前的问题是父母对 readline() 的第二次调用没有返回。
    • 更新了 execl 调用。尝试了 exit(0) 并没有用。根据规范(我认为) exec(...) 永远不会返回。
    • 可能是被调用的进程导致了问题,你调用的是哪个可执行文件?
    【解决方案3】:

    这段代码不起作用,因为您对 execl() 的调用失败,因此子进程将在循环代码处恢复,这也将使用 readline()。

    使用 execl() 时必须将完整路径传递给可执行文件,或使用 execlp() 变体(搜索 PATH 环境变量)使其工作。

    即使是最简单的系统调用,最好始终检查它们的返回码。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-01-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-11-07
      • 2013-04-29
      相关资源
      最近更新 更多