Hiredis: freeReplyObject استخدام

تم إنشاؤها على ١٢ سبتمبر ٢٠١٩  ·  7تعليقات  ·  مصدر: redis/hiredis

1) لماذا يجب علي استخدام freeReplyObject؟ ماذا تفعل هذه الوظيفة في الواقع؟ على سبيل المثال ، يعمل هذا الرمز حتى بدون freeReplyObject:
redReply = (redisReply *) redisCommand (c، "PING") ؛ // استدعاء أمر PING ومؤشر الرجوع إلى بنية الرد
printf ("PING:٪ sn" ، رد أحمر-> str) ؛
// freeReplyObject (redReply) ؛ // هيكل الرد المجاني

redReply = (redisReply*)redisCommand(c, "GET testkey");
printf("testkey: %s\n", redReply->str);

2) هل هذه المعلومات لا تزال ذات صلة؟

هام: الإصدار الحالي من hiredis (0.10.0) يحرر الردود عند استخدام واجهة برمجة التطبيقات غير المتزامنة. هذا يعني أنه لا يجب عليك استدعاء freeReplyObject عند استخدام واجهة برمجة التطبيقات هذه. يتم مسح الرد بواسطة hiredis بعد عودة رد الاتصال. من المحتمل أن يتغير هذا السلوك في الإصدارات المستقبلية ، لذا تأكد من مراقبة سجل التغيير عند الترقية (راجع المشكلة رقم 39).

ال 7 كومينتر

حسنًا ، ليس عليك أن تستدعي freeReplyObject لكن برنامجك سوف يتسبب في تسرب الذاكرة. 😄

#include <stdio.h>
#include <hiredis/hiredis.h>

int main(int argc, const char **argv) {
    redisContext *c = redisConnect("127.0.0.1", 6379);

    for (int i = 0; i < 100; i++) {
        redisReply *r = redisCommand(c, "PING %d", i);

        // Only free the replies if passed an argument
        if (argc > 1) {
            if (r) freeReplyObject(r);
        }
    }

    redisFree(c);
}

إذا قمت بتشغيل هذا البرنامج تحت valgrind بدون تمرير وسيطة ، فإننا نكتشف تسرب الذاكرة. كل تكرار للحلقة hiredis يخصص الذاكرة عبر redisCommand لكننا لا نعيدها إلى نظام التشغيل. في برنامج تشغيل طويل ، سيستخدم هذا في النهاية كل الذاكرة الموجودة على النظام ويعطل البرنامج.

$ valgrind --leak-check=full ./hrleak # No arguments, meaning we don't free replies
==32719== Memcheck, a memory error detector
==32719== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==32719== Using Valgrind-3.14.0 and LibVEX; rerun with -h for copyright info
==32719== Command: ./hrleak
==32719==
==32719==
==32719== HEAP SUMMARY:
==32719==     in use at exit: 5,090 bytes in 200 blocks
==32719==   total heap usage: 1,213 allocs, 1,013 frees, 20,656 bytes allocated
==32719==
==32719== 5,090 (4,800 direct, 290 indirect) bytes in 100 blocks are definitely lost in loss record 2 of 2
==32719==    at 0x483AB35: calloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==32719==    by 0x4874512: createReplyObject (hiredis.c:65)
==32719==    by 0x4874512: createStringObject (hiredis.c:105)
==32719==    by 0x487C77A: processBulkItem (read.c:339)
==32719==    by 0x487C77A: processItem (read.c:481)
==32719==    by 0x487C77A: redisReaderGetReply (read.c:576)
==32719==    by 0x4876633: redisGetReplyFromReader (hiredis.c:838)
==32719==    by 0x487671A: redisGetReply (hiredis.c:865)
==32719==    by 0x4876A14: __redisBlockForReply (hiredis.c:970)
==32719==    by 0x4876A14: redisvCommand (hiredis.c:980)
==32719==    by 0x4876AE3: redisCommand (hiredis.c:986)
==32719==    by 0x1090BE: main (hrleak.c:8)
==32719==
==32719== LEAK SUMMARY:
==32719==    definitely lost: 4,800 bytes in 100 blocks
==32719==    indirectly lost: 290 bytes in 100 blocks
==32719==      possibly lost: 0 bytes in 0 blocks
==32719==    still reachable: 0 bytes in 0 blocks
==32719==         suppressed: 0 bytes in 0 blocks
==32719==
==32719== For counts of detected and suppressed errors, rerun with: -v
==32719== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

