You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自定义shell hsh读取管道输入触发SIGPIPE信号求助

问题:自定义Shell(hsh)读取管道输入时触发SIGPIPE信号终止

我在开发名为hsh的自定义Shell时,尝试将用户传入的全部数据读取到缓冲区处理,但进程因signal 13(SIGPIPE,管道破裂信号)终止。

执行命令

valgrind echo "                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   /bin/ls                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    " | ./hsh

执行结果

==203856== Process terminating with default action of signal 13 (SIGPIPE)
==203856==    at 0x4960077: write (write.c:26)
==203856==    by 0x48E0E8C: _IO_file_write@@GLIBC_2.2.5 (fileops.c:1181)
==203856==    by 0x48E17A7: new_do_write (fileops.c:449)
==203856==    by 0x48E17A7: _IO_new_file_xsputn (fileops.c:1255)
==203856==    by 0x48E17A7: _IO_file_xsputn@@GLIBC_2.2.5 (fileops.c:1197)
==203856==    by 0x48E037F: fputs_unlocked (iofputs_u.c:37)
==203856==    by 0x10A7AE: ??? (in /usr/bin/echo)
==203856==    by 0x4876082: (below main) (libc-start.c:308)
==203856==
==203856== HEAP SUMMARY:
==203856==     in use at exit: 0 bytes in 0 blocks
==203856==   total heap usage: 228 allocs, 228 frees, 25,267 bytes allocated
==203856==
==203856== All heap blocks were freed -- no leaks are possible
==203856==
==203856== For lists of detected and suppressed errors, rerun with: -s
==203856== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

数据读取步骤

  • 使用lseek定位到文件末尾,统计写入的字节数
  • 再次使用lseek回到文件起始位置
  • 分配对应字节数+1(用于空字符)的缓冲区
  • 分块读取数据到临时缓冲区
  • 将临时缓冲区的数据复制到实际缓冲区

但该方法触发了SIGPIPE信号,不清楚原因。

代码实现

注:所有名称带下划线(_)的函数已在别处实现,其原型声明在"main.h"头文件中。

#include "main.h"
#define CHUNK_SIZE 1000

/**
 * _getlines - Read the command lines
 * @commands: The list where commands will be stored
 * Return: On success - number of commands
 *         On error - "-1"
 */
int _getlines(char **commands)
{
    char *line, *page, *chunk, ch, qoute;
    ssize_t bytes_read, total_bytes_read = 0;
    int j = 0, cmd_count = 0, i = 0, file_size;
    line = malloc(CHUNK_SIZE * sizeof(char));
    file_size = lseek(STDIN_FILENO, 0, SEEK_END);
    lseek(STDIN_FILENO, 0, SEEK_SET);
    page = malloc(file_size);
    if (page == NULL)
        return (-1);
    while ((bytes_read = read(STDIN_FILENO, line, CHUNK_SIZE) > 0))
    {
        _strcpy(page + total_bytes_read, line);
        total_bytes_read += bytes_read;
    }
    if (bytes_read == -1)
    {
        free(page);
        return (-1);
    }
    printf("%s\n", page);
    while (i < total_bytes_read)
    {
        ch = page[i++];
        if (ch == '\"' || ch == '\'')
        {
            line[j++] = ch;
            qoute = ch;
            do {
                ch = page[i++];
                line[j++] = ch;
            } while (ch != qoute);
        }
        else if (ch == '\n')
        {
            line[j++] = '\0';
            commands[cmd_count] = _strdup(line);
            j = 0;
            (cmd_count)++;
        }
        else
            line[j++] = ch;
    }
    if (cmd_count == 0)
    {
        commands[cmd_count] = _strdup(line);
        cmd_count++;
    }
    free(line);
    return (cmd_count);
}

问题分析与解决

核心原因

从Valgrind输出可以看到,SIGPIPE信号是由echo进程触发的,而非你的hsh进程。当管道的读端(hsh)提前关闭,而写端(echo)还在尝试写入数据时,就会触发SIGPIPE信号。

你的代码存在几个关键问题:

  1. lseek不适用于管道:管道是流式设备,不支持随机访问,lseek(STDIN_FILENO, 0, SEEK_END)会直接返回-1,但你未检查返回值,导致file_size为负数,后续malloc(file_size)会分配超大内存块(malloc接受无符号参数,负数会被转为大正数),引发一系列异常。
  2. 读取循环逻辑错误:while ((bytes_read = read(STDIN_FILENO, line, CHUNK_SIZE) > 0))的括号位置错误,read的结果先和0比较,再赋值给bytes_read,导致bytes_read只能是0或1,而非实际读取的字节数。这会让你错误处理数据,甚至提前结束读取,使hsh关闭管道读端,此时echo仍在写入,触发SIGPIPE。
  3. 缓冲区越界风险:line仅分配了CHUNK_SIZE大小的内存,但处理输入时未检查j是否超过该大小,容易导致缓冲区溢出,破坏进程内存,引发不可预测的终止行为。

修复步骤

  1. 移除管道上的lseek操作:对于管道、套接字等流式输入,只能通过循环读取直到read返回0(输入结束)或-1(错误),无法提前获取总长度。
  2. 修正读取循环的括号:正确写法为while ((bytes_read = read(STDIN_FILENO, line, CHUNK_SIZE)) > 0),确保bytes_read获取实际读取的字节数。
  3. 动态扩展缓冲区:因为无法提前知道输入长度,需动态扩展page缓冲区,比如每次读取后用realloc重新分配更大的内存。
  4. 添加缓冲区边界检查:向line写入数据时,检查j是否接近CHUNK_SIZE,避免溢出。
  5. 检查系统调用返回值:lseek、malloc、read等系统调用都可能失败,必须检查返回值并处理错误。

修复后的读取逻辑示例

// 替换原有的读取逻辑部分
ssize_t bytes_read;
char *page = NULL;
size_t total_bytes_read = 0;
char line[CHUNK_SIZE]; // 用栈数组代替malloc,减少内存管理负担

while ((bytes_read = read(STDIN_FILENO, line, CHUNK_SIZE)) > 0) {
    // 动态扩展page缓冲区
    char *new_page = realloc(page, total_bytes_read + bytes_read + 1);
    if (!new_page) {
        free(page);
        return -1;
    }
    page = new_page;
    memcpy(page + total_bytes_read, line, bytes_read);
    total_bytes_read += bytes_read;
}

if (bytes_read == -1) {
    free(page);
    return -1;
}

// 确保缓冲区以空字符结尾
if (page) {
    page[total_bytes_read] = '\0';
}

内容的提问来源于stack exchange,提问作者Mohammed Ahmed

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 17:57:03