getline 中的段错误

Segfault in getline

下面的代码应该读取文件“rules.txt”并将其逐行写入设备。 流程应该是:

  1. 从 rules.txt
  2. 中读取行
  3. 回显到设备

由于 readline,以下代码总是以段错误结尾,我不知道为什么:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <fcntl.h>
#include <ctype.h>
#include <termios.h>
#include <sys/types.h>
#include <sys/mman.h>
  

#define BUFFER_LENGTH 256

int main()
{
    char *line;
    size_t len = BUFFER_LENGTH;
    
    int fd = open("./rules.txt", O_RDONLY);
    if(fd == -1)
    { perror("open failed"); return 0; }

    FILE* fout = fopen("/sys/class/Rule_Table_Class/Rule_Table_Class_Rule_Table_Device/sysfs_att", "w+");

    if(fout == NULL)
    { close(fd); perror("fopen failed, log.txt is busy!"); return 0; }

    while (1) 
    {
        line = (char*) malloc(len*sizeof(char));
        
        if(line==NULL){
perror("malloc failed!"); return 0; 
}

        int bytesRead = getline(&line, &len, fd);
        
        if (bytesRead == -1) 
        {
            perror("Failed to read the message from the device.");
            return errno;
        }

        sprintf(line,"%s","lala");
        printf("line = %s", line);
    }

    fclose(fout);
    close(fd);
    return 0;
}



编辑:

我更正了代码,但仍然出现段错误。这是更正后的代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <fcntl.h>
#include <ctype.h>
#include <termios.h>
#include <sys/types.h>
#include <sys/mman.h>
  

#define BUFFER_LENGTH 256

int main()
{
    FILE * fp;
    char * line = NULL;
    size_t len = 0;
    ssize_t read;
    
   fp = fopen("./rules.txt", "r");


    if(fp  == NULL)
    { perror("open failed"); return 0; }

    FILE* fout = fopen("/sys/class/Rule_Table_Class/Rule_Table_Class_Rule_Table_Device/sysfs_att", "w+");

    if(fout == NULL)
    {  perror("fopen failed!"); return 0; }

    while (1) 
    {
        

        ssize_t bytesRead = getline(&line, &len, fp);
        
        if (bytesRead == -1) 
        {
            return 0;
        }

        printf("line = %s", line);
        fprintf(line,"%s",fout);
    }

    fclose(fout);
    fclose(fp);
    return 0;
}

getline 函数将 FILE * 作为参数,而不是 int。替换以下行:

int fd = open("./rules.txt", O_RDONLY);

由;

FILE *fin = fopen("./rules.txt", "r");

相应地修复以下行中的错误检查,就像您对 fout 所做的那样。

然后替换行:

int bytesRead = getline(&line, &len, fd);

现在应该使用 fin:

ssize_t bytesRead = getline(&line, &len, fin);

请注意 getline returns ssize_t,而不是 int。 您也从不写信给 fout,但我猜您仍在编写此代码。

确保启用编译器警告,因为您的编译器肯定会警告您使用 int 参数,而应该是 FILE *

首先,正确的核心getline()循环是

 /* FILE     *in = fopen(..., "r"); */
    char   *line = NULL;
    size_t  size = 0;

    while (1) {
        ssize_t  len = getline(&line, &size, in);
        if (len < 0)
            break;

        /* You have 'len' chars at 'line', with line[len] == '[=10=]'. */
    }

所以,导致段错误的不是 getline(),而是你的 fprintf(line, "%s", fout); 应该是 fprintf(fout, "%s", line); 或者只是 fputs(line, fout);,或者 fwrite(line, 1, bytesRead, fout); 因为该行可以包含嵌入的 NUL 字节,fprintf()fputs() 认为字符串标记结束。


如果我们修改代码,将源文件名和目标文件名作为命令行参数(- 表示标准输入或标准输出),这是我个人希望看到的:

/* SPDX-License-Identifier: CC0-1.0 */

/* This tells the GNU C library to expose POSIX features, including getline(). */
#define  _POSIX_C_SOURCE  200809L

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>