تشغيل البرنامج مع وسيط (مما يعني أننا سنطلق ردودًا مجانية) ولم يعد هناك أي تسرب تم اكتشافه.

$ valgrind --leak-check=full ./hrleak an_argument # Now we will call freeReplyObject
==695== Memcheck, a memory error detector
==695== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==695== Using Valgrind-3.14.0 and LibVEX; rerun with -h for copyright info
==695== Command: ./hrleak an_argument
==695==
==695==
==695== HEAP SUMMARY:
==695==     in use at exit: 0 bytes in 0 blocks
==695==   total heap usage: 1,213 allocs, 1,213 frees, 20,656 bytes allocated
==695==
==695== All heap blocks were freed -- no leaks are possible
==695==
==695== For counts of detected and suppressed errors, rerun with: -v
==695== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

بالنسبة إلى سؤالك الثاني ، نعم ، لا يزال hiredis يحرر الردود في المكالمات غير المتزامنة. لم أكتب هذا الجزء من النظام ، لذا لست متأكدًا تمامًا من سبب اعتقادنا أنه سيتغير في المستقبل.

هل هذا منطقي؟

@ مايكل - غراندر ، شكرا لك على الإجابة! ولكن إذا كان مثل هذا:

#include <stdio.h>
#include <hiredis/hiredis.h>

int main(int argc, const char **argv) {
    redisContext *c = redisConnect("127.0.0.1", 6379);
    redisReply *r;

    for (int i = 0; i < 100; i++)
        redisReply *r = (redisReply*)redisCommand(c, "PING %d", i);

    freeReplyObject(r);
    redisFree(c);
}

هل الامور على ما يرام؟ لست مضطرًا إلى "ذاكرة صفرية" يدويًا لهيكل الرد لاستلام البيانات بأمان؟

بالنسبة إلى سؤالك الثاني ، نعم ، لا يزال hiredis يحرر الردود في المكالمات غير المتزامنة. لم أكتب هذا الجزء من النظام ، لذا لست متأكدًا تمامًا من سبب اعتقادنا أنه سيتغير في المستقبل.

لذلك فهو يعمل فقط في المكالمات غير المتزامنة وإذا استخدمت redisConnect فأنا بحاجة إلى freeReplyObject يدويًا؟

هل الامور على ما يرام؟ لست مضطرًا إلى "ذاكرة صفرية" يدويًا لهيكل الرد لاستلام البيانات بأمان؟

صحيح ، لا تحتاج إلى صفر ذاكرة في البنية حتى إذا اتصلت بـ redisCommand في حلقة. سيهتم Hiredis بالتخصيص ووضع الهيكل.

#include <stdio.h>
#include <hiredis/hiredis.h>

int main(int argc, const char **argv) {
    redisContext *c = redisConnect("127.0.0.1", 6379);
    redisReply *r;
    for (int i = 0; i < 10; i++) {
        r = (redisReply*)redisCommand(c, "PING %d", i);
        if (r) {
            if (r->type == REDIS_REPLY_STRING) {
                printf("redisReply[%p]->str[%p] = '%s'\n", r, r->str, r->str);
            }
            freeReplyObject(r);
        }
    }

    redisFree(c);
}

إذا قمت بتشغيل هذا البرنامج ، فسترى المؤشرات تتغير في الحلقة ، لأن hiredis يقوم بكل التخصيص نيابة عنك.

لذلك فهو يعمل فقط في المكالمات غير المتزامنة وإذا استخدمت redisConnect فأنا بحاجة إلى freeReplyObject يدويًا؟

حسنًا ، لا تزال أوامر redisAsync* تحرر الذاكرة داخليًا لكن الأوامر المتزامنة لا تفعل ذلك ، وستحتاج إلى ردود مجانية.

