nvme-builtin.h 구조와 command 추가 방식
https://github.com/linux-nvme/nvme-cli/blob/master/nvme-builtin.h
COMMAND_LIST(
ENTRY("list", "List all NVMe devices and namespaces on machine", list)
ENTRY("list-subsys", "List nvme subsystems", list_subsys)
// ... (중략)
);
- COMMAND_LIST와 ENTRY 매크로를 사용해서 명령어(command) 정의
- 각 ENTRY는 명령어 이름, 설명, 그리고 실제로 실행될 함수명을 인자로 받음
- 이 구조는 새로운 명령어를 추가할 때 ENTRY를 하나 더 추가하면 되도록 설계되어 있음
cmd.h
https://github.com/linux-nvme/nvme-cli/blob/master/cmd.h
- PLUGIN, COMMAND_LIST 등 매크로의 존재만 알리는 역할
#ifndef _CMD_H
#define _CMD_H
#undef PLUGIN
#define PLUGIN(n, c)
#undef COMMAND_LIST
#define COMMAND_LIST(args...)
#endif
- PLUGIN, COMMAND_LIST 등의 매크로를 빈 정의로 선언
- 즉, 이 파일을 include하면 PLUGIN, COMMAND_LIST가 아무 동작도 하지 않게 됨
- 여러 번 include될 때, 매번 다른 방식으로 매크로를 정의해서 다양한 처리를 할 수 있도록 하기 위함
cmd_handler.h의 역할과 확장성 구현
https://github.com/linux-nvme/nvme-cli/blob/master/cmd_handler.h
- 여러 단계(Stage)에서 PLUGIN, ENTRY, COMMAND_LIST 등의 매크로를 다르게 정의
1. 함수 프로토타입 정의
#undef NAME
#define NAME(n, d)
#undef ENTRY
#define ENTRY(n, h, f, ...) \
static int f(int argc, char **argv, struct command *command, struct plugin *plugin);
#undef COMMAND_LIST
#define COMMAND_LIST(args...) args
#undef PLUGIN
#define PLUGIN(name, cmds) cmds
#include CMD_INCLUDE(CMD_INC_FILE)
- ENTRY 매크로를 함수 프로토타입 생성용으로 정의
- 이렇게 하면 명령어별로 함수 프로토타입이 자동으로 생성됨
- 예시:
ENTRY("list", "List all NVMe devices", list)
→ static int list(int argc, char **argv, struct command *command, struct plugin *plugin);
2. 명령어 구조체 정의
#undef NAME
#define NAME(n, d)
#undef ENTRY_W_ALIAS
#define ENTRY_W_ALIAS(n, h, f, a) \
static struct command f ## _cmd = { \
.name = n, \
.help = h, \
.fn = f, \
.alias = a, \
};
#undef ENTRY_WO_ALIAS
#define ENTRY_WO_ALIAS(n, h, f) \
ENTRY_W_ALIAS(n, h, f, NULL)
#undef ENTRY_SEL
#define ENTRY_SEL(n, h, f, a, CMD, ...) CMD
#undef ENTRY
#define ENTRY(...) \
ENTRY_SEL(__VA_ARGS__, ENTRY_W_ALIAS, ENTRY_WO_ALIAS)(__VA_ARGS__)
#undef COMMAND_LIST
#define COMMAND_LIST(args...) args
#undef PLUGIN
#define PLUGIN(name, cmds) cmds
#include CMD_INCLUDE(CMD_INC_FILE)
- ENTRY 매크로를 구조체 생성용으로 재정의
- 각 명령어에 대해 struct command 구조체가 만들어짐
- 예: static struct command list_cmd = { ... };
- ENTRY_SEL 에서는 ENTRY_W_ALIAS 와 ENTRY_WO_ALIAS 중 하나의 매크로 함수를 반환
- 여기서 (__VA_ARGS__) 는 매크로 함수에 또 인자를 전달하기 위해 작성됨
- 만약 alias가 존재하는 경우 인자가 총 6개가 전달되고 ENTRY_W_ALIAS가 CMD가 됨
- 반대로 alias가 존재하지 않는 경우 인자가 총 5개가 전달되고 ENTRY_WO_ALIAS가 CMD가 됨
3. 플러그인에 대한 Command List 생성
#undef NAME
#define NAME(n, d)
#undef ENTRY
#define ENTRY(n, h, f, ...) &f ## _cmd,
#undef COMMAND_LIST
#define COMMAND_LIST(args...) \
static struct command *commands[] = { \
args \
NULL, \
};
#undef PLUGIN
#define PLUGIN(name, cmds) cmds
#include CMD_INCLUDE(CMD_INC_FILE)
- 각 명령어 구조체의 포인터를 배열로 만듦
- 이 배열이 실제로 명령어 목록이 됨
4. 플러그인 정의 및 등록
#undef NAME
#define NAME(n, d) .name = n, .desc = d,
#undef COMMAND_LIST
#define COMMAND_LIST(args...)
#undef PLUGIN
#define PLUGIN(name, cmds) \
static struct plugin plugin = { \
name \
.commands = commands \
}; \
\
static void init(void) __attribute__((constructor)); \
static void init(void) \
{ \
register_extension(&plugin); \
}
#include CMD_INCLUDE(CMD_INC_FILE)
- PLUGIN 매크로를 통해 plugin 구조체를 만들고, 명령어 배열을 연결
- 그리고 register_extension 함수를 통해 플러그인을 등록
define_cmd.h
https://github.com/linux-nvme/nvme-cli/blob/master/define_cmd.h
#ifdef CREATE_CMD
#undef CREATE_CMD
#define __stringify_1(x...) #x
#define __stringify(x...) __stringify_1(x)
#define __CMD_INCLUDE(cmd) __stringify(cmd.h)
#define CMD_INCLUDE(cmd) __CMD_INCLUDE(cmd)
#define CMD_HEADER_MULTI_READ
#include CMD_INCLUDE(CMD_INC_FILE)
#include "cmd_handler.h"
#undef CMD_HEADER_MULTI_READ
#define CREATE_CMD
#endif
- CMD_INC_FILE에 정의된 이름(예: nvme-builtin)을 이용해 해당 헤더 파일을 include
- 예: #define CMD_INC_FILE nvme-builtin → #include "nvme-builtin.h"가 됨
C 전처리기 동작 방식
- C 전처리기 (컴파일 전 단계)에서는 매크로가 어떻게 정의되어 있느냐에 따라 include된 파일의 코드가 실제로 어떻게 변환될 지 결정함
- 즉, 매크로 정의 → 헤더 include → 코드 생성
예시
#undef CMD_INC_FILE
#define CMD_INC_FILE my-nvme
#if !defined(MY_NVME) || defined(CMD_HEADER_MULTI_READ)
#define MY_NVME
#include "cmd.h"
PLUGIN(NAME("my", "My vendor extension"),
COMMAND_LIST(
ENTRY("hello", "Say hello", my_hello)
ENTRY("bye", "Say bye", my_bye)
)
);
#endif
#include "define_cmd.h"
// Stage 1: 함수 프로토타입
static int my_hello(int argc, char **argv, struct command *command, struct plugin *plugin);
static int my_bye(int argc, char **argv, struct command *command, struct plugin *plugin);
// Stage 2: command 구조체
static struct command my_hello_cmd = {
.name = "hello",
.help = "Say hello",
.fn = my_hello,
.alias = NULL,
};
static struct command my_bye_cmd = {
.name = "bye",
.help = "Say bye",
.fn = my_bye,
.alias = NULL,
};
// Stage 3: command 포인터 배열
static struct command *commands[] = {
&my_hello_cmd,
&my_bye_cmd,
NULL,
};
// Stage 4: plugin 구조체 및 등록
static struct plugin plugin = {
.name = "my",
.desc = "My vendor extension",
.commands = commands
};
static void init(void) __attribute__((constructor));
static void init(void)
{
register_extension(&plugin);
}