循环中的getline()在嵌入式linux中不断消耗更多内存

时间:2015-03-30 08:47:19

标签: c linux memory-leaks getline

在周末运行申请后,我在早上注意到它被杀了,因为没有留下任何记忆。

在评论代码的某些部分后,我发现此过程可能存在问题,但我无法弄清楚原因。

void readData (char * path) {

// Reading and parsing config from file
FILE * fp;
size_t len = 0;
size_t read;

char *begin, *end, line[100], data[100];

fp = fopen(path, "r");
if (fp != NULL)
{
    while ((read = getline(&line, &len, fp)) != -1) {
/*      begin = strstr(line, "\"data\":[")+8;
        end = strstr(begin, "]");
        strncpy(data, begin,strlen(begin) - strlen(end));
        data[strlen(begin) - strlen(end)] = 0;
*/
        int tmp;

//      sscanf(data,"%d,%d,%d,%d,%d,%d",&tmp,&ss.tensionRaw,&tmp,&tmp,&ss.depthRaw,&ss.speedRaw);
        ss.tension = cs.tensionCoeff * ss.tensionRaw;
        ss.speed   = cs.speedCoeff * ss.speedRaw;
        ss.depth   = cs.depthCoeff * ss.depthRaw;

/*      begin = strstr(line, "\"failedRequests\":\"")+18;
        end = strstr(begin, "\"");
        strncpy(data, begin,strlen(begin) - strlen(end));
        data[strlen(begin) - strlen(end)] = 0;
        ss.connectionOK = atoi(data);*/
    }
}
fclose(fp);
}

getline会导致这样的问题吗? 我通过" top"监控应用程序的内存使用情况每隔15-20秒,它就可以获得更多4K。当我整个循环评论时,它没有增加。

2 个答案:

答案 0 :(得分:3)

getline的手册强烈暗示行指针不是固定缓冲区,而是指向已分配字符串的指针,因为它指出

  

或者,在调用 getline ()之前,* lineptr可以包含指针   到 malloc (3) - 已分配的缓冲区* n个字节的大小。如果缓冲区不是   足够大以容纳该行, getline ()使用 realloc (3)调整其大小,   根据需要更新* lineptr和* n。

答案 1 :(得分:0)

Thomas Dickey的回答(由alk编辑)是正确的。

为了理智,这里有一个关于如何使用getline()逐行正确阅读文件的示例:

#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>

/* Read the named file line by line.
 * Returns 0 if successful,
 * nonzero (== errno) error code otherwise.
*/
int line_by_line(const char *const filename)
{
    char   *line = NULL;
    size_t  size = 0;
    ssize_t len;
    long    linenum = 0L;
    FILE   *in;

    /* Invalid filename? */
    if (filename == NULL || *filename == '\0')
        return errno = EINVAL;

    /* Open file. If it fails, fopen() will set errno. */
    in = fopen(filename, "rt");
    if (in == NULL)
        return errno;

    /* Read loop. */
    while (1) {

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

        linenum++;

        /* Note: if (strlen(line) < (size_t)len),
         *       the line contains embedded NUL (zero) bytes.
        */

        /*
         * Do something with the line ...
        */
        printf("%ld: %s", linenum, line);
    }

    /* Discard possibly used line buffer. Note: free(NULL) is safe. */
    free(line);

    /* I/O error? */
    if (!feof(in) || ferror(in)) {
        fclose(in);
        return errno = EIO;
    }
    if (fclose(in))
        return errno = EIO;

    /* All done successfully. */
    return 0;
}

但是,如果它看起来不像文件实际上是面向行的数据;文件中只有一个有趣的记录。谁知道,它也可能被分成多行。

以下是我在这种情况下使用的内容:

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

