제가 공부한 내용을 정리하는 블로그입니다.
아직 많이 부족하고 배울게 너무나도 많습니다. 틀린내용이 있으면 언제나 가감없이 말씀해주시면 감사하겠습니다😁

과제 4 설명


echo2 abc라고 보내면 abc가 응답이 아닌 echo2_abc가 응답으로 오도록

  • bulk 단위
    • Respv2 기준 하나의 명령을 나타내는 block
    • ex) $4\r\npong\r\n
  • echo abc 라고 명령을 입력했을 때
    • c → argc = 2 =>명령어의 길이
    • c → argv[0] = “echo” => 첫번째 명령어
    • c → argb[1] = “abc” => 두번째 명령어

robj(server object)

  • type: 4bit
    • 2^4 ⇒ 16개의 타입 가능
  • encoding: 4bit
  • lru: 24bit
    • lru, lfu 쓸때 모두 사용.
  • type + encoding + lru 총 32bit/4byte
  • refcount: 똑같은거 시키면 +1, 0이 되면 지워짐
  • void * ptr ⇒ 8byte
  • 총 16byte의 메모리 공간을 할당 받게됨.

argc, argv가 만들어지는 곳

  • processMultibulk…에서 생성
  • createStringObject() 또는 createObject()에서 만듦

SDS

이전 포스팅에서도 작성했지만 한번 더 작성.

  • 서버 시스템에서는 메모리를 아끼기 위해.
  • 동적으로 길이를 사용하기에 변수에 따라서 스트링의 길이를 조절하기 위해.
  • redis에서 사용하고 있는 구조체
  • sds의 사이즈를 가변적으로 가르키기위해.

c에서의 메모리의 값을 읽는 스킬

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

typedef struct memory_header {
    unsigned long size;
    char ptr[];
}memheader;

char *STRING = "hello, OSSCA!";
void test(char *ptr) {
    printf("%s\n", ptr);
    memheader *mh = (memheader *)(ptr-sizeof(memheader));
    printf("size: %d\n", mh->size);
}

int main(int argc, char *argv[]) {
    printf("%d\n", sizeof(memheader));

    memheader *mh = (memheader *)malloc(sizeof(memheader) + 16);
    mh->size = strlen(STRING);
    memcpy(mh->ptr, STRING, mh->size);
    mh->ptr[mh->size] = 0;

    test(mh->ptr);
    return 0;
}
8
hello, OSSCA!
size: 13
  • 뒷 부분만 전달했음에도 size를 읽어올 수 있음.
  • 메모리를 다루는 언어인 C라서 가능한 스킬.
  • sdsHdrSize()는 실제 메모리의 사이즈를 알려주는데, 이를 통해서 값을 가져올 수 있음.

과제 4 수행


실제 과제 풀이 해답은 여러개가 있을 수 있다.

  1. robj를 변경하는 방법
  2. addreplyBulkSds()를 사용하는 방법
  3. addReplySds()를 사용하는 방법

이 있는데 2, 3번에 대해서만 보이면

addReplySds()를 사용

void echo2Command(client *c) {
    sds reply = sdscatfmt(sdsempty(), "echo2_%s", c->argv[1]->ptr);
    addReplyLongLongWithPrefix(c, sdslen(reply), '$');
    addReplySds(c, reply);
    addReplyProto(c, "\r\n", 2);
}
  • client 객체로부터 받은 명령어를 sdscatfmt()를 통해 새로운 문자열을 생성.
    • sdsempty()
      • sds 타입의 새로운 빈 문자열을 생성하는 함수.
      • sdsnewlen()을 통해 sds를 새로 생성
    • sdscatfmt()
      • sds 타입의 변수에 서식 지정된 문자열을 추가하는 함수.
      • 형식 지정자(format specifier)를 사용하여 문자열을 생성하고, 해당 문자열을 SDS에 추가합니다. 서식 지정자는 %s, %d, %f 등과 같이 다양한 형태로 사용될 수 있으며, 이를 통해 다양한 타입의 데이터를 문자열로 변환하여 SDS에 추가 가능.
    • addReplyLongLongWithPrefix()
      • Redis 클라이언트에게 정수 값을 포함하는 응답을 보내느 함수.
      • 주어진 정수 값(sds의 길이)이 작을 경우 프로토콜에서 사용되는 header를 공유 객체로 사용.
        • 이렇게 함으로써 메모리 절약.
        • 공유된 헤더를 사용함으로써 응답을 생성.
      • 생성된 응답을 클라이언트에게 전달하기 위해 addReplyProto() 함수를 호출
    • addReplySds()
      • Redis 클라이언트의 버퍼에 s를 추가하는 함수.
      • PrepareClientToWrite()
        • 클라이언트가 쓰기 작업을 준비할 수 있는 상태인지 확인.
      • _addReplyToBufferOrList()
        • flag와 type를 통해 client와의 상태를 확인.
        • _addReplyToBuffer() 함수를 호출하여 클라이언트의 응답 버퍼(client->buf)에 응답을 추가.
      • 이후 sds는 더이상 사용되지 않으므로 메모리를 해제하여 메모리 leak을 방지
    • addReplyProto()
      • Redis 클라이언트에게 프로토콜 형식의 응답을 보내는 역할. s와 len을 이용하여 클라이언트의 버퍼에 응답을 추가.
      • 여기서는 명령어의 종료 역할.
      • PrepareClientToWrite() 실행.
      • _addReplyToBufferOrList() 실행.

다른 방법

/**
 * sdsnewlen을 통해 생성하는 방법
 */
sds reply = sdsnewlen("echo2_", 6); // "echo2_" 문자열을 포함한 SDS 생성
reply = sdscatfmt(reply, "%s", c->argv[1]->ptr); // 뒤에 인자 추가


/**
 * sdssetlen을 통해 생성하는 방법
 */
sds reply = sdsnewlen(NULL, 6); // 초기 크기가 6인 빈 SDS 생성
memcpy(reply, "echo2_", 6); // "echo2_" 문자열 복사
sdssetlen(reply, 6); // SDS 길이 설정
reply = sdscatfmt(reply, "%s", c->argv[1]->ptr); // 뒤에 인자 추가

이외의 다른 방법으로도 가능하다.

addReplyBulkSds()를 사용

void echo2Command(client *c) {
    sds reply = sdscatfmt(sdsempty(), "echo2_%s", c->argv[1]->ptr);
    addReplyBulkSds(c, reply);   
}
  • 그 이후 생성된 새로운 문자열을 클라이언트에게 반환하게 위해 addReplyBulkSds()를 사용.

addReplyBulkSds()

/* Add sds to reply (takes ownership of sds and frees it) */
void addReplyBulkSds(client *c, sds s)  {
    addReplyLongLongWithPrefix(c,sdslen(s),'$');
    addReplySds(c,s);
    addReplyProto(c,"\r\n",2);
}
  • 실제 위의 구현한 방법과 동일한 flow로 진행된다.
127.0.0.1:6379> 
127.0.0.1:6379> echo 123
"123"
127.0.0.1:6379> echo2 123
"echo2_123"
127.0.0.1:6379>

원하는 대로 결과가 잘 출력된 것을 볼 수 있다.

+ Recent posts