int main(int argc, char *argv[])
{
    if (argc != 3 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
        const char *self = (argc > 0 && argv && argv[0] && argv[0][0]) ? argv[0] : "(this)";
        fprintf(stderr, "\n");
        fprintf(stderr, "Usage: %s [ -h | --help ]\n", self);
        fprintf(stderr, "       %s SOURCE TARGET\n", self);
        fprintf(stderr, "\n");
        fprintf(stderr, "This copies all content from SOURCE to TARGET, line by line.\n");
        fprintf(stderr, "Use '-' for standard input source or standard output target.\n");
        fprintf(stderr, "\n");
        return EXIT_SUCCESS;
    }

    const char *srcpath = argv[1];
    const char *dstpath = argv[2];

    /* If the path is "-", set it to NULL. */
    if (srcpath && !strcmp(srcpath, "-"))
        srcpath = NULL;
    if (dstpath && !strcmp(dstpath, "-"))
        dstpath = NULL;

    FILE *src, *dst;

    /* Open source for reading. If srcpath is NULL, use stdin. */
    if (srcpath) {
        src = fopen(srcpath, "r");
        if (!src) {
            fprintf(stderr, "%s: %s.\n", srcpath, strerror(errno));
            return EXIT_FAILURE;
        }
    } else {
        src = stdin;
    }

    /* Open target for writing. If dstpath is NULL, use stdout. */
    if (dstpath) {
        dst = fopen(dstpath, "w");
        if (!dst) {
            fprintf(stderr, "%s: %s.\n", dstpath, strerror(errno));
            fclose(src);
            return EXIT_FAILURE;
        }
    } else {
        dst = stdout;
    }

    char          *line = NULL;
    size_t         size = 0;
    unsigned long  linenum = 0;

    while (1) {
        ssize_t  len = getline(&line, &size, src);
        if (len < 0)
            break;

        linenum++;

        if (fwrite(line, 1, len, dst) != (size_t)len) {
            fprintf(stderr, "%s: Write error on line %lu.\n", dstpath ? dstpath : "(standard output)", linenum);
            fclose(dst);
            fclose(src);
            return EXIT_FAILURE;
        }
    }

    /* Technically, we don't need to release dynamically allocated (non-shared) memory,
       because we're just about to exit, and the OS will automagically do that for us.
       We can do this at any point we want during the loop, too. */
    free(line);
    line = NULL;
    size = 0;

    /* We do not know why getline() returned -1.  Check if an error occurred, and whether we're at end of input. */
    if (ferror(src) || !feof(src)) {
        fprintf(stderr, "%s: Read error on line %lu.\n", srcpath ? srcpath : "(standard input)", linenum);
        fclose(dst);
        fclose(src);
        return EXIT_FAILURE;
    }

    /* Check if any write errors have occurred. */
    if (ferror(dst)) {
        fprintf(stderr, "%s: Write error on line %lu.\n", dstpath ? dstpath : "(standard output)", linenum);
        fclose(dst);
        fclose(src);
    }

    /* Read errors should not occur at close time, but it costs very little for us to test anyway. */
    if (fclose(src)) {
        fprintf(stderr, "%s: Read error on line %lu.\n", srcpath ? srcpath : "(standard input)", linenum);
        fclose(dst);
        return EXIT_FAILURE;
    }

    /* Write errors can occur at close time, if the output has been buffered, or the target is on
       a remote filesystem.  Again, it costs us very little to check. */
    if (fclose(dst)) {
        fprintf(stderr, "%s: Write error on line %lu.\n", dstpath ? dstpath : "(standard output)", linenum);
        return EXIT_FAILURE;
    }

    /* No errors; target is an identical copy of the source file. */
    fprintf(stderr, "%lu lines copied successfully.\n", linenum);
    return EXIT_SUCCESS;
}

SPDX-License-Identifier是表示代码授权的常用方式。我使用 CC0-1.0,这基本上意味着 “随心所欲使用,只是不要因为任何问题而责怪作者:没有保证,没有保证。”

#define _POSIX_C_SOURCE 200809L 告诉 GNU C 库我们希望它公开 POSIX.1-2008 特性,其中包括 getline()。如果您查看 man 3 getline,您会在概要部分看到 glibc 2.10 及更高版本要求将 _POSIX_C_SOURCE 至少定义为 200809L.

大部分代码是打印用法,从命令行获取源文件名和目标文件名,并将-作为一个特殊名称“标准流”处理,这样就可以通过指定 - 作为输入 and/or 输出文件名,甚至在管道中使用。

getline()循环使用fwrite()将行写入目标文件。这样,如果输入包含嵌入的 NUL 字节 ([=29=]),目标文件仍将与源文件相同。

循环后,我们丢弃行缓冲区,尽管由于程序即将退出,我们可以忽略它(因为 OS 将释放所有动态分配的(非共享)内存,当我们无论如何退出)。

我喜欢用 ferror(src) || !feof(src) 检查代码是否在 src 中发生错误或 src 没有到达输入末尾;并检查 fclose() 的 return 值,以防报告延迟(写入)错误。诚然,fclose() 永远不会对只读文件失败,而 fclose() 应该只对我们在特定情况下写入的文件失败,但检查成本非常低,这样用户运行 如果程序检测到数据丢失,将通知程序。

我认为忽略检查此类错误(尤其是“因为它们很少发生”)在道德上是应该受到谴责的,因为此类测试是人类用户了解操作是否成功的唯一方式,或者是否存在一些奇怪的错误出现问题。调查任何问题取决于人,报告可检测的问题取决于我们的程序。