size_t read_file(const char *const filename, char **const dataptr, size_t *const sizeptr)
{
    FILE   *in;
    char   *data;
    size_t  size, more;
    size_t  have = 0;

    /* Invalid parameters? */
    if (filename == NULL || *filename == '\0' || dataptr == NULL || sizeptr == NULL) {
        errno = EINVAL;
        return (size_t)0;
    }

    /* If *dataptr is NULL, *sizeptr is assumed to be 0. */
    data = *dataptr;
    if (data != NULL)
        *sizeptr = 0;
    size = *sizeptr;

    /* Open file, read-only, in binary mode. If it fails, fopen() sets errno. */
    in = fopen(filename, "rb");
    if (in == NULL)
        return (size_t)0;

    while (1) {

        /* Need to grow the data buffer?
         * (The +1 accounts for the end-of-buffer NUL byte). */
        if (have + (size_t)1 >= size) {

            /* Start with a 8k buffer.
             * Double it in size for up to 1M,
             * then round to next full megabyte. */
            if (size < 8190)
                size = 8192;
            else
            if (size < 1048576)
                size *= 2;
            else
                size = (size | 524287) + 524289;

            /* Size too large (wrapped)? */
            if (size <= *sizeptr) {
                free(data);
                fclose(in);
                *dataptr = NULL;
                *sizeptr = 0;
                errno = ENOMEM;
                return (size_t)0;
            }

            /* Reallocate. */
            data = realloc(data, size);
            if (data == NULL) {
                free(data);
                fclose(in);
                *dataptr = NULL;
                *sizeptr = 0;
                errno = ENOMEM;
                return (size_t)0;
            }

            *dataptr = data;
            *sizeptr = size;
        }

        /* Read more data, but remember to leave room for the
         * terminating NUL byte. */
        more = fread(data + have, 1, size - have - 1, in);
        if (more > (size_t)0)
            have += more;
        else
            break;
    }

    /* I/O error? */
    if (!feof(in) || ferror(in)) {
        free(data);
        fclose(in);
        *dataptr = NULL;
        *sizeptr = 0;
        errno = EIO;
        return (size_t)0;
    }
    if (fclose(in)) {
        free(data);
        fclose(in);
        *dataptr = NULL;
        *sizeptr = 0;
        errno = EIO;
        return (size_t)0;
    }

    /* Append terminating NUL byte; we reserved space for it. */
    data[have] = '\0';

    /* Technically we shouldn't need to set errno. */
    errno = 0;
    return have;
}

struct state {
    double  tensionCoeff;
    double  speedCoeff;
    double  depthCoeff;
    double  tension;
    double  speed;
    double  depth;
    double  tensionRaw;
    double  speedRaw;
    double  depthRaw;
    long    connectionOk;
};
#define STATE_INITIALIZER { 0.0,0.0,0.0, 0.0,0.0,0.0, 0.0,0.0,0.0, 0L }

static char   *state_buffer_data = NULL;
static size_t  state_buffer_size = 0;

static void free_state_buffer(void)
{
    free(state_buffer_data);
    state_buffer_data = NULL;
    state_buffer_size = 0;
}

#define UPDATED_TRIPLET (1U << 0)
#define UPDATED_CONN_OK (1U << 1)

unsigned int update_state(const char *const filename, struct state *const current)
{
    unsigned int retval = 0U;
    char        *data;
    size_t       len;

    if (current == NULL) {
        errno = EINVAL;
        return 0U;
    }

    len = read_file(filename, &state_buffer_data, &state_buffer_size);
    if (!len) {
        if (errno == 0)
            errno = ENODATA;
        return 0U;
    }

    data = strstr(state_buffer_data, "\"failedRequests\":");
    if (data != NULL) {
        long conn_ok;
        if (sscanf(data+17, " %ld", &conn_ok) == 1) {
            current->connectionOk = conn_ok;
            retval |= UPDATED_CONN_OK;
        }
    }

    data = strstr(state_buffer_data, "\"data\":[");
    if (data != NULL) {
        double value[6];
        if (sscanf(data+8, " %lf , %lf , %lf , %lf , %lf , %lf ",
                   &(value[0]), &(value[1]), &(value[2]), 
                   &(value[3]), &(value[4]), &(value[5])) == 6) {
            current->tensionRaw = value[1];
            current->depthRaw = value[4];
            current->speedRaw = value[5];
            current->tension = current->tensionCoeff * current->tensionRaw;
            current->depth = current->depthCoeff * current->depthRaw;
            current->speed = current->speedCoeff * current->speedRaw;
            retval |= UPDATED_TRIPLET;
        }
    }

    /* All done. If the buffer is twice as large, or at least two megabytes larger
       than the amount of data we read, discard the buffer. */
    if ((state_buffer_size > len + len && len > 4095) ||
        state_buffer_size > len + 2048576)
        free_state_buffer();
    errno = 0;
    return retval;
}

即使数据分布在多行上,这也可以正常工作。我可能会使用自定义函数来查找数据起始点(strstr()过于严格,即使这里有额外的空间,也会将其抛弃)。

缓冲区在update_state()次调用之间重用,除非新数据远小于缓冲区大小,在这种情况下缓冲区被丢弃(并在下次调用期间根据需要重新分配),以保持低内存开销。 (但是,请注意,大多数C库实现不会将内存返回给操作系统,除非分配最初大到足以使用内存映射而不是sbrk()。)

如果没有发生错误,

update_state()总是将errno设置为零。它的返回值是UPDATED_值的掩码;如果找到failedRequests记录,则(retval & UPDATED_CONN_OK)为true,如果在参数结构中找到并更新了六个第一个数据字段中的三个数字字段,则(retval & UPDATED_TRIPLET)为真。