إذا كنت جديدًا على C ، فمن المهم الإشارة إلى أنه إذا كنت تريد تخزين رد من redis في مكان آخر ، فستحتاج إلى نسخ تلك الذاكرة. وإلا سيتم تحرير الذاكرة بواسطة freeReplyObject وبعد ذلك ستحتوي البيانات التي قمت بتخزينها على القمامة.

لقد أنشأت برنامجًا صغيرًا يوضح ما أعنيه

سأغلق هذه المشكلة لأنها ليست خطأ في hiredis ولكن حظًا سعيدًا في برنامجك.! 😄

@ h3xp1017 مرحبًا ، لقد نظرت إلى أحد أسئلتك مرة أخرى وأردت أن أشير إلى شيء ما.

سيؤدي هذا البرنامج إلى تسرب الذاكرة:

int main(int argc, const char **argv) {
    redisContext *c = redisConnect("127.0.0.1", 6379);
    redisReply *r;

    for (int i = 0; i < 100; i++)
        redisReply *r = (redisReply*)redisCommand(c, "PING %d", i);

    freeReplyObject(r);
    redisFree(c);
}

تحتاج إلى الاتصال بـ freeReplyObject كل مرة تنفذ فيها redisCommand ، لذلك في هذا البرنامج ستفعل شيئًا مثل:

#include <stdio.h>
#include <hiredis/hiredis.h>

int main(int argc, const char **argv) {
    redisContext *c = redisConnect("127.0.0.1", 6379);
    redisReply *r;

    for (int i = 0; i < 100; i++) {
        redisReply *r = (redisReply*)redisCommand(c, "PING %d", i);
        if (r) freeReplyObject(r);
    }

    redisFree(c);
}

تحتاج إلى استدعاء freeReplyObject في كل مرة تقوم فيها بتنفيذ redisCommand

حسنًا ، هذا ما أردت أن أعرفه ، شكرًا لك =)

سوف تسرب الذاكرة

لماذا حدث هذا أعني أنني أستخدم بنية redisReply واحدة يتم استبدالها في كل مرة في الحلقة ، فلماذا تتسرب الذاكرة إذا لم تخلق كائنات جديدة في الكومة / المكدس؟

لماذا حدث هذا أعني أنني أستخدم بنية redisReply واحدة يتم استبدالها في كل مرة في الحلقة ، فلماذا تتسرب الذاكرة إذا لم تخلق كائنات جديدة في الكومة / المكدس؟

يقوم بتخصيص الذاكرة على الكومة. إذا كنت ستدخل إلى الموظف ، فسترى أنه ينفذ malloc لـ redisReply نفسه ، ثم عمليات التخصيص الإضافية بناءً على نوع الإرجاع.

إليك برنامج قصير يفعل شيئًا مشابهًا ولكنه يزيل كل تعقيدات hiredis

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

typedef struct exampleReply {
    char *str;
} exampleReply;

char *createString(int count) {
    char buf[1024], *str;
    size_t len;

    len = snprintf(buf, sizeof(buf), "Example reply %d", count);
    str = malloc(len + 1);
    memcpy(str, buf, len);
    str[len] = '\0';

    return str;
}

exampleReply *getReply(int count) {
    exampleReply *r = malloc(sizeof(exampleReply));
    r->str = createString(count);
    return r;
}

void freeReply(exampleReply *reply) {
    free(reply->str);
    free(reply);
}

int main(void) {
    for (int i = 0; i < 3; i++) {
        exampleReply *r = getReply(i);
        printf("exampleReply[%p]->str[%p] = '%s'\n", r, r->str, r->str);
        freeReply(r);
    }
}

لاحظ كيف أن ذاكرة mallocs getReply للهيكل ، وذاكرة mallocs createString للسلسلة؟ يقوم Hiredis بشيء مشابه لهذا داخليًا (على الرغم من أن المنطق أكثر تعقيدًا بشكل كبير).

أوصي بشدة باستخدام برامج مثل valgrind أو clang's asan عند التعلم لمساعدتك في العثور على أشياء مثل تسرب الذاكرة أو قراءات الذاكرة غير الصالحة. حتى المبرمجين الذين يستخدمون لغة سي لعقود من الزمن يستخدمون هذه الأدوات.

هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات