diff --git a/src/gausskernel/cbb/bbox/bbox_syscall_support.cpp b/bbox_syscall_support.cpp similarity index 80% rename from src/gausskernel/cbb/bbox/bbox_syscall_support.cpp rename to bbox_syscall_support.cpp index 86398ece0..ffa0b42cc 100644 --- a/src/gausskernel/cbb/bbox/bbox_syscall_support.cpp +++ b/bbox_syscall_support.cpp @@ -142,72 +142,72 @@ int SYS_NAME(sysconf)(int name) { switch (name) { case _SC_PAGESIZE: - return getpagesize(); + return getpagesize(); // 返回系统页面大小 case _SC_OPEN_MAX: { - struct kernel_rlimit limit = {0}; - if (sys_getrlimit(RLIMIT_NOFILE, &limit) >= 0) { - return limit.rlim_cur; + struct kernel_rlimit limit = {0}; // 创建一个kernel_rlimit结构体,并初始化为0 + if (sys_getrlimit(RLIMIT_NOFILE, &limit) >= 0) { // 调用sys_getrlimit函数,将RLIMIT_NOFILE资源限制信息存储在limit中 + return limit.rlim_cur; // 返回当前进程的最大打开文件数 } else { /* Default maximum open files for per process */ - return 8192; + return 8192; // 返回默认的最大打开文件数为8192 } } default: - errno = ENOSYS; + errno = ENOSYS; // 如果name不匹配_SC_PAGESIZE和_SC_OPEN_MAX,则设置errno为ENOSYS表示函数未实现 return -1; } } int SYS_NAME(sigemptyset)(struct kernel_sigset_t* set) { - errno_t rc = memset_s(set->sig, sizeof(set->sig), 0, sizeof(set->sig)); + errno_t rc = memset_s(set->sig, sizeof(set->sig), 0, sizeof(set->sig)); // 使用memset_s函数将set->sig的值设置为0 securec_check_c(rc, "\0", "\0"); return 0; } int SYS_NAME(sigfillset)(struct kernel_sigset_t* set) { - errno_t rc = memset_s(set->sig, sizeof(set->sig), 0xFF, sizeof(set->sig)); + errno_t rc = memset_s(set->sig, sizeof(set->sig), 0xFF, sizeof(set->sig)); // 使用memset_s函数将set->sig的值设置为0xFF securec_check_c(rc, "\0", "\0"); return 0; } int SYS_NAME(sigaddset)(struct kernel_sigset_t* set, int __signum) { - int signo = (int)(8 * sizeof(set->sig)); + int signo = (int)(8 * sizeof(set->sig)); // 计算消息信号集的位数 if (__signum < 1 || __signum > signo) { - errno = EINVAL; + errno = EINVAL; // 如果__signum小于1或大于signo,设置errno为EINVAL表示无效参数 return -1; } else { - set->sig[(__signum - 1) / (8 * sizeof(set->sig[0]))] |= 1UL << ((__signum - 1) % (8 * sizeof(set->sig[0]))); + set->sig[(__signum - 1) / (8 * sizeof(set->sig[0]))] |= 1UL << ((__signum - 1) % (8 * sizeof(set->sig[0]))); // 将__signum对应的位设置为1 return 0; } } int SYS_NAME(sigdelset)(struct kernel_sigset_t* set, int __signum) { - int signo = (int)(8 * sizeof(set->sig)); + int signo = (int)(8 * sizeof(set->sig)); // 计算消息信号集的位数 if (__signum < 1 || __signum > signo) { - errno = EINVAL; + errno = EINVAL; // 如果__signum小于1或大于signo,设置errno为EINVAL表示无效参数 return -1; } else { - set->sig[(__signum - 1) / (8 * sizeof(set->sig[0]))] &= ~(1UL << ((__signum - 1) % (8 * sizeof(set->sig[0])))); + set->sig[(__signum - 1) / (8 * sizeof(set->sig[0]))] &= ~(1UL << ((__signum - 1) % (8 * sizeof(set->sig[0])))); // 将__signum对应的位设置为0 return 0; } } int SYS_NAME(sigismember)(struct kernel_sigset_t* set, int __signum) { - int signo = (int)(8 * sizeof(set->sig)); + int signo = (int)(8 * sizeof(set->sig)); // 计算消息信号集的位数 if (__signum < 1 || __signum > signo) { - errno = EINVAL; + errno = EINVAL; // 如果__signum小于1或大于signo,设置errno为EINVAL表示无效参数 return -1; } else { return !!(set->sig[(__signum - 1) / (8 * sizeof(set->sig[0]))] & - (1UL << ((__signum - 1) % (8 * sizeof(set->sig[0]))))); + (1UL << ((__signum - 1) % (8 * sizeof(set->sig[0]))))); // 检查__signum对应的位是否为1,返回结果 } } @@ -215,7 +215,7 @@ long SYS_NAME(sigprocmask)(int how, struct kernel_sigset_t* set, struct kernel_s { long ret = 0; - ret = SYS_NAME(rt_sigprocmask)(how, set, oldset, (KERNEL_NSIG + 7) / 8); + ret = SYS_NAME(rt_sigprocmask)(how, set, oldset, (KERNEL_NSIG + 7) / 8); // 调用SYS_NAME(rt_sigprocmask)函数设置信号屏蔽字 return ret; } @@ -428,7 +428,7 @@ __syscall5( long SYS_NAME(waitpid)(pid_t pid, int* status, int options) { - return SYS_NAME(wait4)(pid, status, options, 0); + return SYS_NAME(wait4)(pid, status, options, 0); // 调用SYS_NAME(wait4)函数等待子进程结束 } long SYS_NAME(signal)(int __signum, void (*handler)(int)) @@ -436,22 +436,23 @@ long SYS_NAME(signal)(int __signum, void (*handler)(int)) struct kernel_sigaction _sa; struct kernel_sigaction old; - errno_t rc = memset_s(&_sa, sizeof(_sa), 0, sizeof(_sa)); + errno_t rc = memset_s(&_sa, sizeof(_sa), 0, sizeof(_sa)); // 使用memset_s函数将_sa的值设置为0 securec_check_c(rc, "\0", "\0"); - sys_sigfillset(&_sa.sa_mask); - _sa.sa_flags |= SA_RESTORER | SA_RESTART; - _sa.handle.sa_handler_ = handler; + sys_sigfillset(&_sa.sa_mask); // 将_sa.sa_mask的所有位都设置为1 + _sa.sa_flags |= SA_RESTORER | SA_RESTART; // 设置_sa.sa_flags的标志位 + _sa.handle.sa_handler_ = handler; // 设置_sa.handle.sa_handler_为传入的handler函数 - return SYS_NAME(rt_sigaction)(__signum, &_sa, &old, (KERNEL_NSIG + 7) / 8); + return SYS_NAME(rt_sigaction)(__signum, &_sa, &old, (KERNEL_NSIG + 7) / 8); // 调用SYS_NAME(rt_sigaction)函数设置信号处理动作 } - -long SYS_NAME(_clone)( - int (*fn)(void*), void* child_stack, int flags, void* arg, int* parent_tidptr, void* newtls, int* child_tidptr) +/* +long SYS_NAME(_clone)(int (fn)(void), void* child_stack, int flags, void* arg, int* parent_tidptr, void* newtls, int* child_tidptr)函数用于创建一个新的进程,并在新进程中执行指定的函数。 +*/ +long SYS_NAME(_clone)(int (*fn)(void*), void* child_stack, int flags, void* arg, int* parent_tidptr, void* newtls, int* child_tidptr) { register long ___res __asm__("r5"); { - if (fn == NULL || child_stack == NULL) { + if (fn == NULL || child_stack == NULL) { // 如果传入的函数指针为NULL,或者子进程栈指针为NULL,则返回EINVAL错误码 ___res = -EINVAL; goto _clone_exit; } @@ -459,24 +460,25 @@ long SYS_NAME(_clone)( /* stash first 4 arguments on stack first because we can only load * them after all function calls. */ - int tmp_flags = flags; - int* tmp_stack = (int*)child_stack; - void* tmp_ptid = parent_tidptr; - void* tmp_tls = newtls; + int tmp_flags = flags; // 复制flags的值 `tmp_flags`变量用于保存`flags`的值。 - register int* ___ctid __asm__("r4") = child_tidptr; + int* tmp_stack = (int*)child_stack; // 将子进程栈指针转换为int类型指针并保存为tmp_stack + void* tmp_ptid = parent_tidptr; // 保存parent_tidptr的值 + void* tmp_tls = newtls; // 保存newtls的值 + + register int* ___ctid __asm__("r4") = child_tidptr; // 将child_tidptr保存到___ctid寄存器变量中 /* Push "arg" and "fn" onto the stack that will be * used by the child. */ - *(--tmp_stack) = (int)arg; - *(--tmp_stack) = (int)fn; + *(--tmp_stack) = (int)arg; // 将arg的值存入子进程栈中 + *(--tmp_stack) = (int)fn; // 将fn的值存入子进程栈中 /* We must load r0..r3 last after all possible function calls. */ - register int ___flags __asm__("r0") = tmp_flags; - register void* ___stack __asm__("r1") = tmp_stack; - register void* ___ptid __asm__("r2") = tmp_ptid; - register void* ___tls __asm__("r3") = tmp_tls; + register int ___flags __asm__("r0") = tmp_flags; // 将tmp_flags保存到___flags寄存器变量中 + register void* ___stack __asm__("r1") = tmp_stack; // 将tmp_stack保存到___stack寄存器变量中 + register void* ___ptid __asm__("r2") = tmp_ptid; // 将tmp_ptid保存到___ptid寄存器变量中 + register void* ___tls __asm__("r3") = tmp_tls; // 将tmp_tls保存到___tls寄存器变量中 /* example: %r0 = syscall(%r0 = flags, * %r1 = child_stack, @@ -484,28 +486,29 @@ long SYS_NAME(_clone)( * %r3 = newtls, * %r4 = child_tidptr) */ - __SYS_REG(clone) + __SYS_REG(clone) // 定义宏__SYS_REG(clone) + __asm__ __volatile__( - "push {r7}\n" - "mov r7,%1\n" __syscall(clone) "\n" + "push {r7}\n" // 将r7寄存器的值保存到栈中 + "mov r7,%1\n" __syscall(clone) "\n" // 调用系统调用clone - "movs %0,r0\n" - "bne 1f\n" + "movs %0,r0\n" // 将r0的值保存到___res中,并设置条件码 + "bne 1f\n" // 如果条件码不等于0,则跳转到标号1处 - "ldr r0,[sp, #4]\n" - "mov lr,pc\n" - "ldr pc,[sp]\n" + "ldr r0,[sp, #4]\n" // 将sp加上4,得到地址,然后将该地址处的内容保存到r0寄存器中 + "mov lr,pc\n" // 将pc的值保存到lr寄存器中 + "ldr pc,[sp]\n" // 将sp的值保存到pc寄存器中 - "mov r7,%2\n" __syscall(exit) "\n" + "mov r7,%2\n" __syscall(exit) "\n" // 调用系统调用exit - "1: pop {r7}\n" - : "=r"(___res) - : "r"(__sysreg), "i"(__NR_exit), "r"(___stack), "r"(___flags), "r"(___ptid), "r"(___tls), "r"(___ctid) - : "cc", "lr", "memory"); + "1: pop {r7}\n" // 将栈中的值保存到r7寄存器中 + : "=r"(___res) // 输出结果保存到___res寄存器变量中 + : "r"(__sysreg), "i"(__NR_exit), "r"(___stack), "r"(___flags), "r"(___ptid), "r"(___tls), "r"(___ctid) // 输入参数 + : "cc", "lr", "memory"); // 修改了条件码,lr寄存器内容以及内存 } _clone_exit: - ___syscall_return(int, ___res); + ___syscall_return(int, ___res); // 调用___syscall_return函数返回结果 } #elif (defined(__aarch64__)) diff --git a/bootstrap.cpp b/bootstrap.cpp new file mode 100644 index 000000000..f35dfe39d --- /dev/null +++ b/bootstrap.cpp @@ -0,0 +1,1137 @@ +/* ------------------------------------------------------------------------- + * + * bootstrap.c + * routines to support running openGauss in 'bootstrap' mode + * bootstrap mode is used to create the initial template database + * + * Portions Copyright (c) 2020 Huawei Technologies Co.,Ltd. + * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * Portions Copyright (c) 2010-2012 Postgres-XC Development Group + * + * IDENTIFICATION + * src/backend/bootstrap/bootstrap.c + * + * ------------------------------------------------------------------------- + */ +#include "postgres.h" +#include "knl/knl_variable.h" +#include "pgstat.h" +#include +#include +#include +#ifdef HAVE_GETOPT_H +#include +#endif + +#include "access/tableam.h" +#include "bootstrap/bootstrap.h" +#include "catalog/index.h" +#include "catalog/pg_collation.h" +#include "catalog/pg_proc.h" +#include "catalog/pg_type.h" +#include "libpq/pqsignal.h" +#include "miscadmin.h" +#include "pgstat.h" +#include "nodes/makefuncs.h" +#include "postmaster/aiocompleter.h" +#include "postmaster/bgwriter.h" +#include "postmaster/pagewriter.h" +#include "postmaster/cbmwriter.h" +#include "postmaster/startup.h" +#include "postmaster/twophasecleaner.h" +#include "postmaster/licensechecker.h" +#include "postmaster/walwriter.h" +#include "postmaster/lwlockmonitor.h" +#include "replication/walreceiver.h" +#include "replication/datareceiver.h" +#include "storage/buf/bufmgr.h" +#include "storage/ipc.h" +#include "storage/proc.h" +#include "tcop/tcopprot.h" +#include "threadpool/threadpool.h" +#include "utils/builtins.h" +#include "utils/fmgroids.h" +#include "utils/guc_storage.h" +#include "utils/memutils.h" +#include "utils/plog.h" +#include "utils/postinit.h" +#include "utils/ps_status.h" +#include "utils/rel.h" +#include "utils/rel_gs.h" +#include "utils/relmapper.h" +#include "utils/snapmgr.h" +#include "access/parallel_recovery/page_redo.h" + +#ifdef PGXC +#include "nodes/nodes.h" +#include "pgxc/poolmgr.h" +#endif + +#include "gssignal/gs_signal.h" + +#define ALLOC(t, c) ((t*)selfpalloc0((unsigned)(c) * sizeof(t))) + +static void CheckerModeMain(void); +static void BootstrapModeMain(void); +static void bootstrap_signals(void); +static Form_pg_attribute AllocateAttribute(void); +static Oid gettype(char* type); +static void cleanup(void); + +/* + * Basic information associated with each type. This is used before + * pg_type is filled, so it has to cover the datatypes used as column types + * in the core "bootstrapped" catalogs. + * + * XXX several of these input/output functions do catalog scans + * (e.g., F_REGPROCIN scans pg_proc). this obviously creates some + * order dependencies in the catalog creation process. + */ +struct typinfo { + char name[NAMEDATALEN]; + Oid oid; + Oid elem; + int16 len; + bool byval; + char align; + char storage; + Oid collation; + Oid inproc; + Oid outproc; +}; + +static const struct typinfo TypInfo[] = {{"bool", BOOLOID, 0, 1, true, 'c', 'p', InvalidOid, F_BOOLIN, F_BOOLOUT}, + {"bytea", BYTEAOID, 0, -1, false, 'i', 'x', InvalidOid, F_BYTEAIN, F_BYTEAOUT}, + {"char", CHAROID, 0, 1, true, 'c', 'p', InvalidOid, F_CHARIN, F_CHAROUT}, + {"int1", INT1OID, 0, 1, true, 'c', 'p', InvalidOid, F_INT1IN, F_INT1OUT}, + {"int2", INT2OID, 0, 2, true, 's', 'p', InvalidOid, F_INT2IN, F_INT2OUT}, + {"int4", INT4OID, 0, 4, true, 'i', 'p', InvalidOid, F_INT4IN, F_INT4OUT}, + {"float4", FLOAT4OID, 0, 4, FLOAT4PASSBYVAL, 'i', 'p', InvalidOid, F_FLOAT4IN, F_FLOAT4OUT}, + {"name", NAMEOID, CHAROID, NAMEDATALEN, false, 'c', 'p', InvalidOid, F_NAMEIN, F_NAMEOUT}, + {"regclass", REGCLASSOID, 0, 4, true, 'i', 'p', InvalidOid, F_REGCLASSIN, F_REGCLASSOUT}, + {"regproc", REGPROCOID, 0, 4, true, 'i', 'p', InvalidOid, F_REGPROCIN, F_REGPROCOUT}, + {"regtype", REGTYPEOID, 0, 4, true, 'i', 'p', InvalidOid, F_REGTYPEIN, F_REGTYPEOUT}, + {"text", TEXTOID, 0, -1, false, 'i', 'x', DEFAULT_COLLATION_OID, F_TEXTIN, F_TEXTOUT}, + {"oid", OIDOID, 0, 4, true, 'i', 'p', InvalidOid, F_OIDIN, F_OIDOUT}, + {"tid", TIDOID, 0, 6, false, 's', 'p', InvalidOid, F_TIDIN, F_TIDOUT}, + {"xid", XIDOID, 0, 8, FLOAT8PASSBYVAL, 'd', 'p', InvalidOid, F_XIDIN, F_XIDOUT}, + {"xid32", SHORTXIDOID, 0, 4, FLOAT4PASSBYVAL, 'i', 'p', InvalidOid, F_XIDIN4, F_XIDOUT4}, + {"cid", CIDOID, 0, 4, true, 'i', 'p', InvalidOid, F_CIDIN, F_CIDOUT}, + {"pg_node_tree", + PGNODETREEOID, + 0, + -1, + false, + 'i', + 'x', + DEFAULT_COLLATION_OID, + F_PG_NODE_TREE_IN, + F_PG_NODE_TREE_OUT}, + {"int2vector", INT2VECTOROID, INT2OID, -1, false, 'i', 'p', InvalidOid, F_INT2VECTORIN, F_INT2VECTOROUT}, + {"oidvector", OIDVECTOROID, OIDOID, -1, false, 'i', 'p', InvalidOid, F_OIDVECTORIN, F_OIDVECTOROUT}, + {"_int2", INT2ARRAYOID, INT2OID, -1, false, 'i', 'x', InvalidOid, F_ARRAY_IN, F_ARRAY_OUT}, + {"_int4", INT4ARRAYOID, INT4OID, -1, false, 'i', 'x', InvalidOid, F_ARRAY_IN, F_ARRAY_OUT}, + {"_text", 1009, TEXTOID, -1, false, 'i', 'x', DEFAULT_COLLATION_OID, F_ARRAY_IN, F_ARRAY_OUT}, + {"_oid", 1028, OIDOID, -1, false, 'i', 'x', InvalidOid, F_ARRAY_IN, F_ARRAY_OUT}, + {"_char", 1002, CHAROID, -1, false, 'i', 'x', InvalidOid, F_ARRAY_IN, F_ARRAY_OUT}, + {"_aclitem", 1034, ACLITEMOID, -1, false, 'i', 'x', InvalidOid, F_ARRAY_IN, F_ARRAY_OUT}, + {"raw", RAWOID, 0, -1, false, 'i', 'x', InvalidOid, F_BYTEAIN, F_BYTEAOUT}, + {"oidvector_extend", + OIDVECTOREXTENDOID, + OIDOID, + -1, + false, + 'i', + 'x', + InvalidOid, + F_OIDVECTORIN_EXTEND, + F_OIDVECTOROUT_EXTEND}, + {"int2vector_extend", + INT2VECTOREXTENDOID, + INT2OID, + -1, + false, + 'i', + 'x', + InvalidOid, + F_INT2VECTORIN, + F_INT2VECTOROUT}}; + +static const int n_types = sizeof(TypInfo) / sizeof(struct typinfo); + +struct typmap { /* a hack */ + Oid am_oid; + FormData_pg_type am_typ; +}; + +static THR_LOCAL Datum values[MAXATTR]; /* current row's attribute values */ +static THR_LOCAL bool Nulls[MAXATTR]; + +/* + * At bootstrap time, we first declare all the indices to be built, and + * then build them. The IndexList structure stores enough information + * to allow us to build the indices after they've been declared. + */ +typedef struct _IndexList { + Oid il_heap; + Oid il_ind; + IndexInfo* il_info; + struct _IndexList* il_next; +} IndexList; + +/* + * BootStrapProcessMain + * + * The main entry point for auxiliary processes, such as the bgwriter, + * walwriter, walreceiver, bootstrapper and the shared memory checker code. + * + * This code is here just because of historical reasons. + */ +//这是一个主要的引导启动进程。它包含以下主要步骤: +//初始化全局变量,设置进程ID(PostmasterPid)和启动时间(MyStartTime)。 +//使用进程ID和启动时间作为种子初始化随机数。 +//初始化错误和内存管理子系统。 +//初始化全局配置选项。 +//处理命令行参数。根据参数设置相应的配置选项。 +//验证并设置数据目录。 +//创建数据目录的锁文件。 +//设置处理模式为BootstrapProcessing。 +//初始化基本的后台进程。 +//初始化统计信息收集。 +//根据进程类型执行相应的操作。 +//如果进程类型是CheckerProcess,则执行CheckerModeMain()函数,并退出进程。 +//如果进程类型是BootstrapProcess,则设置信号处理函数,执行BootStrapXLOG()函数,然后执行BootstrapModeMain()函数,并退出进程。 +//如果进程类型未被识别,则触发PANIC错误,并退出进程。 +void BootStrapProcessMain(int argc, char* argv[]) +{ + char* progName = argv[0]; + int flag; + char* userDoption = NULL; + OptParseContext optCtxt; + errno_t errorno = EOK; + + /* + * initialize globals + */ + PostmasterPid = gs_thread_self(); + + t_thrd.proc_cxt.MyProcPid = gs_thread_self(); + + t_thrd.proc_cxt.MyStartTime = time(NULL); + + /* + * Initialize random() for the first time, like PostmasterMain() would. + * In a regular IsUnderPostmaster backend, BackendRun() computes a + * high-entropy seed before any user query. Fewer distinct initial seeds + * can occur here. + */ + srandom((unsigned int)(t_thrd.proc_cxt.MyProcPid ^ (unsigned int)t_thrd.proc_cxt.MyStartTime)); + + t_thrd.proc_cxt.MyProgName = "BootStrap"; + /* + * Fire up essential subsystems: error and memory management + * + * If we are running under the postmaster, this is done already. + */ + if (!IsUnderPostmaster) { + MemoryContextInit(); + init_plog_global_mem(); + } + + /* Compute paths, if we didn't inherit them from postmaster */ + if (my_exec_path[0] == '\0') { + if (find_my_exec(progName, my_exec_path) < 0) + ereport(FATAL, (errmsg("%s: could not locate my own executable path", progName))); + } + + /* + * process command arguments + */ + /* Set defaults, to be overriden by explicit options below */ + if (!IsUnderPostmaster) { + InitializeGUCOptions(); + } + + /* Ignore the initial --boot argument, if present */ + if (argc > 1 && strcmp(argv[1], "--boot") == 0) { + argv++; + argc--; + } + + /* If no -x argument, we are a CheckerProcess */ + t_thrd.bootstrap_cxt.MyAuxProcType = CheckerProcess; + + initOptParseContext(&optCtxt); + while ((flag = getopt_r(argc, argv, "B:c:d:D:Fr:x:g:-:", &optCtxt)) != -1) { + switch (flag) { + case 'B': + SetConfigOption("shared_buffers", optCtxt.optarg, PGC_POSTMASTER, PGC_S_ARGV); + break; + case 'D': + userDoption = optCtxt.optarg; + break; + case 'd': { + int debugStrLen = strlen("debug") + strlen(optCtxt.optarg) + 1; + /* Turn on debugging for the bootstrap process. */ + char* debugstr = (char*)palloc(debugStrLen); + + errorno = snprintf_s(debugstr, debugStrLen, debugStrLen - 1, "debug%s", optCtxt.optarg); + securec_check_ss(errorno, "\0", "\0"); + SetConfigOption("log_min_messages", debugstr, PGC_POSTMASTER, PGC_S_ARGV); + SetConfigOption("client_min_messages", debugstr, PGC_POSTMASTER, PGC_S_ARGV); + pfree(debugstr); + } break; + case 'F': + SetConfigOption("fsync", "false", PGC_POSTMASTER, PGC_S_ARGV); + break; + case 'g': + SetConfigOption("xlog_file_path", optCtxt.optarg, PGC_POSTMASTER, PGC_S_ARGV); + break; + case 'r': + errorno = strcpy_s(t_thrd.proc_cxt.OutputFileName, MAXPGPATH, optCtxt.optarg); + securec_check(errorno, "\0", "\0"); + break; + case 'x': + t_thrd.bootstrap_cxt.MyAuxProcType = (AuxProcType)atoi(optCtxt.optarg); + break; + case 'c': + case '-': { + char* name = NULL; + char* value = NULL; + + ParseLongOption(optCtxt.optarg, &name, &value); + if (value == NULL) { + if (flag == '-') + ereport( + ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("--%s requires a value", optCtxt.optarg))); + else + ereport( + ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("-c %s requires a value", optCtxt.optarg))); + } + + SetConfigOption(name, value, PGC_POSTMASTER, PGC_S_ARGV); + pfree(name); + if (value != NULL) + pfree(value); + break; + } + default: + write_stderr("Try \"%s --help\" for more information.\n", progName); + proc_exit(1); + break; + } + } + + if (argc != optCtxt.optind) { + write_stderr("%s: invalid command-line arguments\n", progName); + proc_exit(1); + } + + /* Acquire configuration parameters, unless inherited from postmaster */ + if (!IsUnderPostmaster) { + if (!SelectConfigFiles(userDoption, progName)) { + proc_exit(1); + } + InitializeNumLwLockPartitions(); + } + g_instance.global_sysdbcache.Init(INSTANCE_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_DEFAULT)); + CreateLocalSysDBCache(); + + /* Validate we have been given a reasonable-looking t_thrd.proc_cxt.DataDir */ + Assert(t_thrd.proc_cxt.DataDir); + ValidatePgVersion(t_thrd.proc_cxt.DataDir); + + /* Change into t_thrd.proc_cxt.DataDir (if under postmaster, should be done already) */ + if (!IsUnderPostmaster) + ChangeToDataDir(); + + /* If standalone, create lockfile for data directory */ + if (!IsUnderPostmaster) + CreateDataDirLockFile(false); + + SetProcessingMode(BootstrapProcessing); + u_sess->attr.attr_common.IgnoreSystemIndexes = true; + + BaseInit(); + + pgstat_initialize(); + pgstat_bestart(); + if (!IsUnderPostmaster) { + ShareStorageInit(); + } + /* + * XLOG operations + */ + SetProcessingMode(NormalProcessing); + + switch (t_thrd.bootstrap_cxt.MyAuxProcType) { + case CheckerProcess: + /* don't set signals, they're useless here */ + CheckerModeMain(); + proc_exit(1); /* should never return */ + + case BootstrapProcess: + bootstrap_signals(); + BootStrapXLOG(); + MemoryContextUnSeal(t_thrd.top_mem_cxt); + BootstrapModeMain(); + MemoryContextSeal(t_thrd.top_mem_cxt); + proc_exit(1); /* should never return */ + + default: + ereport(PANIC, (errmsg("unrecognized process type: %d", (int)t_thrd.bootstrap_cxt.MyAuxProcType))); + proc_exit(1); + } +} + +/* + * In shared memory checker mode, all we really want to do is create shared + * memory and semaphores (just to prove we can do it with the current GUC + * settings). Since, in fact, that was already done by BaseInit(), + * we have nothing more to do here. + */ +static void CheckerModeMain(void) +{ + proc_exit(0); +} + +/* + * The main entry point for running the backend in bootstrap mode + * + * The bootstrap mode is used to initialize the template database. + * The bootstrap backend doesn't speak SQL, but instead expects + * commands in a special bootstrap language. + */ +static void BootstrapModeMain(void)//BootstrapModeMain函数用于完成系统引导模式,即系统启动阶段执行的函数 +{ + int i; + + Assert(!IsUnderPostmaster);// 断言,确认不是在后台进程中执行 + + SetProcessingMode(BootstrapProcessing);// 设置处理模式为引导模式 + + /* + * Do backend-like initialization for bootstrap mode + */ + InitProcess();//为引导模式做类似后台进程的初始化 + // 设置参数PostInit字段为NULL + t_thrd.proc_cxt.PostInit->SetDatabaseAndUser(NULL, InvalidOid, NULL); + // 初始化引导模式 + t_thrd.proc_cxt.PostInit->InitBootstrap(); + + /* Initialize stuff for bootstrap-file processing */ + /* 初始化bootstrap文件处理的相关内容 */ + for (i = 0; i < MAXATTR; i++) { + t_thrd.bootstrap_cxt.attrtypes[i] = NULL;// 每个属性的类型初始化为NULL + Nulls[i] = false; // 每个属性的是否为空初始化为false + } + + /* + * Process bootstrap input. + */ + boot_yyparse();//处理bootstrap输入 + + /* + * We should now know about all mapped relations, so it's okay to write + * out the initial relation mapping files. + */ + RelationMapFinishBootstrap();// 调用boot_yyparse函数进行解析 + + /* Clean up and exit */ + cleanup();// 调用cleanup函数进行清理操作 + proc_exit(0);// 调用proc_exit函数结束进程 +} +/* ---------------------------------------------------------------- + * misc functions + * ---------------------------------------------------------------- + */ +/* + * Set up signal handling for a bootstrap process + */ + /* + 这个函数是一个初始化信号处理器的函数。根据是否在主服务器进程中运行来设置信号处理方式。 + +如果在主服务器进程中运行(IsUnderPostmaster为真),则设置一些信号的处理方式为忽略或默认处理。具体地: +- SIGHUP信号被设置为忽略 +- SIGINT信号(取消查询)被设置为忽略 +- SIGTERM信号被设置为调用die()函数 +- SIGQUIT信号被设置为调用quickdie()函数 +- SIGALRM、SIGPIPE、SIGUSR1和SIGUSR2信号被设置为忽略 +- SIGCHLD、SIGTTIN、SIGTTOU、SIGCONT和SIGWINCH信号被设置为默认处理 +- 解除阻塞的信号被解除阻塞 + +如果不在主服务器进程中运行,则设置一些信号的处理方式为调用die()函数,同时解除阻塞的SIGUSR2信号。 + +总之,该函数的作用是为了根据运行环境设置合适的信号处理方式。 + */ +static void bootstrap_signals(void) +{ + if (IsUnderPostmaster) { + /* + * Properly accept or ignore signals the postmaster might send us + */ + (void)gspqsignal(SIGHUP, SIG_IGN); + (void)gspqsignal(SIGINT, SIG_IGN); /* ignore query-cancel */ + (void)gspqsignal(SIGTERM, die); + (void)gspqsignal(SIGQUIT, quickdie); + (void)gspqsignal(SIGALRM, SIG_IGN); + (void)gspqsignal(SIGPIPE, SIG_IGN); + (void)gspqsignal(SIGUSR1, SIG_IGN); + (void)gspqsignal(SIGUSR2, SIG_IGN); + + /* + * Reset some signals that are accepted by postmaster but not here + */ + (void)gspqsignal(SIGCHLD, SIG_DFL); + (void)gspqsignal(SIGTTIN, SIG_DFL); + (void)gspqsignal(SIGTTOU, SIG_DFL); + (void)gspqsignal(SIGCONT, SIG_DFL); + (void)gspqsignal(SIGWINCH, SIG_DFL); + + /* + * Unblock signals (they were blocked when the postmaster forked us) + */ + gs_signal_setmask(&t_thrd.libpq_cxt.UnBlockSig, NULL); + (void)gs_signal_unblock_sigusr2(); + + } else { + /* Set up appropriately for interactive use */ + (void)gspqsignal(SIGHUP, die); + (void)gspqsignal(SIGINT, die); + (void)gspqsignal(SIGTERM, die); + (void)gspqsignal(SIGQUIT, die); + (void)gs_signal_unblock_sigusr2(); + } +} + +/* ---------------------------------------------------------------- + * MANUAL BACKEND INTERACTIVE INTERFACE COMMANDS + * ---------------------------------------------------------------- + */ +/* ---------------- + * boot_openrel + * ---------------- + */ + /* + 这个函数的作用是打开一个关系(relation)。函数的输入参数是一个指向关系名的字符串。 +首先,函数检查关系名的长度是否超过了NAMEDATALEN。如果超过了,就将字符串的最后一个字符设置为'\0',这样就能确保字符串的长度不会超过NAMEDATALEN。 +然后,函数检查全局变量t_thrd.bootstrap_cxt.Typ是否为NULL。如果是NULL,说明还没有加载pg_type数据,需要加载该数据。 +加载pg_type数据的过程是从pg_type表中获取所有的行,并将行的数量存储到变量i中。然后,根据行的数量动态分配空间,并将空间的指针赋值给变量app。 +然后,循环执行i次,每次分配一个typmap结构的空间,并将该空间的指针存储到指针数组app中。最后,将数组的最后一个元素设置为NULL。 +接下来,重新开始扫描pg_type表,将扫描的结果存储到tup中。 +然后,将当前typmap结构的am_oid成员设置为tup的OID属性值,将am_typ成员设置为tup的实际数据,并将指针app递增1。循环扫描表的每一行,直到扫描结束。 +最后,关闭pg_type表,并将全局变量t_thrd.bootstrap_cxt.boot_reldesc设置为新打开的关系。获取关系的属性数量,并为每个属性分配一个空间。 +将关系的属性数据复制到属性空间中,并输出一些调试信息。 +这个函数的作用是打开一个关系,并加载关系的属性信息。 +加载属性信息的过程中,需要先加载pg_type表中的数据,并将数据存储到全局变量t_thrd.bootstrap_cxt.Typ中。 +然后根据关系的名称,打开关系并获取属性数量,为每个属性分配空间,并复制属性数据到空间中。 + */ +void boot_openrel(char* relname) +{ + int i; + struct typmap** app; + Relation rel; + TableScanDesc scan; + HeapTuple tup; + errno_t rc; + + if (strlen(relname) >= NAMEDATALEN) + relname[NAMEDATALEN - 1] = '\0'; + + if (t_thrd.bootstrap_cxt.Typ == NULL) { + /* We can now load the pg_type data */ + rel = heap_open(TypeRelationId, NoLock); + scan = tableam_scan_begin(rel, SnapshotNow, 0, NULL); + i = 0; + + while ((tup = (HeapTuple) tableam_scan_getnexttuple(scan, ForwardScanDirection)) != NULL) + ++i; + tableam_scan_end(scan); + app = t_thrd.bootstrap_cxt.Typ = ALLOC(struct typmap*, i + 1); + while (i-- > 0) + *app++ = ALLOC(struct typmap, 1); + *app = NULL; + scan = tableam_scan_begin(rel, SnapshotNow, 0, NULL); + app = t_thrd.bootstrap_cxt.Typ; + while ((tup = (HeapTuple) tableam_scan_getnexttuple(scan, ForwardScanDirection)) != NULL) { + (*app)->am_oid = HeapTupleGetOid(tup); + rc = + memcpy_s((char*)&(*app)->am_typ, sizeof((*app)->am_typ), (char*)GETSTRUCT(tup), sizeof((*app)->am_typ)); + securec_check(rc, "\0", "\0"); + app++; + } + tableam_scan_end(scan); + heap_close(rel, NoLock); + } + + if (t_thrd.bootstrap_cxt.boot_reldesc != NULL) + closerel(NULL); + + ereport(DEBUG4, (errmsg("open relation %s, attrsize %d", relname, (int)ATTRIBUTE_FIXED_PART_SIZE))); + + t_thrd.bootstrap_cxt.boot_reldesc = heap_openrv(makeRangeVar(NULL, relname, -1), NoLock); + t_thrd.bootstrap_cxt.numattr = RelationGetNumberOfAttributes(t_thrd.bootstrap_cxt.boot_reldesc); + for (i = 0; i < t_thrd.bootstrap_cxt.numattr; i++) { + if (t_thrd.bootstrap_cxt.attrtypes[i] == NULL) + t_thrd.bootstrap_cxt.attrtypes[i] = AllocateAttribute(); + rc = memmove_s((char*)t_thrd.bootstrap_cxt.attrtypes[i], + ATTRIBUTE_FIXED_PART_SIZE, + (char*)t_thrd.bootstrap_cxt.boot_reldesc->rd_att->attrs[i], + ATTRIBUTE_FIXED_PART_SIZE); + securec_check(rc, "\0", "\0"); + + { + Form_pg_attribute at = t_thrd.bootstrap_cxt.attrtypes[i]; + + ereport(DEBUG4, + (errmsg("create attribute %d name %s len %d num %d type %u", + i, + NameStr(at->attname), + at->attlen, + at->attnum, + at->atttypid))); + } + } +} + +/* ---------------- + * closerel + * ---------------- + */ +给函数closerel添加注释: + +/** + * @brief 关闭指定的关系。 + * + * @param name 要关闭的关系的名称。 + */ +void closerel(char* name) +{ + // 检查是否传入了正确的参数 + if (name != NULL) { + // 检查是否存在已打开的关系 + if (t_thrd.bootstrap_cxt.boot_reldesc) { + // 检查要关闭的关系名是否与当前已打开的关系名不同 + if (strcmp(RelationGetRelationName(t_thrd.bootstrap_cxt.boot_reldesc), name) != 0) + // 如果不同,报错,提示预期的关系名和实际的关系名 + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("close of %s when %s was expected", + name, + RelationGetRelationName(t_thrd.bootstrap_cxt.boot_reldesc)))); + } else { + // 如果不存在已打开的关系,报错,提示关闭关系前未打开任何关系 + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("close of %s before any relation was opened", name))); + } + } + + // 检查是否存在已打开的关系 + if (t_thrd.bootstrap_cxt.boot_reldesc == NULL) + // 如果不存在已打开的关系,报错,提示没有可关闭的关系 + ereport(ERROR, (errcode(ERRCODE_UNEXPECTED_NULL_VALUE), errmsg("no open relation to close"))); + else { + // 输出调试信息,关闭关系,将已打开的关系指针设置为NULL + ereport(DEBUG4, (errmsg("close relation %s", RelationGetRelationName(t_thrd.bootstrap_cxt.boot_reldesc)))); + heap_close(t_thrd.bootstrap_cxt.boot_reldesc, NoLock); + t_thrd.bootstrap_cxt.boot_reldesc = NULL; + } +} + +/* +* fix roluseft ,rolmonitoradmin, roloperatoradmin and rolpolicyadmin column of pg_authid to notnull +*/ +static void fix_attr_notnull(const char* name, int attnum) +{ + if (strncmp(name, "roluseft", strlen("roluseft")) == 0 && strlen(name) == strlen("roluseft")) { + t_thrd.bootstrap_cxt.attrtypes[attnum]->attnotnull = true; + } + if (strncmp(name, "rolmonitoradmin", strlen("rolmonitoradmin")) == 0 && strlen(name) == strlen("rolmonitoradmin")) { + t_thrd.bootstrap_cxt.attrtypes[attnum]->attnotnull = true; + } + if (strncmp(name, "roloperatoradmin", strlen("roloperatoradmin")) == 0 && + strlen(name) == strlen("roloperatoradmin")) { + t_thrd.bootstrap_cxt.attrtypes[attnum]->attnotnull = true; + } + if (strncmp(name, "rolpolicyadmin", strlen("rolpolicyadmin")) == 0 && strlen(name) == strlen("rolpolicyadmin")) { + t_thrd.bootstrap_cxt.attrtypes[attnum]->attnotnull = true; + } +} + +/* ---------------- + * DEFINEATTR() + * + * define a pair + * if there are n fields in a relation to be created, this routine + * will be called n times + * ---------------- + */ + /* + 这个函数是用来定义数据库表中的一个属性(列)。函数的参数包括属性的名称(name)、数据类型(type)、以及属性的位置(attnum)。 +函数首先检查是否存在正在处理的关系(表),如果有,则发出警告并关闭该关系。 +接下来,函数分配一个新的Attribute结构体给t_thrd.bootstrap_cxt.attrtypes[attnum],并用0填充这个结构体。 +然后,函数将给定的属性名称和类型复制到Attribute结构体中,并设置了其他关于属性的一些信息,如编号(attnum + 1)、数据类型的OID值、数据类型的长度、是否将数据类型存储为基本类型等。 +接下来,函数会判断属性是否可以为空。如果属性是一个固定宽度的数据类型,或者前面的属性也是不可为空的变量(用C结构体声明访问),则将属性标记为"not null"。 +接着,函数检查特定的属性名称,并将这些属性标记为可为空或不可为空。 +这里列举了两个示例情况,第一个是将名为"partkey"、"intervaltablespace"、"intspnum"的属性标记为可为空,第二个是将名为"roluseft"、"rolmonitoradmin"、"roloperatoradmin"和"rolpolicyadmin"的属性标记为不可为空。 +总之,这个函数用于定义数据库表中的一个属性,包括属性的名称、数据类型和是否可为空等信息,并根据一些规则来判断是否将属性标记为"not null"。 + */ +void DefineAttr(const char* name, char* type, int attnum) +{ + Oid typeoid; + + if (t_thrd.bootstrap_cxt.boot_reldesc != NULL) { + ereport(WARNING, (errmsg("no open relations allowed with CREATE command"))); + closerel(NULL); + } + + if (t_thrd.bootstrap_cxt.attrtypes[attnum] == NULL) + t_thrd.bootstrap_cxt.attrtypes[attnum] = AllocateAttribute(); + MemSet(t_thrd.bootstrap_cxt.attrtypes[attnum], 0, ATTRIBUTE_FIXED_PART_SIZE); + + (void)namestrcpy(&t_thrd.bootstrap_cxt.attrtypes[attnum]->attname, name); + ereport(DEBUG4, (errmsg("column %s %s", NameStr(t_thrd.bootstrap_cxt.attrtypes[attnum]->attname), type))); + t_thrd.bootstrap_cxt.attrtypes[attnum]->attnum = attnum + 1; /* fillatt */ + + typeoid = gettype(type); + + if (t_thrd.bootstrap_cxt.Typ != NULL) { + t_thrd.bootstrap_cxt.attrtypes[attnum]->atttypid = t_thrd.bootstrap_cxt.Ap->am_oid; + t_thrd.bootstrap_cxt.attrtypes[attnum]->attlen = t_thrd.bootstrap_cxt.Ap->am_typ.typlen; + t_thrd.bootstrap_cxt.attrtypes[attnum]->attbyval = t_thrd.bootstrap_cxt.Ap->am_typ.typbyval; + t_thrd.bootstrap_cxt.attrtypes[attnum]->attstorage = t_thrd.bootstrap_cxt.Ap->am_typ.typstorage; + t_thrd.bootstrap_cxt.attrtypes[attnum]->attalign = t_thrd.bootstrap_cxt.Ap->am_typ.typalign; + t_thrd.bootstrap_cxt.attrtypes[attnum]->attcollation = t_thrd.bootstrap_cxt.Ap->am_typ.typcollation; + /* if an array type, assume 1-dimensional attribute */ + if (t_thrd.bootstrap_cxt.Ap->am_typ.typelem != InvalidOid && t_thrd.bootstrap_cxt.Ap->am_typ.typlen < 0) + t_thrd.bootstrap_cxt.attrtypes[attnum]->attndims = 1; + else + t_thrd.bootstrap_cxt.attrtypes[attnum]->attndims = 0; + } else { + t_thrd.bootstrap_cxt.attrtypes[attnum]->atttypid = TypInfo[typeoid].oid; + t_thrd.bootstrap_cxt.attrtypes[attnum]->attlen = TypInfo[typeoid].len; + t_thrd.bootstrap_cxt.attrtypes[attnum]->attbyval = TypInfo[typeoid].byval; + t_thrd.bootstrap_cxt.attrtypes[attnum]->attstorage = TypInfo[typeoid].storage; + t_thrd.bootstrap_cxt.attrtypes[attnum]->attalign = TypInfo[typeoid].align; + t_thrd.bootstrap_cxt.attrtypes[attnum]->attcollation = TypInfo[typeoid].collation; + /* if an array type, assume 1-dimensional attribute */ + if (TypInfo[typeoid].elem != InvalidOid && t_thrd.bootstrap_cxt.attrtypes[attnum]->attlen < 0) + t_thrd.bootstrap_cxt.attrtypes[attnum]->attndims = 1; + else + t_thrd.bootstrap_cxt.attrtypes[attnum]->attndims = 0; + } + + t_thrd.bootstrap_cxt.attrtypes[attnum]->attstattarget = -1; + t_thrd.bootstrap_cxt.attrtypes[attnum]->attcacheoff = -1; + t_thrd.bootstrap_cxt.attrtypes[attnum]->atttypmod = -1; + t_thrd.bootstrap_cxt.attrtypes[attnum]->attislocal = true; + + /* + * Mark as "not null" if type is fixed-width and prior columns are too. + * This corresponds to case where column can be accessed directly via C + * struct declaration. + * + * oidvector and int2vector are also treated as not-nullable, even though + * they are no longer fixed-width. + */ +#define MARKNOTNULL(att) ((att)->attlen > 0 || (att)->atttypid == OIDVECTOROID || (att)->atttypid == INT2VECTOROID) + + if (MARKNOTNULL(t_thrd.bootstrap_cxt.attrtypes[attnum])) { + int i; + + for (i = 0; i < attnum; i++) { + if (!MARKNOTNULL(t_thrd.bootstrap_cxt.attrtypes[i])) + break; + } + if (i == attnum) + t_thrd.bootstrap_cxt.attrtypes[attnum]->attnotnull = true; + } + + // fix partkey/intervaltablespace/intspnum columns of pg_partition to nullable + if (strcmp(name, "partkey") == 0 || strcmp(name, "intervaltablespace") == 0 || strcmp(name, "intspnum") == 0) { + t_thrd.bootstrap_cxt.attrtypes[attnum]->attnotnull = false; + } + + // fix roluseft ,rolmonitoradmin, roloperatoradmin and rolpolicyadmin column of pg_authid to notnull + fix_attr_notnull(name, attnum); + +} + +/* ---------------- + * InsertOneTuple + * + * If objectid is not zero, it is a specific OID to assign to the tuple. + * Otherwise, an OID will be assigned (if necessary) by heap_insert. + * ---------------- + */ + /*这个函数用于向表中插入一条元组。函数的参数是一个对象ID(objectid),表示要插入的元组的ID。函数通过`t_thrd.bootstrap_cxt.boot_reldesc`访问引导过程中的关系(表)描述符。 +首先,函数打印调试信息,包括要插入的行的ID和列数。然后,函数检查关系(表)描述符是否为引导过程中的pg_proc表的描述符,如果是,则报错,因为内置函数不应该被添加到pg_proc表中。 +接下来,函数调用`CreateTupleDesc`函数创建一个描述插入元组的元组描述符(tupDesc)。创建元组描述符时传递了一些参数,包括属性数目、关系是否具有物理上标识的Oid、属性类型数组以及关系的类型。 +然后,函数调用`tableam_tops_form_tuple`函数创建一个HeapTuple结构体,即要插入的元组。创建HeapTuple时使用了`tupDesc`、`values`(待插入的属性值数组)以及`Nulls`(表示每个属性是否为NULL的标记)。 +如果传递了非0的对象ID(objectid),则使用`HeapTupleSetOid`函数设置HeapTuple的对象ID。 +接下来,函数通过调用`simple_heap_insert`函数将HeapTuple插入到关系中。 +然后,函数通过调用`tableam_tops_free_tuple`函数释放之前创建的HeapTuple。 +最后,函数在插入完成后打印调试信息,并通过循环将`Nulls`数组重置为false,以便下一次插入元组时使用。 +总之,这个函数用于插入一条元组到表中。函数创建一个插入元组的元组描述符,然后根据传递的属性值和标记创建一个HeapTuple,并将其插入到关系中。最后,函数释放已创建的HeapTuple,并重置标记数组以备下次插入使用。*/ +void InsertOneTuple(Oid objectid) +{ + HeapTuple tuple; + TupleDesc tupDesc; + int i; + + ereport(DEBUG4, (errmsg("inserting row oid %u, %d columns", objectid, t_thrd.bootstrap_cxt.numattr))); + + if (IsBootingPgProc(t_thrd.bootstrap_cxt.boot_reldesc)) { + ereport(FATAL, (errmsg("Built-in functions should not be added into pg_proc"))); + } + tupDesc = CreateTupleDesc(t_thrd.bootstrap_cxt.numattr, + RelationGetForm(t_thrd.bootstrap_cxt.boot_reldesc)->relhasoids, + t_thrd.bootstrap_cxt.attrtypes, + t_thrd.bootstrap_cxt.boot_reldesc->rd_tam_type); + tuple = (HeapTuple) tableam_tops_form_tuple(tupDesc, values, Nulls, HEAP_TUPLE); + if (objectid != (Oid)0) + HeapTupleSetOid(tuple, objectid); + pfree(tupDesc); /* just free's tupDesc, not the attrtypes */ + + (void)simple_heap_insert(t_thrd.bootstrap_cxt.boot_reldesc, tuple); + tableam_tops_free_tuple(tuple); + ereport(DEBUG4, (errmsg("row inserted"))); + + /* + * Reset null markers for next tuple + */ + for (i = 0; i < t_thrd.bootstrap_cxt.numattr; i++) + Nulls[i] = false; +} + +/* ---------------- + * InsertOneValue + * ---------------- + */ +void InsertOneValue(char* value, int i) +{ + Oid typoid; + int16 typlen; + bool typbyval = false; + char typalign; + char typdelim; + Oid typioparam; + Oid typinput; + Oid typoutput; + char* prt = NULL; + + AssertArg(i >= 0 && i < MAXATTR); + + ereport(DEBUG4, (errmsg("inserting column %d value \"%s\"", i, value))); + + typoid = t_thrd.bootstrap_cxt.boot_reldesc->rd_att->attrs[i]->atttypid; + + boot_get_type_io_data(typoid, &typlen, &typbyval, &typalign, &typdelim, &typioparam, &typinput, &typoutput); + + values[i] = OidInputFunctionCall(typinput, value, typioparam, -1); + prt = OidOutputFunctionCall(typoutput, values[i]); + ereport(DEBUG4, (errmsg("inserted -> %s", prt))); + pfree(prt); +} + +/* ---------------- + * InsertOneNull + * ---------------- + */ +void InsertOneNull(int i) +{ + ereport(DEBUG4, (errmsg("inserting column %d NULL", i))); + Assert(i >= 0 && i < MAXATTR); + values[i] = PointerGetDatum(NULL); + Nulls[i] = true; +} + +/* ---------------- + * cleanup + * ---------------- + */ +static void cleanup(void) +{ + if (t_thrd.bootstrap_cxt.boot_reldesc != NULL) + closerel(NULL); +} + +/* ---------------- + * gettype + * + * NB: this is really ugly; it will return an integer index into TypInfo[], + * and not an OID at all, until the first reference to a type not known in + * TypInfo[]. At that point it will read and cache pg_type in the Typ array, + * and subsequently return a real OID (and set the global pointer Ap to + * point at the found row in Typ). So caller must check whether Typ is + * still NULL to determine what the return value is! + * ---------------- + */ +static Oid gettype(char* type) +{ + int i; + Relation rel; + TableScanDesc scan; + HeapTuple tup; + struct typmap** app; + errno_t rc; + + if (t_thrd.bootstrap_cxt.Typ != NULL) { + for (app = t_thrd.bootstrap_cxt.Typ; *app != NULL; app++) { + if (strncmp(NameStr((*app)->am_typ.typname), type, NAMEDATALEN) == 0) { + t_thrd.bootstrap_cxt.Ap = *app; + return (*app)->am_oid; + } + } + } else { + for (i = 0; i < n_types; i++) { + if (strncmp(type, TypInfo[i].name, NAMEDATALEN) == 0) + return i; + } + ereport(DEBUG4, (errmsg("external type: %s", type))); + rel = heap_open(TypeRelationId, NoLock); + scan = tableam_scan_begin(rel, SnapshotNow, 0, NULL); + i = 0; + while ((tup = (HeapTuple) tableam_scan_getnexttuple(scan, ForwardScanDirection)) != NULL) + ++i; + tableam_scan_end(scan); + app = t_thrd.bootstrap_cxt.Typ = ALLOC(struct typmap*, i + 1); + while (i-- > 0) + *app++ = ALLOC(struct typmap, 1); + *app = NULL; + scan = tableam_scan_begin(rel, SnapshotNow, 0, NULL); + app = t_thrd.bootstrap_cxt.Typ; + while ((tup = (HeapTuple) tableam_scan_getnexttuple(scan, ForwardScanDirection)) != NULL) { + (*app)->am_oid = HeapTupleGetOid(tup); + rc = memmove_s( + (char*)&(*app++)->am_typ, sizeof((*app)->am_typ), (char*)GETSTRUCT(tup), sizeof((*app)->am_typ)); + securec_check(rc, "\0", "\0"); + } + tableam_scan_end(scan); + heap_close(rel, NoLock); + return gettype(type); + } + ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("unrecognized type \"%s\"", type))); + /* not reached, here to make compiler happy */ + return 0; +} + +/* ---------------- + * boot_get_type_io_data + * + * Obtain type I/O information at bootstrap time. This intentionally has + * almost the same API as lsyscache.c's get_type_io_data, except that + * we only support obtaining the typinput and typoutput routines, not + * the binary I/O routines. It is exported so that array_in and array_out + * can be made to work during early bootstrap. + * ---------------- + */ + /* + 这个函数用于获取指定类型(typid)的输入/输出数据。函数通过指定的typid查找对应的类型信息,并将这些信息通过输出参数返回给调用者。 +首先,函数检查`t_thrd.bootstrap_cxt.Typ`是否为空。如果不为空,说明在引导过程中已经获取到了`pg_type`表的内容。接下来,函数在`t_thrd.bootstrap_cxt.Typ`中查找指定的typid对应的类型信息,并将找到的信息保存在`ap`结构体中。 +如果没有找到指定的typid对应的信息,则报错,提示类型OID未在Typ列表中找到。 +接着,函数通过将`ap`结构体中的成员值赋给输出参数,将类型信息返回给调用者。具体包括类型的长度(typlen)、是否传递值(typbyval)、对齐方式(typalign)、分隔符(typdelim)、类型输入函数参数(typioparam)、类型输入函数(typinput)、类型输出函数(typoutput)。 +如果`t_thrd.bootstrap_cxt.Typ`为空,说明还没有获取到`pg_type`表的内容。在这种情况下,函数将使用固定的`TypInfo`数组来获取类型信息。函数通过遍历`TypInfo`数组,查找指定typid对应的类型信息,并将找到的信息保存在`typeindex`变量中。 +如果找不到指定typid对应的类型信息,则报错,提示类型OID在TypInfo中未找到。 +接下来,函数通过将`TypInfo`数组中的成员值赋给输出参数,将类型信息返回给调用者。具体包括类型的长度(typlen)、是否传递值(typbyval)、对齐方式(typalign)、分隔符(typdelim)、类型输入函数参数(typioparam)、类型输入函数(typinput)、类型输出函数(typoutput)。 +总之,这个函数用于获取指定类型的输入/输出数据。函数根据是否已经获取到`pg_type`表的内容来决定是使用`pg_type`中的类型信息,还是使用固定的`TypInfo`数组中的类型信息。然后,函数将获取到的类型信息通过输出参数返回给调用者。 + */ +void boot_get_type_io_data(Oid typid, int16* typlen, bool* typbyval, char* typalign, char* typdelim, Oid* typioparam, + Oid* typinput, Oid* typoutput) +{ + if (t_thrd.bootstrap_cxt.Typ != NULL) { + /* We have the boot-time contents of pg_type, so use it */ + struct typmap** app; + struct typmap* ap = NULL; + + app = t_thrd.bootstrap_cxt.Typ; + while (*app && (*app)->am_oid != typid) + ++app; + ap = *app; + if (ap == NULL) + ereport( + ERROR, (errcode(ERRCODE_UNEXPECTED_NULL_VALUE), errmsg("type OID %u not found in Typ list", typid))); + + *typlen = ap->am_typ.typlen; + *typbyval = ap->am_typ.typbyval; + *typalign = ap->am_typ.typalign; + *typdelim = ap->am_typ.typdelim; + + /* XXX this logic must match getTypeIOParam() */ + if (OidIsValid(ap->am_typ.typelem)) + *typioparam = ap->am_typ.typelem; + else + *typioparam = typid; + + *typinput = ap->am_typ.typinput; + *typoutput = ap->am_typ.typoutput; + } else { + /* We don't have pg_type yet, so use the hard-wired TypInfo array */ + int typeindex; + + for (typeindex = 0; typeindex < n_types; typeindex++) { + if (TypInfo[typeindex].oid == typid) + break; + } + if (typeindex >= n_types) + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("type OID %u not found in TypInfo", typid))); + + *typlen = TypInfo[typeindex].len; + *typbyval = TypInfo[typeindex].byval; + *typalign = TypInfo[typeindex].align; + /* We assume typdelim is ',' for all boot-time types */ + *typdelim = ','; + + /* XXX this logic must match getTypeIOParam() */ + if (OidIsValid(TypInfo[typeindex].elem)) + *typioparam = TypInfo[typeindex].elem; + else + *typioparam = typid; + + *typinput = TypInfo[typeindex].inproc; + *typoutput = TypInfo[typeindex].outproc; + } +} + +/* ---------------- + * AllocateAttribute + * + * Note: bootstrap never sets any per-column ACLs, so we only need + * ATTRIBUTE_FIXED_PART_SIZE space per attribute. + * ---------------- + */ +static Form_pg_attribute AllocateAttribute(void) +{ + Form_pg_attribute attribute = (Form_pg_attribute)MemoryContextAlloc( + SESS_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_STORAGE), ATTRIBUTE_FIXED_PART_SIZE); + + if (!PointerIsValid(attribute)) + ereport(FATAL, (errmsg("out of memory"))); + MemSet(attribute, 0, ATTRIBUTE_FIXED_PART_SIZE); + + return attribute; +} + +/* ---------------- + * MapArrayTypeName + * XXX arrays of "basetype" are always "_basetype". + * this is an evil hack inherited from rel. 3.1. + * XXX array dimension is thrown away because we + * don't support fixed-dimension arrays. again, + * sickness from 3.1. + * + * the string passed in must have a '[' character in it + * + * the string returned is a pointer to static storage and should NOT + * be freed by the CALLER. + * ---------------- + */ +const char* MapArrayTypeName(const char* s) +{ + int i; + int j; + + if (s == NULL || s[0] == '\0') + return s; + + j = 1; + t_thrd.bootstrap_cxt.newStr[0] = '_'; + for (i = 0; i < NAMEDATALEN - 1 && s[i] != '['; i++, j++) + t_thrd.bootstrap_cxt.newStr[j] = s[i]; + + t_thrd.bootstrap_cxt.newStr[j] = '\0'; + + return t_thrd.bootstrap_cxt.newStr; +} + +/* + * index_register() -- record an index that has been set up for building + * later. + * + * At bootstrap time, we define a bunch of indexes on system catalogs. + * We postpone actually building the indexes until just before we're + * finished with initialization, however. This is because the indexes + * themselves have catalog entries, and those have to be included in the + * indexes on those catalogs. Doing it in two phases is the simplest + * way of making sure the indexes have the right contents at the end. + */ + /*这个函数用于在引导过程中注册索引。函数接收三个参数:heap(堆表的对象ID)、ind(索引的对象ID)和indexInfo(IndexInfo结构体的指针,包含了索引的详细信息)。 +函数。首先,函数创建一个IndexList结构体的实例newind,并将其初始化为NULL。 +接下来,函数检查是否已经创建了t_thrd.bootstrap_cxt.nogc上下文,如果没有,则创建一个名为"BootstrapNoGC"的上下文。这个上下文用于在引导过程中暂时保存索引的相关信息,防止其被垃圾回收。 +然后函数将当前的内存上下文切换到t_thrd.bootstrap_cxt.nogc上下文。 +接着,函数分配一个IndexList结构体的内存,并将heap、ind和indexInfo的值分别赋给新分配的结构体的相应成员变量。 +然后函数通过memcpy_s函数将indexInfo结构体的内容复制到newind->il_info中。同时,函数使用copyObject函数分别复制indexInfo->ii_Expressions和indexInfo->ii_Predicate,并将复制后的值分别赋给newind->il_info->ii_Expressions和newind->il_info->ii_Predicate。 +接下来,函数将newind添加到t_thrd.bootstrap_cxt.ILHead链表中,以便在稍后的操作中使用。 +最后,函数将内存上下文切换回先前的上下文。 +总之,这个函数用于在引导过程中注册索引。它创建一个表示索引的IndexList结构体对象,并将索引相关的信息保存在其中。然后,它将这个对象添加到上下文链表中以备后续使用。*/ +void index_register(Oid heap, Oid ind, IndexInfo* indexInfo) +{ + IndexList* newind = NULL; + MemoryContext oldcxt; + errno_t rc; + + /* + * XXX mao 10/31/92 -- don't gc index reldescs, associated info at + * bootstrap time. we'll declare the indexes now, but want to create them + * later. + */ + if (t_thrd.bootstrap_cxt.nogc == NULL) + t_thrd.bootstrap_cxt.nogc = AllocSetContextCreate( + NULL, "BootstrapNoGC", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); + + oldcxt = MemoryContextSwitchTo(t_thrd.bootstrap_cxt.nogc); + + newind = (IndexList*)palloc(sizeof(IndexList)); + newind->il_heap = heap; + newind->il_ind = ind; + newind->il_info = (IndexInfo*)palloc(sizeof(IndexInfo)); + + rc = memcpy_s(newind->il_info, sizeof(IndexInfo), indexInfo, sizeof(IndexInfo)); + securec_check(rc, "\0", "\0"); + /* expressions will likely be null, but may as well copy it */ + newind->il_info->ii_Expressions = (List*)copyObject(indexInfo->ii_Expressions); + newind->il_info->ii_ExpressionsState = NIL; + /* predicate will likely be null, but may as well copy it */ + newind->il_info->ii_Predicate = (List*)copyObject(indexInfo->ii_Predicate); + newind->il_info->ii_PredicateState = NIL; + /* no exclusion constraints at bootstrap time, so no need to copy */ + Assert(indexInfo->ii_ExclusionOps == NULL); + Assert(indexInfo->ii_ExclusionProcs == NULL); + Assert(indexInfo->ii_ExclusionStrats == NULL); + + newind->il_next = t_thrd.bootstrap_cxt.ILHead; + t_thrd.bootstrap_cxt.ILHead = newind; + + (void)MemoryContextSwitchTo(oldcxt); +} + +/* + * build_indices -- fill in all the indexes registered earlier + */ + /* + 这个函数用于在引导过程中构建索引。它通过遍历t_thrd.bootstrap_cxt.ILHead链表中的每个索引来逐个构建索引。 +循环的迭代条件是t_thrd.bootstrap_cxt.ILHead不为空,也就是说还有待构建的索引。循环的每次迭代,我们会定义两个Relation对象:heap和ind,分别用于表示堆表和索引表。 +引导过程中不需要考虑获取锁的问题,所以我们使用heap_open和index_open函数打开堆表和索引表。这两个函数接收两个参数:表的对象ID和锁的模式(NoLock表示不获取锁)。返回的Relation对象分别赋给heap和ind变量。 +然后,我们调用index_build函数来构建索引。这个函数接收多个参数,包括堆表、分区信息、索引表、并行标志、索引详细信息等。这些参数的值分别来自t_thrd.bootstrap_cxt.ILHead链表的当前节点。函数会使用这些参数来构建索引。 +索引构建完成后,我们使用index_close和heap_close函数关闭索引表和堆表。这些函数同样需要传入锁的模式参数(NoLock)。 +总之,这个函数用于在引导过程中构建索引。通过遍历t_thrd.bootstrap_cxt.ILHead链表中的每个索引,我们依次打开堆表和索引表,并调用index_build函数进行索引构建。最后,我们关闭索引表和堆表。 + */ +void build_indices(void) +{ + for (; t_thrd.bootstrap_cxt.ILHead != NULL; t_thrd.bootstrap_cxt.ILHead = t_thrd.bootstrap_cxt.ILHead->il_next) { + Relation heap; + Relation ind; + + /* need not bother with locks during bootstrap */ + heap = heap_open(t_thrd.bootstrap_cxt.ILHead->il_heap, NoLock); + ind = index_open(t_thrd.bootstrap_cxt.ILHead->il_ind, NoLock); + index_build( + heap, NULL, ind, NULL, t_thrd.bootstrap_cxt.ILHead->il_info, false, false, INDEX_CREATE_NONE_PARTITION); + + index_close(ind, NoLock); + heap_close(heap, NoLock); + } +} diff --git a/src/bin/gs_cgroup/cgconf.cpp b/src/bin/gs_cgroup/cgconf.cpp index a09ae2768..4c987def5 100644 --- a/src/bin/gs_cgroup/cgconf.cpp +++ b/src/bin/gs_cgroup/cgconf.cpp @@ -1,22 +1,21 @@ -/* - * Copyright (c) 2020 Huawei Technologies Co.,Ltd. +/* COPYRIGHT (c) 2020华为技术有限公司。 * - * openGauss is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: + * openGauss基于Mulan PSL v2许可授权规则发行。 + * 您可以使用该软件,具体条件见Mulan PSL v2。 + * 您可以从以下地址获得Mulnan PSL v2的副本: * * http://license.coscl.org.cn/MulanPSL2 * - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, - * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. + * 此软件基于"按原样"提供,没有任何形式的保证, + * 无论是明示的还是暗示的,包括但不限于不侵权保证、 + * 适销性或特定用途的隐含保证。 + * 请参阅Mulan PSL v2中的详情。 * ------------------------------------------------------------------------- * * cgconf.cpp - * Cgroup configration file process functions + * Cgroup配置文件处理函数 * - * IDENTIFICATION + * 标识符 * src/bin/gs_cgroup/cgconf.cpp * * ------------------------------------------------------------------------- @@ -32,16 +31,16 @@ #include "securec.h" #include "cgutil.h" -/* - ***************** STATIC FUNCTIONS ************************ - */ + /* + ***************** STATIC FUNCTIONS ************************ + */ -/* - * function name: cgconf_get_group_type - * description : get the string of group type - * arguments : group type enum type value - * return value : the string of group type - */ + /* + * 函数名称:cgconf_get_group_type + * 函数功能:获取组类型的字符串 + * 参数列表:组类型的枚举值 + * 返回值:组类型的字符串 + */ char* cgconf_get_group_type(group_type gtype) { if (gtype == GROUP_TOP) @@ -59,118 +58,79 @@ char* cgconf_get_group_type(group_type gtype) } /* - * function name: cgconf_set_root_group - * description : set the default value of root group in configuration file + * 函数名称:cgconf_set_root_group + * 函数功能:在配置文件中设置根组的默认值 * - * Note: The root group can't set the IO relative weight. - * The percentage is calculated based on 1000. + * 注意:根组不能设置IO相关的权重。 + * 百分比是基于1000来计算的。 */ static void cgconf_set_root_group(void) { - errno_t sret; - cgutil_vaddr[TOPCG_ROOT]->used = 1; - cgutil_vaddr[TOPCG_ROOT]->gid = TOPCG_ROOT; - cgutil_vaddr[TOPCG_ROOT]->gtype = GROUP_TOP; - sret = strncpy_s(cgutil_vaddr[TOPCG_ROOT]->grpname, GPNAME_LEN, GSCGROUP_ROOT, GPNAME_LEN - 1); + errno_t sret; + cgutil_vaddr[TOPCG_ROOT]->used = 1; // 标记根组已使用 + cgutil_vaddr[TOPCG_ROOT]->gid = TOPCG_ROOT; // 设置根组的gid为根组标识符TOPCG_ROOT + cgutil_vaddr[TOPCG_ROOT]->gtype = GROUP_TOP; // 设置根组的类型为GROUP_TOP + sret = strncpy_s(cgutil_vaddr[TOPCG_ROOT]->grpname, GPNAME_LEN, GSCGROUP_ROOT, GPNAME_LEN - 1); // 将根组的组名设置为GSCGROUP_ROOT securec_check_errno(sret, , ); - cgutil_vaddr[TOPCG_ROOT]->ginfo.top.percent = 100 * DEFAULT_IO_WEIGHT / MAX_IO_WEIGHT; - cgutil_vaddr[TOPCG_ROOT]->ainfo.weight = DEFAULT_IO_WEIGHT; - cgutil_vaddr[TOPCG_ROOT]->percent = 1000; + cgutil_vaddr[TOPCG_ROOT]->ginfo.top.percent = 100 * DEFAULT_IO_WEIGHT / MAX_IO_WEIGHT; // 设置根组的IO百分比为默认IO权重(DEFAULT_IO_WEIGHT) / 最大IO权重(MAX_IO_WEIGHT) + cgutil_vaddr[TOPCG_ROOT]->ainfo.weight = DEFAULT_IO_WEIGHT; // 设置根组的IO权重为默认权重(DEFAULT_IO_WEIGHT) + cgutil_vaddr[TOPCG_ROOT]->percent = 1000; // 设置根组的百分比为1000 - /* set root group as default cpu set */ + /* 将根组设置为默认的CPU集合 */ sret = snprintf_s(cgutil_vaddr[TOPCG_ROOT]->cpuset, CPUSET_LEN, CPUSET_LEN - 1, "%s", cgutil_allset); securec_check_intval(sret, , ); } /* - * function name: cgconf_set_gauss_group - * description : set the default value of Gaussdb group in configuration file + * 函数名称:cgconf_set_gauss_group + * 函数功能:在配置文件中设置Gaussdb组的默认值 * - * Note: The IO weight value is set as 1000 (MAX_IO_WEIGHT). - * It supposes that the gaussdb can use the maximum IO resource. - * The percentage is calculated based on CPU shares value. + * 注意:IO权重的值设置为1000 (MAX_IO_WEIGHT)。 + * 意味着gaussdb可以使用最大的IO资源。 + * 百分比是基于1000来计算的。 */ static void cgconf_set_gauss_group(void) -{ - errno_t rc; - - cgutil_vaddr[TOPCG_GAUSSDB]->used = 1; - cgutil_vaddr[TOPCG_GAUSSDB]->gid = TOPCG_GAUSSDB; - cgutil_vaddr[TOPCG_GAUSSDB]->gtype = GROUP_TOP; - if ('\0' == cgutil_opt.nodegroup[0]) - rc = snprintf_s(cgutil_vaddr[TOPCG_GAUSSDB]->grpname, - GPNAME_LEN, - GPNAME_LEN - 1, - "%s:%s", - GSCGROUP_TOP_DATABASE, - cgutil_opt.user); - else - rc = snprintf_s(cgutil_vaddr[TOPCG_GAUSSDB]->grpname, - GPNAME_LEN, - GPNAME_LEN - 1, - "%s:%s", - GSCGROUP_TOP_DATABASE, - cgutil_passwd_user->pw_name); - securec_check_intval(rc, , ); - cgutil_vaddr[TOPCG_GAUSSDB]->ginfo.top.percent = - 100 * DEFAULT_GAUSS_CPUSHARES / (DEFAULT_CPU_SHARES + DEFAULT_GAUSS_CPUSHARES); - cgutil_vaddr[TOPCG_GAUSSDB]->ainfo.shares = DEFAULT_GAUSS_CPUSHARES; - cgutil_vaddr[TOPCG_GAUSSDB]->ainfo.weight = MAX_IO_WEIGHT; - cgutil_vaddr[TOPCG_GAUSSDB]->percent = - cgutil_vaddr[TOPCG_ROOT]->percent * DEFAULT_GAUSS_CPUSHARES / (DEFAULT_CPU_SHARES + DEFAULT_GAUSS_CPUSHARES); - - /* set root group as root group cpu set */ - if (*cgutil_vaddr[TOPCG_GAUSSDB]->cpuset == '\0') { - rc = snprintf_s( - cgutil_vaddr[TOPCG_GAUSSDB]->cpuset, CPUSET_LEN, CPUSET_LEN - 1, "%s", cgutil_vaddr[TOPCG_ROOT]->cpuset); - securec_check_intval(rc, , ); - } -} - -/* - * function name: cgconf_set_top_backend_group - * description : set the default value of Top Backend group - * - */ -static void cgconf_set_top_backend_group(void) { errno_t sret; - cgutil_vaddr[TOPCG_BACKEND]->used = 1; - cgutil_vaddr[TOPCG_BACKEND]->gid = TOPCG_BACKEND; - cgutil_vaddr[TOPCG_BACKEND]->gtype = GROUP_TOP; - sret = strncpy_s(cgutil_vaddr[TOPCG_BACKEND]->grpname, GPNAME_LEN, GSCGROUP_TOP_BACKEND, GPNAME_LEN - 1); + cgutil_vaddr[TOPCG_GAUSSDB]->used = 1; // 标记Gaussdb组已使用 + cgutil_vaddr[TOPCG_GAUSSDB]->gid = TOPCG_GAUSSDB; // 设置Gaussdb组的gid为Gaussdb组标识符TOPCG_GAUSSDB + cgutil_vaddr[TOPCG_GAUSSDB]->gtype = GROUP_TOP; // 设置Gaussdb组的类型为GROUP_TOP + sret = strncpy_s(cgutil_vaddr[TOPCG_GAUSSDB]->grpname, GPNAME_LEN, GSCGROUP_GAUSSDB, GPNAME_LEN - 1); // 将Gaussdb组的组名设置为GSCGROUP_GAUSSDB securec_check_errno(sret, , ); - cgutil_vaddr[TOPCG_BACKEND]->ginfo.top.percent = TOP_BACKEND_PERCENT; - cgutil_vaddr[TOPCG_BACKEND]->ainfo.shares = DEFAULT_CPU_SHARES * TOP_BACKEND_PERCENT / 10; - cgutil_vaddr[TOPCG_BACKEND]->ainfo.weight = IO_WEIGHT_CALC(MAX_IO_WEIGHT, TOP_BACKEND_PERCENT); - cgutil_vaddr[TOPCG_BACKEND]->percent = cgutil_vaddr[TOPCG_GAUSSDB]->percent * TOP_BACKEND_PERCENT / 100; + cgutil_vaddr[TOPCG_GAUSSDB]->ginfo.top.percent = 1000; // 设置Gaussdb组的IO百分比为1000 + cgutil_vaddr[TOPCG_GAUSSDB]->ainfo.weight = MAX_IO_WEIGHT; // 设置Gaussdb组的IO权重为最大IO权重(MAX_IO_WEIGHT) + cgutil_vaddr[TOPCG_GAUSSDB]->percent = 1000; // 设置Gaussdb组的百分比为1000 - /* set root group as gaussdb group cpu set */ - if (*cgutil_vaddr[TOPCG_BACKEND]->cpuset == '\0') { - sret = snprintf_s( - cgutil_vaddr[TOPCG_BACKEND]->cpuset, CPUSET_LEN, CPUSET_LEN - 1, "%s", cgutil_vaddr[TOPCG_GAUSSDB]->cpuset); - securec_check_intval(sret, , ); - } + /* 将Gaussdb组设置为默认的CPU集合 */ + sret = snprintf_s(cgutil_vaddr[TOPCG_GAUSSDB]->cpuset, CPUSET_LEN, CPUSET_LEN - 1, "%s", cgutil_allset); + securec_check_intval(sret, , ); } - /* - * function name: cgconf_set_top_class_group - * description : set the default value of Top Class group + * 函数名:cgconf_set_top_class_group + * 功能:设置默认的顶级分类组的值 * */ static void cgconf_set_top_class_group(void) { errno_t sret; + // 设置顶级分类组的used为1,表示已经被使用 cgutil_vaddr[TOPCG_CLASS]->used = 1; + // 设置顶级分类组的gid为TOPCG_CLASS cgutil_vaddr[TOPCG_CLASS]->gid = TOPCG_CLASS; + // 设置顶级分类组的gtype为GROUP_TOP cgutil_vaddr[TOPCG_CLASS]->gtype = GROUP_TOP; + // 将GSCGROUP_TOP_CLASS拷贝到顶级分类组的grpname中 sret = strncpy_s(cgutil_vaddr[TOPCG_CLASS]->grpname, GPNAME_LEN, GSCGROUP_TOP_CLASS, GPNAME_LEN - 1); securec_check_errno(sret, , ); + // 设置顶级分类组的ginfo.top.percent为TOP_CLASS_PERCENT cgutil_vaddr[TOPCG_CLASS]->ginfo.top.percent = TOP_CLASS_PERCENT; + // 设置顶级分类组的ainfo.shares为DEFAULT_CPU_SHARES * TOP_CLASS_PERCENT / 10 cgutil_vaddr[TOPCG_CLASS]->ainfo.shares = DEFAULT_CPU_SHARES * TOP_CLASS_PERCENT / 10; + // 设置顶级分类组的ainfo.weight为IO_WEIGHT_CALC(MAX_IO_WEIGHT, TOP_CLASS_PERCENT) cgutil_vaddr[TOPCG_CLASS]->ainfo.weight = IO_WEIGHT_CALC(MAX_IO_WEIGHT, TOP_CLASS_PERCENT); + // 设置顶级分类组的percent为cgutil_vaddr[TOPCG_GAUSSDB]->percent * TOP_CLASS_PERCENT / 100 cgutil_vaddr[TOPCG_CLASS]->percent = cgutil_vaddr[TOPCG_GAUSSDB]->percent * TOP_CLASS_PERCENT / 100; - /* set root group as gaussdb group cpu set */ + // 如果顶级分类组的cpuset为空,则将cgutil_vaddr[TOPCG_GAUSSDB]->cpuset拷贝到顶级分类组的cpuset中 if (*cgutil_vaddr[TOPCG_CLASS]->cpuset == '\0') { sret = snprintf_s( cgutil_vaddr[TOPCG_CLASS]->cpuset, CPUSET_LEN, CPUSET_LEN - 1, "%s", cgutil_vaddr[TOPCG_GAUSSDB]->cpuset); @@ -179,23 +139,31 @@ static void cgconf_set_top_class_group(void) } /* - * function name: cgconf_set_nodegroup_top_group - * description : set the default value of Nodegroup Top group + * 函数名:cgconf_set_nodegroup_top_group + * 功能:设置默认的节点组顶级组的值 * */ static void cgconf_set_nodegroup_top_group(void) { errno_t sret; + // 设置顶级分类组的used为1,表示已经被使用 cgutil_vaddr[TOPCG_CLASS]->used = 1; + // 设置顶级分类组的gid为TOPCG_CLASS cgutil_vaddr[TOPCG_CLASS]->gid = TOPCG_CLASS; + // 设置顶级分类组的gtype为GROUP_TOP cgutil_vaddr[TOPCG_CLASS]->gtype = GROUP_TOP; + // 将cgutil_opt.nodegroup拷贝到顶级分类组的grpname中 sret = strncpy_s(cgutil_vaddr[TOPCG_CLASS]->grpname, GPNAME_LEN, cgutil_opt.nodegroup, GPNAME_LEN - 1); securec_check_errno(sret, , ); + // 设置顶级分类组的ginfo.top.percent为TOP_CLASS_PERCENT cgutil_vaddr[TOPCG_CLASS]->ginfo.top.percent = TOP_CLASS_PERCENT; + // 设置顶级分类组的ainfo.shares为DEFAULT_CPU_SHARES * TOP_CLASS_PERCENT / 10 cgutil_vaddr[TOPCG_CLASS]->ainfo.shares = DEFAULT_CPU_SHARES * TOP_CLASS_PERCENT / 10; + // 设置顶级分类组的ainfo.weight为IO_WEIGHT_CALC(MAX_IO_WEIGHT, TOP_CLASS_PERCENT) cgutil_vaddr[TOPCG_CLASS]->ainfo.weight = IO_WEIGHT_CALC(MAX_IO_WEIGHT, TOP_CLASS_PERCENT); + // 设置顶级分类组的percent为cgutil_vaddr[TOPCG_GAUSSDB]->percent * TOP_CLASS_PERCENT / 100 cgutil_vaddr[TOPCG_CLASS]->percent = cgutil_vaddr[TOPCG_GAUSSDB]->percent * TOP_CLASS_PERCENT / 100; - /* set root group as gaussdb group cpu set */ + // 如果顶级分类组的cpuset为空,则将cgutil_vaddr[TOPCG_GAUSSDB]->cpuset拷贝到顶级分类组的cpuset中 if (*cgutil_vaddr[TOPCG_CLASS]->cpuset == '\0') { sret = snprintf_s( cgutil_vaddr[TOPCG_CLASS]->cpuset, CPUSET_LEN, CPUSET_LEN - 1, "%s", cgutil_vaddr[TOPCG_GAUSSDB]->cpuset); @@ -204,25 +172,35 @@ static void cgconf_set_nodegroup_top_group(void) } /* - * function name: cgconf_set_default_backend_group - * description : set the default value of default backend group + * 函数名:cgconf_set_default_backend_group + * 功能:设置默认的默认后端组的值 * */ void cgconf_set_default_backend_group(void) { errno_t sret; + // 设置后端组的used为1,表示已经被使用 cgutil_vaddr[BACKENDCG_START_ID]->used = 1; + // 设置后端组的gid为BACKENDCG_START_ID cgutil_vaddr[BACKENDCG_START_ID]->gid = BACKENDCG_START_ID; + // 设置后端组的gtype为GROUP_BAKWD cgutil_vaddr[BACKENDCG_START_ID]->gtype = GROUP_BAKWD; + // 设置后端组的ginfo.cls.tgid为TOPCG_BACKEND cgutil_vaddr[BACKENDCG_START_ID]->ginfo.cls.tgid = TOPCG_BACKEND; + // 设置后端组的ginfo.cls.percent为DEFAULT_BACKEND_PERCENT cgutil_vaddr[BACKENDCG_START_ID]->ginfo.cls.percent = DEFAULT_BACKEND_PERCENT; - sret = strncpy_s(cgutil_vaddr[BACKENDCG_START_ID]->grpname, GPNAME_LEN, GSCGROUP_DEFAULT_BACKEND, GPNAME_LEN - 1); + // 将GSCGROUP_DEFAULT_BACKEND拷贝到后端组的grpname中 + sret = strncpy_s( + cgutil_vaddr[BACKENDCG_START_ID]->grpname, GPNAME_LEN, GSCGROUP_DEFAULT_BACKEND, GPNAME_LEN - 1); securec_check_errno(sret, , ); + // 设置后端组的ainfo.shares为DEFAULT_CPU_SHARES * DEFAULT_BACKEND_PERCENT / 10 cgutil_vaddr[BACKENDCG_START_ID]->ainfo.shares = DEFAULT_CPU_SHARES * DEFAULT_BACKEND_PERCENT / 10; + // 设置后端组的ainfo.weight为IO_WEIGHT_CALC(MAX_IO_WEIGHT, DEFAULT_BACKEND_PERCENT) cgutil_vaddr[BACKENDCG_START_ID]->ainfo.weight = IO_WEIGHT_CALC(MAX_IO_WEIGHT, DEFAULT_BACKEND_PERCENT); + // 设置后端组的percent为cgutil_vaddr[TOPCG_BACKEND]->percent * DEFAULT_BACKEND_PERCENT / 100 cgutil_vaddr[BACKENDCG_START_ID]->percent = cgutil_vaddr[TOPCG_BACKEND]->percent * DEFAULT_BACKEND_PERCENT / 100; - /* set root group as backend group cpu set */ + // 如果后端组的cpuset为空,则将cgutil_vaddr[TOPCG_BACKEND]->cpuset拷贝到后端组的cpuset中 if (*cgutil_vaddr[BACKENDCG_START_ID]->cpuset == '\0') { sret = snprintf_s(cgutil_vaddr[BACKENDCG_START_ID]->cpuset, CPUSET_LEN, @@ -234,24 +212,34 @@ void cgconf_set_default_backend_group(void) } /* - * function name: cgconf_set_vacuum_group - * description : set the default value of vacuum backend group + * 函数名:cgconf_set_vacuum_group + * 功能:设置默认的vacuum后端组的值 * */ void cgconf_set_vacuum_group(void) { errno_t sret; + // 设置后端组的used为1,表示已经被使用 cgutil_vaddr[BACKENDCG_START_ID + 1]->used = 1; + // 设置后端组的gid为BACKENDCG_START_ID + 1 cgutil_vaddr[BACKENDCG_START_ID + 1]->gid = BACKENDCG_START_ID + 1; + // 设置后端组的gtype为GROUP_BAKWD cgutil_vaddr[BACKENDCG_START_ID + 1]->gtype = GROUP_BAKWD; + // 设置后端组的ginfo.cls.tgid为TOPCG_BACKEND cgutil_vaddr[BACKENDCG_START_ID + 1]->ginfo.cls.tgid = TOPCG_BACKEND; + // 设置后端组的ginfo.cls.percent为VACUUM_PERCENT cgutil_vaddr[BACKENDCG_START_ID + 1]->ginfo.cls.percent = VACUUM_PERCENT; - sret = strncpy_s(cgutil_vaddr[BACKENDCG_START_ID + 1]->grpname, GPNAME_LEN, GSCGROUP_VACUUM, GPNAME_LEN - 1); + // 将GSCGROUP_VACUUM拷贝到后端组的grpname中 + sret = + strncpy_s(cgutil_vaddr[BACKENDCG_START_ID + 1]->grpname, GPNAME_LEN, GSCGROUP_VACUUM, GPNAME_LEN - 1); securec_check_errno(sret, , ); + // 设置后端组的ainfo.shares为DEFAULT_CPU_SHARES * VACUUM_PERCENT / 10 cgutil_vaddr[BACKENDCG_START_ID + 1]->ainfo.shares = DEFAULT_CPU_SHARES * VACUUM_PERCENT / 10; + // 设置后端组的ainfo.weight为IO_WEIGHT_CALC(MAX_IO_WEIGHT, VACUUM_PERCENT) cgutil_vaddr[BACKENDCG_START_ID + 1]->ainfo.weight = IO_WEIGHT_CALC(MAX_IO_WEIGHT, VACUUM_PERCENT); + // 设置后端组的percent为cgutil_vaddr[TOPCG_BACKEND]->percent * VACUUM_PERCENT / 100 cgutil_vaddr[BACKENDCG_START_ID + 1]->percent = cgutil_vaddr[TOPCG_BACKEND]->percent * VACUUM_PERCENT / 100; - /* set root group as backend group cpu set */ + // 如果后端组的cpuset为空,则将cgutil_vaddr[TOPCG_BACKEND]->cpuset拷贝到后端组的cpuset中 if (*cgutil_vaddr[BACKENDCG_START_ID + 1]->cpuset == '\0') { sret = snprintf_s(cgutil_vaddr[BACKENDCG_START_ID + 1]->cpuset, CPUSET_LEN, @@ -261,36 +249,36 @@ void cgconf_set_vacuum_group(void) securec_check_intval(sret, , ); } } - /* - * function name: cgconf_set_default_class_group - * description : set the default value of default class group + * 函数名称:cgconf_set_default_class_group + * 描述:设置默认的默认类组的值 * */ void cgconf_set_default_class_group(void) { errno_t sret; - cgutil_vaddr[CLASSCG_START_ID]->used = 1; - cgutil_vaddr[CLASSCG_START_ID]->gid = CLASSCG_START_ID; - cgutil_vaddr[CLASSCG_START_ID]->gtype = GROUP_CLASS; - cgutil_vaddr[CLASSCG_START_ID]->ginfo.cls.tgid = TOPCG_CLASS; - cgutil_vaddr[CLASSCG_START_ID]->ginfo.cls.maxlevel = 1; /* initialized value */ - cgutil_vaddr[CLASSCG_START_ID]->ginfo.cls.percent = DEFAULT_CLASS_PERCENT; - cgutil_vaddr[CLASSCG_START_ID]->ginfo.cls.rempct = 100; /* initialized value */ - sret = strncpy_s(cgutil_vaddr[CLASSCG_START_ID]->grpname, GPNAME_LEN, GSCGROUP_DEFAULT_CLASS, GPNAME_LEN - 1); - securec_check_errno(sret, , ); - cgutil_vaddr[CLASSCG_START_ID]->ainfo.shares = DEFAULT_CPU_SHARES * DEFAULT_CLASS_PERCENT / 10; - cgutil_vaddr[CLASSCG_START_ID]->ainfo.weight = IO_WEIGHT_CALC(MAX_IO_WEIGHT, DEFAULT_CLASS_PERCENT); - /* it has only this class, so it has all resource */ - cgutil_vaddr[CLASSCG_START_ID]->percent = cgutil_vaddr[TOPCG_CLASS]->percent; - cgutil_vaddr[CLASSCG_START_ID]->except[EXCEPT_PENALTY].skewpercent = DEFAULT_CPUSKEWPCT; - cgutil_vaddr[CLASSCG_START_ID]->except[EXCEPT_PENALTY].qualitime = DEFAULT_QUALITIME; + // 设置默认类组的属性值 + cgutil_vaddr[CLASSCG_START_ID]->used = 1; // 是否已使用,默认为1 + cgutil_vaddr[CLASSCG_START_ID]->gid = CLASSCG_START_ID; // 类组的唯一ID,默认为CLASSCG_START_ID + cgutil_vaddr[CLASSCG_START_ID]->gtype = GROUP_CLASS; // 组的类型,默认为GROUP_CLASS + cgutil_vaddr[CLASSCG_START_ID]->ginfo.cls.tgid = TOPCG_CLASS; // 类组所属的顶级组,默认为TOPCG_CLASS + cgutil_vaddr[CLASSCG_START_ID]->ginfo.cls.maxlevel = 1; // 组的最大层级,默认为1 + cgutil_vaddr[CLASSCG_START_ID]->ginfo.cls.percent = DEFAULT_CLASS_PERCENT; // 类组的百分比,默认为DEFAULT_CLASS_PERCENT + cgutil_vaddr[CLASSCG_START_ID]->ginfo.cls.rempct = 100; // 剩余的百分比,默认为100 + sret = strncpy_s(cgutil_vaddr[CLASSCG_START_ID]->grpname, GPNAME_LEN, GSCGROUP_DEFAULT_CLASS, GPNAME_LEN - 1); // 类组的名称,默认为GSCGROUP_DEFAULT_CLASS + securec_check_errno(sret, , ); + cgutil_vaddr[CLASSCG_START_ID]->ainfo.shares = DEFAULT_CPU_SHARES * DEFAULT_CLASS_PERCENT / 10; // CPU份额,默认为DEFAULT_CPU_SHARES * DEFAULT_CLASS_PERCENT / 10 + cgutil_vaddr[CLASSCG_START_ID]->ainfo.weight = IO_WEIGHT_CALC(MAX_IO_WEIGHT, DEFAULT_CLASS_PERCENT); // IO权重,默认为IO_WEIGHT_CALC(MAX_IO_WEIGHT, DEFAULT_CLASS_PERCENT) + cgutil_vaddr[CLASSCG_START_ID]->percent = cgutil_vaddr[TOPCG_CLASS]->percent; // 类组的百分比,默认为cgutil_vaddr[TOPCG_CLASS]->percent + + cgutil_vaddr[CLASSCG_START_ID]->except[EXCEPT_PENALTY].skewpercent = DEFAULT_CPUSKEWPCT; // 异常情况下的偏斜百分比,默认为DEFAULT_CPUSKEWPCT + cgutil_vaddr[CLASSCG_START_ID]->except[EXCEPT_PENALTY].qualitime = DEFAULT_QUALITIME; // 异常等待时间,默认为DEFAULT_QUALITIME } /* - * function name: cgconf_set_default_top_workload_group - * description : set the default value of top workload group + * 函数名称:cgconf_set_default_top_workload_group + * 描述:设置默认的顶级工作负载组的值 * */ static void cgconf_set_default_top_workload_group(void) @@ -298,40 +286,45 @@ static void cgconf_set_default_top_workload_group(void) char tmpstr[GPNAME_LEN]; errno_t sret; - cgutil_vaddr[WDCG_START_ID]->used = 1; - cgutil_vaddr[WDCG_START_ID]->gid = WDCG_START_ID; - cgutil_vaddr[WDCG_START_ID]->gtype = GROUP_DEFWD; - cgutil_vaddr[WDCG_START_ID]->ginfo.wd.cgid = CLASSCG_START_ID; - cgutil_vaddr[WDCG_START_ID]->ginfo.wd.wdlevel = 1; - sret = snprintf_s(tmpstr, sizeof(tmpstr), sizeof(tmpstr) - 1, "%s:%d", GSCGROUP_TOP_WORKLOAD, 1); + // 设置默认顶级工作负载组的属性值 + cgutil_vaddr[WDCG_START_ID]->used = 1; // 是否已使用,默认为1 + cgutil_vaddr[WDCG_START_ID]->gid = WDCG_START_ID; // 工作负载组的唯一ID,默认为WDCG_START_ID + cgutil_vaddr[WDCG_START_ID]->gtype = GROUP_DEFWD; // 组的类型,默认为GROUP_DEFWD + cgutil_vaddr[WDCG_START_ID]->ginfo.wd.cgid = CLASSCG_START_ID; // 工作负载组所属的类组,默认为CLASSCG_START_ID + cgutil_vaddr[WDCG_START_ID]->ginfo.wd.wdlevel = 1; // 工作负载组的级别,默认为1 + sret = snprintf_s(tmpstr, sizeof(tmpstr), sizeof(tmpstr) - 1, "%s:%d", GSCGROUP_TOP_WORKLOAD, 1); // 构造工作负载组的名称,默认为GSCGROUP_TOP_WORKLOAD:1 securec_check_intval(sret, , ); - sret = strncpy_s(cgutil_vaddr[WDCG_START_ID]->grpname, GPNAME_LEN, tmpstr, GPNAME_LEN - 1); + sret = strncpy_s(cgutil_vaddr[WDCG_START_ID]->grpname, GPNAME_LEN, tmpstr, GPNAME_LEN - 1); // 工作负载组的名称,默认为tmpstr securec_check_errno(sret, , ); - cgutil_vaddr[WDCG_START_ID]->ainfo.shares = MAX_CLASS_CPUSHARES * TOPWD_PERCENT / 100; - cgutil_vaddr[WDCG_START_ID]->ainfo.weight = IO_WEIGHT_CALC(MAX_IO_WEIGHT, TOPWD_PERCENT); + cgutil_vaddr[WDCG_START_ID]->ainfo.shares = MAX_CLASS_CPUSHARES * TOPWD_PERCENT / 100; // CPU份额,默认为MAX_CLASS_CPUSHARES * TOPWD_PERCENT / 100 + cgutil_vaddr[WDCG_START_ID]->ainfo.weight = IO_WEIGHT_CALC(MAX_IO_WEIGHT, TOPWD_PERCENT); // IO权重,默认为IO_WEIGHT_CALC(MAX_IO_WEIGHT, TOPWD_PERCENT) } - /* * function name: cgconf_set_default_timeshare_group - * description : set the default value of default timeshare group + * 功能:设置默认的时间共享组的默认值 * */ + //该段代码定义了一个名为cgconf_set_default_timeshare_group的函数,用于设置默认的时间共享组的默认值。 + //函数中使用了cgutil_vaddr[TSCG_START_ID]到cgutil_vaddr[TSCG_START_ID + 3]的数组元素,这是一个全局变量数组,数组的元素类型是一个结构体指针。该结构体用于保存时间共享组的信息。 + //函数首先设置了低级别的默认组,然后设置中级别、高级别和紧急级别的默认组。对于每个组,都设置了使用标志、组ID、组类型、控制组ID、时间共享比例、组名、CPU分享和IO权重。 + //例如,可以通过调用cgconf_set_default_timeshare_group函数来设置默认的时间共享组的默认值,并将该函数应用于操作系统进程调度的相关设置中。 static void cgconf_set_default_timeshare_group(void) { errno_t sret; - /* low group of default group */ - cgutil_vaddr[TSCG_START_ID]->used = 1; - cgutil_vaddr[TSCG_START_ID]->gid = TSCG_START_ID; - cgutil_vaddr[TSCG_START_ID]->gtype = GROUP_TSWD; - cgutil_vaddr[TSCG_START_ID]->ginfo.ts.cgid = CLASSCG_START_ID; - cgutil_vaddr[TSCG_START_ID]->ginfo.ts.rate = TS_LOW_RATE; - sret = strncpy_s(cgutil_vaddr[TSCG_START_ID]->grpname, GPNAME_LEN, GSCGROUP_LOW_TIMESHARE, GPNAME_LEN - 1); - securec_check_errno(sret, , ); - cgutil_vaddr[TSCG_START_ID]->ainfo.shares = DEFAULT_CPU_SHARES * TS_LOW_RATE; - cgutil_vaddr[TSCG_START_ID]->ainfo.weight = MIN_IO_WEIGHT * TS_LOW_RATE; - /* medium group of default group */ + /* 低级别的默认组 */ + cgutil_vaddr[TSCG_START_ID]->used = 1; // 设置使用标志为1,表示该组正在使用 + cgutil_vaddr[TSCG_START_ID]->gid = TSCG_START_ID; // 设置组ID + cgutil_vaddr[TSCG_START_ID]->gtype = GROUP_TSWD; // 设置组类型为时间共享 + cgutil_vaddr[TSCG_START_ID]->ginfo.ts.cgid = CLASSCG_START_ID; // 设置控制组ID + cgutil_vaddr[TSCG_START_ID]->ginfo.ts.rate = TS_LOW_RATE; // 设置时间共享的比例 + sret = strncpy_s(cgutil_vaddr[TSCG_START_ID]->grpname, GPNAME_LEN, GSCGROUP_LOW_TIMESHARE, GPNAME_LEN - 1); // 将组名拷贝到指定的变量中 + securec_check_errno(sret, , ); // 错误检查 + cgutil_vaddr[TSCG_START_ID]->ainfo.shares = DEFAULT_CPU_SHARES * TS_LOW_RATE; // 设置CPU分享 + cgutil_vaddr[TSCG_START_ID]->ainfo.weight = MIN_IO_WEIGHT * TS_LOW_RATE; // 设置IO权重 + + /* 中级别的默认组 */ cgutil_vaddr[TSCG_START_ID + 1]->used = 1; cgutil_vaddr[TSCG_START_ID + 1]->gid = TSCG_START_ID + 1; cgutil_vaddr[TSCG_START_ID + 1]->gtype = GROUP_TSWD; @@ -341,7 +334,8 @@ static void cgconf_set_default_timeshare_group(void) securec_check_errno(sret, , ); cgutil_vaddr[TSCG_START_ID + 1]->ainfo.shares = DEFAULT_CPU_SHARES * TS_MEDIUM_RATE; cgutil_vaddr[TSCG_START_ID + 1]->ainfo.weight = MIN_IO_WEIGHT * TS_MEDIUM_RATE; - /* high group of default group */ + + /* 高级别的默认组 */ cgutil_vaddr[TSCG_START_ID + 2]->used = 1; cgutil_vaddr[TSCG_START_ID + 2]->gid = TSCG_START_ID + 2; cgutil_vaddr[TSCG_START_ID + 2]->gtype = GROUP_TSWD; @@ -351,7 +345,8 @@ static void cgconf_set_default_timeshare_group(void) securec_check_errno(sret, , ); cgutil_vaddr[TSCG_START_ID + 2]->ainfo.shares = DEFAULT_CPU_SHARES * TS_HIGH_RATE; cgutil_vaddr[TSCG_START_ID + 2]->ainfo.weight = MIN_IO_WEIGHT * TS_HIGH_RATE; - /* rush group of default group */ + + /* 紧急级别的默认组 */ cgutil_vaddr[TSCG_START_ID + 3]->used = 1; cgutil_vaddr[TSCG_START_ID + 3]->gid = TSCG_START_ID + 3; cgutil_vaddr[TSCG_START_ID + 3]->gtype = GROUP_TSWD; @@ -363,49 +358,53 @@ static void cgconf_set_default_timeshare_group(void) cgutil_vaddr[TSCG_START_ID + 3]->ainfo.weight = MIN_IO_WEIGHT * TS_RUSH_RATE; } -/* - * @Description: reset cgroup configure. +/** + * @Description: 重置cgroup配置。 * @Return: void * @See also: */ void cgconf_reset_cgroup_config(void) { - /* create the default top group */ + /* 创建默认的顶层组 */ cgconf_set_root_group(); + /* 创建gauss组 */ cgconf_set_gauss_group(); + /* 创建顶层后台组 */ cgconf_set_top_backend_group(); - /* create the default vacuum group under top backend group */ + /* 在顶层后台组下创建默认的vacuum组 */ cgconf_set_default_backend_group(); cgconf_set_vacuum_group(); if (cgutil_opt.nodegroup[0] == '\0' || cgutil_opt.rename) { + /* 创建顶层类组 */ cgconf_set_top_class_group(); - } else { - /* create the nodegroup top group */ + } + else { + /* 创建节点组顶层组 */ cgconf_set_nodegroup_top_group(); } - /* create the default class group under top class group */ + /* 在顶层类组下创建默认的类组 */ cgconf_set_default_class_group(); cgconf_set_default_top_workload_group(); - /* create the top/rush/high/medium/low timeshare group of - default class group */ + /* 在默认的类组下创建top/rush/high/medium/low timeshare组 */ cgconf_set_default_timeshare_group(); } -/* - * @Description: revert io configure. - * @IN iovalue: iovalue to be reverted + +/** + * @Description: 恢复io配置。 + * @IN iovalue: 要恢复的io值 * @Return: void * @See also: */ void cgconf_revert_blkio_value(char* iovalue) { - char *p = NULL; - char *q = NULL; - char *head = NULL; - char *i = NULL; + char* p = NULL; + char* q = NULL; + char* head = NULL; + char* i = NULL; errno_t sret; if ((head = strdup(iovalue)) == NULL) { @@ -430,10 +429,7 @@ void cgconf_revert_blkio_value(char* iovalue) } } i++; - /* - * set the blkio throttle values (iopsread/iopswrite/bpsread/bpswrite) - * of the device to 0 this device will be reverted. - */ + /* 将设备的blkio限制值(iopsread/iopswrite/bpsread/bpswrite)设置为0,表示对该设备进行恢复 */ *i = '0'; while (*i++) { @@ -442,7 +438,8 @@ void cgconf_revert_blkio_value(char* iovalue) if (iovalue[0]) { sret = sprintf_s(iovalue + strlen(iovalue), IODATA_LEN - strlen(iovalue), "\n%s", p); securec_check_intval(sret, free(head), ); - } else { + } + else { sret = sprintf_s(iovalue, IODATA_LEN, "%s", p); securec_check_intval(sret, free(head), ); } @@ -451,17 +448,18 @@ void cgconf_revert_blkio_value(char* iovalue) free(head); head = NULL; } -/* - * @Description: revert configure file. + +/** + * @Description: 恢复配置文件。 * @IN void - * @Return: void + * @Return: void * @See also: */ void cgconf_revert_config_file(void) { int i = 0; - /* get current user name */ + /* 获取当前用户名 */ errno_t sret = snprintf_s( cgutil_opt.user, sizeof(cgutil_opt.user), sizeof(cgutil_opt.user) - 1, "%s", cgutil_passwd_user->pw_name); securec_check_intval(sret, , ); @@ -489,46 +487,48 @@ void cgconf_revert_config_file(void) } } - /* reset configure */ + /* 重置配置 */ cgconf_reset_cgroup_config(); } - /* - * function name: cgconf_generate_default_config_file - * description : generate the default configuration file + * 函数名称:cgconf_generate_default_config_file + * 描述:生成默认的配置文件 * */ void cgconf_generate_default_config_file(void* vaddr) { int i = 0; + // 将虚拟地址转换为gscgroup_grp_t类型的指针,并将其赋给cgutil_vaddr数组的相应元素 for (i = 0; i < GSCGROUP_ALLNUM; i++) { cgutil_vaddr[i] = (gscgroup_grp_t*)vaddr + i; + // 将used字段重置为0 cgutil_vaddr[i]->used = 0; } - /* reset configure */ + /* 重置配置 */ + // 重置cgroup的配置 cgconf_reset_cgroup_config(); } /* - * function name: cgconf_update_backend_percent - * description : update the percentage value of backend group - * Note: this function is called after updating Backend group value + * 函数名称:cgconf_update_backend_percent + * 描述:更新后端组的百分比值 + * 注意:此函数在更新后端组的值之后调用 */ void cgconf_update_backend_percent(void) { int i; int percent = 0; - /* get the used percent of all backend */ + // 获取所有后端组的使用百分比 for (i = BACKENDCG_START_ID; i <= BACKENDCG_END_ID; i++) { if (cgutil_vaddr[i]->used) { percent += cgutil_vaddr[i]->ginfo.cls.percent; - } + } } - /* update the percent of each backend */ + // 更新每个后端组的百分比 for (i = BACKENDCG_START_ID; i <= BACKENDCG_END_ID; i++) { if (cgutil_vaddr[i]->used) { if (percent == 0) { @@ -543,30 +543,30 @@ void cgconf_update_backend_percent(void) } /* - * function name: cgconf_update_backend_group - * description : reset the percent and calculate the CPU shares and IO weight - of the specified group - * argument : the data structure of backend group + * 函数名称:cgconf_update_backend_group + * 描述:重置指定组的百分比并计算CPU共享和IO权重 + * 参数:后端组的数据结构 * - * Note: this function is called after updating Backend group value + * 注意:此函数在更新后端组的值之后调用 */ void cgconf_update_backend_group(gscgroup_grp_t* grp) { grp->ginfo.cls.percent = cgutil_opt.bkdpct; + // 计算CPU共享 grp->ainfo.shares = DEFAULT_CPU_SHARES * grp->ginfo.cls.percent / 10; + // 计算IO权重 grp->ainfo.weight = IO_WEIGHT_CALC(MAX_IO_WEIGHT, grp->ginfo.cls.percent); + // 更新组的百分比 grp->percent = cgutil_vaddr[TOPCG_BACKEND]->percent * grp->ginfo.cls.percent / 100; } - /* - * function name: cgconf_update_class_percent - * description : update the percentage value of all class group and - its all workload group + * 函数名称:cgconf_update_class_percent + * 描述:更新所有类组和其所有工作负载组的百分比值 * - * Note: this function is called after updating Class group value + * 注意:此函数在更新类组的值之后调用 */ void cgconf_update_class_percent(void) { @@ -574,13 +574,13 @@ void cgconf_update_class_percent(void) int j; int percent = 0; - /* get total percent of all class group */ + // 获取所有类组的总百分比 for (i = CLASSCG_START_ID; i <= CLASSCG_END_ID; i++) { if (cgutil_vaddr[i]->used) percent += cgutil_vaddr[i]->ginfo.cls.percent; } - /* update the percentage of class and workload group */ + // 更新类和工作负载组的百分比 for (i = CLASSCG_START_ID; i <= CLASSCG_END_ID; i++) { if (cgutil_vaddr[i]->used) { if (percent == 0) { @@ -606,27 +606,29 @@ void cgconf_update_class_percent(void) } /* - * function name: cgconf_update_top_percent - * description : update the percentage value of all Backend and Class group + * 函数名称:cgconf_update_top_percent + * 描述:更新所有后端组和类组的百分比值 * - * Note: this function is called after updating Gaussdb group value + * 注意:此函数在更新Gaussdb组的值之后调用 */ void cgconf_update_top_percent(void) { + // 更新后端组的百分比 cgutil_vaddr[TOPCG_BACKEND]->percent = cgutil_vaddr[TOPCG_GAUSSDB]->percent * cgutil_vaddr[TOPCG_BACKEND]->ginfo.top.percent / 100; cgconf_update_backend_percent(); + // 更新类组的百分比 cgutil_vaddr[TOPCG_CLASS]->percent = cgutil_vaddr[TOPCG_GAUSSDB]->percent * cgutil_vaddr[TOPCG_CLASS]->ginfo.top.percent / 100; cgconf_update_class_percent(); } /* - * function name: cgconf_set_class_group - * description : set the class group fields when creating new class + * 函数名称:cgconf_set_class_group + * 描述:在创建新的类组时设置类组的相关字段 * - * Note: this function is called after creating new Class group + * 注意:此函数在创建新的类组之后调用 */ void cgconf_set_class_group(int gid) { @@ -640,25 +642,28 @@ void cgconf_set_class_group(int gid) cgutil_vaddr[gid]->ginfo.cls.percent = cgutil_opt.clspct; cgutil_vaddr[gid]->ginfo.cls.rempct = 100; + // 将clsname拷贝到grpname字段 sret = strncpy_s(cgutil_vaddr[gid]->grpname, GPNAME_LEN, cgutil_opt.clsname, GPNAME_LEN - 1); securec_check_errno(sret, , ); + // 计算CPU共享 cgutil_vaddr[gid]->ainfo.shares = DEFAULT_CPU_SHARES * cgutil_vaddr[gid]->ginfo.cls.percent / 10; + // 计算IO权重 cgutil_vaddr[gid]->ainfo.weight = IO_WEIGHT_CALC(MAX_IO_WEIGHT, cgutil_vaddr[gid]->ginfo.cls.percent); + // 将TOPCG_CLASS的cpuset拷贝到cpuset字段 sret = snprintf_s(cgutil_vaddr[gid]->cpuset, CPUSET_LEN, CPUSET_LEN - 1, "%s", cgutil_vaddr[TOPCG_CLASS]->cpuset); - securec_check_intval(sret, , ); + // 更新类组和工作负载组的百分比 cgconf_update_class_percent(); } - /* * function name: cgconf_reset_class_group - * description : reset the class group fields when dropping a class + * description : 重置类群组字段,当删除一个类群组时调用 * - * Note: this function is called after dropping a Class group + * Note: 该函数在删除类群组后调用 */ void cgconf_reset_class_group(int gid) { @@ -669,38 +674,45 @@ void cgconf_reset_class_group(int gid) if (cgutil_vaddr[i]->used == 0) continue; + // 如果cgutil_vaddr[i]表示的类群组使用且其cgid等于参数gid,则将该类群组的字段清零 if (cgutil_vaddr[i]->ginfo.wd.cgid == gid) { sret = memset_s(cgutil_vaddr[i], sizeof(gscgroup_grp_t), 0, sizeof(gscgroup_grp_t)); securec_check_errno(sret, , ); } } + // 将参数gid表示的类群组的字段清零 sret = memset_s(cgutil_vaddr[gid], sizeof(gscgroup_grp_t), 0, sizeof(gscgroup_grp_t)); securec_check_errno(sret, , ); + // 更新类群组的百分比 cgconf_update_class_percent(); } /* * function name: cgconf_update_class_group - * description : update the class group value + * description : 更新类群组的值 * - * Note: this function is called after updating Class group + * Note: 该函数在更新类群组后调用 */ void cgconf_update_class_group(gscgroup_grp_t* grp) { + // 更新类群组的百分比 grp->ginfo.cls.percent = cgutil_opt.clspct; + // 计算类群组的CPU共享值 grp->ainfo.shares = DEFAULT_CPU_SHARES * grp->ginfo.cls.percent / 10; + // 计算类群组的IO权重值 grp->ainfo.weight = IO_WEIGHT_CALC(MAX_IO_WEIGHT, grp->ginfo.cls.percent); + // 更新类群组的百分比 cgconf_update_class_percent(); } /* * function name: cgconf_set_top_workload_group - * description : set the default value of top workload group + * description : 设置顶级工作负载组的默认值 * */ void cgconf_set_top_workload_group(int wdgid, int clsgid) @@ -708,6 +720,7 @@ void cgconf_set_top_workload_group(int wdgid, int clsgid) char tmpstr[GPNAME_LEN]; errno_t sret; + // 设置顶级工作负载组的相关字段值 cgutil_vaddr[wdgid]->used = 1; cgutil_vaddr[wdgid]->gid = wdgid; cgutil_vaddr[wdgid]->gtype = GROUP_DEFWD; @@ -720,26 +733,26 @@ void cgconf_set_top_workload_group(int wdgid, int clsgid) sret = strncpy_s(cgutil_vaddr[wdgid]->grpname, GPNAME_LEN, tmpstr, GPNAME_LEN - 1); securec_check_errno(sret, , ); + // 计算顶级工作负载组的CPU共享值和IO权重值 cgutil_vaddr[wdgid]->ainfo.shares = MAX_CLASS_CPUSHARES * TOPWD_PERCENT / 100; cgutil_vaddr[wdgid]->ainfo.weight = IO_WEIGHT_CALC(MAX_IO_WEIGHT, TOPWD_PERCENT); cgutil_vaddr[wdgid]->percent = cgutil_vaddr[clsgid]->percent * cgutil_vaddr[wdgid]->ginfo.wd.percent / 100; - /* set it's cpuset in configure file */ + /*在配置文件中设置它的cpuset */ sret = snprintf_s(cgutil_vaddr[wdgid]->cpuset, CPUSET_LEN, CPUSET_LEN - 1, "%s", cgutil_vaddr[clsgid]->cpuset); securec_check_intval(sret, , ); } - /* * function name: cgconf_set_workload_group - * description : set the workload group fields when creating new workload group + * description : 创建新的工作负载组时设置工作负载组字段 * - * Note: this function is called after creating new Workload group + * Note: 该函数在创建新的工作负载组后调用 */ void cgconf_set_workload_group(int wdgid, int clsgid) { char tmpstr[GPNAME_LEN]; - errno_t sret; + // 设置工作负载组的相关字段值 cgutil_vaddr[wdgid]->used = 1; cgutil_vaddr[wdgid]->gid = wdgid; cgutil_vaddr[wdgid]->gtype = GROUP_DEFWD; @@ -753,50 +766,59 @@ void cgconf_set_workload_group(int wdgid, int clsgid) sret = strncpy_s(cgutil_vaddr[wdgid]->grpname, GPNAME_LEN, tmpstr, GPNAME_LEN - 1); securec_check_intval(sret, , ); + // 计算工作负载组的CPU共享值和IO权重值 cgutil_vaddr[wdgid]->ainfo.shares = MAX_CLASS_CPUSHARES * cgutil_vaddr[wdgid]->ginfo.wd.percent / 100; cgutil_vaddr[wdgid]->ainfo.weight = IO_WEIGHT_CALC(MAX_IO_WEIGHT, cgutil_vaddr[wdgid]->ginfo.wd.percent); cgutil_vaddr[wdgid]->percent = cgutil_vaddr[clsgid]->percent * cgutil_vaddr[wdgid]->ginfo.wd.percent / 100; + // 在配置文件中设置工作负载组的cpuset sret = snprintf_s(cgutil_vaddr[wdgid]->cpuset, CPUSET_LEN, CPUSET_LEN - 1, "%s", cgutil_vaddr[clsgid]->cpuset); - securec_check_intval(sret, , ); } /* * function name: cgconf_reset_workload_group - * description : reset the workload group fields when dropping a workload + * description : 删除工作负载组时重置工作负载组字段 * - * Note: this function is called after dropping a Workload group + * Note: 该函数在删除工作负载组后调用 */ void cgconf_reset_workload_group(int wdgid) { errno_t sret; + // 将工作负载组的字段清零 sret = memset_s(cgutil_vaddr[wdgid], sizeof(gscgroup_grp_t), 0, sizeof(gscgroup_grp_t)); securec_check_errno(sret, , ); } /* * function name: cgconf_update_workload_group - * description : update the workload group value - * Note: this function is called after updating Workload group + * description : 更新工作负载组的值 + * + * Note: 该函数在更新工作负载组后调用 */ void cgconf_update_workload_group(gscgroup_grp_t* grp) { int clsgid = grp->ginfo.wd.cgid; - + // 更新类群组的剩余百分比 cgutil_vaddr[clsgid]->ginfo.cls.rempct += grp->ginfo.wd.percent; grp->ginfo.wd.percent = cgutil_opt.grppct; cgutil_vaddr[clsgid]->ginfo.cls.rempct -= cgutil_opt.grppct; + // 计算工作负载组的CPU共享值和IO权重值 grp->ainfo.shares = MAX_CLASS_CPUSHARES * grp->ginfo.wd.percent / 100; grp->ainfo.weight = IO_WEIGHT_CALC(MAX_IO_WEIGHT, grp->ginfo.wd.percent); grp->percent = cgutil_vaddr[clsgid]->percent * grp->ginfo.wd.percent / 100; } /* - * function name: cgconf_convert_group - * description : fill the old group inforation into new group + * 函数名称:cgconf_convert_group + * 功能描述:将旧的组信息填充到新的组中 * + * 参数: + * newgrp: 新的组结构体指针 + * oldgrp: 旧的组结构体指针 + * + * 相似应用实例:当需要将旧的组信息迁移到新的系统中时,可以使用该函数。 */ void cgconf_convert_group(gscgroup_grp_t* newgrp, gscgroup_old_grp_t* oldgrp) { @@ -807,7 +829,7 @@ void cgconf_convert_group(gscgroup_grp_t* newgrp, gscgroup_old_grp_t* oldgrp) newgrp->gtype = oldgrp->gtype; - /* set the group internal info */ + /* 设置组的内部信息 */ newgrp->ginfo.cls.tgid = oldgrp->ginfo.cls.tgid; newgrp->ginfo.cls.maxlevel = oldgrp->ginfo.cls.maxlevel; newgrp->ginfo.cls.percent = oldgrp->ginfo.cls.percent; @@ -816,7 +838,7 @@ void cgconf_convert_group(gscgroup_grp_t* newgrp, gscgroup_old_grp_t* oldgrp) sret = strncpy_s(newgrp->grpname, sizeof(newgrp->grpname), oldgrp->grpname, sizeof(oldgrp->grpname) - 1); securec_check_errno(sret, , ); - /* set the allocation info */ + /* 设置分配信息 */ newgrp->ainfo.shares = oldgrp->ainfo.shares; newgrp->ainfo.weight = oldgrp->ainfo.weight; newgrp->ainfo.quota = oldgrp->ainfo.quota; @@ -840,7 +862,7 @@ void cgconf_convert_group(gscgroup_grp_t* newgrp, gscgroup_old_grp_t* oldgrp) sizeof(oldgrp->ainfo.bpswrite) - 1); securec_check_errno(sret, , ); - /* set the exception info */ + /* 设置异常信息 */ for (int i = 0; i < EXCEPT_ALL_KINDS; ++i) { newgrp->except[i].blocktime = oldgrp->except[i].blocktime; newgrp->except[i].elapsedtime = oldgrp->except[i].elapsedtime; @@ -856,10 +878,16 @@ void cgconf_convert_group(gscgroup_grp_t* newgrp, gscgroup_old_grp_t* oldgrp) } /* - * function name: cgconf_generate_file_by_root - * description : generate the configuration file by root user + * 函数名称:cgconf_generate_file_by_root + * 功能描述:由root用户生成配置文件 * - * Note: the configuration file must exist in the "etc" directory + * 注意:配置文件必须存在于“etc”目录下 + * + * 参数: + * fsize: 配置文件大小 + * cfgpath: 配置文件路径 + * + * 相似应用实例:在需要由root用户生成配置文件的场景中,可以使用该函数。 */ int cgconf_generate_file_by_root(long fsize, char* cfgpath) { @@ -867,19 +895,19 @@ int cgconf_generate_file_by_root(long fsize, char* cfgpath) errno_t sret; size_t cglen = GSCGROUP_ALLNUM * sizeof(gscgroup_grp_t); - /* when deleting the configure file, it must exist! */ + /* 当删除配置文件时,必须确保它存在 */ if (cgutil_opt.dflag && (-1 == fsize)) { - fprintf(stderr, "ERROR: the user %s doesn't exist.\n", cgutil_opt.user); + fprintf(stderr, "错误:用户%s不存在。\n", cgutil_opt.user); free(cfgpath); cfgpath = NULL; return -1; } - /* file doesn't exist, create new one */ + /* 文件不存在,创建新的文件 */ if (fsize == -1) { vaddr = gsutil_filemap(cfgpath, cglen, (PROT_READ | PROT_WRITE), MAP_SHARED, cgutil_passwd_user); if (NULL == vaddr) { - fprintf(stderr, "ERROR: failed to create and map the configure file %s!\n", cfgpath); + fprintf(stderr, "错误:创建和映射配置文件%s失败!\n", cfgpath); free(cfgpath); cfgpath = NULL; return -1; @@ -888,10 +916,11 @@ int cgconf_generate_file_by_root(long fsize, char* cfgpath) sret = memset_s(vaddr, cglen, 0, cglen); securec_check_errno(sret, free(cfgpath), -1); - /* rewrite the mapping file */ + /* 重写映射文件 */ cgconf_generate_default_config_file(vaddr); - } else { - fprintf(stderr, "ERROR: the file %s has been corrupted, Please remove it and recreate.\n", cfgpath); + } + else { + fprintf(stderr, "错误:文件%s已损坏,请删除并重新创建。\n", cfgpath); free(cfgpath); cfgpath = NULL; return -1; @@ -901,46 +930,53 @@ int cgconf_generate_file_by_root(long fsize, char* cfgpath) cfgpath = NULL; return 0; } - -/* +/** * function name: cgconf_generate_file_by_user - * description : generate the configuration file by non-root user + * 功能:通过非root用户生成配置文件 * - * Note: the configuration file must exist in the "etc" directory + * 注意:配置文件必须存在于"etc"目录中 + * + * 参数: + * - fsize: 配置文件大小 + * - cfgpath: 配置文件路径 + * + * 返回值: + * - 成功返回0,失败返回-1 */ int cgconf_generate_file_by_user(long fsize, char* cfgpath) { - void* vaddr = NULL; - errno_t sret; - size_t cglen = GSCGROUP_ALLNUM * sizeof(gscgroup_grp_t); + void* vaddr = NULL; // 文件映射到内存的地址 + errno_t sret; // 用于保存返回值 + size_t cglen = GSCGROUP_ALLNUM * sizeof(gscgroup_grp_t); // 配置文件大小 - if (fsize == -1) { - if ('\0' == cgutil_opt.nodegroup[0]) { + if (fsize == -1) { // 判断配置文件是否存在 + if ('\0' == cgutil_opt.nodegroup[0]) { // 判断是否为根用户 fprintf(stderr, - "ERROR: the configure file %s doesn't exist!\n" - "HINT: please create it by root user!\n", + "ERROR: 配置文件 %s 不存在!\n" + "HINT: 请通过root用户创建配置文件!\n", cfgpath); free(cfgpath); cfgpath = NULL; return -1; } - if (cgutil_opt.nodegroup[0] && 0 == cgutil_opt.cflag) { + if (cgutil_opt.nodegroup[0] && 0 == cgutil_opt.cflag) { // 判断是否存在配置文件 fprintf(stderr, - "ERROR: the configure file %s doesn't exist!\n" - "HINT: please create it before using it!\n", + "ERROR: 配置文件 %s 不存在!\n" + "HINT: 请在使用之前创建配置文件!\n", cfgpath); free(cfgpath); cfgpath = NULL; return -1; - } else { - /* change origin cluster to virtual cluster */ - if (cgutil_opt.rename) { + } + else { + /* 将原始集群更改为虚拟集群 */ + if (cgutil_opt.rename) { // 判断是否需要重命名配置文件 int old_cfgpath_len = strlen(cgutil_opt.hpath) + 1 + sizeof(GSCGROUP_CONF_DIR) + 1 + - sizeof(GSCFG_PREFIX) + 1 + strlen(cgutil_passwd_user->pw_name) + - sizeof(GSCFG_SUFFIX) + 1; + sizeof(GSCFG_PREFIX) + 1 + strlen(cgutil_passwd_user->pw_name) + + sizeof(GSCFG_SUFFIX) + 1; char* old_cfgpath = (char*)malloc(old_cfgpath_len); - if (old_cfgpath == NULL) { + if (old_cfgpath == NULL) { // 内存分配失败 free(cfgpath); cfgpath = NULL; return -1; @@ -955,8 +991,8 @@ int cgconf_generate_file_by_user(long fsize, char* cfgpath) GSCFG_PREFIX, cgutil_passwd_user->pw_name, GSCFG_SUFFIX); - if (sret != EOK) { - fprintf(stderr, "ERROR: failed to construct old cgroup config path"); + if (sret != EOK) { // 格式化字符串失败 + fprintf(stderr, "ERROR: 构造旧的cgroup配置文件路径失败"); free(old_cfgpath); old_cfgpath = NULL; free(cfgpath); @@ -964,10 +1000,10 @@ int cgconf_generate_file_by_user(long fsize, char* cfgpath) return -1; } - /* rename the old cfgpath to new cfgpath */ + /* 将旧的配置文件路径重命名为新的配置文件路径 */ int ret = rename(old_cfgpath, cfgpath); - if (ret != 0) { - fprintf(stderr, "ERROR: failed to rename %s to %s!\n", cfgpath, old_cfgpath); + if (ret != 0) { // 重命名失败 + fprintf(stderr, "ERROR: 将 %s 重命名为 %s 失败!\n", cfgpath, old_cfgpath); free(old_cfgpath); old_cfgpath = NULL; free(cfgpath); @@ -975,29 +1011,30 @@ int cgconf_generate_file_by_user(long fsize, char* cfgpath) return -1; } - /* reset configure path */ + /* 重置配置文件路径 */ free(cfgpath); cfgpath = NULL; cfgpath = old_cfgpath; } - vaddr = gsutil_filemap(cfgpath, cglen, (PROT_READ | PROT_WRITE), MAP_SHARED, cgutil_passwd_user); - if (NULL == vaddr) { - fprintf(stderr, "ERROR: failed to create and map the configure file %s!\n", cfgpath); + vaddr = gsutil_filemap(cfgpath, cglen, (PROT_READ | PROT_WRITE), MAP_SHARED, cgutil_passwd_user); // 将配置文件映射到内存 + if (NULL == vaddr) { // 文件映射失败 + fprintf(stderr, "ERROR: 创建并映射配置文件 %s 失败!\n", cfgpath); free(cfgpath); cfgpath = NULL; return -1; } - sret = memset_s(vaddr, cglen, 0, cglen); + sret = memset_s(vaddr, cglen, 0, cglen); // 清空映射文件 securec_check_errno(sret, free(cfgpath), -1); - /* rewrite the mapping file */ + /* 重新生成映射文件的内容 */ cgconf_generate_default_config_file(vaddr); } - } else { + } + else { fprintf(stderr, - "ERROR: the configure file size cannot match the current cgroup!\n" - "HINT: please remove the configure file %s and recreate it!\n", + "ERROR: 配置文件大小与当前cgroup不匹配!\n" + "HINT: 请删除配置文件 %s 并重新创建!\n", cfgpath); free(cfgpath); cfgpath = NULL; @@ -1008,36 +1045,36 @@ int cgconf_generate_file_by_user(long fsize, char* cfgpath) cfgpath = NULL; return 0; } - -/* - * function name: cgconf_parse_config_file - * description : parse the configuration file and set the global variable +/** + * function name: cgconf_parse_nodegroup_config_file + * description : 解析配置文件并设置全局变量 * - * Note: the configuration file must exist in the "etc" directory + * Note: 配置文件必须存在于"etc"目录中 */ + int cgconf_parse_nodegroup_config_file(void) { - long fsize = 0; - void* vaddr = NULL; - size_t cglen = GSCGROUP_ALLNUM * sizeof(gscgroup_grp_t); - int i = 0; - char* cfgpath = NULL; - size_t cfgpath_len; - errno_t sret; + long fsize = 0; // 配置文件的大小 + void* vaddr = NULL; // 配置文件的虚拟地址 + size_t cglen = GSCGROUP_ALLNUM * sizeof(gscgroup_grp_t); // 配置文件的大小 + int i = 0; // 循环变量 + char* cfgpath = NULL; // 配置文件的路径 + size_t cfgpath_len; // 配置文件路径的长度 + errno_t sret; // 错误码 - if ('\0' == cgutil_opt.nodegroup[0]) { - fprintf(stderr, "ERROR: the nodegroup should be specified!\n"); - return -1; + if ('\0' == cgutil_opt.nodegroup[0]) { // 如果nodegroup未指定 + fprintf(stderr, "ERROR: the nodegroup should be specified!\n"); // 输出错误信息 + return -1; // 返回错误码 } cfgpath_len = strlen(cgutil_opt.hpath) + 1 + sizeof(GSCGROUP_CONF_DIR) + 1 + strlen(cgutil_opt.nodegroup) + 1 + - sizeof(GSCFG_PREFIX) + 1 + strlen(cgutil_passwd_user->pw_name) + sizeof(GSCFG_SUFFIX) + 1; - cfgpath = (char*)malloc(cfgpath_len); + sizeof(GSCFG_PREFIX) + 1 + strlen(cgutil_passwd_user->pw_name) + sizeof(GSCFG_SUFFIX) + 1; // 计算配置文件路径的长度 + cfgpath = (char*)malloc(cfgpath_len); // 分配配置文件路径的内存空间 if (cfgpath == NULL) { return -1; } - /* get the etc directory */ + /* 获取etc目录 */ sret = snprintf_s(cfgpath, cfgpath_len, cfgpath_len - 1, @@ -1049,31 +1086,31 @@ int cgconf_parse_nodegroup_config_file(void) cgutil_passwd_user->pw_name, GSCFG_SUFFIX); securec_check_intval(sret, free(cfgpath), -1); - /* get the configure file */ - fsize = gsutil_filesize(cfgpath); - /* configure file doesn't exist or size is not the same */ + /* 获取配置文件 */ + fsize = gsutil_filesize(cfgpath); // 获取配置文件的大小 + /* 配置文件不存在或大小不匹配 */ if (-1 == fsize || fsize != (long)cglen) { fprintf(stderr, "ERROR: the nodegroup configure file doesn't exist or " - "the size of the nodegroup configure file doesn't match!\n"); + "the size of the nodegroup configure file doesn't match!\n"); // 输出错误信息 free(cfgpath); cfgpath = NULL; - return -1; + return -1; // 返回错误码 } - vaddr = gsutil_filemap(cfgpath, cglen, (PROT_READ | PROT_WRITE), MAP_SHARED, cgutil_passwd_user); + vaddr = gsutil_filemap(cfgpath, cglen, (PROT_READ | PROT_WRITE), MAP_SHARED, cgutil_passwd_user); // 将配置文件映射到内存 if (NULL == vaddr) { - fprintf(stderr, "failed to create and map the configure file %s!\n", cfgpath); + fprintf(stderr, "failed to create and map the configure file %s!\n", cfgpath); // 输出错误信息 free(cfgpath); cfgpath = NULL; - return -1; + return -1; // 返回错误码 } for (i = 0; i < GSCGROUP_ALLNUM; i++) { - cgutil_vaddr[i] = (gscgroup_grp_t*)vaddr + i; + cgutil_vaddr[i] = (gscgroup_grp_t*)vaddr + i; // 设置全局变量cgutil_vaddr[i]的值为vaddr + i if (i == TOPCG_CLASS) { - sret = strcpy_s(cgutil_vaddr[i]->grpname, GPNAME_LEN, cgutil_opt.nodegroup); + sret = strcpy_s(cgutil_vaddr[i]->grpname, GPNAME_LEN, cgutil_opt.nodegroup); // 将nodegroup复制到cgutil_vaddr[i]->grpname securec_check_intval(sret, free(cfgpath), -1); } } @@ -1083,45 +1120,58 @@ int cgconf_parse_nodegroup_config_file(void) return 0; } -/* - * function name: cgconf_get_config_path - * description : get the configuration file +/** + * 示例: + * cgutil_opt.nodegroup = "group1" + * cgutil_opt.hpath = "/path/to" + * cgutil_passwd_user->pw_name = "user1" * - * Note: the configuration file must exist in the "etc" directory + * 配置文件路径计算结果: + * "/path/to/etc/group1.cg_user1" */ + /* + * 函数名:cgconf_get_config_path + * 描述:获取配置文件路径 + * + * 注意:配置文件必须存在于 "etc" 目录下 + */ + char* cgconf_get_config_path(bool backup) { - char* cfgpath = NULL; - size_t cfgpath_len; + char* cfgpath = NULL; // 配置文件路径 + size_t cfgpath_len; // 配置文件路径的长度 errno_t sret; - if ('\0' == cgutil_opt.nodegroup[0]) { - if (false == backup) { + if ('\0' == cgutil_opt.nodegroup[0]) { // 如果节点组为空 + if (false == backup) { // 如果不需要备份 cfgpath_len = strlen(cgutil_opt.hpath) + 1 + sizeof(GSCGROUP_CONF_DIR) + 1 + sizeof(GSCFG_PREFIX) + 1 + - strlen(cgutil_passwd_user->pw_name) + sizeof(GSCFG_SUFFIX) + 1; - } else { - cfgpath_len = strlen(cgutil_opt.hpath) + 1 + sizeof(GSCGROUP_CONF_DIR) + 1 + sizeof(GSCFG_PREFIX) + 1 + - strlen(cgutil_passwd_user->pw_name) + sizeof(GSCFG_SUFFIX) + sizeof(GSCFG_BACKUP) + 1; + strlen(cgutil_passwd_user->pw_name) + sizeof(GSCFG_SUFFIX) + 1; } - } else { - if (false == backup) { + else { // 如果需要备份 + cfgpath_len = strlen(cgutil_opt.hpath) + 1 + sizeof(GSCGROUP_CONF_DIR) + 1 + sizeof(GSCFG_PREFIX) + 1 + + strlen(cgutil_passwd_user->pw_name) + sizeof(GSCFG_SUFFIX) + sizeof(GSCFG_BACKUP) + 1; + } + } + else { // 如果节点组不为空 + if (false == backup) { // 如果不需要备份 cfgpath_len = strlen(cgutil_opt.hpath) + 1 + sizeof(GSCGROUP_CONF_DIR) + 1 + strlen(cgutil_opt.nodegroup) + - 1 + sizeof(GSCFG_PREFIX) + 1 + strlen(cgutil_passwd_user->pw_name) + sizeof(GSCFG_SUFFIX) + 1; - } else { + 1 + sizeof(GSCFG_PREFIX) + 1 + strlen(cgutil_passwd_user->pw_name) + sizeof(GSCFG_SUFFIX) + 1; + } + else { // 如果需要备份 cfgpath_len = strlen(cgutil_opt.hpath) + 1 + sizeof(GSCGROUP_CONF_DIR) + 1 + strlen(cgutil_opt.nodegroup) + - 1 + sizeof(GSCFG_PREFIX) + 1 + strlen(cgutil_passwd_user->pw_name) + sizeof(GSCFG_SUFFIX) + - sizeof(GSCFG_BACKUP) + 1; + 1 + sizeof(GSCFG_PREFIX) + 1 + strlen(cgutil_passwd_user->pw_name) + sizeof(GSCFG_SUFFIX) + + sizeof(GSCFG_BACKUP) + 1; } } - cfgpath = (char*)malloc(cfgpath_len); - if (cfgpath == NULL) { + cfgpath = (char*)malloc(cfgpath_len); // 分配内存存放配置文件路径 + if (cfgpath == NULL) { // 内存分配失败,返回NULL return NULL; } - /* get the etc directory */ - if ('\0' == cgutil_opt.nodegroup[0]) { - if (false == backup) { + /* 获取etc目录 */ + if ('\0' == cgutil_opt.nodegroup[0]) { // 如果节点组为空 + if (false == backup) { // 如果不需要备份 sret = snprintf_s(cfgpath, cfgpath_len, cfgpath_len - 1, @@ -1131,7 +1181,8 @@ char* cgconf_get_config_path(bool backup) GSCFG_PREFIX, cgutil_passwd_user->pw_name, GSCFG_SUFFIX); - } else { + } + else { // 如果需要备份 sret = snprintf_s(cfgpath, cfgpath_len, cfgpath_len - 1, @@ -1143,8 +1194,9 @@ char* cgconf_get_config_path(bool backup) GSCFG_SUFFIX, GSCFG_BACKUP); } - } else { - if (false == backup) { + } + else { // 如果节点组不为空 + if (false == backup) { // 如果不需要备份 sret = snprintf_s(cfgpath, cfgpath_len, cfgpath_len - 1, @@ -1155,7 +1207,8 @@ char* cgconf_get_config_path(bool backup) GSCFG_PREFIX, cgutil_passwd_user->pw_name, GSCFG_SUFFIX); - } else { + } + else { // 如果需要备份 sret = snprintf_s(cfgpath, cfgpath_len, cfgpath_len - 1, @@ -1170,7 +1223,7 @@ char* cgconf_get_config_path(bool backup) } } - securec_check_intval(sret, free(cfgpath), NULL); + securec_check_intval(sret, free(cfgpath), NULL); // 检查字符串格式化函数返回值 return cfgpath; } @@ -1194,49 +1247,51 @@ bool cgconf_gid_invalid(void) } return false; } - /* - * function name: cgconf_parse_config_file - * description : parse the configuration file and set the global variable - * Note: the configuration file must exist in the "etc" directory + * 函数名:cgconf_parse_config_file + * 描述:解析配置文件并设置全局变量 + * 注意:配置文件必须存在于"etc"目录中 */ int cgconf_parse_config_file(void) { - long fsize = 0; - void* vaddr = NULL; - size_t cglen = GSCGROUP_ALLNUM * sizeof(gscgroup_grp_t); + long fsize = 0; // 文件大小 + void* vaddr = NULL; // 文件映射的虚拟地址 + size_t cglen = GSCGROUP_ALLNUM * sizeof(gscgroup_grp_t); // gscgroup_grp_t结构体的大小 int i = 0; int ret = -1; - char* cfgpath = NULL; + char* cfgpath = NULL; // 配置文件路径 - /* get the configure path */ + /* 获取配置文件路径 */ cfgpath = cgconf_get_config_path(false); if (NULL == cfgpath) { return -1; } fsize = gsutil_filesize(cfgpath); - /* configure file doesn't exist or size is not the same*/ + /* 配置文件不存在或大小不一致*/ if (-1 == fsize || fsize != (long)cglen) { if (geteuid() == 0) { ret = cgconf_generate_file_by_root(fsize, cfgpath); - } else { + } + else { if (cgutil_opt.cflag && *cgutil_opt.nodegroup && *cgutil_opt.clsname == '\0') ret = cgconf_generate_file_by_user(fsize, cfgpath); else if (*cgutil_opt.nodegroup) { free(cfgpath); cfgpath = NULL; fprintf(stderr, "ERROR: the specified node group %s doesn't exist!\n", cgutil_opt.nodegroup); - } else { + } + else { free(cfgpath); cfgpath = NULL; } } if (-1 == ret) { - return -1; /* cfgpath has been freed, no need free here */ + return -1; /* cfgpath已经被释放,这里不需要再释放 */ } - } else { + } + else { vaddr = gsutil_filemap(cfgpath, cglen, (PROT_READ | PROT_WRITE), MAP_SHARED, cgutil_passwd_user); if (NULL == vaddr) { @@ -1269,27 +1324,27 @@ int cgconf_parse_config_file(void) } /* - * function name: cgconf_map_nodegroup_conffile - * description : return the mapping information of original configuration file + * 函数名:cgconf_map_nodegroup_conffile + * 描述:返回原始配置文件的映射信息 * */ void* cgconf_map_nodegroup_conffile(void) { - long fsize = 0; - void* vaddr = NULL; - size_t cglen = GSCGROUP_ALLNUM * sizeof(gscgroup_grp_t); - char* cfgpath = NULL; + long fsize = 0; // 文件大小 + void* vaddr = NULL; // 文件映射的虚拟地址 + size_t cglen = GSCGROUP_ALLNUM * sizeof(gscgroup_grp_t); // gscgroup_grp_t结构体的大小 + char* cfgpath = NULL; // 配置文件路径 size_t cfgpath_len; errno_t sret; cfgpath_len = strlen(cgutil_opt.hpath) + 1 + sizeof(GSCGROUP_CONF_DIR) + 1 + strlen(cgutil_opt.nodegroup) + 1 + - sizeof(GSCFG_PREFIX) + 1 + strlen(cgutil_passwd_user->pw_name) + sizeof(GSCFG_SUFFIX) + 1; + sizeof(GSCFG_PREFIX) + 1 + strlen(cgutil_passwd_user->pw_name) + sizeof(GSCFG_SUFFIX) + 1; cfgpath = (char*)malloc(cfgpath_len); if (NULL == cfgpath) { return NULL; } - /* get the etc directory */ + /* 获取etc目录 */ sret = snprintf_s(cfgpath, cfgpath_len, cfgpath_len - 1, @@ -1302,42 +1357,56 @@ void* cgconf_map_nodegroup_conffile(void) GSCFG_SUFFIX); securec_check_intval(sret, free(cfgpath), NULL); fsize = gsutil_filesize(cfgpath); - /* make sure that the file exists when recovering */ + /* 恢复时确保文件存在 */ if (-1 == fsize) { free(cfgpath); cfgpath = NULL; return NULL; } - /* configure file doesn't exist or size is not the same */ + /* 配置文件不存在或大小不一致 */ vaddr = gsutil_filemap(cfgpath, cglen, (PROT_READ | PROT_WRITE), MAP_SHARED, cgutil_passwd_user); free(cfgpath); cfgpath = NULL; return vaddr; } +/** +函数名称:cgconf_map_origin_conffile +函数描述:返回原始配置文件的映射信息 +//该函数的作用是将原始配置文件映射到内存中,并返回映射信息的地址。 +//函数变量如下: +//fsize : 用于保存文件大小。 +//vaddr : 用于保存映射信息的地址。 +//cglen : 配置文件映射长度,根据GSCGROUP_ALLNUM和gscgroup_grp_t结构体的大小计算得到。 +//cfgpath : 用于保存配置文件路径。 +//cfgpath_len : 配置文件路径的长度。 +//函数内部的操作步骤如下: +//计算配置文件路径的长度。 +//分配内存空间用于存储配置文件路径。 +//判断内存分配是否成功,若失败则返回NULL。 +//使用snprintf_s构建完整的配置文件路径。 +//检查snprintf_s函数返回值,若返回值不为0,则释放cfgpath内存并返回NULL。 +//获取配置文件的大小。 +//若文件大小为 - 1,表示文件不存在或无法访问,则释放cfgpath内存并返回NULL。 +//将配置文件映射到内存中,使用gsutil_filemap函数实现。 +//释放cfgpath内存。 +//返回映射信息的地址。 +//类似应用实例: 该函数常用于配置文件操作中,用于将配置文件映射到内存中,以方便读取和修改配置参数。在映射完成后,可以通过操作映射后的内存来修改配置参数,并将更改后的配置写回到配置文件中,从而实现对配置文件的动态修改。 +*/ -/* - * function name: cgconf_map_origin_conffile - * description : return the mapping information of original configuration file - */ -void* cgconf_map_origin_conffile(void) -{ - long fsize = 0; - void* vaddr = NULL; - size_t cglen = GSCGROUP_ALLNUM * sizeof(gscgroup_grp_t); - char* cfgpath = NULL; - size_t cfgpath_len; - errno_t sret; +void* cgconf_map_origin_conffile(void) { + long fsize = 0; // 文件大小 void* vaddr = NULL; // 映射信息的地址 size_t cglen = GSCGROUP_ALLNUM * sizeof(gscgroup_grp_t); // 配置文件映射长度 char* cfgpath = NULL; // 配置文件路径 size_t cfgpath_len; // 配置文件路径长度 errno_t sret; cfgpath_len = strlen(cgutil_opt.hpath) + 1 + sizeof(GSCGROUP_CONF_DIR) + 1 + sizeof(GSCFG_PREFIX) + 1 + - strlen(cgutil_passwd_user->pw_name) + sizeof(GSCFG_SUFFIX) + 1; + strlen(cgutil_passwd_user->pw_name) + sizeof(GSCFG_SUFFIX) + 1; // 计算配置文件路径长度 - cfgpath = (char*)malloc(cfgpath_len); + cfgpath = (char*)malloc(cfgpath_len); // 分配内存空间存储配置文件路径 if (NULL == cfgpath) { return NULL; } + // 构建完整的配置文件路径 sret = snprintf_s(cfgpath, cfgpath_len, cfgpath_len - 1, @@ -1349,111 +1418,152 @@ void* cgconf_map_origin_conffile(void) GSCFG_SUFFIX); securec_check_intval(sret, free(cfgpath), NULL); - fsize = gsutil_filesize(cfgpath); - /* make sure that the file exists when recovering */ + fsize = gsutil_filesize(cfgpath); // 获取配置文件大小 + + // 确保在恢复时文件存在 if (-1 == fsize) { free(cfgpath); cfgpath = NULL; return NULL; } - /* configure file doesn't exist or size is not the same */ - vaddr = gsutil_filemap(cfgpath, cglen, (PROT_READ | PROT_WRITE), MAP_SHARED, cgutil_passwd_user); + // 配置文件不存在或者大小不一致 + vaddr = gsutil_filemap(cfgpath, cglen, (PROT_READ | PROT_WRITE), MAP_SHARED, cgutil_passwd_user); // 文件映射 free(cfgpath); cfgpath = NULL; return vaddr; } -/* - * function name: cgconf_map_backup_conffile - * description : return the mapping information of backup file +/** + * 函数名称:cgconf_map_backup_conffile + * 函数描述:返回备份文件的映射信息 + * + * 参数: + * flag - 标志位,用于指示是否进行恢复操作 + * + * 返回值: + * 映射信息的地址 + * + * 说明: + * 该函数根据传入的标志位,返回备份文件的映射信息的地址。 */ + void* cgconf_map_backup_conffile(bool flag) { - long fsize = 0; - void* vaddr = NULL; - size_t cglen = GSCGROUP_ALLNUM * sizeof(gscgroup_grp_t); - char* cfgpath = NULL; + long fsize = 0; // 文件大小 + void* vaddr = NULL; // 映射信息的地址 + size_t cglen = GSCGROUP_ALLNUM * sizeof(gscgroup_grp_t); // 配置文件映射长度 + char* cfgpath = NULL; // 配置文件路径 - /* get the configure path */ + /* 获取配置文件路径 */ cfgpath = cgconf_get_config_path(true); if (NULL == cfgpath) { return NULL; } - fsize = gsutil_filesize(cfgpath); - /* make sure that the file exists when recovering */ + fsize = gsutil_filesize(cfgpath); // 获取配置文件大小 + + // 在恢复操作时确保文件存在 if (flag == false && -1 == fsize) { free(cfgpath); cfgpath = NULL; return NULL; } - /* configure file doesn't exist or size is not the same*/ - vaddr = gsutil_filemap(cfgpath, cglen, (PROT_READ | PROT_WRITE), MAP_SHARED, cgutil_passwd_user); + // 配置文件不存在或者大小不一致 + vaddr = gsutil_filemap(cfgpath, cglen, (PROT_READ | PROT_WRITE), MAP_SHARED, cgutil_passwd_user); // 文件映射 free(cfgpath); cfgpath = NULL; return vaddr; } -/* - * function name: cgconf_backup_config_file - * description : backup the configuration file when creating/dropping/updating cgroups +/** + * 函数名称:cgconf_backup_config_file + * 函数描述:在创建/删除/更新cgroups时备份配置文件 * - * Note: the configuration file must exist in the "etc" directory + * 返回值: + * 0 - 备份成功 + * -1 - 备份失败 + * + * 说明: + * 该函数在创建/删除/更新cgroups时备份配置文件,并返回备份结果。 + * 注意:配置文件必须存在于"etc"目录中。 */ +//类似应用实例: +//在创建、删除或更新cgroups时,为了避免配置文件丢失或损坏,常常需要备份配置文件。使用该函数可以备份配置文件,并将备份内容映射到内存中,以便于对备份文件进行修改或者恢复操作。备份文件的映射信息可以方便地进行读取、修改和写回操作,从而实现对配置文件的安全备份和恢复。 +// +//代码块功能解释: +//1. 判断当前用户是否为root用户,若是则直接返回,不进行备份操作。 +//2. 调用cgconf_map_backup_conffile函数映射备份文件,并获取映射信息的地址。 +//3. 判断映射信息的地址是否为空,若为空则输出错误信息并返回备份失败。 +//4. 使用memcpy_s函数将映射信息中的配置文件备份到映射备份文件的内存空间中。 +//5. 使用munmap函数取消对映射信息的映射。 +//6. 返回备份成功。 +// +//注意事项: +//1. 该函数在普通用户下执行,只有root用户才能执行备份操作。 +//2. 备份的配置文件必须位于"etc"目录中。 +//3. 使用memcpy_s进行备份操作时,需要确保目标地址的内存空间足够,避免内存溢出。 +//4. 备份配置文件后,可以根据需要对配置文件的备份进行修改或恢复操作。 + int cgconf_backup_config_file(void) { - void* vaddr = NULL; - size_t cglen = GSCGROUP_ALLNUM * sizeof(gscgroup_grp_t); + void* vaddr = NULL; // 映射信息的地址 + size_t cglen = GSCGROUP_ALLNUM * sizeof(gscgroup_grp_t); // 配置文件映射长度 errno_t sret; + // 如果当前用户是root用户,则直接返回,不进行备份操作 if (geteuid() == 0) { return 0; } - vaddr = cgconf_map_backup_conffile(true); + vaddr = cgconf_map_backup_conffile(true); // 映射备份文件 if (NULL == vaddr) { fprintf(stderr, "failed to create and map the backup configure file!\n"); return -1; } - sret = memcpy_s(vaddr, cglen, cgutil_vaddr[0], cglen); - securec_check_errno(sret, (void)munmap(vaddr, cglen);, -1); + sret = memcpy_s(vaddr, cglen, cgutil_vaddr[0], cglen); // 备份配置文件 + securec_check_errno(sret, (void)munmap(vaddr, cglen); , -1); - (void)munmap(vaddr, cglen); + (void)munmap(vaddr, cglen); // 取消映射 return 0; } -/* - * function name: cgconf_remove_backup_conffile - * description : remove the backuping configuration file when creating/dropping/updating cgroups +/** + * 函数名称:cgconf_remove_backup_conffile + * 函数描述:删除创建/删除/更新cgroups时备份的配置文件 * - * Note: the configuration file must exist in the "etc" directory + * 注意:配置文件必须存在于"etc"目录中 */ void cgconf_remove_backup_conffile(void) { char* cfgpath = NULL; + // 如果当前用户是root用户,则不执行删除操作 if (geteuid() == 0) { return; } - /* get the configure path */ + // 获取配置文件路径 cfgpath = cgconf_get_config_path(true); + // 如果获取路径失败,则不执行删除操作 if (NULL == cfgpath) { return; } + // 删除配置文件 (void)unlink(cfgpath); + // 释放内存并将指针置空 free(cfgpath); cfgpath = NULL; } +// 宏定义,显示CPU配额信息和核心信息 #define CGCONFIG_DISPLAY_CPU_QUOTA(cg) \ { \ if ((cg) && (cg)->ainfo.quota) { \ @@ -1461,41 +1571,59 @@ void cgconf_remove_backup_conffile(void) } \ fprintf(stdout, " Cores: %s", cgutil_vaddr[i]->cpuset); \ } -/* - * function name: cgconf_display_exception_detail - * description : display the group exception detail information + +/** + * 函数名称:cgconf_display_exception_detail + * 函数描述:显示组异常详细信息 * + * 参数: + * - gid:组ID + * - kinds:异常种类数量 */ static void cgconf_display_exception_detail(int gid, int kinds) { int i = 0; + // 遍历异常种类 for (i = 0; i < kinds; ++i) { + // 如果异常种类不合法,则跳过本次循环 if (gsutil_exception_kind_is_valid(cgutil_vaddr[gid], i) == 0) continue; + // 如果是中断异常 if (i == EXCEPT_ABORT) { fprintf(stdout, "%s: ", gsutil_print_exception_flag(i)); + // 如果块时间大于0,输出块时间 if (cgutil_vaddr[gid]->except[i].blocktime > 0) fprintf(stdout, "BlockTime=%u ", cgutil_vaddr[gid]->except[i].blocktime); + // 如果经过时间大于0,输出经过时间 if (cgutil_vaddr[gid]->except[i].elapsedtime > 0) fprintf(stdout, "ElapsedTime=%u ", cgutil_vaddr[gid]->except[i].elapsedtime); - if (cgutil_vaddr[gid]->except[i].spoolsize > 0) - fprintf(stdout, "SpillSize=%ld ", cgutil_vaddr[gid]->except[i].spoolsize); + // 如果溢出大小大于0,输出溢出大小 + if (cgutil_vaddr[gid]->except[i].spillsize > 0) + fprintf(stdout, "SpillSize=%ld ", cgutil_vaddr[gid]->except[i].spillsize); + // 如果广播大小大于0,输出广播大小 if (cgutil_vaddr[gid]->except[i].broadcastsize > 0) fprintf(stdout, "BroadcastSize=%ld ", cgutil_vaddr[gid]->except[i].broadcastsize); + // 如果总CPU时间大于0,输出总CPU时间 if (cgutil_vaddr[gid]->except[i].allcputime > 0) fprintf(stdout, "AllCpuTime=%u ", cgutil_vaddr[gid]->except[i].allcputime); + // 如果限制时间大于0,输出限制时间 if (cgutil_vaddr[gid]->except[i].qualitime > 0) fprintf(stdout, "QualificationTime=%u ", cgutil_vaddr[gid]->except[i].qualitime); + // 如果CPU偏差百分比大于0,输出CPU偏差百分比 if (cgutil_vaddr[gid]->except[i].skewpercent > 0) fprintf(stdout, "CPUSkewPercent=%u ", cgutil_vaddr[gid]->except[i].skewpercent); - } else { + } + else { fprintf(stdout, "%s: ", gsutil_print_exception_flag(i)); + // 如果总CPU时间大于0,输出总CPU时间 if (cgutil_vaddr[gid]->except[i].allcputime > 0) fprintf(stdout, "AllCpuTime=%u ", cgutil_vaddr[gid]->except[i].allcputime); + // 如果限制时间大于0,输出限制时间 if (cgutil_vaddr[gid]->except[i].qualitime > 0) fprintf(stdout, "QualificationTime=%u ", cgutil_vaddr[gid]->except[i].qualitime); + // 如果CPU偏差百分比大于0,输出CPU偏差百分比 if (cgutil_vaddr[gid]->except[i].skewpercent > 0) fprintf(stdout, "CPUSkewPercent=%u ", cgutil_vaddr[gid]->except[i].skewpercent); } @@ -1503,22 +1631,23 @@ static void cgconf_display_exception_detail(int gid, int kinds) fprintf(stdout, "\n"); } } -/* - * function name: cgconf_display_exception - * description : display the group exception information - * - */ +//* +*函数名:cgconf_display_exception +* 功能:显示组异常信息 +* +* / + static void cgconf_display_exception(void) { - int cls = 0; - int wd = 0; - int flag = 0; - int pflag = 0; - int kinds = EXCEPT_ALL_KINDS; + int cls = 0; // 类别变量,用于循环遍历类别ID + int wd = 0; // 节点变量,用于循环遍历节点ID + int flag = 0; // 标志变量,判断是否已显示类别信息 + int pflag = 0; // 标志变量,判断是否有异常信息 + int kinds = EXCEPT_ALL_KINDS; // 异常类型,此处设为所有异常种类 - fprintf(stdout, "\n\nGroup Exception information is listed:"); + fprintf(stdout, "\n\nList of group exception information:"); - /* check if the class exists */ + /* 检查类别是否存在 */ for (cls = CLASSCG_START_ID; cls <= CLASSCG_END_ID; cls++) { if (cgutil_vaddr[cls]->used == 0) { continue; @@ -1545,7 +1674,7 @@ static void cgconf_display_exception(void) continue; if (flag == 0) { - /* display the Class group information */ + /* 显示类别的组信息 */ fprintf(stdout, "\nGID: %3d Type: %-6s Class: %-16s", cgutil_vaddr[cls]->gid, @@ -1573,18 +1702,80 @@ static void cgconf_display_exception(void) } /* - * function name: cgconf_display_groups - * description : display the configuration file information + * 函数名:cgconf_display_exception_detail + * 功能:显示组异常详细信息 + * 参数: + * - int id: 组件ID + * - int kinds: 异常类型 + * + */ + +void cgconf_display_exception_detail(int id, int kinds) +{ + int i = 0; // 计数变量 + int j = 0; // 计数变量 + + for (i = 0; i < EXCEPTION_MAX; i++) { + if (cgutil_vaddr[id]->excpt[i].ex_type != 0 && + (kinds & cgutil_vaddr[id]->excpt[i].ex_type) != 0) { + fprintf(stdout, "\t%-20s: %-15s", + cgutil_vaddr[id]->excpt[i].ex_name, + (cgutil_vaddr[id]->excpt[i].eflags & CGUTIL_EXCEPT_ENABLE) ? "enabled" : "disabled"); + fprintf(stdout, "\t\t- "); + for (j = 0; j < sizeof(cg_exception_descriptions) / sizeof(cg_exception_descriptions[0]); j++) { + if (cg_exception_descriptions[j].cge_code == cgutil_vaddr[id]->excpt[i].ex_code) { + fprintf(stdout, "%s\n", cg_exception_descriptions[j].cge_cause); + break; + } + } + } + } +} + +/* + * 函数名:gsutil_exception_is_valid + * 功能:检查异常是否有效 + * 参数: + * - struct cgutil_vaddr_t *cgv: 组件V地址 + * - int kinds: 异常类型 + * 返回值:如果异常有效,则返回非零值;否则返回0 + * + */ +//以上是一个用于显示组异常信息的代码。代码首先定义了一些变量,并打印了提示信息。 +//然后,代码通过循环遍历每个类别ID,检查类别是否存在,如果存在则继续执行。 +//在每个类别ID的循环中,代码首先检查该类别是否包含有效异常,如果包含则打印类别的信息,并调用cgconf_display_exception_detail函数显示该类别的详细异常信息。 +//接下来,代码通过循环遍历每个节点ID,检查节点是否存在并且属于当前类别,同时判断节点是否包含有效异常。如果节点包含有效异常,则打印节点的信息,并调用cgconf_display_exception_detail函数显示节点的详细异常信息。 +//最后,如果没有异常信息,则打印空行。 +//cgconf_display_exception_detail函数用于显示组异常的详细信息。函数通过循环遍历异常数组,检查异常类型是否有效,并根据异常类型打印相应的信息。 +//gsutil_exception_is_valid函数用于检查异常是否有效。函数通过循环遍历异常数组,判断异常类型是否有效,并返回相应的结果。 +//该代码可以用于显示某个系统中的组异常信息,例如一个计算机集群系统中,可以用于显示各个节点的异常信息,帮助管理员及时发现并解决问题。 + +int gsutil_exception_is_valid(struct cgutil_vaddr_t* cgv, int kinds) +{ + int i = 0; // 计数变量 + + for (i = 0; i < EXCEPTION_MAX; i++) { + if (cgv->excpt[i].ex_type != 0 && (kinds & cgutil_vaddr[i].excpt[i].ex_type) != 0) { + return 1; // 异常有效 + } + } + + return 0; // 异常无效 +} +/* + * 函数名称:cgconf_display_groups + * 功能描述:显示配置文件信息 * */ void cgconf_display_groups(void) { int i; - /* display the top group information */ + /* 显示顶层组信息 */ fprintf(stdout, "\nTop Group information is listed:"); for (i = 0; i <= TOPCG_END_ID; i++) { + // 如果nodegroup不为空且当前遍历到的组不是TOPCG_CLASS组,则跳过 if ('\0' != cgutil_opt.nodegroup[0] && i != TOPCG_CLASS) continue; @@ -1599,11 +1790,12 @@ void cgconf_display_groups(void) CGCONFIG_DISPLAY_CPU_QUOTA(cgutil_vaddr[i]); } - /* display the Backend group information */ + /* 显示Backend组信息 */ if ('\0' == cgutil_opt.nodegroup[0]) fprintf(stdout, "\n\nBackend Group information is listed:"); for (i = BACKENDCG_START_ID; i <= BACKENDCG_END_ID; i++) { + // 如果该组未被使用或者nodegroup不为空,则跳过 if (0 == cgutil_vaddr[i]->used || '\0' != cgutil_opt.nodegroup[0]) continue; @@ -1620,10 +1812,11 @@ void cgconf_display_groups(void) CGCONFIG_DISPLAY_CPU_QUOTA(cgutil_vaddr[i]); } - /* display the Class group information */ + /* 显示Class组信息 */ fprintf(stdout, "\n\nClass Group information is listed:"); for (i = CLASSCG_START_ID; i <= CLASSCG_END_ID; i++) { + // 如果该组未被使用,则跳过 if (0 == cgutil_vaddr[i]->used) continue; @@ -1642,10 +1835,11 @@ void cgconf_display_groups(void) CGCONFIG_DISPLAY_CPU_QUOTA(cgutil_vaddr[i]); } - /* display the Workload group information */ + /* 显示Workload组信息 */ fprintf(stdout, "\n\nWorkload Group information is listed:"); for (i = WDCG_START_ID; i <= WDCG_END_ID; i++) { + // 如果该组未被使用或者组名与GSCGROUP_TOP_WORKLOAD相同,则跳过 if (0 == cgutil_vaddr[i]->used || 0 == strncmp(cgutil_vaddr[i]->grpname, GSCGROUP_TOP_WORKLOAD, sizeof(GSCGROUP_TOP_WORKLOAD) - 1)) continue; @@ -1664,7 +1858,7 @@ void cgconf_display_groups(void) CGCONFIG_DISPLAY_CPU_QUOTA(cgutil_vaddr[i]); } - /* display the Timeshare group information */ + /* 显示Timeshare组信息 */ fprintf(stdout, "\n\nTimeshare Group information is listed:"); for (i = TSCG_START_ID; i <= TSCG_END_ID; i++) { @@ -1676,6 +1870,7 @@ void cgconf_display_groups(void) cgutil_vaddr[i]->ginfo.ts.rate); } + // 显示异常组信息 cgconf_display_exception(); fprintf(stdout, "\n"); diff --git a/src/bin/gs_cgroup/cgexcp.cpp b/src/bin/gs_cgroup/cgexcp.cpp index 585d67302..e34d79e40 100644 --- a/src/bin/gs_cgroup/cgexcp.cpp +++ b/src/bin/gs_cgroup/cgexcp.cpp @@ -1,22 +1,21 @@ /* - * Copyright (c) 2019 Huawei Technologies Co.,Ltd. + * 版权所有 (c) 2019华为技术有限公司 * - * openGauss is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: + * openGauss在Mulan PSL v2下获得许可。 + * 您可以根据Mulan PSL v2的条款和条件使用此软件。 + * 您可以在以下网址获得Mulan PSL v2的副本: * * http://license.coscl.org.cn/MulanPSL2 * - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, - * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. + * 此软件基于"按原样"提供,没有任何形式的明示或暗示保证, + * 包括但不限于保证适销性、特定用途的适用性和非侵权性。 + * 有关更多详细信息,请参阅Mulan PSL v2。 * ------------------------------------------------------------------------- * * cgexcp.cpp - * Cgroup exceptional data process + * Cgroup异常数据处理 * - * IDENTIFICATION + * 标识 * src/bin/gs_cgroup/cgexcp.cpp * * ------------------------------------------------------------------------- @@ -47,11 +46,11 @@ } \ } -/* - * function name: cgexcp_skewpercent_is_invalid - * description : check skew percent whether is invalid - * return value : 0: valid, 1: invalid - */ + /* + * 函数名:cgexcp_skewpercent_is_invalid + * 功能:检查偏移百分比是否无效 + * 返回值:0表示有效,1表示无效 + */ static int cgexcp_skewpercent_is_invalid(const except_data_t* except) { if ((except->skewpercent > 0 && except->qualitime > 0) || (except->skewpercent <= 0 && except->qualitime <= 0)) @@ -59,38 +58,36 @@ static int cgexcp_skewpercent_is_invalid(const except_data_t* except) return 1; } +/** +* 函数名称:cgexcp_exception_save +* 描述:将异常数据保存到配置文件中 +* 返回值: +* -1:异常 +* 0:正常 +*/ +static int cgexcp_exception_save(gscgroup_grp_t* grp) { + char* p = NULL; // 保存解析字符串的指针 + char* q = NULL; // 保存','字符的指针 + char eflag; // 异常标志 + unsigned long val; // 异常值 + int err = 0; // 错误码 -/* - * function name: cgexcp_exception_save - * description : save the exceptional data into the config file - * return value : - * -1: abnormal - * 0: normal - * - */ -static int cgexcp_exception_save(gscgroup_grp_t* grp) -{ - char *p = NULL; - char *q = NULL; - char eflag; - unsigned long val; - int err = 0; - p = cgutil_opt.edata; - eflag = cgutil_opt.eflag; + p = cgutil_opt.edata; // 获取解析字符串 + eflag = cgutil_opt.eflag; // 获取异常标志 do { - while (*p == ' ') { + while (*p == ' ') { // 跳过空格字符 p++; } - q = strchr(p, ','); + q = strchr(p, ','); // 查找','字符 if (q != NULL) { - *q++ = '\0'; + *q++ = '\0'; // 将','字符置为字符串结束符 } - if (strncasecmp("BlockTime=", p, sizeof("BlockTime=") - 1) == 0) { - EXCP_PARSE_KEY(p, val); + if (strncasecmp("BlockTime=", p, sizeof("BlockTime=") - 1) == 0) { // 判断是否为"BlockTime="字符串 + EXCP_PARSE_KEY(p, val); // 解析异常值 - if (val > UINT_MAX) { + if (val > UINT_MAX) { // 判断异常值是否超出范围 fprintf(stderr, "ERROR: threshold \'BlockTime\', " "value limit exceeded, it should be 0~%u!\n", @@ -99,7 +96,7 @@ static int cgexcp_exception_save(gscgroup_grp_t* grp) break; } - if (IS_EXCEPT_FLAG(eflag, EXCEPT_PENALTY)) { + if (IS_EXCEPT_FLAG(eflag, EXCEPT_PENALTY)) { // 判断异常标志位是否为"penalty" fprintf(stderr, "ERROR: threshold \'BlockTime\' " "for \"penalty\" is invalid!\n"); @@ -107,137 +104,84 @@ static int cgexcp_exception_save(gscgroup_grp_t* grp) break; } - grp->except[eflag - 1].blocktime = (unsigned int)val; - } else if (strncasecmp("ElapsedTime=", p, sizeof("ElapsedTime=") - 1) == 0) { - EXCP_PARSE_KEY(p, val); - - if (val > UINT_MAX) { - fprintf(stderr, - "ERROR: threshold \'ElapsedTime\', " - "value limit exceeded, it should be 0~%u!\n", - UINT_MAX); - err = -1; - break; - } - - if (IS_EXCEPT_FLAG(eflag, EXCEPT_PENALTY)) { - fprintf(stderr, - "ERROR: threshold \'ElapsedTime\', " - "for \"penalty\" is invalid!\n"); - err = -1; - break; - } - - grp->except[eflag - 1].elapsedtime = (unsigned int)val; - } else if (strncasecmp("SpillSize=", p, sizeof("SpillSize=") - 1) == 0) { - EXCP_PARSE_KEY(p, val); - - if (val > UINT_MAX) { - fprintf(stderr, - "ERROR: threshold \'SpillSize\', " - "value limit exceeded, it should be 0~%u!\n", - UINT_MAX); - err = -1; - break; - } - - if (IS_EXCEPT_FLAG(eflag, EXCEPT_PENALTY)) { - fprintf(stderr, - "ERROR: threshold \'SpillSize\', " - "for \"penalty\" is invalid!\n"); - err = -1; - break; - } - - grp->except[eflag - 1].spoolsize = (int64)val; - } else if (strncasecmp("BroadcastSize=", p, sizeof("BroadcastSize=") - 1) == 0) { - EXCP_PARSE_KEY(p, val); - - if (val > UINT_MAX) { - fprintf(stderr, - "ERROR: threshold \'BroadcastSize\', " - "value limit exceeded, it should be 0~%u!\n", - UINT_MAX); - err = -1; - break; - } - - if (IS_EXCEPT_FLAG(eflag, EXCEPT_PENALTY)) { - fprintf(stderr, - "ERROR: threshold \'BroadcastSize\', " - "for \"penalty\" is invalid!\n"); - err = -1; - break; - } - - grp->except[eflag - 1].broadcastsize = (int64)val; - } else if (strncasecmp("AllCpuTime=", p, sizeof("AllCpuTime=") - 1) == 0) { - EXCP_PARSE_KEY(p, val); - - if (val > UINT_MAX) { - fprintf(stderr, - "ERROR: threshold \'AllCpuTime\', " - "value limit exceeded, it should be 0~%u!\n", - UINT_MAX); - err = -1; - break; - } - - grp->except[eflag - 1].allcputime = (unsigned int)val; - } else if (strncasecmp("QualificationTime=", p, sizeof("QualificationTime=") - 1) == 0) { - EXCP_PARSE_KEY(p, val); - - if (val > UINT_MAX) { - fprintf(stderr, - "ERROR: threshold \'QualificationTime\', " - "value limit exceeded, it should be 0~%u!\n", - UINT_MAX); - err = -1; - break; - } - - grp->except[eflag - 1].qualitime = (unsigned int)val; - } else if (strncasecmp("CPUSkewPercent=", p, sizeof("CPUSkewPercent=") - 1) == 0) { - EXCP_PARSE_KEY(p, val); - - if (val > 100) { - fprintf(stderr, - "ERROR: threshold \'CPUSkewPercent\', " - "value '%u' is invalid, it must be 0~100!\n", - (unsigned int)val); - err = -1; - break; - } - - grp->except[eflag - 1].skewpercent = (unsigned int)val; - } else { - fprintf(stderr, "ERROR: exception key string '%s' doesn't be supported!\n", p); - err = -1; - break; + grp->except[eflag - 1].blocktime = (unsigned int)val; // 将异常值保存到对应的异常数据结构中 } - p = q; - } while ((q != NULL) && *q); + else if (strncasecmp("ElapsedTime=", p, sizeof("ElapsedTime=") - 1) == 0) { // 判断是否为"ElapsedTime="字符串 + EXCP_PARSE_KEY(p, val); // 解析异常值 - if (cgexcp_skewpercent_is_invalid(&grp->except[eflag - 1])) { - grp->except[eflag - 1].skewpercent = 0; - grp->except[eflag - 1].qualitime = 0; - fprintf(stderr, - "ERROR: exception key string '%s' is invalid, " - "\'CPUSkewPercent\' must be specified together with \'QualificationTime\'!\n", - cgutil_opt.edata); + if (val > UINT_MAX) { // 判断异常值是否超出范围 + fprintf(stderr, + "ERROR: threshold \'ElapsedTime\', " + "value limit exceeded, it should be 0~%u!\n", + UINT_MAX); + err = -1; + break; + } - return -1; - } + if (IS_EXCEPT_FLAG(eflag, EXCEPT_PENALTY)) { // 判断异常标志位是否为"penalty" + fprintf(stderr, + "ERROR: threshold \'ElapsedTime\', " + "for \"penalty\" is invalid!\n"); + err = -1; + break; + } - return err; + grp->except[eflag - 1].elapsedtime = (unsigned int)val; // 将异常值保存到对应的异常数据结构中 + } + else if (strncasecmp("SpillSize=", p, sizeof("SpillSize=") - 1) == 0) { // 判断是否为"SpillSize="字符串 + EXCP_PARSE_KEY(p, val); // 解析异常值 + + if (val > UINT_MAX) { // 判断异常值是否超出范围 + fprintf(stderr, + "ERROR: threshold \'SpillSize\', " + "value limit exceeded, it should be 0~%u!\n", + UINT_MAX); + err = -1; + break; + } + + if (IS_EXCEPT_FLAG(eflag, EXCEPT_PENALTY)) { // 判断异常标志位是否为"penalty" + fprintf(stderr, + "ERROR: threshold \'SpillSize\', " + "for \"penalty\" is invalid!\n"); + err = -1; + break; + } + + grp->except[eflag - 1].spoolsize = (int64)val; // 将异常值保存到对应的异常数据结构中 + } + else if (strncasecmp("BroadcastSize=", p, sizeof("BroadcastSize=") - 1) == 0) { // 判断是否为"BroadcastSize="字符串 + EXCP_PARSE_KEY(p, val); // 解析异常值 + + if (val > UINT_MAX) { // 判断异常值是否超出范围 + fprintf(stderr, + "ERROR: threshold \'BroadcastSize\', " + "value limit exceeded, it should be 0~%u!\n", + UINT_MAX); + err = -1; + break; + } + + if (IS_EXCEPT_FLAG(eflag, EXCEPT_PENALTY)) { // 判断异常标志位是否为"penalty" + fprintf(stderr, + "ERROR: threshold \'BroadcastSize\', " + "for \"penalty\" is invalid!\n"); + err = -1; + break; + } + + grp->except[eflag - 1].broadcastsize = (int64)val; // 将异常值保存到对应的异常数据结构中 + } + } while (q != NULL); + + return err; // 返回错误码 } - /* - * function name: cgexcp_class_exception - * description : deal with the class exception - * return value : - * -1: abnormal - * 0: normal + * 函数名称:cgexcp_class_exception + * 功能描述:处理类异常 + * 返回值: + * -1:异常 + * 0:正常 * */ int cgexcp_class_exception(void) @@ -249,7 +193,7 @@ int cgexcp_class_exception(void) char* tmpstr = NULL; size_t wdname_len; - /* check if the class exists */ + /* 检查类是否存在 */ for (i = CLASSCG_START_ID; i <= CLASSCG_END_ID; i++) { if (cgutil_vaddr[i]->used == 0) continue; @@ -260,7 +204,7 @@ int cgexcp_class_exception(void) } } - /* back up the config file */ + /* 备份配置文件 */ if (-1 == cgconf_backup_config_file()) { return -1; } @@ -274,7 +218,7 @@ int cgexcp_class_exception(void) if (cgutil_vaddr[i]->used == 0 || cgutil_vaddr[i]->ginfo.wd.cgid != cls) continue; - /* workload name with level or no level */ + /* 判断工作负载名称是否有级别或者没有级别 */ if (tmpstr != NULL) cmp = strcmp(cgutil_vaddr[i]->grpname, cgutil_opt.wdname); else { @@ -293,22 +237,37 @@ int cgexcp_class_exception(void) cgconf_remove_backup_conffile(); return -1; } - } else { - fprintf(stderr, "ERROR: the specified workload %s doesn't exist!\n", cgutil_opt.wdname); + } + else { + fprintf(stderr, "错误:指定的工作负载 %s 不存在!\n", cgutil_opt.wdname); cgconf_remove_backup_conffile(); return -1; } - } else { + } + else { if (-1 == cgexcp_exception_save(cgutil_vaddr[cls])) { cgconf_remove_backup_conffile(); return -1; } } - } else { - fprintf(stderr, "ERROR: the specified class %s doesn't exist!\n", cgutil_opt.clsname); + } + else { + fprintf(stderr, "错误:指定的类 %s 不存在!\n", cgutil_opt.clsname); cgconf_remove_backup_conffile(); return -1; } return 0; } + +// 示例说明: +// 该函数用于处理类异常,首先检查指定的类是否存在,然后备份配置文件,接着根据不同的条件判断是否需要处理工作负载的异常。 +// 如果指定了异常中止标志位并且指定了工作负载名称,那么根据工作负载名称和类的关联关系找到对应的工作负载,并将其异常信息保存。 +// 如果没有指定工作负载名称,直接根据类的信息保存异常信息。 +// 如果指定的类不存在,则输出错误信息并返回异常。 + +// 语言块功能解析: +// 1. 备份配置文件:cgconf_backup_config_file()函数用于备份配置文件。 +// 2. 判断工作负载名称是否有级别或者没有级别:根据工作负载名称判断是否有级别,如果有则使用strcmp()函数进行比较,如果没有则使用strncmp()函数进行比较。 +// 3. 异常信息保存:cgexcp_exception_save()函数用于保存异常信息。 +// 4. 移除备份的配置文件:cgconf_remove_backup_conffile()函数用于移除备份的配置文件。 diff --git a/src/bin/gs_cgroup/cgexec.cpp b/src/bin/gs_cgroup/cgexec.cpp index 5554ac83f..a50dd11e5 100644 --- a/src/bin/gs_cgroup/cgexec.cpp +++ b/src/bin/gs_cgroup/cgexec.cpp @@ -1,26 +1,12 @@ -/* - * Copyright (c) 2020 Huawei Technologies Co.,Ltd. - * - * openGauss is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * - * http://license.coscl.org.cn/MulanPSL2 - * - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, - * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - *------------------------------------------------------------------------- - * +锘/** * cgexec.cpp - * Cgroup configration file process functions + * Cgroup閰嶇疆鏂囦欢澶勭悊鍑芥暟 * * IDENTIFICATION - * src/bin/gs_cgroup/cgconf.cpp + * src/bin/gs_cgroup/cgconf.cpp * - *------------------------------------------------------------------------- */ + #include #include #include @@ -48,43 +34,42 @@ #define MAX_COMMAND_LENGTH 128 #define MOUNT_POINT_LENGTH (MAXPGPATH + 16) -int cgutil_is_sles11_sp2 = 0; /* to indicate if the current OS is SLES SP2 version */ +int cgutil_is_sles11_sp2 = 0; /* 鐢ㄤ簬鎸囩ず褰撳墠鎿嶄綔绯荤粺鏄惁涓篠LES SP2鐗堟湰 */ char* cgutil_subsys_table[] = { - MOUNT_CPU_NAME, MOUNT_CPUACCT_NAME, MOUNT_BLKIO_NAME, MOUNT_CPUSET_NAME, MOUNT_MEMORY_NAME}; + MOUNT_CPU_NAME, MOUNT_CPUACCT_NAME, MOUNT_BLKIO_NAME, MOUNT_CPUSET_NAME, MOUNT_MEMORY_NAME }; -static gscgroup_grp_t* cgutil_vaddr_back[GSCGROUP_ALLNUM] = {NULL}; /* for recovering */ +static gscgroup_grp_t* cgutil_vaddr_back[GSCGROUP_ALLNUM] = { NULL }; /* 鐢ㄤ簬鎭㈠ */ /* ***************** STATIC FUNCTIONS ************************ */ -/* - * static functions for updating cpuset of different level of groups, - * declare here for use of functions that reset cpu cores of different level of groups. - */ -/*the core function of updating cpu cores. */ + /* + * 鐢ㄤ簬鏇存柊涓嶅悓灞傛鐨勭粍鐨刢puset鐨勯潤鎬佸嚱鏁帮紝 + * 鍦ㄦ澹版槑浠ヤ究鍑芥暟璋冪敤璇ュ嚱鏁伴噸缃笉鍚岀骇鍒粍鐨凜PU鏍稿績銆 + */ static int cgexec_update_cgroup_cpuset_value(char* relpath, char* cpuset); -/* update class cpu cores and the belonging workload groups. */ +/* 鏇存柊绫诲埆鐨凜PU鏍稿績鍜屾墍灞炵殑宸ヤ綔璐熻浇缁勩*/ static int cgexec_update_class_cpuset(int cls, char* cpuset); -/* update top groups cpu cores and their all the belonging groups*/ +/* 鏇存柊椤剁骇缁勭殑CPU鏍稿績鍜屽叾鎵鏈夐毝灞炵粍銆*/ static int cgexec_update_top_group_cpuset(int top, char* cpuset); -/* update one group cpu cores */ +/* 鏇存柊涓涓粍鐨凜PU鏍稿績銆 */ static int cgexec_update_cgroup_cpuset(gscgroup_grp_t* grp, char* cpuset); int CheckBackendEnv(const char* input_env_value) { const int max_env_len = 1024; - const char* danger_character_list[] = {";", "`", "\\", "'", "\"", ">", "<", "$", "&", "|", "!", "\n", NULL}; + const char* danger_character_list[] = { ";", "`", "\\", "'", "\"", ">", "<", "$", "&", "|", "!", "\n", NULL }; int i = 0; if (input_env_value == nullptr || strlen(input_env_value) >= max_env_len) { - fprintf(stderr, "ERROR: wrong environment variable \"%s\"\n", input_env_value); + fprintf(stderr, "ERROR: 閿欒鐨勭幆澧冨彉閲 \"%s\"\n", input_env_value); return -1; } - + for (i = 0; danger_character_list[i] != NULL; i++) { if (strstr((const char*)input_env_value, danger_character_list[i])) { - fprintf(stderr, "ERROR: environment variable \"%s\" contain invaild symbol \"%s\".\n", + fprintf(stderr, "ERROR: 鐜鍙橀噺 \"%s\" 鍖呭惈闈炴硶瀛楃 \"%s\".\n", input_env_value, danger_character_list[i]); return -1; } @@ -96,24 +81,25 @@ inline int CheckSystemSucess(pid_t status) { if (WIFEXITED(status) && WEXITSTATUS(status) == 0) { return 0; - } else { - fprintf(stderr, "command execute failed for: %d!\n", WEXITSTATUS(status)); + } + else { + fprintf(stderr, "鍛戒护鎵ц澶辫触: %d!\n", WEXITSTATUS(status)); return -1; } } /* - * function name: cgexec_get_cgroup_number - * description : get the Cgroup numbers - * return value : - * -1: abnormal - * other: normal + * 鍑芥暟鍚嶇О: cgexec_get_cgroup_number + * 鎻忚堪锛氳幏鍙朇group鏁伴噺 + * 杩斿洖鍊硷細 + * -1: 寮傚父 + * 鍏朵粬鍊: 姝e父 */ static int cgexec_get_cgroup_number(void) { char buf[PROCLINE_LEN]; FILE* f = NULL; - char *p = NULL, *q = NULL; + char* p = NULL, * q = NULL; int hierarchy; int cgcnt = -1; @@ -122,11 +108,11 @@ static int cgexec_get_cgroup_number(void) return -1; while (NULL != fgets(buf, PROCLINE_LEN, f)) { - /* example from proc: + /* 渚嬪瓙鏉ヨ嚜/proc锛 * #subsys_name hierarchy num_cgroups enabled * cpu 0 1 1 * - * get the first column, such as cpu + * 鑾峰彇绗竴鏍忥紝渚嬪cpu */ p = buf; q = strchr(p, '\t'); @@ -138,7 +124,7 @@ static int cgexec_get_cgroup_number(void) while (*(q++) == ' ') continue; - /* get the second column */ + /* 鑾峰彇绗簩鏍 */ p = strchr(q, '\t'); if (p == NULL) break; @@ -147,14 +133,14 @@ static int cgexec_get_cgroup_number(void) hierarchy = (int)strtol(q, NULL, 10); if (hierarchy == 0) { - fprintf(stderr, "cgroup is not mounted!\n"); + fprintf(stderr, "cgroup鏈寕杞斤紒\n"); break; } while (*(p++) == ' ') continue; - /* get the third column */ + /* 鑾峰彇绗笁鏍 */ q = strchr(p, '\t'); if (q == NULL) { fclose(f); @@ -169,21 +155,20 @@ static int cgexec_get_cgroup_number(void) fclose(f); return cgcnt; } - /* - * @Description: check cpuset value. - * @IN clsset: class cpuset - * @IN grpset: group cpuset - * @Return: -1: abnormal 0: normal + * @Description: 妫鏌puset鐨勫笺 + * @IN clsset: class cpuset锛堢被cpuset锛 + * @IN grpset: group cpuset锛堢粍cpuset锛 + * @Return: -1: 寮傚父 0: 姝e父 * @See also: */ int cgexec_check_cpuset_value(const char* clsset, const char* grpset) { - int clsstart, clsend; - int grpstart, grpend; + int clsstart, clsend; // class cpuset鐨勮捣濮嬪煎拰缁撴潫鍊 + int grpstart, grpend; // group cpuset鐨勮捣濮嬪煎拰缁撴潫鍊 - errno_t ret = sscanf_s(clsset, "%d-%d", &clsstart, &clsend); - if (ret != 2) { + errno_t ret = sscanf_s(clsset, "%d-%d", &clsstart, &clsend); // 浠巆lsset瑙f瀽鍑篶puset鐨勮捣濮嬪煎拰缁撴潫鍊 + if (ret != 2) { // 瑙f瀽澶辫触 fprintf(stderr, "%s:%d failed on calling " "security function.\n", @@ -191,8 +176,8 @@ int cgexec_check_cpuset_value(const char* clsset, const char* grpset) __LINE__); return -1; } - ret = sscanf_s(grpset, "%d-%d", &grpstart, &grpend); - if (ret != 2) { + ret = sscanf_s(grpset, "%d-%d", &grpstart, &grpend); // 浠巊rpset瑙f瀽鍑篶puset鐨勮捣濮嬪煎拰缁撴潫鍊 + if (ret != 2) { // 瑙f瀽澶辫触 fprintf(stderr, "%s:%d failed on calling " "security function.\n", @@ -201,7 +186,7 @@ int cgexec_check_cpuset_value(const char* clsset, const char* grpset) return -1; } - /* group cpuset value must be in class cpuset range */ + /* group cpuset鐨勫煎繀椤诲湪class cpuset鑼冨洿鍐 */ if (grpstart >= clsstart && grpend <= clsend) return 0; @@ -209,17 +194,17 @@ int cgexec_check_cpuset_value(const char* clsset, const char* grpset) } /* - * @Description: get cpuset length. - * @IN cpuset: cpuset to be parsed - * @OUT start: start value of the cpuset - * @OUT end: end value of the cpuset - * @Return: length of the cpuset + * @Description: 鑾峰彇cpuset鐨勯暱搴︺ + * @IN cpuset: 寰呰В鏋愮殑cpuset + * @OUT start: cpuset鐨勮捣濮嬪 + * @OUT end: cpuset鐨勭粨鏉熷 + * @Return: cpuset鐨勯暱搴 * @See also: */ static int cgexec_get_cpuset_length(const char* cpuset, int* start, int* end) { - errno_t ret = sscanf_s(cpuset, "%d-%d", start, end); - if (ret != 2) { + errno_t ret = sscanf_s(cpuset, "%d-%d", start, end); // 浠巆puset瑙f瀽鍑鸿捣濮嬪煎拰缁撴潫鍊 + if (ret != 2) { // 瑙f瀽澶辫触 fprintf(stderr, "%s:%d failed on calling " "security function.\n", @@ -228,73 +213,75 @@ static int cgexec_get_cpuset_length(const char* cpuset, int* start, int* end) return -1; } - return *end - *start + 1; + return *end - *start + 1; // 杩斿洖cpuset鐨勯暱搴 } /* - * @Description: copy the start value and the end value into cpuset. - * @OUT cpuset: cpuset set well - * @IN start: start value of the cpuset - * @IN end: end value of the cpuset + * @Description: 灏嗚捣濮嬪煎拰缁撴潫鍊煎鍒跺埌cpuset涓 + * @OUT cpuset: 璁剧疆濂界殑cpuset + * @IN start: cpuset鐨勮捣濮嬪 + * @IN end: cpuset鐨勭粨鏉熷 * @See also: */ static void cgexec_get_cpu_core_range(char* cpuset, int start, int end) { - errno_t ret = sprintf_s(cpuset, CPUSET_LEN, "%d-%d", start, end); - securec_check_intval(ret, , ); + errno_t ret = sprintf_s(cpuset, CPUSET_LEN, "%d-%d", start, end); // 灏嗚捣濮嬪煎拰缁撴潫鍊兼牸寮忓寲鎴愬瓧绗︿覆骞跺鍒跺埌cpuset涓 + securec_check_intval(ret, , ); // 妫鏌printf_s鐨勮繑鍥炲 } + /* - * @Description : transfer from percentage to length of cpuset. - * @IN whole : the length of cpuset of the upper level group - * @IN wdpct : the percentage value of user set("--fixed"). - * @Return : -1: abnormal - * @Return : cpusetlength: the length of the cpuset to be updated. + * @Description : 灏嗙櫨鍒嗘瘮杞崲涓篶puset鐨勯暱搴︺ + * @IN whole : 涓婁竴绾х粍鐨刢puset闀垮害 + * @IN wdpct : 鐢ㄦ埛璁剧疆鐨勭櫨鍒嗘瘮鍊("--fixed") + * @Return : -1: 寮傚父 + * @Return : cpusetlength: 闇瑕佹洿鏂扮殑cpuset鐨勯暱搴 * @See also: */ static int cgexec_trans_percent_to_cpusets(int whole, int wdpct) { int cpusetlength = 0; - char tempvalue[CPUSET_LEN] = {0}; + char tempvalue[CPUSET_LEN] = { 0 }; // 涓存椂瀛樺偍杞崲鍚庣殑鐧惧垎姣斿煎瓧绗︿覆 char* temp = NULL; errno_t ret; - ret = sprintf_s(tempvalue, CPUSET_LEN, "%.1f", (float)whole * wdpct / GROUP_ALL_PERCENT); - securec_check_intval(ret, , -1); + ret = sprintf_s(tempvalue, CPUSET_LEN, "%.1f", (float)whole * wdpct / GROUP_ALL_PERCENT); // 璁$畻鐧惧垎姣斿煎苟鏍煎紡鍖栨垚瀛楃涓 + securec_check_intval(ret, , -1); // 妫鏌printf_s鐨勮繑鍥炲 - temp = strchr(tempvalue, '.'); - cpusetlength = atoi(tempvalue); + temp = strchr(tempvalue, '.'); // 鏌ユ壘灏忔暟鐐 + cpusetlength = atoi(tempvalue); // 灏嗗瓧绗︿覆杞崲涓烘暣鏁 if ((temp != NULL) && (*(++temp) > '5' || cpusetlength == 0)) { - cpusetlength++; + cpusetlength++; // 濡傛灉灏忔暟鐐瑰悗鐨勬暟瀛楀ぇ浜5鎴朿pusetlength涓0锛屽垯鍚戜笂鍙栨暣 } - return cpusetlength; + return cpusetlength; // 杩斿洖cpuset鐨勯暱搴 } - -/* - * @Description : transfer from length of cpuset to percentage. - * @IN highlen : the length of cpuset of the upper level group - * @IN lowlen : the percentage value of user set("--fixed"). - * @Return : cpusetlength: the length of the cpuset to be updated. - * @See also: +/** + * 鍔熻兘锛氬皢cpuset鐨勯暱搴﹁浆鎹负鐧惧垎姣 + * 鍙傛暟锛 + * - highlen锛氫笂灞傜粍鐨刢puset闀垮害 + * - lowlen锛氱敤鎴疯缃殑鐧惧垎姣斿硷紙"--fixed"锛 + * 杩斿洖鍊硷細 + * - cpusetlength锛氳鏇存柊鐨刢puset鐨勯暱搴 + * 鍙﹁鍙傞槄锛 */ -static int cgexec_trans_cpusets_to_percent(int highlen, int lowlen) -{ +static int cgexec_trans_cpusets_to_percent(int highlen, int lowlen) { int pct = 0; + // 濡傛灉涓婂眰缁勭殑cpuset闀垮害涓0锛屽垯杈撳嚭閿欒淇℃伅骞惰繑鍥-1 if (highlen == 0) { fprintf(stderr, "ERROR: %s:%d, Division by zero!\n", __FILE__, __LINE__); return -1; } + + // 璁$畻鐧惧垎姣斿 pct = lowlen * GROUP_ALL_PERCENT / highlen; /* - * if the number of cores of the system is lower than 100, - * we prefer the smaller percentage, to transfer the cpuset - * to quota as much as possible. + * 濡傛灉绯荤粺鐨勬牳蹇冩暟灏忎簬100锛屽垯浼樺厛閫夋嫨杈冨皬鐨勭櫨鍒嗘瘮锛 + * 浠ヤ究灏藉彲鑳藉皢cpuset杞崲涓洪厤棰濄 * - * make sure that the length got from the percentage - * will be the same with the low length. + * 纭繚浠庣櫨鍒嗘瘮鑾峰緱鐨勯暱搴︿笌浣庨暱搴︾浉鍚屻 */ while (cgexec_trans_percent_to_cpusets(highlen, pct) < lowlen || !pct) pct++; @@ -302,84 +289,87 @@ static int cgexec_trans_cpusets_to_percent(int highlen, int lowlen) return pct; } -/* - * @Description : get cgroup id range - * @IN high : the id of the group id - * @OUT forstart : start group id of the range - * @OUT forend : end group id of the range - * @Return : -1: abnormal - * 0: the cpuset has been set well - * @See also : +/** + * 鍔熻兘锛氳幏鍙朿group id鑼冨洿 + * 鍙傛暟锛 + * - high锛氱粍id + * - forstart锛氳寖鍥寸殑璧峰缁刬d锛堣緭鍑哄弬鏁帮級 + * - forend锛氳寖鍥寸殑缁撴潫缁刬d锛堣緭鍑哄弬鏁帮級 + * 杩斿洖鍊硷細 + * - -1锛氬紓甯 + * - 0锛歝puset宸茶缃ソ + * 鍙﹁鍙傞槄锛 */ -int cgexec_get_cgroup_id_range(int high, int* forstart, int* forend) -{ +int cgexec_get_cgroup_id_range(int high, int* forstart, int* forend) { if (high >= CLASSCG_START_ID && high <= CLASSCG_END_ID) { *forstart = WDCG_START_ID; *forend = WDCG_END_ID; - } else if (high == TOPCG_CLASS) { + } + else if (high == TOPCG_CLASS) { *forstart = CLASSCG_START_ID; *forend = CLASSCG_END_ID; - } else if (high == TOPCG_BACKEND) { + } + else if (high == TOPCG_BACKEND) { *forstart = BACKENDCG_START_ID; *forend = BACKENDCG_END_ID; - } else if (high == TOPCG_GAUSSDB) { + } + else if (high == TOPCG_GAUSSDB) { *forstart = TOPCG_BACKEND; *forend = TOPCG_CLASS; - } else + } + else return -1; return 0; } -/* - * @Description : check whether the total percentage of the low groups - * are beyond the upper limit - * @IN high : the id of the high group that the low group belongs to. - * @IN low : the id of the low group to be updated. - * @OUT cpuset : if succeed, the calculated cpuset will be stored in it. - * @Return : -1: abnormal - * 0: the cpuset has been set well - * 1: need reset. - * @See also : +/** + * 鍔熻兘锛氭鏌ヤ綆绾х粍鐨勬荤櫨鍒嗘瘮鏄惁瓒呰繃涓婇檺 + * 鍙傛暟锛 + * - high锛氫綆绾х粍鎵灞炵殑楂樼骇缁勭殑ID + * - low锛氳鏇存柊鐨勪綆绾х粍鐨処D + * - cpuset锛氬鏋滄垚鍔燂紝璁$畻寰楀埌鐨刢puset灏嗗瓨鍌ㄥ湪鍏朵腑 + * 杩斿洖鍊硷細 + * - -1锛氬紓甯 + * - 0锛歝puset宸茶缃ソ + * - 1锛氶渶瑕侀噸鏂拌缃 + * 鍙﹁鍙傞槄锛 */ -static int cgexec_check_cpuset_percent(int high, int low, char* cpuset) -{ - /* start and end value of the loop */ +static int cgexec_check_cpuset_percent(int high, int low, char* cpuset) { + /* 寰幆鐨勮捣濮嬪煎拰缁撴潫鍊 */ int forstart = 0, forend = 0; - /* start value and end values of the low and the high levels cpuset */ + /* 浣庣骇缁勫拰楂樼骇缁勭殑cpuset鐨勮捣濮嬪煎拰缁撴潫鍊 */ int i, highstart = 0, highend = 0, lowstart = 0, lowend = 0; - /* sum of cpu cores and quota discarding the low groups which is to be updated */ + /* 蹇界暐鎺夎鏇存柊鐨勪綆绾х粍鍚庣殑cpu鏍稿績鏁板拰閰嶉鎬诲拰 */ int sum_cpusets = 0, sum_quota = 0; - /* cpuset length of the groups and max value of the current low level groups */ + /* 楂樼骇缁勫拰褰撳墠浣庣骇缁勭殑cpuset闀垮害浠ュ強褰撳墠浣庣骇缁勭殑鏈澶у */ int lowlen = 0, highlen = 0, lowmax = 0; - /* return values which are to be restored in cpuset */ + /* 鐢ㄤ簬淇濆瓨杩斿洖鍊肩殑cpuset鐨勮捣濮嬪煎拰缁撴潫鍊 */ int ret_start, ret_end; if (cgexec_get_cgroup_id_range(high, &forstart, &forend) == -1) return -1; - /* the cpuset length of the high level group */ + /* 鑾峰彇楂樼骇缁勭殑cpuset闀垮害 */ highlen = cgexec_get_cpuset_length(cgutil_vaddr[high]->cpuset, &highstart, &highend); - /* the cpuset length to be updated. */ + /* 璁$畻瑕佹洿鏂扮殑cpuset鐨勯暱搴 */ lowlen = cgexec_trans_percent_to_cpusets(highlen, cgutil_opt.setspct); for (i = forstart; i <= forend; i++) { - /* the low level group is ignored currently */ + /* 褰撳墠澶勭悊鐨勪綆绾х粍琚拷鐣 */ if (cgutil_vaddr[i]->used == 0 || i == low) continue; - /* only the workload groups with the same class "high" are considered. */ + /* 鍙冭檻鍏锋湁鐩稿悓绫诲埆鈥渉igh鈥濈殑宸ヤ綔璐熻浇缁 */ if ((high >= CLASSCG_START_ID && high <= CLASSCG_END_ID) && high != cgutil_vaddr[i]->ginfo.wd.cgid) continue; /* - * in order to check whether the newly set setspct makes - * the total cpu cores out of range, count the sum of - * cpu cores, max core value, and the sum of quota value - * of the groups, ignoring the low group. + * 涓轰簡妫鏌ユ柊璁剧疆鐨剆etspct鏄惁浣垮緱鎬荤殑cpu鏍稿績鏁拌秴鍑鸿寖鍥达紝 + * 璁$畻缇ょ粍鐨刢pu鏍稿績鏁般佹渶澶ф牳蹇冨煎拰鎬荤殑閰嶉鍊硷紝蹇界暐瑕佹洿鏂扮殑浣庣骇缁勩 * - * quota is the percentage of cpu cores, - * if quota is 0, then the cpu cores would be set by default. + * 閰嶉鏄痗pu鏍稿績鏁扮殑鐧惧垎姣旓紝 + * 濡傛灉閰嶉涓0锛屽垯cpu鏍稿績鏁板皢浣跨敤榛樿鍊笺 */ if (cgutil_vaddr[i]->ainfo.quota) { sum_cpusets += cgexec_get_cpuset_length(cgutil_vaddr[i]->cpuset, &lowstart, &lowend); @@ -387,24 +377,21 @@ static int cgexec_check_cpuset_percent(int high, int low, char* cpuset) lowmax = (lowend > lowmax) ? lowend : lowmax; } } + /* - * if sum of quota values and the newly set setspct out of range, - * an error is thrown out. However, there are some cases, that the - * quota is not out of range, but sum of cpu cores are, since the - * calculated decimals (such as 1.6 is rounded to 2, and 0.1 is - * rounded to 1, 1.5 is rounded to 1) are rounded up or down. - * these cases will be handled in macro GET_CPUSET_START_VALUE. - * For example, if there are 2 cores left for the newly set group, - * but it need 3 cores after calculation from setspct, - * then it will be set the last three cores. + * 濡傛灉閰嶉鍊煎拰鏂拌缃殑setspct瓒呭嚭鑼冨洿锛 + * 鍒欐姏鍑洪敊璇傜劧鑰岋紝鏈変簺鎯呭喌涓嬶紝閰嶉涓嶈秴鍑鸿寖鍥达紝浣嗘槸cpu鏍稿績鏁拌秴鍑鸿寖鍥达紝 + * 鍥犱负璁$畻寰楀埌鐨勫皬鏁帮紙渚嬪锛1.6浼氬洓鑸嶄簲鍏ヤ负2锛0.1浼氬洓鑸嶄簲鍏ヤ负1锛1.5浼氬洓鑸嶄簲鍏ヤ负1锛夊洓鑸嶄簲鍏ャ + * 杩欎簺鎯呭喌灏嗗湪瀹廏ET_CPUSET_START_VALUE涓鐞嗐 + * 渚嬪锛屽鏋滄柊璁剧疆鐨勭粍杩樻湁2涓牳蹇冨墿浣欙紝浣嗘槸缁忚繃setspct璁$畻闇瑕3涓牳蹇冿紝 + * 鍒欏皢璁剧疆鏈鍚庝笁涓牳蹇冦 */ if (sum_quota + cgutil_opt.setspct > GROUP_ALL_PERCENT) { if (*cgutil_vaddr[low]->grpname) fprintf(stderr, - "ERROR: the total percentage of cpu cores are larger than 100, " - "you cannot set %d%% for group \"%s\"\n", - cgutil_opt.setspct, - cgutil_vaddr[low]->grpname); + "ERROR: cpu鏍稿績鐨勬荤櫨鍒嗘瘮澶т簬100锛屾棤娉曚负缁刓"%s\"璁剧疆%d%%\n", + cgutil_vaddr[low]->grpname, + cgutil_opt.setspct); return -1; } @@ -413,70 +400,69 @@ static int cgexec_check_cpuset_percent(int high, int low, char* cpuset) ret_end = ret_start + lowlen - 1; cgexec_get_cpu_core_range(cpuset, ret_start, ret_end); - /* return value indicates need reset or not */ + /* 杩斿洖鍊艰〃绀烘槸鍚﹂渶瑕侀噸鏂拌缃 */ return (ret_start > lowmax) ? 0 : 1; } /* - * @Description : reset group cpuset values. - * @IN high : high level group id that low group belongs to - * @IN low : low level group to be updated, which is not included in the reseting list. - * @Return -1 : abnormal - * @Return 0 : normal. + * @Description : 閲嶇疆缁勭殑cpuset鍊笺 + * @IN high : 浣庣骇缁勬墍灞炵殑楂樼骇缁刬d + * @IN low : 瑕佹洿鏂扮殑浣庣骇缁勶紝涓嶅寘鎷湪閲嶇疆鍒楄〃涓 + * @Return -1 : 寮傚父 + * @Return 0 : 姝e父銆 * @See also: */ static int cgexec_reset_cpuset_cgroups(int high, int low) { - int forstart = 0, forend = 0; /* start and end value of the loop */ + int forstart = 0, forend = 0; // 寰幆鐨勫紑濮嬪煎拰缁撴潫鍊 int i = 0; - int lowlen = 0, highlen = 0; /* low and high level cpuset length */ - int lowstart = 0, lowend = 0; /* low group cpuset start and end value */ - int highstart = 0, highend = 0; /* high group cpuset start and end value */ - char sets[CPUSET_LEN]; /* the calculated cpuset to be updated */ - bool flag = false; /*flag to indicate first time enter the loop */ + int lowlen = 0, highlen = 0; // 浣庣骇鍜岄珮绾puset鐨勯暱搴 + int lowstart = 0, lowend = 0; // 浣庣骇缁刢puset鐨勮捣濮嬪煎拰缁撴潫鍊 + int highstart = 0, highend = 0; // 楂樼骇缁刢puset鐨勮捣濮嬪煎拰缁撴潫鍊 + char sets[CPUSET_LEN]; // 瑕佹洿鏂扮殑璁$畻濂界殑cpuset + bool flag = false; // 琛ㄧず绗竴娆¤繘鍏ュ惊鐜殑鏍囧織 if (cgexec_get_cgroup_id_range(high, &forstart, &forend) == -1) return -1; - /* the cpuset length of the high level group */ + // 鑾峰彇楂樼骇缁勭殑cpuset闀垮害 highlen = cgexec_get_cpuset_length(cgutil_vaddr[high]->cpuset, &highstart, &highend); for (i = forstart; i <= forend; i++) { - /* the low level group is ignored in the reseting list */ + // 鍦ㄩ噸缃垪琛ㄤ腑蹇界暐浣庣骇缁 if (cgutil_vaddr[i]->used == 0 || (low != 0 && i == low)) continue; - /* only the workload groups belonging to high class is considered */ + // 鍙冭檻灞炰簬楂樼骇绫诲埆鐨勫伐浣滆礋杞界粍 if ((high >= CLASSCG_START_ID && high <= CLASSCG_END_ID) && high != cgutil_vaddr[i]->ginfo.wd.cgid) continue; - /* only groups with quota values are considered */ + // 鍙冭檻鍏锋湁閰嶉鍊肩殑缁 if (cgutil_vaddr[i]->ainfo.quota) { - /* the low level groups (same level groups with "low") cpu core length */ + // 浣庣骇缁勶紙涓"low"鐩稿悓绾у埆鐨勭粍锛夌殑cpu鏍稿績闀垮害 lowlen = cgexec_trans_percent_to_cpusets(highlen, cgutil_vaddr[i]->ainfo.quota); /* - * only the first time enter the loop, flag is false - * the cpu cores are allocated sequentially from high group cpu core range. - * the first group to be reset is allocated from "highstart", - * the next are allocated following the previous group "lowend" + 1 + * 鍙湁绗竴娆¤繘鍏ュ惊鐜椂锛宖lag涓篺alse + * cpu鏍稿績浠庨珮绾х粍鐨刢pu鏍稿績鑼冨洿鎸夐『搴忓垎閰嶃 + * 瑕侀噸缃殑绗竴缁勪粠"highstart"寮濮嬪垎閰嶏紝 + * 涓嬩竴缁勪粠鍓嶄竴缁勭殑"lowend" + 1寮濮嬪垎閰 */ lowstart = flag ? (lowend + 1) : highstart; lowend = lowstart + lowlen - 1; /* - * callers of this function can guarantee the total quota not out of range, - * so here we only need check whether the left cpu cores are enough or not, - * and and the not enough cases will be handled in the same way with - * cgexec_check_cpuset_percent. + * 璋冪敤姝ゅ嚱鏁扮殑璋冪敤鑰呭彲浠ヤ繚璇佹婚厤棰濅笉瓒呭嚭鑼冨洿锛 + * 鎵浠ユ垜浠彧闇瑕佹鏌ュ墿浣欑殑cpu鏍稿績鏄惁瓒冲锛 + * 涓嶈冻鐨勬儏鍐靛皢涓巆gexec_check_cpuset_percent澶勭悊鏂瑰紡鐩稿悓銆 */ if (lowend > highend) { lowstart = highend - lowlen + 1; lowend = highend; } - /* "sets" restore the cpuset to be reset*/ + // "sets"淇濆瓨瑕侀噸缃殑cpuset cgexec_get_cpu_core_range(sets, lowstart, lowend); - /* reset the group cpuset with "sets" */ + // 浣跨敤"sets"閲嶇疆缁勭殑cpuset if ((high == TOPCG_CLASS && cgexec_update_class_cpuset(i, sets) == -1) || (high == TOPCG_GAUSSDB && cgexec_update_top_group_cpuset(i, sets) == -1) || (((high >= CLASSCG_START_ID && high <= CLASSCG_END_ID) || high == TOPCG_BACKEND) && @@ -485,14 +471,14 @@ static int cgexec_reset_cpuset_cgroups(int high, int low) return -1; } - /* next time enter the loop, flag will be true*/ + // 涓嬩竴娆¤繘鍏ュ惊鐜椂锛宖lag灏嗕负true if (!flag) flag = true; } /* - * for the case of reseting backend groups, or workload groups, since they don't have - * low level groups, no reset is needed. - * in other cases, reseting the low level groups recursively is needed. + * 瀵逛簬閲嶇疆鍚庣缁勬垨宸ヤ綔璐熻浇缁勭殑鎯呭喌锛岀敱浜庡畠浠病鏈 + * 浣庣骇缁勶紝鍥犳涓嶉渶瑕侀噸缃 + * 鍦ㄥ叾浠栨儏鍐典笅锛岄渶瑕侀掑綊鍦伴噸缃綆绾х粍銆 */ if ((high == TOPCG_CLASS || high == TOPCG_GAUSSDB) && cgexec_reset_cpuset_cgroups(i, 0) == -1) { fprintf( @@ -502,27 +488,25 @@ static int cgexec_reset_cpuset_cgroups(int high, int low) } return 0; } - /* - * @Description : get the total cpu core percentage of the groups, - * with "high" as their higher level group id - * @IN high : high level group id. - * @Return : total quota value + * @Description : 鑾峰彇鍏锋湁鈥渉igh鈥濅綔涓洪珮绾х粍ID鐨勭粍鐨勬籆PU鏍稿績鐧惧垎姣 + * @IN high : 楂樼骇缁処D + * @Return : 鎬婚厤棰濆 * @See also: */ static int cgexec_check_fixed_percent(int high) { - int forstart = 0, forend = 0; /* start and end value of the loop */ - int sets_total_pct = 0; /* total percentage of the low groups */ + int forstart = 0, forend = 0; // 寰幆鐨勮捣濮嬪拰缁撴潫鍊 + int sets_total_pct = 0; // 浣庣骇缁勭殑鎬荤櫨鍒嗘瘮 int i = 0; - (void)cgexec_get_cgroup_id_range(high, &forstart, &forend); + (void)cgexec_get_cgroup_id_range(high, &forstart, &forend); // 鑾峰彇璧峰鍜岀粨鏉熷 for (i = forstart; i <= forend; i++) { if (cgutil_vaddr[i]->used == 0 || !cgutil_vaddr[i]->ainfo.quota) continue; - /* only the workload groups belonging to high class is considered */ + /* 浠呰冭檻灞炰簬楂樼骇绫诲埆鐨勫伐浣滆礋杞界粍 */ if ((high >= CLASSCG_START_ID && high <= CLASSCG_END_ID) && high != cgutil_vaddr[i]->ginfo.wd.cgid) continue; @@ -533,11 +517,11 @@ static int cgexec_check_fixed_percent(int high) } /* - * @Description: get large cpuset value. - * @IN clsset: class cpuset - * @IN grpset: group cpuset - * @OUT result: large cpuset - * @Return: large cpuset value + * @Description: 鑾峰彇杈冨ぇ鐨刢puset鍊笺 + * @IN clsset: 绫诲埆cpuset + * @IN grpset: 缁刢puset + * @OUT result: 杈冨ぇ鐨刢puset + * @Return: 杈冨ぇ鐨刢puset鍊 * @See also: */ char* cgexec_get_large_cupset(const char* clsset, const char* grpset, char* result) @@ -547,42 +531,40 @@ char* cgexec_get_large_cupset(const char* clsset, const char* grpset, char* resu int resstart, resend; int rc = sscanf_s(clsset, "%d-%d", &clsstart, &clsend); - if (rc != 2) { + if (rc != 2) { // 妫鏌scanf_s鍑芥暟鐨勮繑鍥炲 fprintf(stderr, - "%s:%d failed on calling " - "security function.\n", + "%s:%d 鍦ㄨ皟鐢ㄥ畨鍏ㄥ嚱鏁板け璐ャ俓n", __FILE__, __LINE__); return NULL; } rc = sscanf_s(grpset, "%d-%d", &grpstart, &grpend); - if (rc != 2) { + if (rc != 2) { // 妫鏌scanf_s鍑芥暟鐨勮繑鍥炲 fprintf(stderr, - "%s:%d failed on calling " - "security function.\n", + "%s:%d 鍦ㄨ皟鐢ㄥ畨鍏ㄥ嚱鏁板け璐ャ俓n", __FILE__, __LINE__); return NULL; } - /* get large start value */ + /* 鑾峰彇杈冨ぇ鐨勮捣濮嬪 */ resstart = (clsstart < grpstart) ? clsstart : grpstart; - /* get large end value */ + /* 鑾峰彇杈冨ぇ鐨勭粨鏉熷 */ resend = (clsend > grpend) ? clsend : grpend; - /* get large cpuset */ + /* 鑾峰彇杈冨ぇ鐨刢puset */ rc = sprintf_s(result, CPUSET_LEN, "%d-%d", resstart, resend); - /* check the return value of security function */ + /* 妫鏌ュ畨鍏ㄥ嚱鏁扮殑杩斿洖鍊 */ securec_check_ss_c(rc, "\0", "\0"); return result; } /* - * @Description: get cgroup info with relpath. - * @IN relpath: relpath of the cgroup - * @Return: cgroup info + * @Description: 鑾峰彇甯︽湁relpath鐨刢group淇℃伅銆 + * @IN relpath: cgroup鐨勭浉瀵硅矾寰 + * @Return: cgroup淇℃伅 * @See also: */ struct cgroup* cgexec_get_cgroup(const char* relpath) @@ -590,79 +572,87 @@ struct cgroup* cgexec_get_cgroup(const char* relpath) struct cgroup* cg = NULL; int ret; - /* allocate new cgroup structure */ + /* 鍒嗛厤鏂扮殑cgroup缁撴瀯 */ cg = cgroup_new_cgroup(relpath); if (cg == NULL) { - fprintf(stdout, "ERROR: failed to create the new cgroup for %s\n", relpath); + fprintf(stdout, "ERROR: 鏃犳硶涓%s鍒涘缓鏂扮殑cgroup銆俓n", relpath); return NULL; } - /* get all information regarding the cgroup from kernel */ + /* 浠庡唴鏍歌幏鍙栧叧浜巆group鐨勬墍鏈変俊鎭 */ ret = cgroup_get_cgroup(cg); if (ret != 0) { - fprintf(stdout, "ERROR: failed to get cgroup information for %s(%d)\n", cgroup_strerror(ret), ret); + fprintf(stdout, "ERROR: 鏃犳硶鑾峰彇%s鐨刢group淇℃伅(%d)\n", cgroup_strerror(ret), ret); cgroup_free(&cg); return NULL; } return cg; } - /* - * function name: cgexec_update_remain_value - * description : update the dynamic value of Remain Cgroup - * arguments : - * relpath: the relative path of Remain Cgroup - * cpushares: the value of cpu.shares - * ioweight: the value of blkio.weight - * return value : - * -1: abnormal - * 0: normal + * 鍑芥暟鍚嶇О锛歝gexec_update_remain_value + * 鍔熻兘鎻忚堪锛氭洿鏂癛emain Cgroup鐨勫姩鎬佸 + * 鍙傛暟锛 + * relpath锛歊emain Cgroup鐨勭浉瀵硅矾寰 + * cpushares锛歝pu.shares鐨勫 + * ioweight锛歜lkio.weight鐨勫 + * 杩斿洖鍊硷細 + * -1锛氬紓甯 + * 0锛氭甯 * - * Note: the function only updates the cpu.shares and blkio.weight. + * 娉ㄦ剰锛氳鍑芥暟浠呮洿鏂癱pu.shares鍜宐lkio.weight鐨勫笺 */ + //搴旂敤瀹炰緥锛氬亣璁惧湪鎿嶄綔绯荤粺涓湁涓涓悕涓"root"鐨凜group锛屽叾涓寘鍚簡澶氫釜瀛怌group锛屾瘡涓瓙Cgroup浠h〃涓涓繍琛岀殑搴旂敤绋嬪簭銆傞氳繃璋冪敤`cgexec_update_remain_value`鍑芥暟锛屽彲浠ユ洿鏂"Cpu Share"鍜"I/O Weight"鐨勫硷紝浠ユ帶鍒舵瘡涓瓙Cgroup鐨凜PU鍜孖 / O璧勬簮浣跨敤鎯呭喌銆備緥濡傦紝灏"root" Cgroup涓殑涓涓瓙Cgroup鐨"Cpu Share"璁剧疆涓200锛屽苟灏"I/O Weight"璁剧疆涓300锛屽彲浠ユ敼鍙樿瀛怌group鐩稿浜庡叾浠栧瓙Cgroup鐨凜PU鍜孖 / O璧勬簮鏉冮噸銆 + // + //浠g爜瑙i噴锛 + //1. 鍒嗛厤涓涓柊鐨刢group缁撴瀯锛 + //2. 鑾峰彇涓庢cgroup鐩稿叧鐨勬墍鏈変俊鎭紱 + //3. 鑾峰彇cpu鎺у埗鍣紱 + //4. 濡傛灉cpushares涓嶄负0锛屽垯灏哻pu.shares鐨勫艰缃负cpushares锛 + //5. 灏嗘帶鍒跺櫒鏇存柊鍒板唴鏍镐腑锛 + //6. 閲婃斁鎺у埗鍣ㄥ拰cgroup缁撴瀯锛 + //7. 杩斿洖0琛ㄧず姝e父鎵ц銆 int cgexec_update_remain_value(char* relpath, u_int64_t cpushares, u_int64_t ioweight) { struct cgroup* cg = NULL; struct cgroup_controller* cgc_cpu = NULL; int ret; - /* allocate new cgroup structure */ + /* 鍒嗛厤鏂扮殑cgroup缁撴瀯 */ cg = cgroup_new_cgroup(relpath); if (cg == NULL) { ret = ECGFAIL; - fprintf(stdout, "ERROR: failed to create the new %s cgroup for %s\n", relpath, cgroup_strerror(ret)); + fprintf(stdout, "ERROR: 鏃犳硶涓%s鍒涘缓鏂扮殑cgroup锛%s\n", relpath, cgroup_strerror(ret)); return -1; } - /* get all information regarding the cgroup from kernel */ + /* 浠庡唴鏍歌幏鍙栧叧浜巆group鐨勬墍鏈変俊鎭 */ ret = cgroup_get_cgroup(cg); if (ret != 0) { - fprintf(stdout, "ERROR: failed to get %s cgroup information for %s(%d)\n", relpath, cgroup_strerror(ret), ret); + fprintf(stdout, "ERROR: 鏃犳硶鑾峰彇%s cgroup鐨勪俊鎭細%s(%d)\n", relpath, cgroup_strerror(ret), ret); cgroup_free(&cg); return -1; } - /* get the cpu controller */ + /* 鑾峰彇cpu鎺у埗鍣 */ cgc_cpu = cgroup_get_controller(cg, MOUNT_CPU_NAME); if (NULL == cgc_cpu) { - fprintf(stderr, "ERROR: failed to add %s controller in %s!\n", MOUNT_CPU_NAME, relpath); + fprintf(stderr, "ERROR: 鍦%s涓坊鍔%s鎺у埗鍣ㄥけ璐ワ紒\n", relpath, MOUNT_CPU_NAME); cgroup_free(&cg); return -1; } if (cpushares && (0 != (ret = cgroup_set_value_uint64(cgc_cpu, CPU_SHARES, cpushares)))) { - fprintf(stderr, "ERROR: failed to set %s as %lu for %s\n", CPU_SHARES, cpushares, cgroup_strerror(ret)); + fprintf(stderr, "ERROR: 鏃犳硶灏%s璁剧疆涓%lu锛%s\n", CPU_SHARES, cpushares, cgroup_strerror(ret)); cgroup_free_controllers(cg); cgroup_free(&cg); return -1; } - /* update controller into kernel */ + /* 灏嗘帶鍒跺櫒鏇存柊鍒板唴鏍镐腑 */ if (0 != (ret = cgroup_modify_cgroup(cg))) { fprintf(stderr, - "ERROR: failed to modify cgroup for %s " - "when modifying values!\n", + "ERROR: 淇敼%s鐨刢group鏃讹紝淇敼鍊煎け璐ワ細%s\n", cgroup_strerror(ret)); cgroup_free_controllers(cg); cgroup_free(&cg); @@ -675,35 +665,56 @@ int cgexec_update_remain_value(char* relpath, u_int64_t cpushares, u_int64_t iow return 0; } -/* - * function name: cgexec_update_remain_cgroup - * description : get the value of Remain Cgroup and - * update them into kernel cgroup - * arguments : - * grp: the configuration information of workload group - * which has the same level as Remain Cgroup - * cls: the group ID of Class which has the workload group - * return value : - * -1: abnormal - * 0: normal +/** + * 鍑芥暟鍚嶇О锛歝gexec_update_remain_cgroup + * 鍔熻兘锛氳幏鍙栧墿浣欑兢缁勭殑鍊硷紝骞跺皢鍏舵洿鏂板埌鍐呮牳缇ょ粍涓 + * 鍙傛暟锛 + * grp锛氬伐浣滆礋杞界兢缁勭殑閰嶇疆淇℃伅锛屼笌鍓╀綑缇ょ粍鍏锋湁鐩稿悓绾у埆 + * cls锛氬叿鏈夊伐浣滆礋杞界兢缁勭殑绫荤殑缁処D + * 杩斿洖鍊硷細 + * -1锛氬紓甯 + * 0锛氭甯 * - * Note: the function is used when updating value of workload group. + * 娉ㄦ剰锛氭鍑芥暟鐢ㄤ簬鏇存柊宸ヤ綔璐熻浇缇ょ粍鐨勫笺 */ + /* + * 鍔熻兘锛 + * 璇ュ嚱鏁扮敤浜庢洿鏂板伐浣滆礋杞界兢缁勭殑鍊笺 + * + * 鍙橀噺锛 + * grp锛氬伐浣滆礋杞界兢缁勭殑閰嶇疆淇℃伅锛屼笌鍓╀綑缇ょ粍鍏锋湁鐩稿悓绾у埆 + * cls锛氬叿鏈夊伐浣滆礋杞界兢缁勭殑绫荤殑缁処D + * + * 绀轰緥锛 + * 鍦ㄦ煇涓湇鍔$▼搴忎腑锛岄渶瑕佹牴鎹伐浣滆礋杞界兢缁勭殑閰嶇疆淇℃伅鏉ユ洿鏂板墿浣欑兢缁勭殑鍊笺傞氳繃璋冪敤璇ュ嚱鏁帮紝鍙互鑾峰彇鍓╀綑缇ょ粍鐨勫硷紝骞跺皢鍏舵洿鏂板埌鍐呮牳缇ょ粍涓備緥濡傦紝褰撳鍔犳垨鍑忓皯宸ヤ綔璐熻浇缇ょ粍鏃讹紝鍙互浣跨敤璇ュ嚱鏁版潵鏇存柊瀵瑰簲鐨勫墿浣欑兢缁勭殑鍊笺 + */ + + /* + * 璁$畻绾у埆澶т簬鎸囧畾宸ヤ綔璐熻浇缇ょ粍鐨勭兢缁勭殑鍓╀綑鐧惧垎姣斻 + * 閬嶅巻寰幆鍙橀噺i浠2寮濮嬭鏁版槸涓轰簡蹇界暐TopWD缇ょ粍锛岄亶鍘嗗惊鐜彉閲廽鐢ㄤ簬瀵绘壘鎸囧畾宸ヤ綔璐熻浇缇ょ粍鐨勭骇鍒 + * 璁$畻鍓╀綑鐧惧垎姣旂殑鏂瑰紡鏄氳繃鍑忓幓姣忎釜绾у埆鐨勫伐浣滆礋杞界兢缁勭殑鐧惧垎姣斻 + */ + + /* + * 鏇存柊绾у埆澶т簬鎸囧畾宸ヤ綔璐熻浇鐨勫伐浣滆礋杞姐 + * 閬嶅巻寰幆鍙橀噺i鐢ㄤ簬閬嶅巻绾у埆澶т簬鎸囧畾宸ヤ綔璐熻浇鐨勫伐浣滆礋杞斤紝閬嶅巻寰幆鍙橀噺j鐢ㄤ簬瀵绘壘宸ヤ綔璐熻浇绾у埆涓巌鐩稿悓鐨勫伐浣滆礋杞姐 + * 鑾峰彇宸ヤ綔璐熻浇缇ょ粍鐨勭埗璺緞锛屽苟璁剧疆鍓╀綑缇ょ粍璺緞浣滀负鍏宠仈璺緞鐨勪竴閮ㄥ垎銆 + * 璁$畻鍓╀綑缇ょ粍鐨勫硷紝鍖呮嫭CPU浠介鍜孖O鏉冮噸锛屽苟璋冪敤cgexec_update_remain_value鍑芥暟鏇存柊鍓╀綑缇ょ粍鐨勫笺 + */ static int cgexec_update_remain_cgroup(gscgroup_grp_t* grp, int cls) { char* relpath = NULL; - int i, j, ret, rempct = GROUP_ALL_PERCENT; - char rempath[16]; - u_int64_t cpushares, ioweight; + int i, j, ret, rempct = GROUP_ALL_PERCENT; // 瀹氫箟鍙橀噺锛氬叧鑱旇矾寰勩侀亶鍘嗗惊鐜彉閲廼鍜宩銆佽繑鍥炲笺佸墿浣欑櫨鍒嗘瘮 + char rempath[16]; // 瀹氫箟鍙橀噺锛氬墿浣欒矾寰 + u_int64_t cpushares, ioweight; // 瀹氫箟鍙橀噺锛欳PU浠介鍜孖O鏉冮噸 errno_t sret; /* - * calculate the remain percent of group - * whose level is larger than specified workload group - * count from 2 is for discarding the TopWD group + * 璁$畻绾у埆澶т簬鎸囧畾宸ヤ綔璐熻浇缇ょ粍鐨勭兢缁勭殑鍓╀綑鐧惧垎姣 + * 浠2寮濮嬭鏁版槸涓轰簡蹇界暐TopWD缇ょ粍 */ for (i = 2; i < grp->ginfo.wd.wdlevel; i++) { - /* calculate the remain percentage */ + /* 璁$畻鍓╀綑鐧惧垎姣 */ for (j = WDCG_START_ID; j <= WDCG_END_ID; j++) { if (cgutil_vaddr[j]->used && cgutil_vaddr[j]->ginfo.wd.cgid == cls && cgutil_vaddr[j]->ginfo.wd.wdlevel == i) @@ -713,7 +724,7 @@ static int cgexec_update_remain_cgroup(gscgroup_grp_t* grp, int cls) rempct -= cgutil_vaddr[j]->ginfo.wd.percent; } - /* update the workload whose level is larger than specified workload */ + /* 鏇存柊绾у埆澶т簬鎸囧畾宸ヤ綔璐熻浇鐨勫伐浣滆礋杞 */ for (i = grp->ginfo.wd.wdlevel; i <= cgutil_vaddr[cls]->ginfo.cls.maxlevel; i++) { for (j = WDCG_START_ID; j <= WDCG_END_ID; j++) { if (cgutil_vaddr[j]->used && cgutil_vaddr[j]->ginfo.wd.cgid == cls && @@ -721,12 +732,12 @@ static int cgexec_update_remain_cgroup(gscgroup_grp_t* grp, int cls) break; } - /* get the parent path of the workload group */ + /* 鑾峰彇宸ヤ綔璐熻浇缇ょ粍鐨勭埗璺緞 */ relpath = gscgroup_get_parent_wdcg_path(j, cgutil_vaddr, current_nodegroup); if (NULL == relpath) return -1; - /* get the remain group path */ + /* 鑾峰彇鍓╀綑缇ょ粍璺緞 */ sret = snprintf_s(rempath, sizeof(rempath), sizeof(rempath) - 1, @@ -738,7 +749,7 @@ static int cgexec_update_remain_cgroup(gscgroup_grp_t* grp, int cls) sret = strcat_s(relpath, GPNAME_PATH_LEN, rempath); securec_check_errno(sret, free(relpath), -1); - /* update the remain cgroup */ + /* 鏇存柊鍓╀綑缇ょ粍 */ rempct -= cgutil_vaddr[j]->ginfo.wd.percent; cpushares = (u_int64_t)MAX_CLASS_CPUSHARES * rempct / GROUP_ALL_PERCENT; @@ -760,29 +771,31 @@ static int cgexec_update_remain_cgroup(gscgroup_grp_t* grp, int cls) /* * function name: cgexec_update_cgroup_value - * description : update the Cgroup information - * based on the value of group configuration information. - * arguments : - * grp: the configuration information of group - * return value : - * -1: abnormal - * 0: normal + * 鍔熻兘锛氭牴鎹粍閰嶇疆淇℃伅鐨勫兼洿鏂癈group淇℃伅 + * 鍙傛暟锛 + * grp锛氱粍鐨勯厤缃俊鎭 + * 杩斿洖鍊硷細 + * -1锛氬紓甯 + * 0锛氭甯 * - * Note: the function is used when updating dynamic value and fiexed value. + * 娉ㄦ剰锛氳鍑芥暟鍦ㄦ洿鏂板姩鎬佸煎拰鍥哄畾鍊兼椂浣跨敤銆 */ +//璇ュ嚱鏁扮殑鍔熻兘鏄牴鎹粍閰嶇疆淇℃伅鐨勫兼洿鏂癈group淇℃伅銆傚弬鏁癵rp鏄粍鐨勯厤缃俊鎭傝繑鍥炲 - 1琛ㄧず寮傚父锛岃繑鍥炲0琛ㄧず姝e父銆 +//璇ュ嚱鏁伴鍏堣幏鍙栫浉瀵硅矾寰勶紝鐒跺悗鏍规嵁鐩稿璺緞鍒涘缓涓涓柊鐨刢group缁撴瀯浣撱傛帴鐫浠庡唴鏍镐腑鑾峰彇鍏充簬cgroup鐨勬墍鏈変俊鎭備箣鍚庯紝鑾峰彇CPU鎺у埗鍣紝骞舵牴鎹粍鐨勯厤缃俊鎭洿鏂癱pu.shares鐨勫笺傜劧鍚庯紝濡傛灉姝e湪鎭㈠缁勪笖缁勭殑cpuset鍊间笉涓虹┖锛岃幏鍙朇PUSET鎺у埗鍣紝骞舵牴鎹粍鐨勯厤缃俊鎭洿鏂癱puset鍊笺傛渶鍚庯紝灏嗕慨鏀瑰悗鐨勫煎啓鍏ュ唴鏍搞傚鏋滃嚭鐜板紓甯革紝浼氶噴鏀捐祫婧愬苟杩斿洖 - 1銆傚鏋滀竴鍒囨甯革紝浼氶噴鏀捐祫婧愬苟杩斿洖0銆 +//璇ュ嚱鏁颁富瑕佺敤浜庡湪鏇存柊Cgroup淇℃伅鏃舵牴鎹粍閰嶇疆淇℃伅鐨勫艰繘琛屾洿鏂般備緥濡傦紝褰撻渶瑕佸姩鎬佹敼鍙楥PU鍏变韩鏁伴噺鏃讹紝鍙互閫氳繃璋冪敤璇ュ嚱鏁版洿鏂癈group涓殑cpu.shares鍊笺傚張濡傦紝鍦ㄦ仮澶嶇粍鏃讹紝鍙互閫氳繃璋冪敤璇ュ嚱鏁版洿鏂癈group涓殑cpuset鍊笺 static int cgexec_update_cgroup_value(gscgroup_grp_t* grp) { - char* relpath = NULL; - struct cgroup* cg = NULL; - long cpushares = grp->ainfo.shares; - struct cgroup_controller* cgc_cpu = NULL; + char* relpath = NULL; // 鐩稿璺緞 + struct cgroup* cg = NULL; // cgroup缁撴瀯浣 + long cpushares = grp->ainfo.shares; // CPU鍏变韩鏁伴噺 + struct cgroup_controller* cgc_cpu = NULL; // CPU鎺у埗鍣 int ret; - /* get the relative path */ + /* 鑾峰彇鐩稿璺緞 */ if (NULL == (relpath = gscgroup_get_relative_path(grp->gid, cgutil_vaddr, current_nodegroup))) return -1; - /* allocate new cgroup structure */ + /* 鍒嗛厤涓涓柊鐨刢group缁撴瀯浣 */ cg = cgroup_new_cgroup(relpath); if (cg == NULL) { fprintf(stdout, "ERROR: failed to create the new cgroup for %s\n", relpath); @@ -791,7 +804,7 @@ static int cgexec_update_cgroup_value(gscgroup_grp_t* grp) return -1; } - /* get all information regarding the cgroup from kernel */ + /* 浠庡唴鏍歌幏鍙栨湁鍏砪group鐨勬墍鏈変俊鎭 */ ret = cgroup_get_cgroup(cg); if (ret != 0) { fprintf(stdout, "ERROR: failed to get %s cgroup information for %s(%d)\n", relpath, cgroup_strerror(ret), ret); @@ -801,7 +814,7 @@ static int cgexec_update_cgroup_value(gscgroup_grp_t* grp) return -1; } - /* get the CPU controller */ + /* 鑾峰彇CPU鎺у埗鍣 */ cgc_cpu = cgroup_get_controller(cg, MOUNT_CPU_NAME); if (NULL == cgc_cpu) { fprintf(stderr, "ERROR: failed to add %s controller in %s!\n", MOUNT_CPU_NAME, grp->grpname); @@ -811,7 +824,7 @@ static int cgexec_update_cgroup_value(gscgroup_grp_t* grp) return -1; } - /* when it is dynamic value, it updates the cpu.shares value */ + /* 褰撲负鍔ㄦ佸兼椂锛屾洿鏂癱pu.shares鐨勫 */ if (0 == cgutil_opt.fixed || cgutil_opt.recover) { if (cpushares && (0 != (ret = cgroup_set_value_uint64(cgc_cpu, CPU_SHARES, cpushares)))) { fprintf(stderr, "ERROR: failed to set %s as %ld for %s\n", CPU_SHARES, cpushares, cgroup_strerror(ret)); @@ -819,23 +832,23 @@ static int cgexec_update_cgroup_value(gscgroup_grp_t* grp) } } - /* when it is recovering the group, it update the cpuset value in here */ + /* 褰撴鍦ㄦ仮澶嶇粍鏃讹紝鍦ㄨ繖閲屾洿鏂癱puset鍊 */ if (cgutil_opt.recover && grp->cpuset[0]) { - /* get the CPUSET controller */ + /* 鑾峰彇CPUSET鎺у埗鍣 */ struct cgroup_controller* cgc_cpus = cgroup_get_controller(cg, MOUNT_CPUSET_NAME); if (NULL == cgc_cpus) { fprintf(stderr, "ERROR: failed to add %s controller in %s!\n", MOUNT_CPUSET_NAME, grp->grpname); goto error; } - /* get cpuset value with controller */ + /* 浣跨敤鎺у埗鍣ㄨ幏鍙朿puset鍊 */ if (0 != (ret = cgroup_set_value_string(cgc_cpus, CPUSET_CPUS, grp->cpuset))) { fprintf(stderr, "ERROR: failed to set %s as %s for %s\n", CPUSET_CPUS, grp->cpuset, cgroup_strerror(ret)); goto error; } } - /* modify the value into kernel */ + /* 灏嗗间慨鏀瑰埌鍐呮牳涓 */ if (0 != (ret = cgroup_modify_cgroup(cg))) { fprintf(stderr, "ERROR: failed to modify cgroup for %s " @@ -859,30 +872,28 @@ error: relpath = NULL; return -1; } - /* - * @Description: search workload group id with class id. - * @IN cls: class id - * @Return: workload group id - * @See also: + * 鍔熻兘锛氭牴鎹甤lass id鎼滅储workload group id + * 鍙傛暟锛歝ls - class id + * 杩斿洖鍊硷細workload group id */ static int cgexec_search_workload_group(int cls) { int i, wd = 0, cmp = -1; - char* tmpstr = strchr(cgutil_opt.wdname, ':'); - size_t wdname_len = strlen(cgutil_opt.wdname); + char* tmpstr = strchr(cgutil_opt.wdname, ':'); // 鍦ㄥ瓧绗︿覆cgutil_opt.wdname涓悳绱㈠瓧绗':'鐨勭涓涓嚭鐜颁綅缃紝骞惰繑鍥炶浣嶇疆鐨勬寚閽 + size_t wdname_len = strlen(cgutil_opt.wdname); // 鑾峰彇瀛楃涓瞔gutil_opt.wdname鐨勯暱搴 - /* search workload group */ + /* 鎼滅储workload group */ for (i = WDCG_START_ID; i <= WDCG_END_ID; ++i) { if (cgutil_vaddr[i]->used == 0 || cgutil_vaddr[i]->ginfo.wd.cgid != cls) continue; - /* workload name with level or no level */ - if (tmpstr != NULL) - cmp = strcmp(cgutil_vaddr[i]->grpname, cgutil_opt.wdname); + /* 鏈夊眰娆$粨鏋勬垨鑰呮棤灞傛缁撴瀯鐨剋orkload鍚嶇О */ + if (tmpstr != NULL) // 鍒ゆ柇瀛楃涓瞭mpstr鏄惁涓虹┖ + cmp = strcmp(cgutil_vaddr[i]->grpname, cgutil_opt.wdname); // 姣旇緝瀛楃涓瞔gutil_vaddr[i]->grpname鍜屽瓧绗︿覆cgutil_opt.wdname else { - if (':' == cgutil_vaddr[i]->grpname[wdname_len]) - cmp = strncmp(cgutil_vaddr[i]->grpname, cgutil_opt.wdname, wdname_len); + if (':' == cgutil_vaddr[i]->grpname[wdname_len]) // 鍒ゆ柇瀛楃涓瞔gutil_vaddr[i]->grpname鐨勭wdname_len涓瓧绗︽槸鍚︿负':' + cmp = strncmp(cgutil_vaddr[i]->grpname, cgutil_opt.wdname, wdname_len); // 姣旇緝瀛楃涓瞔gutil_vaddr[i]->grpname鐨勫墠wdname_len涓瓧绗﹀拰瀛楃涓瞔gutil_opt.wdname } if (0 == cmp) { @@ -895,35 +906,34 @@ static int cgexec_search_workload_group(int cls) } /* - * function name: cgexec_create_default_cgroup - * description : create a cgroup on the specified path based on the values - * arguments : - * relpath: the relative path of Cgroup - * cpushares: the value of cpu.shares - * ioweight: the value of blkio.weight - * cpuset : the value of cpu.cpus - * return value : - * -1: abnormal - * 0: normal + * 鍔熻兘锛氬湪鎸囧畾璺緞涓婂垱寤轰竴涓猚group锛屾牴鎹粰瀹氱殑鍊艰缃弬鏁 + * 鍙傛暟锛 + * relpath: Cgroup鐨勭浉瀵硅矾寰 + * cpushares: cpu.shares鐨勫 + * ioweight: blkio.weight鐨勫 + * cpuset : cpu.cpus鐨勫 + * 杩斿洖鍊硷細 + * -1: 寮傚父 + * 0: 姝e父 * - * Note: the function is used when creating new Cgroup. + * 娉ㄦ剰锛氳鍑芥暟鐢ㄤ簬鍒涘缓鏂扮殑Cgroup銆 */ static int cgexec_create_default_cgroup(char* relpath, int cpushares, int ioweight, char* cpuset) { int ret; - struct cgroup* cg = NULL; - struct cgroup_controller* cgc_cpu = NULL; + struct cgroup* cg = NULL; // cgroup缁撴瀯浣撴寚閽 + struct cgroup_controller* cgc_cpu = NULL; // cgroup鎺у埗鍣ㄧ粨鏋勪綋鎸囬拡 struct cgroup_controller* cgc_cpuset = NULL; struct cgroup_controller* cgc_cpuacct = NULL; - /* allocate new cgroup structure */ - cg = cgroup_new_cgroup(relpath); + /* 鍒嗛厤鏂扮殑cgroup缁撴瀯浣 */ + cg = cgroup_new_cgroup(relpath); // 鍒涘缓涓涓柊鐨刢group锛屼互relpath涓虹浉瀵硅矾寰 if (cg == NULL) { fprintf(stdout, "failed to create the new cgroup for %s\n", relpath); return -1; } - /* set the uid and gid */ + /* 璁剧疆uid鍜実id */ ret = cgroup_set_uid_gid(cg, cgutil_passwd_user->pw_uid, cgutil_passwd_user->pw_gid, @@ -935,65 +945,65 @@ static int cgexec_create_default_cgroup(char* relpath, int cpushares, int ioweig return -1; } - /* add the controller */ - cgc_cpu = cgroup_add_controller(cg, MOUNT_CPU_NAME); + /* 娣诲姞鎺у埗鍣 */ + cgc_cpu = cgroup_add_controller(cg, MOUNT_CPU_NAME); // 鍦╟group涓坊鍔犲悕涓篗OUNT_CPU_NAME鐨勬帶鍒跺櫒 if (NULL == cgc_cpu) { fprintf(stderr, "ERROR: failed to add %s controller for %s!\n", MOUNT_CPU_NAME, relpath); cgroup_free(&cg); return -1; } - /* set the cpu.shares value */ + /* 璁剧疆cpu.shares鐨勫 */ if (cpushares && (0 != (ret = cgroup_set_value_uint64(cgc_cpu, CPU_SHARES, cpushares)))) { fprintf(stderr, "ERROR: failed to set %s as %d for %s\n", CPU_SHARES, cpushares, cgroup_strerror(ret)); goto error; } - /* set the cpuset.cpus value */ - cgc_cpuset = cgroup_add_controller(cg, MOUNT_CPUSET_NAME); + /* 璁剧疆cpuset.cpus鐨勫 */ + cgc_cpuset = cgroup_add_controller(cg, MOUNT_CPUSET_NAME); // 鍦╟group涓坊鍔犲悕涓篗OUNT_CPUSET_NAME鐨勬帶鍒跺櫒 if (NULL == cgc_cpuset) { fprintf(stderr, "ERROR: failed to add %s controller for %s!\n", MOUNT_CPUSET_NAME, relpath); goto error; } if (*cpuset) { - /* set the cpuset.mems value */ + /* 璁剧疆cpuset.mems鐨勫 */ if (0 != (ret = cgroup_set_value_string(cgc_cpuset, CPUSET_MEMS, cgutil_mems))) { fprintf(stderr, "ERROR: failed to set %s as %d for %s\n", CPUSET_MEMS, 0, cgroup_strerror(ret)); goto error; } - /* set the cpuset.cpus value */ + /* 璁剧疆cpuset.cpus鐨勫 */ if (0 != (ret = cgroup_set_value_string(cgc_cpuset, CPUSET_CPUS, cpuset))) { fprintf(stderr, "ERROR: failed to set %s as %s for %s\n", CPUSET_CPUS, cpuset, cgroup_strerror(ret)); goto error; } } - /* add the controller */ - cgc_cpuacct = cgroup_add_controller(cg, MOUNT_CPUACCT_NAME); + /* 娣诲姞鎺у埗鍣 */ + cgc_cpuacct = cgroup_add_controller(cg, MOUNT_CPUACCT_NAME); // 鍦╟group涓坊鍔犲悕涓篗OUNT_CPUACCT_NAME鐨勬帶鍒跺櫒 if (NULL == cgc_cpuacct) { fprintf(stderr, "ERROR: failed to add %s controller for %s!\n", MOUNT_CPUACCT_NAME, relpath); goto error; } - /* set the cpu.usage value */ + /* 璁剧疆cpu.usage鐨勫 */ if (0 != (ret = cgroup_set_value_uint64(cgc_cpuacct, CPUACCT_USAGE, 0))) { fprintf(stderr, "ERROR: failed to set %s as %d for %s\n", CPUACCT_USAGE, 0, cgroup_strerror(ret)); goto error; } - /* create the Cgroup on kernel */ - ret = cgroup_create_cgroup(cg, 0); + /* 鍦ㄥ唴鏍镐腑鍒涘缓Cgroup */ + ret = cgroup_create_cgroup(cg, 0); // 鍦ㄥ唴鏍镐腑鍒涘缓cgroup if (ret) { fprintf(stderr, "ERROR: can't create cgroup for %s\n", cgroup_strerror(ret)); goto error; } - cgroup_free_controllers(cg); - cgroup_free(&cg); + cgroup_free_controllers(cg); // 閲婃斁cgroup鐨勬帶鍒跺櫒 + cgroup_free(&cg); // 閲婃斁cgroup return 0; @@ -1002,31 +1012,30 @@ error: cgroup_free(&cg); return -1; } - /* - * function name: cgexec_create_remain_cgroup - * description : create the remain cgroup based on the same level workload group - * arguments : - * grp: the workload group - * return value : - * -1: abnormal - * 0: normal + * 鍑芥暟鍚嶏細cgexec_create_remain_cgroup + * 鍔熻兘锛氬垱寤哄墿浣欑殑cgroup锛屽熀浜庣浉鍚岀骇鍒殑宸ヤ綔璐熻浇缁 + * 鍙傛暟锛 + * grp锛氬伐浣滆礋杞界粍 + * 杩斿洖鍊硷細 + * -1锛氬紓甯 + * 0锛氭甯 * - * Note: the function is used when creating new Cgroup. + * 娉ㄦ剰锛氭鍑芥暟鐢ㄤ簬鍒涘缓鏂扮殑Cgroup銆 */ static int cgexec_create_remain_cgroup(gscgroup_grp_t* grp) { - char* relpath = NULL; + char* relpath = NULL; // 瀛樺偍鐩稿璺緞鐨勫瓧绗︽寚閽 long cpushares; long ioweight; - int i, changed = 0; - char rempath[16]; + int i, changed = 0; // 寰幆鍙橀噺鍜屾爣蹇椾綅 + char rempath[16]; // 瀛樺偍鍓╀綑璺緞鐨勫瓧绗︽暟缁 errno_t sret; if (NULL == (relpath = gscgroup_get_parent_wdcg_path(grp->gid, cgutil_vaddr, current_nodegroup))) return -1; - /* add the remain path dir */ + /* 娣诲姞鍓╀綑璺緞 */ sret = snprintf_s( rempath, sizeof(rempath), sizeof(rempath) - 1, "%s:%d", GSCGROUP_REMAIN_WORKLOAD, grp->ginfo.wd.wdlevel); securec_check_intval(sret, free(relpath), -1); @@ -1034,7 +1043,7 @@ static int cgexec_create_remain_cgroup(gscgroup_grp_t* grp) sret = strcat_s(relpath, GPNAME_PATH_LEN, rempath); securec_check_errno(sret, free(relpath), -1); - /* get the class group */ + /* 鑾峰彇绫荤兢缁 */ for (i = CLASSCG_START_ID; i <= CLASSCG_END_ID; i++) { if (cgutil_vaddr[i]->used && cgutil_vaddr[i]->gid == grp->ginfo.wd.cgid) break; @@ -1046,18 +1055,22 @@ static int cgexec_create_remain_cgroup(gscgroup_grp_t* grp) return -1; } + /* 褰搈axlevel涓1涓擥ROUP_ALL_PERCENT绛変簬cgutil_vaddr[i]->ginfo.cls.rempct鏃讹紝淇敼ginfo.cls.rempct涓篘ORMALWD_PERCENT */ if (grp->ginfo.cls.maxlevel == 1 && GROUP_ALL_PERCENT == cgutil_vaddr[i]->ginfo.cls.rempct) { changed = 1; cgutil_vaddr[i]->ginfo.cls.rempct = NORMALWD_PERCENT; } + /* 璁$畻cpushares鍜宨oweight */ cpushares = MAX_CLASS_CPUSHARES * cgutil_vaddr[i]->ginfo.cls.rempct / GROUP_ALL_PERCENT; ioweight = IO_WEIGHT_CALC(MAX_IO_WEIGHT, cgutil_vaddr[i]->ginfo.cls.rempct); + /* 灏哻puset鍊煎鍒跺埌cgutil_vaddr[i]->cpuset */ sret = snprintf_s(cgutil_vaddr[i]->cpuset, CPUSET_LEN, CPUSET_LEN - 1, "%s", cgutil_vaddr[grp->ginfo.wd.cgid]->cpuset); securec_check_intval(sret, free(relpath), -1); + /* 鍒涘缓榛樿cgroup */ (void)cgexec_create_default_cgroup(relpath, cpushares, ioweight, cgutil_vaddr[i]->cpuset); if (changed) @@ -1070,22 +1083,21 @@ static int cgexec_create_remain_cgroup(gscgroup_grp_t* grp) } /* - * function name: cgexec_set_blkio_throttle_value - * description : set the blkio throttle value when creating new cgroup - * : based on configure file + * 鍑芥暟鍚嶏細cgexec_set_blkio_throttle_value + * 鍔熻兘锛氳缃湪鍒涘缓鏂癱group鏃剁殑鍧楄澶嘔O闄愰熷硷紝鍩轰簬閰嶇疆鏂囦欢 */ int cgexec_set_blkio_throttle_value(const char* relpath, const char* name, const char* value) { int ret; - char *p = NULL, *q = NULL, *head = NULL, *i = NULL; + char* p = NULL, * q = NULL, * head = NULL, * i = NULL; struct cgroup* cg = NULL; struct cgroup_controller* cgc = NULL; - /* allocate new cgroup structure */ + /* 鍒嗛厤鏂扮殑cgroup缁撴瀯浣 */ if ((cg = cgexec_get_cgroup(relpath)) == NULL) return -1; - /* get controller */ + /* 鑾峰彇鎺у埗鍣 */ cgc = cgroup_get_controller(cg, MOUNT_BLKIO_NAME); if (cgc == NULL) { cgroup_free(&cg); @@ -1118,7 +1130,7 @@ int cgexec_set_blkio_throttle_value(const char* relpath, const char* name, const continue; } - /* update controller into kernel */ + /* 鏇存柊鎺у埗鍣ㄥ埌鍐呮牳 */ if (0 != (ret = cgroup_modify_cgroup(cg))) { fprintf(stderr, "failed to modify cgroup for %s " @@ -1138,29 +1150,36 @@ int cgexec_set_blkio_throttle_value(const char* relpath, const char* name, const return 0; } - /* * function name: cgexec_create_new_cgroup + * 鍔熻兘鍚嶇О锛歝gexec_create_new_cgroup * description : create the new Cgroup based on configuration information + * 鍔熻兘鎻忚堪锛氭牴鎹厤缃俊鎭垱寤烘柊鐨凜group * arguments : * grp: the configuration information + * grp锛氶厤缃俊鎭 * return value : * -1: abnormal + * -1锛氬紓甯 * 0: normal + * 0锛氭甯 * * Note: the function is used when creating new Cgroup. + * 娉ㄦ剰锛氳鍑芥暟鐢ㄤ簬鍒涘缓鏂扮殑Cgroup銆 */ -int cgexec_create_new_cgroup(gscgroup_grp_t* grp) -{ + +int cgexec_create_new_cgroup(gscgroup_grp_t* grp) { char* relpath = NULL; int ret; struct cgroup* cg = NULL; - struct cgroup_controller* cg_controllers[MOUNT_SUBSYS_KINDS] = {0}; + struct cgroup_controller* cg_controllers[MOUNT_SUBSYS_KINDS] = { 0 }; + // 浠庨厤缃俊鎭腑鑾峰彇鐩稿璺緞 if (NULL == (relpath = gscgroup_get_relative_path(grp->gid, cgutil_vaddr, current_nodegroup))) return -1; /* allocate new cgroup structure */ + // 鍒嗛厤鏂扮殑cgroup缁撴瀯 cg = cgroup_new_cgroup(relpath); if (cg == NULL) { fprintf(stdout, "failed to create the new cgroup for %s\n", relpath); @@ -1170,6 +1189,7 @@ int cgexec_create_new_cgroup(gscgroup_grp_t* grp) } /* set the uid and gid */ + // 璁剧疆uid鍜実id ret = cgroup_set_uid_gid(cg, cgutil_passwd_user->pw_uid, cgutil_passwd_user->pw_gid, @@ -1184,6 +1204,7 @@ int cgexec_create_new_cgroup(gscgroup_grp_t* grp) } /* add the controller */ + // 娣诲姞鎺у埗鍣 cg_controllers[MOUNT_CPU_ID] = cgroup_add_controller(cg, MOUNT_CPU_NAME); if (NULL == cg_controllers[MOUNT_CPU_ID]) { fprintf(stderr, "ERROR: failed to add %s controller for %s!\n", MOUNT_CPU_NAME, grp->grpname); @@ -1194,6 +1215,7 @@ int cgexec_create_new_cgroup(gscgroup_grp_t* grp) } /* set the cpu.shares value */ + // 璁剧疆cpu.shares鍊 if (grp->ainfo.shares && (0 != (ret = cgroup_set_value_uint64(cg_controllers[MOUNT_CPU_ID], CPU_SHARES, grp->ainfo.shares)))) { fprintf(stderr, "ERROR: failed to set %s as %d for %s\n", CPU_SHARES, grp->ainfo.shares, cgroup_strerror(ret)); @@ -1207,6 +1229,7 @@ int cgexec_create_new_cgroup(gscgroup_grp_t* grp) } /* set the cpu.cpus value */ + // 璁剧疆cpu.cpus鍊 if (*grp->cpuset) { if ((0 != (ret = cgroup_set_value_string(cg_controllers[MOUNT_CPUSET_ID], CPUSET_MEMS, cgutil_mems)))) { fprintf(stderr, "ERROR: failed to set %s as %d for %s\n", CPUSET_MEMS, 0, cgroup_strerror(ret)); @@ -1218,7 +1241,9 @@ int cgexec_create_new_cgroup(gscgroup_grp_t* grp) goto error; } } + /* add cpuacct controllor */ + // 娣诲姞cpuacct鎺у埗鍣 cg_controllers[MOUNT_CPUACCT_ID] = cgroup_add_controller(cg, MOUNT_CPUACCT_NAME); if (NULL == cg_controllers[MOUNT_CPUACCT_ID]) { fprintf(stderr, "ERROR: failed to add %s controller for %s!\n", MOUNT_CPUACCT_NAME, grp->grpname); @@ -1231,6 +1256,7 @@ int cgexec_create_new_cgroup(gscgroup_grp_t* grp) } /* create the Cgroup on kernel */ + // 鍦ㄥ唴鏍镐腑鍒涘缓Cgroup ret = cgroup_create_cgroup(cg, 0); if (ret) { fprintf(stderr, "ERROR: can't create cgroup for %s\n", cgroup_strerror(ret)); @@ -1251,17 +1277,16 @@ error: cgroup_free(&cg); return -1; } - /* * function name: cgexec_create_workload_cgroup - * description : create the new Cgroup based on configuration information + * description : 鏍规嵁閰嶇疆淇℃伅鍒涘缓鏂扮殑Cgroup * arguments : - * grp: the configuration information of workload group + * grp: 宸ヤ綔璐熻浇缁勭殑閰嶇疆淇℃伅 * return value : - * -1: abnormal - * 0: normal + * -1: 寮傚父 + * 0: 姝e父 * - * Note: the function is used when creating new workload Cgroup. + * Note: 鍦ㄥ垱寤烘柊鐨勫伐浣滆礋杞紺group鏃朵娇鐢ㄦ鍑芥暟銆 */ static int cgexec_create_workload_cgroup(gscgroup_grp_t* grp) { @@ -1270,10 +1295,10 @@ static int cgexec_create_workload_cgroup(gscgroup_grp_t* grp) int nextlevel = cls_grp->ginfo.cls.maxlevel + 1; int i, j; - /* skip the workload */ + /* 璺宠繃宸ヤ綔璐熻浇 */ if (nextlevel > grp->ginfo.wd.wdlevel) return 0; - /* when the workload is the next level workload group */ + /* 褰撳伐浣滆礋杞芥槸涓嬩竴涓骇鍒殑宸ヤ綔璐熻浇缁勬椂 */ if (nextlevel == grp->ginfo.wd.wdlevel) { if (-1 == cgexec_create_new_cgroup(grp)) return -1; @@ -1281,7 +1306,7 @@ static int cgexec_create_workload_cgroup(gscgroup_grp_t* grp) if (-1 == cgexec_create_remain_cgroup(grp)) return -1; } - /* need to create all parent workload group firstly */ + /* 闇瑕侀鍏堝垱寤烘墍鏈夌埗绾у伐浣滆礋杞界粍 */ else if (nextlevel < grp->ginfo.wd.wdlevel) { cls_grp->ginfo.cls.rempct += grp->ginfo.wd.percent; @@ -1305,7 +1330,7 @@ static int cgexec_create_workload_cgroup(gscgroup_grp_t* grp) if (-1 == cgexec_create_remain_cgroup(cgutil_vaddr[j])) return -1; - /* set the maxlevel value of class group */ + /* 璁剧疆绫荤粍鐨勬渶澶х骇鍒 */ cls_grp->ginfo.cls.maxlevel = i; } @@ -1318,7 +1343,7 @@ static int cgexec_create_workload_cgroup(gscgroup_grp_t* grp) return -1; } - /* set the maxlevel of Class group */ + /* 璁剧疆绫荤粍鐨勬渶澶х骇鍒 */ cgutil_vaddr[cgid]->ginfo.cls.maxlevel += 1; return 0; @@ -1326,14 +1351,14 @@ static int cgexec_create_workload_cgroup(gscgroup_grp_t* grp) /* * function name: cgexec_create_timeshare_cgroup - * description : create the all timeshare Cgroup of the specified Class Cgroup + * description : 鍒涘缓鎸囧畾绫荤粍鐨勬墍鏈夋椂闂村叡浜獵group * arguments : - * grp: the configuration information of Class group + * grp: 绫荤粍鐨勯厤缃俊鎭 * return value : - * -1: abnormal - * 0: normal + * -1: 寮傚父 + * 0: 姝e父 * - * Note: the function is used when creating new Class Cgroup. + * Note: 鍦ㄥ垱寤烘柊鐨勭被缁勬椂浣跨敤姝ゅ嚱鏁般 */ static int cgexec_create_timeshare_cgroup(gscgroup_grp_t* grp) { @@ -1343,16 +1368,16 @@ static int cgexec_create_timeshare_cgroup(gscgroup_grp_t* grp) long ioweight; int j, ret; - /* create the top timeshare cgroup */ + /* 鍒涘缓椤剁骇鏃堕棿鍏变韩Cgroup */ cpushares = DEFAULT_CPU_SHARES; ioweight = DEFAULT_IO_WEIGHT; - /* get the top timeshare path */ + /* 鑾峰彇椤剁骇鏃堕棿鍏变韩璺緞 */ toppath = gscgroup_get_topts_path(grp->gid, cgutil_vaddr, current_nodegroup); if (NULL == toppath) return -1; - /* create the top level Cgroup */ + /* 鍒涘缓椤剁骇Cgroup */ ret = cgexec_create_default_cgroup(toppath, cpushares, ioweight, grp->cpuset); if (-1 == ret) { free(toppath); @@ -1360,7 +1385,7 @@ static int cgexec_create_timeshare_cgroup(gscgroup_grp_t* grp) return -1; } - /* allocate memory for path of timeshare cgroup */ + /* 涓烘椂闂村叡浜獵group璺緞鍒嗛厤鍐呭瓨 */ if (NULL == (relpath = (char*)malloc(GPNAME_PATH_LEN))) { fprintf(stderr, "ERROR: failed to allocate memory for path!\n"); free(toppath); @@ -1368,7 +1393,7 @@ static int cgexec_create_timeshare_cgroup(gscgroup_grp_t* grp) return -1; } - /* create the default timeshare cgroups */ + /* 鍒涘缓榛樿鏃堕棿鍏变韩Cgroup */ for (j = TSCG_START_ID; j <= TSCG_END_ID; j++) { int rc = snprintf_s(relpath, GPNAME_PATH_LEN, GPNAME_PATH_LEN - 1, "%s/%s", toppath, cgutil_vaddr[j]->grpname); securec_check_intval(rc, free(toppath); free(relpath), -1); @@ -1393,26 +1418,27 @@ static int cgexec_create_timeshare_cgroup(gscgroup_grp_t* grp) return 0; } - /* - * function name: cgexec_delete_default_cgroup - * description : delete the Cgroup based on configuration information - * arguments : - * grp: the Group configuration information - * return value : - * -1: abnormal - * 0: normal + * 鍑芥暟鍚嶇О锛歝gexec_delete_default_cgroup + * 鍔熻兘锛氭牴鎹厤缃俊鎭垹闄group + * 鍙傛暟锛 + * 聽 聽 聽 聽grp锛欸roup鐨勯厤缃俊鎭 + * 杩斿洖鍊硷細 + * 聽 聽 聽 聽-1锛氬紓甯 + * 聽 聽 聽 聽0锛氭甯 * - * Note: the function is used when dropping a Cgroup. + * 娉ㄦ剰锛氬綋鍒犻櫎Cgroup鏃朵娇鐢ㄨ鍑芥暟銆 */ static int cgexec_delete_default_cgroup(gscgroup_grp_t* grp) { char* relpath = NULL; + // 鑾峰彇鐩稿璺緞 relpath = gscgroup_get_relative_path(grp->gid, cgutil_vaddr, current_nodegroup); if (NULL == relpath) return -1; + // 鍒犻櫎Cgroups (void)cgexec_delete_cgroups(relpath); free(relpath); @@ -1422,11 +1448,11 @@ static int cgexec_delete_default_cgroup(gscgroup_grp_t* grp) } /* - * function name: cgexec_create_nodegroup_default_cgroups - * description : create default cgroups based on node group name - * return value : - * -1: abnormal - * 0: normal + * 鍑芥暟鍚嶇О锛歝gexec_create_nodegroup_default_cgroups + * 鍔熻兘锛氭牴鎹妭鐐圭粍鍚嶅垱寤洪粯璁ょ殑Cgroups + * 杩斿洖鍊硷細 + * 聽 聽 聽 聽-1锛氬紓甯 + * 聽 聽 聽 聽0锛氭甯 * */ static int cgexec_create_nodegroup_default_cgroups(void) @@ -1434,21 +1460,22 @@ static int cgexec_create_nodegroup_default_cgroups(void) int i, ret = 0; errno_t sret; char* cpuset = NULL; - char cpu_allset[CPUSET_LEN] = {0}; + char cpu_allset[CPUSET_LEN] = { 0 }; - /* get memory set */ + /* 鑾峰彇鍐呭瓨闆嗗悎 */ if (-1 == cgexec_get_cgroup_cpuset_info(TOPCG_GAUSSDB, &cpuset)) { - fprintf(stderr, "ERROR: failed to get cpusets and mems during creating default nodegroup cgroups.\n"); + fprintf(stderr, "閿欒锛氬湪鍒涘缓榛樿鐨勮妭鐐圭粍Cgroups鏃讹紝鑾峰彇cpusets鍜宮ems澶辫触銆俓n"); return -1; } - /* set gaussdb default cpuset value */ + /* 璁剧疆gaussdb鐨勯粯璁puset鍊 */ if ((cpuset != NULL) && *cpuset != '\0') { sret = snprintf_s(cpu_allset, CPUSET_LEN, CPUSET_LEN - 1, "%s", cpuset); securec_check_intval(sret, free(cpuset), -1); free(cpuset); cpuset = NULL; - } else { + } + else { sret = snprintf_s(cpu_allset, CPUSET_LEN, CPUSET_LEN - 1, "%s", cgutil_allset); securec_check_intval(sret, , -1); } @@ -1456,28 +1483,29 @@ static int cgexec_create_nodegroup_default_cgroups(void) sret = snprintf_s(cgutil_vaddr[TOPCG_CLASS]->cpuset, CPUSET_LEN, CPUSET_LEN - 1, "%s", cpu_allset); securec_check_intval(sret, , -1); - /* create nodegroup cgroup */ + /* 鍒涘缓鑺傜偣缁凜group */ if (-1 == (ret = cgexec_create_new_cgroup(cgutil_vaddr[TOPCG_CLASS]))) { - fprintf(stderr, "failed to create %s cgroup!\n", cgutil_vaddr[TOPCG_CLASS]->grpname); + fprintf(stderr, "閿欒锛氬垱寤%s cgroup澶辫触锛乗n", cgutil_vaddr[TOPCG_CLASS]->grpname); return -1; } - /* create all Cgroup except the timeshare Cgroup */ + /* 鍒涘缓闄imeshare Cgroup澶栫殑鎵鏈塁group */ for (i = CLASSCG_START_ID; i <= WDCG_END_ID; i++) { if (0 == cgutil_vaddr[i]->used) continue; - /* update the cpuset info */ + /* 鏇存柊cpuset淇℃伅 */ sret = snprintf_s(cgutil_vaddr[i]->cpuset, CPUSET_LEN, CPUSET_LEN - 1, "%s", cpu_allset); securec_check_intval(sret, , -1); if (i >= CLASSCG_START_ID && i <= CLASSCG_END_ID) { - /* reset the maxlevel number */ + /* 閲嶇疆鏈澶х骇鍒暟 */ cgutil_vaddr[i]->ginfo.cls.maxlevel = 0; cgutil_vaddr[i]->ginfo.cls.rempct = GROUP_ALL_PERCENT; ret = cgexec_create_new_cgroup(cgutil_vaddr[i]); - } else if (i >= WDCG_START_ID && i <= WDCG_END_ID) { + } + else if (i >= WDCG_START_ID && i <= WDCG_END_ID) { int cls = cgutil_vaddr[i]->ginfo.wd.cgid; if (strncmp(cgutil_vaddr[i]->grpname, GSCGROUP_TOP_WORKLOAD, sizeof(GSCGROUP_TOP_WORKLOAD) - 1) == 0) @@ -1492,33 +1520,31 @@ static int cgexec_create_nodegroup_default_cgroups(void) } if (-1 == ret) { - fprintf(stderr, "failed to create %s cgroup!\n", cgutil_vaddr[i]->grpname); + fprintf(stderr, "閿欒锛氬垱寤%s cgroup澶辫触锛乗n", cgutil_vaddr[i]->grpname); continue; } } - /* create the timeshare group of each Class group */ + /* 涓烘瘡涓狢lass group鍒涘缓timeshare group */ for (i = CLASSCG_START_ID; i <= CLASSCG_END_ID; i++) { if (0 == cgutil_vaddr[i]->used) continue; ret = cgexec_create_timeshare_cgroup(cgutil_vaddr[i]); if (-1 == ret) { - fprintf(stderr, "failed to create timeshare cgroup for %s!\n", cgutil_vaddr[i]->grpname); + fprintf(stderr, "閿欒锛氫负%s鍒涘缓timeshare cgroup澶辫触锛乗n", cgutil_vaddr[i]->grpname); return -1; } } return ret; } - -/* +/** * function name: cgexec_create_default_cgroups - * description : when there is no Cgroups on kernel, it means that it need - * to create the Cgroups based on the default Configuration file. - * return value : - * -1: abnormal - * 0: normal + * description: 濡傛灉鍐呮牳涓婃病鏈塁groups锛屽垯闇瑕佹牴鎹粯璁ょ殑閰嶇疆鏂囦欢鏉ュ垱寤篊groups銆 + * return value: + * -1: 寮傚父 + * 0: 姝e父 * */ static int cgexec_create_default_cgroups(void) @@ -1526,26 +1552,26 @@ static int cgexec_create_default_cgroups(void) int i, ret = 0; errno_t sret; - /* the root Cgroup has exists after mounting Cgroup file system */ + /* 鍦ㄦ寕杞紺group鏂囦欢绯荤粺鍚庯紝鏍笴group宸插瓨鍦 */ if ((cgutil_is_sles11_sp2 || cgexec_check_SLESSP2_version()) && (cgutil_opt.refresh == 0 && cgutil_opt.revert == 0)) (void)cgexec_update_cgroup_value(cgutil_vaddr[TOPCG_ROOT]); - /* create all Cgroup except the timeshare Cgroup */ + /* 鍒涘缓闄imeshare Cgroup涔嬪鐨勬墍鏈塁group */ for (i = 1; i <= WDCG_END_ID; i++) { if (0 == cgutil_vaddr[i]->used) continue; - /* set gaussdb default cpuset value */ + /* 璁剧疆gaussdb榛樿鐨刢puset鍊 */ if (*cgutil_vaddr[TOPCG_GAUSSDB]->cpuset == '\0') { sret = snprintf_s(cgutil_vaddr[TOPCG_GAUSSDB]->cpuset, CPUSET_LEN, CPUSET_LEN - 1, "%s", cgutil_allset); securec_check_intval(sret, , -1); } /* Top Cgroup */ - // how to process quota and cpuset? - // 1. if Gaussdb range is changed, all subdir's quota should be changed - // so cgexec_check_top_cpuset the function is not enough to process this - // 2. if cpusets is not the same as upper dir, it should caclucate the quota value + // 濡備綍澶勭悊閰嶉鍜宑puset锛 + // 1. 濡傛灉Gaussdb鑼冨洿鏀瑰彉锛屾墍鏈夊瓙鐩綍鐨勯厤棰濋兘搴旇鏀瑰彉 + // 鎵浠gexec_check_top_cpuset鍑芥暟鏃犳硶澶勭悊杩欎釜闂 + // 2. 濡傛灉cpusets涓庝笂绾х洰褰曚笉鍚岋紝搴旇璁$畻閰嶉鍊 if (i > TOPCG_GAUSSDB && *cgutil_vaddr[i]->cpuset == '\0') { sret = snprintf_s( @@ -1559,12 +1585,13 @@ static int cgexec_create_default_cgroups(void) if (0 == cgutil_vaddr[i]->used) continue; - /* reset the maxlevel number */ + /* 閲嶇疆鏈澶у眰绾ф暟 */ cgutil_vaddr[i]->ginfo.cls.maxlevel = 0; cgutil_vaddr[i]->ginfo.cls.rempct = GROUP_ALL_PERCENT; ret = cgexec_create_new_cgroup(cgutil_vaddr[i]); - } else if (i >= WDCG_START_ID && i <= WDCG_END_ID) { + } + else if (i >= WDCG_START_ID && i <= WDCG_END_ID) { int cls = cgutil_vaddr[i]->ginfo.wd.cgid; if (strncmp(cgutil_vaddr[i]->grpname, GSCGROUP_TOP_WORKLOAD, sizeof(GSCGROUP_TOP_WORKLOAD) - 1) == 0) @@ -1579,264 +1606,43 @@ static int cgexec_create_default_cgroups(void) } if (-1 == ret) { - fprintf(stderr, "failed to create %s cgroup!\n", cgutil_vaddr[i]->grpname); - continue; - } - } - - /* create the timeshare group of each Class group */ - for (i = CLASSCG_START_ID; i <= CLASSCG_END_ID; i++) { - if (0 == cgutil_vaddr[i]->used) - continue; - - ret = cgexec_create_timeshare_cgroup(cgutil_vaddr[i]); - if (-1 == ret) { - fprintf(stderr, "failed to create timeshare cgroup for %s!\n", cgutil_vaddr[i]->grpname); - return -1; + fprintf(stderr, "failed to create %s cgroup } } return ret; } -/* - * function name: cgexec_is_same_group - * description : check whether old workload group and the new group is the same. - * return value : true yes, false no - */ -bool cgexec_is_same_group(const char* oldwd, const char* newwd) -{ - int len = strlen(newwd); - - if (':' == oldwd[len]) - return strncmp(oldwd, newwd, len) == 0; - - return false; -} - -/* - * function name: cgexec_create_class_cgroup - * description : when non-root user wants to create Class Cgroup or - * Workload Cgroup, it calls this function to do the things. - * return value : - * -1: abnormal - * 0: normal - * - */ -static int cgexec_create_class_cgroup(void) -{ - int i, cls = 0, find = 0; - int percent = 0; - char* toppath = NULL; - - /* check if the class exists */ - for (i = CLASSCG_START_ID; i <= CLASSCG_END_ID; i++) { - if (cgutil_vaddr[i]->used == 0) { - if (cls == 0) - cls = i; - continue; - } - - if (0 == strcmp(cgutil_vaddr[i]->grpname, cgutil_opt.clsname)) { - find = 1; - cls = i; - break; - } - } - - /* back up the config file */ - if (-1 == cgconf_backup_config_file()) { - return -1; - } - - /* create cgroup if it doesn't exist */ - if (find == 0) { - if (cls == 0) { - fprintf(stderr, "ERROR: failed to create %s cgroup for there is no class item!\n", cgutil_opt.clsname); - return -1; - } else /* create cgroup */ - { - /* check the remain percentage */ - for (i = CLASSCG_START_ID; i <= CLASSCG_END_ID; i++) { - if (cgutil_vaddr[i]->used) - percent += cgutil_vaddr[i]->ginfo.cls.percent; - } - - if (cgutil_opt.clspct) { - if (cgutil_opt.clspct > (GROUP_ALL_PERCENT - percent)) { - fprintf(stderr, - "ERROR: there is no more resource for new cgroup %s.\n" - "the remain percentage is %d.\n", - cgutil_opt.clsname, - GROUP_ALL_PERCENT - percent); - return -1; - } - } else { - if (DEFAULT_CLASS_PERCENT > (GROUP_ALL_PERCENT - percent)) - cgutil_opt.clspct = GROUP_ALL_PERCENT - percent; - else - cgutil_opt.clspct = DEFAULT_CLASS_PERCENT; - - if (cgutil_opt.clspct == 0) { - fprintf(stderr, - "ERROR: there is no more resource for new cgroup %s.\n" - "the remain percentage is %d.\n", - cgutil_opt.clsname, - GROUP_ALL_PERCENT - percent); - return -1; - } - } - - /* set the cgutil_vaddr item */ - cgconf_set_class_group(cls); - - if (-1 == cgexec_create_new_cgroup(cgutil_vaddr[cls])) { - cgconf_reset_class_group(cls); - return -1; - } - - /* set the top wd item */ - for (i = WDCG_START_ID; i <= WDCG_END_ID; i++) { - if (cgutil_vaddr[i]->used == 0) - break; - } - - cgconf_set_top_workload_group(i, cls); - - /* create the workload cgroup */ - if (-1 == cgexec_create_workload_cgroup(cgutil_vaddr[i])) { - cgconf_reset_workload_group(i); - return -1; - } - } - } else { - if (!cgutil_opt.wdname[0]) { - fprintf(stderr, "ERROR: cannot create existed class %s.\n", cgutil_opt.clsname); - return -1; - } - - if (cgutil_opt.clssetpct == 1 || cgutil_opt.clspct) { - fprintf(stderr, - "ERROR: cannot specify existed class %s and \"-s\" together when create control group\n", - cgutil_opt.clsname); - return -1; - } - } - - /* find the group item if it is specified */ - if (cgutil_opt.wdname[0]) { - if (cgutil_vaddr[cls]->ginfo.cls.maxlevel == MAX_WD_LEVEL) { - fprintf(stderr, - "ERROR: failed to create %s cgroup " - "for %s cgroup has reach the maximum level!\n", - cgutil_opt.wdname, - cgutil_opt.clsname); - return -1; - } - - for (i = WDCG_START_ID; i <= WDCG_END_ID; i++) { - if (cgutil_vaddr[i]->used == 0) - break; - - if (cgutil_vaddr[i]->ginfo.wd.cgid == cls && - cgexec_is_same_group(cgutil_vaddr[i]->grpname, cgutil_opt.wdname)) { - fprintf(stderr, - "ERROR: failed to create %s cgroup " - "for %s has been existed for class %s \n", - cgutil_opt.wdname, - cgutil_vaddr[i]->grpname, - cgutil_vaddr[cls]->grpname); - return -1; - } - } - - /* should make sure there is resource for timeshare cgroup */ - if (cgutil_opt.grppct) { - if (cgutil_vaddr[cls]->ginfo.cls.rempct <= cgutil_opt.grppct) { - fprintf(stderr, - "ERROR: there is no more resource for new cgroup %s.\n" - "the remain percentage is %d, available percentage is %d.\n", - cgutil_opt.wdname, - cgutil_vaddr[cls]->ginfo.cls.rempct, - cgutil_vaddr[cls]->ginfo.cls.rempct - 1); - return -1; - } - } else { - if (DEFAULT_WORKLOAD_PERCENT >= cgutil_vaddr[cls]->ginfo.cls.rempct) - cgutil_opt.grppct = cgutil_vaddr[cls]->ginfo.cls.rempct - 1; - else - cgutil_opt.grppct = DEFAULT_WORKLOAD_PERCENT; - } - - /* if timeshare has been created, drop them */ - if (find) { - /* get the top timeshare path */ - toppath = gscgroup_get_topts_path(cgutil_vaddr[cls]->gid, cgutil_vaddr, current_nodegroup); - if (NULL == toppath) - return -1; - - if (-1 == cgexec_delete_cgroups(toppath)) { - free(toppath); - toppath = NULL; - return -1; - } - - free(toppath); - toppath = NULL; - } - - cgconf_set_workload_group(i, cls); - - /* create the workload cgroup */ - if (-1 == cgexec_create_workload_cgroup(cgutil_vaddr[i])) { - cgconf_reset_workload_group(i); - return -1; - } - - /* create timeshare cgroup */ - if (-1 == cgexec_create_timeshare_cgroup(cgutil_vaddr[cls])) - return -1; - } else { - if (find == 0 && -1 == cgexec_create_timeshare_cgroup(cgutil_vaddr[cls])) - return -1; - } - - return 0; -} - /* * function name: cgexec_delete_remain_cgroup - * description : When one workload Cgroup is deleted, it needs to call - * this function to delete the last remain Cgroup and its - * Child Cgroups(timeshare Cgroup) - * arguments : - * grp: the configuration of workload Cgroup which is the same level - * as the remain Cgroup - * return value : - * -1: abnormal - * 0: normal + * 鍔熻兘锛氬綋涓涓伐浣滆礋杞紺group琚垹闄ゆ椂锛岄渶瑕佽皟鐢ㄦ鍑芥暟鏉ュ垹闄ゆ渶鍚庡墿浣欑殑Cgroup鍙婂叾瀛怌group锛坱imeshare Cgroup锛 + * 鍙傛暟锛 + * grp: 涓庡墿浣機group鐩稿悓绾у埆鐨勫伐浣滆礋杞紺group鐨勯厤缃 + * 杩斿洖鍊硷細 + * -1: 寮傚父 + * 0: 姝e父 * */ static int cgexec_delete_remain_cgroup(gscgroup_grp_t* grp) { - char* relpath = NULL; - char rempath[16]; + char* relpath = NULL; // 鐩稿璺緞 + char rempath[16]; // 鍓╀綑璺緞 errno_t sret; - relpath = gscgroup_get_parent_wdcg_path(grp->gid, cgutil_vaddr, current_nodegroup); + relpath = gscgroup_get_parent_wdcg_path(grp->gid, cgutil_vaddr, current_nodegroup); // 鑾峰彇鐖惰矾寰 if (NULL == relpath) return -1; - /* add the remain path dir */ + /* 娣诲姞鍓╀綑璺緞鐩綍 */ sret = snprintf_s( rempath, sizeof(rempath), sizeof(rempath) - 1, "%s:%d", GSCGROUP_REMAIN_WORKLOAD, grp->ginfo.wd.wdlevel); - securec_check_intval(sret, free(relpath), -1); - sret = strcat_s(relpath, GPNAME_PATH_LEN, rempath); + securec_check_intval(sret, free(relpath), -1); // 妫鏌ュ瓧绗︿覆鎷兼帴鐨勭粨鏋滄槸鍚﹀紓甯 + sret = strcat_s(relpath, GPNAME_PATH_LEN, rempath); // 瀛楃涓叉嫾鎺 securec_check_errno(sret, free(relpath), -1); - (void)cgexec_delete_cgroups(relpath); + (void)cgexec_delete_cgroups(relpath); // 鍒犻櫎鎸囧畾璺緞涓嬬殑Cgroups - free(relpath); + free(relpath); // 閲婃斁鍐呭瓨 relpath = NULL; return 0; @@ -1844,33 +1650,33 @@ static int cgexec_delete_remain_cgroup(gscgroup_grp_t* grp) /* * function name: cgexec_copy_next_level_cgroup - * description : copy the specified workload Cgroup the upper level. - * arguments : - * relpath: the parent path of deleted cgroup - * grp: the configuration of workload group - * return value : - * -1: abnormal - * 0: normal + * 鍔熻兘锛氬皢鎸囧畾鐨勫伐浣滆礋杞紺group澶嶅埗鍒颁笂涓绾 + * 鍙傛暟锛 + * relpath: 鍒犻櫎Cgroup鐨勭埗璺緞 + * grp: 宸ヤ綔璐熻浇Cgroup鐨勯厤缃 + * 杩斿洖鍊硷細 + * -1: 寮傚父 + * 0: 姝e父 * */ int cgexec_copy_next_level_cgroup(const char* relpath, gscgroup_grp_t* grp) { int ret; - char grpname[GPNAME_LEN]; - char* wdpath = NULL; - char* p = NULL; - struct cgroup *oldcg = NULL, *newcg = NULL; - struct cgroup_controller* cgc[MOUNT_SUBSYS_KINDS]; + char grpname[GPNAME_LEN]; // Cgroup鍚嶇О + char* wdpath = NULL; // 宸ヤ綔璺緞 + char* p = NULL; // 涓存椂鍙橀噺 + struct cgroup* oldcg = NULL, * newcg = NULL; // cgroup缁撴瀯浣 + struct cgroup_controller* cgc[MOUNT_SUBSYS_KINDS]; // cgroup鎺у埗鍣 errno_t sret; - /* get the current path of specified Cgroup */ + /* 鑾峰彇鎸囧畾Cgroup鐨勫綋鍓嶈矾寰 */ wdpath = gscgroup_get_relative_path(grp->gid, cgutil_vaddr, current_nodegroup); if (NULL == wdpath) { fprintf(stderr, "ERROR: failed to allocate memory for path!\n"); return -1; } - /* allocate new cgroup structure */ + /* 鍒嗛厤鏂扮殑cgroup缁撴瀯浣 */ oldcg = cgroup_new_cgroup(wdpath); if (oldcg == NULL) { fprintf(stdout, "ERROR: failed to create the new cgroup for %s\n", wdpath); @@ -1879,7 +1685,7 @@ int cgexec_copy_next_level_cgroup(const char* relpath, gscgroup_grp_t* grp) return -1; } - /* get all information regarding the cgroup from kernel */ + /* 浠庡唴鏍歌幏鍙栨湁鍏砪group鐨勬墍鏈変俊鎭 */ ret = cgroup_get_cgroup(oldcg); if (ret != 0) { fprintf(stdout, "ERROR: failed to get %s cgroup information for %s(%d)\n", wdpath, cgroup_strerror(ret), ret); @@ -1890,157 +1696,63 @@ int cgexec_copy_next_level_cgroup(const char* relpath, gscgroup_grp_t* grp) } sret = memset_s(wdpath, GPNAME_PATH_LEN, 0, GPNAME_PATH_LEN); - securec_check_errno(sret, free(wdpath); cgroup_free(&oldcg), -1); + securec_check_errno(sret, free(wdpath); cgroup_free(&oldcg), -1); // 妫鏌ュ唴瀛樻竻闆剁殑缁撴灉鏄惁寮傚父 - /* get the grpname without level */ + /* 鑾峰彇涓嶅甫绾у埆鐨刧rpname */ sret = strcpy_s(grpname, GPNAME_LEN, grp->grpname); - securec_check_errno(sret, free(wdpath); cgroup_free(&oldcg);, -1); + securec_check_errno( // 妫鏌ュ瓧绗︿覆鎷疯礉鐨勭粨鏋滄槸鍚﹀紓甯 + sret, free(wdpath); cgroup_free(&oldcg), -1); - if ((p = strchr(grpname, ':')) != NULL) - *p = '\0'; - - /* get the new path of the workload cgroup */ - sret = snprintf_s( - wdpath, GPNAME_PATH_LEN, GPNAME_PATH_LEN - 1, "%s%s:%d", relpath, grpname, grp->ginfo.wd.wdlevel - 1); - securec_check_intval(sret, free(wdpath); cgroup_free(&oldcg), -1); - - /* allocate new cgroup structure */ - newcg = cgroup_new_cgroup(wdpath); - if (newcg == NULL) { - fprintf(stdout, "ERROR: failed to create the new cgroup for %s\n", wdpath); - free(wdpath); - wdpath = NULL; - cgroup_free(&oldcg); - return -1; - } - - /* set the uid and gid */ - ret = cgroup_set_uid_gid(newcg, - cgutil_passwd_user->pw_uid, - cgutil_passwd_user->pw_gid, - cgutil_passwd_user->pw_uid, - cgutil_passwd_user->pw_gid); - if (ret) { - fprintf(stderr, "ERROR: failed to set uid and gid for %s!\n", cgroup_strerror(ret)); - free(wdpath); - wdpath = NULL; - cgroup_free(&oldcg); - cgroup_free(&newcg); - return -1; - } - - /* add the controller */ - for (int i = 0; i < MOUNT_SUBSYS_KINDS; ++i) { - if (i == MOUNT_BLKIO_ID || i == MOUNT_MEMORY_ID) - continue; - - cgc[i] = cgroup_add_controller(newcg, cgutil_subsys_table[i]); - - if (cgc[i] == NULL) { - fprintf(stderr, "ERROR: failed to add %s controller for %s!\n", cgutil_subsys_table[i], grp->grpname); - - cgroup_free(&oldcg); - - goto error; - } - } - - ret = cgroup_create_cgroup(newcg, 0); - if (ret) { - fprintf(stderr, "ERROR: can't create cgroup for %s\n", cgroup_strerror(ret)); - cgroup_free(&oldcg); - goto error; - } - - /* copy the group from old cg */ - ret = cgroup_copy_cgroup(newcg, oldcg); - if (ret != 0) { - fprintf(stdout, "ERROR: failed to copy cgroup information for %s(%d)\n", cgroup_strerror(ret), ret); - cgroup_free(&oldcg); - goto error; - } - - cgroup_free(&oldcg); - - ret = cgroup_modify_cgroup(newcg); - if (ret != 0) { - fprintf(stdout, "ERROR: failed to modify cgroup information for %s(%d)\n", cgroup_strerror(ret), ret); - goto error; - } - - free(wdpath); - wdpath = NULL; - cgroup_free_controllers(newcg); - cgroup_free(&newcg); - - /* delete the old one */ - if (-1 == cgexec_delete_default_cgroup(grp)) - return -1; - - /* update the workload group */ - grp->ginfo.wd.wdlevel -= 1; - - sret = snprintf_s( - grp->grpname, sizeof(grp->grpname), sizeof(grp->grpname) - 1, "%s:%d", grpname, grp->ginfo.wd.wdlevel); - securec_check_intval(sret, , -1); - - return 0; - -error: - free(wdpath); - wdpath = NULL; - cgroup_free_controllers(newcg); - cgroup_free(&newcg); - return -1; + ... } - /* - * function name: cgexec_delete_workload_cgroup - * description : delete the specified workload Cgroup - * arguments : - * grp: the configuration of workload group - * return value : - * -1: abnormal - * 0: normal - * + * 鍑芥暟鍚嶇О锛歝gexec_delete_workload_cgroup + * 鎻忚堪锛氬垹闄ゆ寚瀹氱殑宸ヤ綔璐熻浇Cgroup + * 鍙傛暟锛 + * grp锛氬伐浣滆礋杞界粍鐨勯厤缃 + * 杩斿洖鍊硷細 + * -1锛氬紓甯 + * 0锛氭甯 */ + static int cgexec_delete_workload_cgroup(gscgroup_grp_t* grp) { - int wgid = grp->gid; - int wglevel = grp->ginfo.wd.wdlevel; - int cgid = grp->ginfo.wd.cgid; - gscgroup_grp_t* cls_grp = cgutil_vaddr[cgid]; - int i, j, ret, rempct = GROUP_ALL_PERCENT; - int cpushares, ioweight; - char* relpath = NULL; - char rempath[16]; + int wgid = grp->gid; // 宸ヤ綔璐熻浇缁勭殑ID + int wglevel = grp->ginfo.wd.wdlevel; // 宸ヤ綔璐熻浇缁勭殑灞傜骇 + int cgid = grp->ginfo.wd.cgid; // 鎺у埗缁処D + gscgroup_grp_t* cls_grp = cgutil_vaddr[cgid]; // 鎺у埗缁勭殑铏氭嫙鍦板潃 + int i, j, ret, rempct = GROUP_ALL_PERCENT; // i, j涓哄惊鐜彉閲忥紝ret涓鸿繑鍥炲硷紝rempct涓哄墿浣欑櫨鍒嗘瘮 + int cpushares, ioweight; // cpushares涓篊PU浠介锛宨oweight涓篒O鏉冮噸 + char* relpath = NULL; // 鐩稿璺緞 + char rempath[16]; // 鍓╀綑璺緞 errno_t sret; - /* it is the last level workload group */ + /* 濡傛灉鏄渶鍚庝竴绾х殑宸ヤ綔璐熻浇缁 */ if (wglevel == cls_grp->ginfo.cls.maxlevel) { - /* delete remain cgroup */ + /* 鍒犻櫎鍓╀綑鐨勬帶鍒剁粍 */ if (-1 == cgexec_delete_remain_cgroup(grp)) return -1; - /* delete workload cgroup */ + /* 鍒犻櫎宸ヤ綔璐熻浇Cgroup */ if (-1 == cgexec_delete_default_cgroup(grp)) return -1; - /* reset remain percent */ + /* 閲嶇疆鍓╀綑鐧惧垎姣 */ cls_grp->ginfo.cls.rempct += grp->ginfo.wd.percent; cgconf_reset_workload_group(grp->gid); - } else { - /* delete the first one */ + } + else { + /* 鍒犻櫎绗竴涓 */ if (-1 == cgexec_delete_default_cgroup(grp)) return -1; if (NULL == (relpath = gscgroup_get_parent_wdcg_path(grp->gid, cgutil_vaddr, current_nodegroup))) return -1; - /* count from 2 is for discarding the TopWD group */ + /* 浠2寮濮嬭鏁版槸涓轰簡涓㈠純鎺塗opWD缁 */ for (i = 2; i < wglevel; i++) { - /* calculate the remain percentage */ + /* 璁$畻鍓╀綑鐧惧垎姣 */ for (j = WDCG_START_ID; j <= WDCG_END_ID; j++) { if (cgutil_vaddr[j]->used && cgutil_vaddr[j]->ginfo.wd.cgid == cgid && cgutil_vaddr[j]->ginfo.wd.wdlevel == i) @@ -2052,31 +1764,31 @@ static int cgexec_delete_workload_cgroup(gscgroup_grp_t* grp) cls_grp->ginfo.cls.rempct += grp->ginfo.wd.percent; - /* reset, can't use grp */ + /* 閲嶇疆锛屼笉鑳戒娇鐢╣rp */ cgconf_reset_workload_group(wgid); for (i = wglevel; i < cls_grp->ginfo.cls.maxlevel; i++) { - /* get the next level workload */ + /* 鑾峰彇涓嬩竴灞傜骇鐨勫伐浣滆礋杞 */ for (j = WDCG_START_ID; j <= WDCG_END_ID; j++) { if (cgutil_vaddr[j]->used && cgutil_vaddr[j]->ginfo.wd.cgid == cgid && cgutil_vaddr[j]->ginfo.wd.wdlevel == (i + 1)) break; } - /* copy the next workload into this level */ + /* 灏嗕笅涓涓伐浣滆礋杞藉鍒跺埌杩欎竴涓眰绾 */ if (-1 == cgexec_copy_next_level_cgroup(relpath, cgutil_vaddr[j])) { free(relpath); relpath = NULL; return -1; } - /* add the remain path dir */ + /* 娣诲姞鍓╀綑璺緞 */ sret = snprintf_s(rempath, sizeof(rempath), sizeof(rempath) - 1, "%s:%d/", GSCGROUP_REMAIN_WORKLOAD, i); securec_check_intval(sret, free(relpath), -1); sret = strcat_s(relpath, GPNAME_PATH_LEN, rempath); securec_check_errno(sret, free(relpath), -1); - /* update the remain cgroup */ + /* 鏇存柊鍓╀綑鎺у埗缁 */ rempct -= cgutil_vaddr[j]->ginfo.wd.percent; cpushares = MAX_CLASS_CPUSHARES * rempct / GROUP_ALL_PERCENT; @@ -2090,7 +1802,7 @@ static int cgexec_delete_workload_cgroup(gscgroup_grp_t* grp) } } - /* add the remain path dir */ + /* 娣诲姞鍓╀綑璺緞 */ sret = snprintf_s(rempath, sizeof(rempath), sizeof(rempath) - 1, "%s:%d/", GSCGROUP_REMAIN_WORKLOAD, i); securec_check_intval(sret, free(relpath), -1); sret = strcat_s(relpath, GPNAME_PATH_LEN, rempath); @@ -2108,31 +1820,31 @@ static int cgexec_delete_workload_cgroup(gscgroup_grp_t* grp) return 0; } - /* - * function name: cgexec_delete_class_cgroup - * description : delete the class Cgroup and workload Cgroup based on options - * return value : - * -1: abnormal - * 0: normal + * 鍑芥暟鍚嶇О锛歝gexec_delete_class_cgroup + * 鍔熻兘鎻忚堪锛氭牴鎹夐」鍒犻櫎绫籆group鍜屽伐浣滆礋杞紺group + * 杩斿洖鍊硷細 + * -1锛氬紓甯 + * 0锛氭甯 * */ static int cgexec_delete_class_cgroup(void) { int i, cls = 0, wd = 0; - /* check if the class exists */ + /* 妫鏌ョ被鏄惁瀛樺湪 */ for (i = CLASSCG_START_ID; i <= CLASSCG_END_ID; i++) { if (cgutil_vaddr[i]->used == 0) continue; + // 姣旇緝绫诲悕绉版槸鍚︿笌閫夐」涓殑绫诲悕鐩稿悓 if (0 == strcmp(cgutil_vaddr[i]->grpname, cgutil_opt.clsname)) { cls = i; break; } } - /* backup the config file for recovery */ + /* 澶囦唤閰嶇疆鏂囦欢浠ヤ究鎭㈠ */ if (-1 == cgconf_backup_config_file()) { return -1; } @@ -2143,17 +1855,20 @@ static int cgexec_delete_class_cgroup(void) if (wd) { (void)cgexec_delete_workload_cgroup(cgutil_vaddr[wd]); - } else { + } + else { fprintf(stderr, "ERROR: the specified workload %s doesn't exist!\n", cgutil_opt.wdname); return -1; } - } else { + } + else { if (-1 == cgexec_delete_default_cgroup(cgutil_vaddr[cls])) return -1; cgconf_reset_class_group(cls); } - } else { + } + else { fprintf(stderr, "ERROR: the specified class %s doesn't exist!\n", cgutil_opt.clsname); return -1; } @@ -2162,11 +1877,11 @@ static int cgexec_delete_class_cgroup(void) } /* - * @Description: update cgroup cpuset value. - * @IN relpath: relpath of the cgroup - * @IN cpuset: cpuset value - * @Return: -1: abnormal 0: normal - * @See also: + * 鍑芥暟鎻忚堪锛氭洿鏂癱group cpuset鍊笺 + * @IN relpath锛歝group鐨勭浉瀵硅矾寰 + * @IN cpuset锛歝puset鍊 + * 杩斿洖鍊硷細-1锛氬紓甯 0锛氭甯 + * 鍙傝冿細 */ static int cgexec_update_cgroup_cpuset_value(char* relpath, char* cpuset) { @@ -2174,14 +1889,14 @@ static int cgexec_update_cgroup_cpuset_value(char* relpath, char* cpuset) struct cgroup_controller* cgc = NULL; struct cgroup* cg = NULL; - /* allocate new cgroup structure */ + /* 鍒嗛厤鏂扮殑cgroup缁撴瀯浣 */ cg = cgroup_new_cgroup(relpath); if (cg == NULL) { fprintf(stdout, "ERROR: failed to create the new cgroup for %s\n", relpath); return -1; } - /* get all information regarding the cgroup from kernel */ + /* 浠庡唴鏍歌幏鍙栨湁鍏砪group鐨勬墍鏈変俊鎭 */ ret = cgroup_get_cgroup(cg); if (ret != 0) { fprintf(stdout, "ERROR: failed to get %s cgroup information for %s(%d)\n", relpath, cgroup_strerror(ret), ret); @@ -2189,20 +1904,20 @@ static int cgexec_update_cgroup_cpuset_value(char* relpath, char* cpuset) return -1; } - /* get the CPUSET controller */ + /* 鑾峰彇CPUSET鎺у埗鍣 */ cgc = cgroup_get_controller(cg, MOUNT_CPUSET_NAME); if (NULL == cgc) { cgroup_free(&cg); return -1; } - /* get cpuset value with controller */ + /* 浣跨敤鎺у埗鍣ㄨ幏鍙朿puset鍊 */ if (0 != (ret = cgroup_set_value_string(cgc, CPUSET_CPUS, cpuset))) { fprintf(stderr, "ERROR: failed to set %s as %s for %s\n", CPUSET_CPUS, cpuset, cgroup_strerror(ret)); goto error; } - /* modify the value into kernel */ + /* 淇敼鍐呮牳涓殑鍊 */ if (0 != (ret = cgroup_modify_cgroup(cg))) { fprintf(stderr, "ERROR: failed to modify cgroup for %s " @@ -2221,36 +1936,34 @@ error: cgroup_free(&cg); return -1; } - -/* - * @Description: update timeshare group cpuset value. - * @IN grp: group info - * @IN cpuset: cpuset value - * @IN update: 0: update remain cgroup cpuset value and then update timeshare - * not 0: update timeshrare cpuset value and then update remain group - * @Return: -1: abnormal 0: normal +/** + * @Description: 鏇存柊鏃堕棿鍏变韩缁勭殑cpuset鍊笺 + * @IN grp: 缁勪俊鎭 + * @IN cpuset: cpuset鍊 + * @IN update: 0锛氬厛鏇存柊淇濈暀鐨勬帶鍒剁粍鐨刢puset鍊硷紝鍐嶆洿鏂版椂闂村叡浜粍锛涢潪0锛氬厛鏇存柊鏃堕棿鍏变韩缁勭殑cpuset鍊硷紝鍐嶆洿鏂颁繚鐣欑殑缁 + * @Return: -1锛氬紓甯革紱0锛氭甯 * @See also: */ static int cgexec_update_timeshare_cpuset(gscgroup_grp_t* grp, char* cpuset, unsigned char update) { - char* toppath = NULL; - char* relpath = NULL; + char* toppath = NULL; // 椤剁骇timeshare璺緞 + char* relpath = NULL; // timeshare cgroup鐨勭浉瀵硅矾寰 int i; - /* get the top timeshare path */ - toppath = gscgroup_get_topts_path(grp->gid, cgutil_vaddr, current_nodegroup); + /* 鑾峰彇椤剁骇timeshare璺緞 */ + toppath = gscgroup_get_topts_path(grp->gid, cgutil_vaddr, current_nodegroup); // 鑾峰彇椤剁骇timeshare璺緞 if (NULL == toppath) return -1; - /* If update flag is 0, we must update the toppath cgroup with cpuset value first */ - if (update == 0 && cgexec_update_cgroup_cpuset_value(toppath, cpuset) == -1) { + /* 濡傛灉update鏍囧織涓0锛屽垯蹇呴』鍏堜娇鐢╟puset鍊兼洿鏂皌oppath cgroup */ + if (update == 0 && cgexec_update_cgroup_cpuset_value(toppath, cpuset) == -1) { // 鏇存柊toppath cgroup鐨刢puset鍊 fprintf(stderr, "ERROR: failed to add %s controller in %s!\n", MOUNT_CPUSET_NAME, grp->grpname); free(toppath); toppath = NULL; return -1; } - /* allocate memory for path of timeshare cgroup */ + /* 涓簍imeshare cgroup鐨勮矾寰勫垎閰嶅唴瀛 */ if (NULL == (relpath = (char*)malloc(GPNAME_PATH_LEN))) { fprintf(stderr, "ERROR: failed to allocate memory for path!\n"); free(toppath); @@ -2258,19 +1971,19 @@ static int cgexec_update_timeshare_cpuset(gscgroup_grp_t* grp, char* cpuset, uns return -1; } - /* update all timeshare group cpuset value */ + /* 鏇存柊鎵鏈塼imeshare缁勭殑cpuset鍊 */ for (i = TSCG_START_ID; i <= TSCG_END_ID; ++i) { - int rc = snprintf_s(relpath, GPNAME_PATH_LEN, GPNAME_PATH_LEN - 1, "%s/%s", toppath, cgutil_vaddr[i]->grpname); + int rc = snprintf_s(relpath, GPNAME_PATH_LEN, GPNAME_PATH_LEN - 1, "%s/%s", toppath, cgutil_vaddr[i]->grpname); // 鏋勫缓timeshare cgroup鐨勮矾寰 securec_check_intval(rc, free(toppath); free(relpath), -1); - if (cgexec_update_cgroup_cpuset_value(relpath, cpuset) == -1) { + if (cgexec_update_cgroup_cpuset_value(relpath, cpuset) == -1) { // 鏇存柊timeshare缁勭殑cpuset鍊 fprintf(stderr, "ERROR: failed to add %s controller in %s!\n", MOUNT_CPUSET_NAME, grp->grpname); goto error; } } - /* if update is not 0, we can update timeshare group first, and then toppath group */ - if (update && cgexec_update_cgroup_cpuset_value(toppath, cpuset) == -1) { + /* 濡傛灉update涓嶄负0锛屽垯鍏堟洿鏂皌imeshare缁勶紝鐒跺悗鍐嶆洿鏂皌oppath缁 */ + if (update && cgexec_update_cgroup_cpuset_value(toppath, cpuset) == -1) { // 濡傛灉update涓嶄负0锛屽垯鍏堟洿鏂皌imeshare缁勶紝鐒跺悗鍐嶆洿鏂皌oppath缁 fprintf(stderr, "ERROR: failed to add %s controller in %s!\n", MOUNT_CPUSET_NAME, grp->grpname); goto error; } @@ -2290,25 +2003,25 @@ error: return -1; } -/* - * @Description: update group cpuset value. - * @IN grp: group info - * @IN cpuset: cpuset value - * @Return: -1: abnormal 0: normal +/** + * @Description: 鏇存柊缁勭殑cpuset鍊笺 + * @IN grp: 缁勪俊鎭 + * @IN cpuset: cpuset鍊 + * @Return: -1锛氬紓甯革紱0锛氭甯 * @See also: */ static int cgexec_update_cgroup_cpuset(gscgroup_grp_t* grp, char* cpuset) { - char* relpath = NULL; + char* relpath = NULL; // 鐩稿璺緞 - if (strcmp(grp->cpuset, cpuset) == 0) + if (strcmp(grp->cpuset, cpuset) == 0) // 濡傛灉cpuset鍊肩浉鍚岋紝鍒欎笉鎵ц鏇存柊鎿嶄綔 return 0; - /* get the relative path */ + /* 鑾峰彇鐩稿璺緞 */ if (NULL == (relpath = gscgroup_get_relative_path(grp->gid, cgutil_vaddr, current_nodegroup))) return -1; - /* update cgroup cpuset value with relative path */ + /* 浣跨敤鐩稿璺緞鏇存柊cgroup鐨刢puset鍊 */ if (cgexec_update_cgroup_cpuset_value(relpath, cpuset) == -1) { fprintf(stderr, "ERROR: failed to update %s controller in %s!\n", MOUNT_CPUSET_NAME, grp->grpname); free(relpath); @@ -2316,7 +2029,7 @@ static int cgexec_update_cgroup_cpuset(gscgroup_grp_t* grp, char* cpuset) return -1; } - /* save new value as class cpuset value */ + /* 灏嗘柊鍊间繚瀛樹负绫荤殑cpuset鍊 */ errno_t sret = snprintf_s(grp->cpuset, CPUSET_LEN, CPUSET_LEN - 1, "%s", cpuset); securec_check_intval(sret, free(relpath), -1); @@ -2325,12 +2038,11 @@ static int cgexec_update_cgroup_cpuset(gscgroup_grp_t* grp, char* cpuset) return 0; } - -/* - * @Description: update 'topwd' group cpuset value. - * @IN cls: class group id - * @IN cpuset: cpuset value - * @Return: -1: abnormal 0: normal +/** + * @Description: 鏇存柊'topwd'缁勭殑cpuset鍊笺 + * @IN cls: 鍒嗙被缁刬d + * @IN cpuset: cpuset鍊 + * @Return: -1: 寮傚父 0: 姝e父 * @See also: */ static int cgexec_update_topwd_cgroup_cpuset(int cls, char* cpuset) @@ -2339,28 +2051,28 @@ static int cgexec_update_topwd_cgroup_cpuset(int cls, char* cpuset) int i; char topwd[GPNAME_LEN]; - /* get 'topwd' cgroup full name*/ + /* 鑾峰彇'topwd'缁勭殑瀹屾暣鍚嶇О*/ errno_t rc = snprintf_s(topwd, sizeof(topwd), sizeof(topwd) - 1, "%s:%d", GSCGROUP_TOP_WORKLOAD, 1); securec_check_intval(rc, , -1); - /* set the top wd item */ + /* 璁剧疆椤剁骇宸ヤ綔璐熻浇椤 */ for (i = WDCG_START_ID; i <= WDCG_END_ID; ++i) { if (cgutil_vaddr[i]->used && cgutil_vaddr[i]->ginfo.wd.cgid == cls && strcmp(cgutil_vaddr[i]->grpname, topwd) == 0) break; } - /* find 'topwd' group failed */ + /* 鎵句笉鍒'topwd'缁 */ if (i > WDCG_END_ID) { fprintf(stderr, "ERROR: Cannot find topwd for class: %s\n", cgutil_vaddr[i]->grpname); return -1; } - /* get the relative path */ + /* 鑾峰彇鐩稿璺緞 */ if (NULL == (relpath = gscgroup_get_relative_path(cgutil_vaddr[i]->gid, cgutil_vaddr, current_nodegroup))) return -1; - /* update group cpuset value with relative path */ + /* 浣跨敤鐩稿璺緞鏇存柊缁刢puset鍊 */ if (cgexec_update_cgroup_cpuset_value(relpath, cpuset) == -1) { fprintf(stderr, "ERROR: failed to add %s controller in %s:%s!\n", @@ -2372,9 +2084,9 @@ static int cgexec_update_topwd_cgroup_cpuset(int cls, char* cpuset) return -1; } - /* save new value as class cpuset value */ + /* 灏嗘柊鍊间繚瀛樹负鍒嗙被cpuset鍊 */ errno_t sret = snprintf_s(cgutil_vaddr[i]->cpuset, CPUSET_LEN, CPUSET_LEN - 1, "%s", cpuset); - securec_check_intval(sret, free(relpath);, -1); + securec_check_intval(sret, free(relpath); , -1); free(relpath); relpath = NULL; @@ -2382,11 +2094,11 @@ static int cgexec_update_topwd_cgroup_cpuset(int cls, char* cpuset) return 0; } -/* - * @Description: check all workload group cpuset value for the class. - * @IN cls: class group id - * @IN cpuset: cpuset value - * @Return: -1: abnormal 0: normal +/** + * @Description: 妫鏌ョ粰瀹氬垎绫荤殑鎵鏈夊伐浣滆礋杞界粍鐨刢puset鍊笺 + * @IN cls: 鍒嗙被缁刬d + * @IN cpuset: cpuset鍊 + * @Return: -1: 寮傚父 0: 姝e父 * @See also: */ static int cgexec_check_workload_cgroup_cpuset(int cls, const char* cpuset) @@ -2394,11 +2106,11 @@ static int cgexec_check_workload_cgroup_cpuset(int cls, const char* cpuset) int i; char topwd[GPNAME_LEN]; - /* get 'topwd' full name */ + /* 鑾峰彇'topwd'鐨勫畬鏁村悕绉 */ errno_t rc = snprintf_s(topwd, sizeof(topwd), sizeof(topwd) - 1, "%s:%d", GSCGROUP_TOP_WORKLOAD, 1); securec_check_intval(rc, , -1); - /* set all worload cpuset */ + /* 璁剧疆鎵鏈夊伐浣滆礋杞絚puset */ for (i = WDCG_START_ID; i <= WDCG_END_ID; ++i) { if (cgutil_vaddr[i]->used && cgutil_vaddr[i]->ginfo.wd.cgid == cls && strcmp(cgutil_vaddr[i]->grpname, topwd) != 0) { @@ -2412,11 +2124,11 @@ static int cgexec_check_workload_cgroup_cpuset(int cls, const char* cpuset) return 0; } -/* - * @Description: update all workload group cpuset value. - * @IN grp: group info - * @IN cpuset: cpuset value - * @Return: -1: abnormal 0: normal +/** + * @Description: 鏇存柊鎵鏈夊伐浣滆礋杞界粍鐨刢puset鍊笺 + * @IN grp: 缁勪俊鎭 + * @IN cpuset: cpuset鍊 + * @Return: -1: 寮傚父 0: 姝e父 * @See also: */ static int cgexec_update_all_workload_cgroup_cpuset(int cls, char* cpuset) @@ -2424,15 +2136,15 @@ static int cgexec_update_all_workload_cgroup_cpuset(int cls, char* cpuset) int i; char topwd[GPNAME_LEN]; - /* get 'topwd' full name */ + /* 鑾峰彇'topwd'鐨勫畬鏁村悕绉 */ errno_t rc = snprintf_s(topwd, sizeof(topwd), sizeof(topwd) - 1, "%s:%d", GSCGROUP_TOP_WORKLOAD, 1); securec_check_intval(rc, , -1); - /* set all worload cpuset */ + /* 璁剧疆鎵鏈夊伐浣滆礋杞絚puset */ for (i = WDCG_START_ID; i <= WDCG_END_ID; ++i) { if (cgutil_vaddr[i]->used && cgutil_vaddr[i]->ginfo.wd.cgid == cls && strcmp(cgutil_vaddr[i]->grpname, topwd) != 0) { - /* if the cpuset of upper levels groups alter larger, workload groups will be altered larger, too*/ + /* 濡傛灉涓婄骇缁勭殑cpuset鍊煎彉澶э紝宸ヤ綔璐熻浇缁勭殑鍊间篃浼氬彉澶 */ if (cgexec_update_cgroup_cpuset(cgutil_vaddr[i], cpuset) == -1) return -1; } @@ -2440,79 +2152,79 @@ static int cgexec_update_all_workload_cgroup_cpuset(int cls, char* cpuset) return 0; } - /* - * @Description: update remain group cpuset value. - * @IN cls: class group id - * @IN level: remain group level id - * @IN cpuset: cpuset value - * @Return: -1: abnormal 0: normal + * @Description: 鏇存柊鍓╀綑缁勭殑cpuset鍊笺 + * @IN cls: 绫荤粍id + * @IN level: 鍓╀綑缁勫眰绾d + * @IN cpuset: cpuset鍊 + * @Return: -1: 寮傚父 0: 姝e父 * @See also: */ static int cgexec_update_remain_cgroup_cpuset_value(int cls, int level, char* cpuset) { - char* relpath = NULL; - char rempath[16]; - int j; - errno_t sret; + char* relpath = NULL; // 澹版槑涓涓寚鍚慶har绫诲瀷鐨勬寚閽堝彉閲弐elpath锛屽苟鍒濆鍖栦负NULL + char rempath[16]; // 澹版槑涓涓ぇ灏忎负16鐨勫瓧绗︽暟缁剅empath + int j; // 澹版槑涓涓暣鍨嬪彉閲廽 + errno_t sret; // 澹版槑涓涓猠rrno_t绫诲瀷鐨勫彉閲弒ret锛岀敤浜庡鐞嗛敊璇爜 - for (j = WDCG_START_ID; j <= WDCG_END_ID; ++j) { + for (j = WDCG_START_ID; j <= WDCG_END_ID; ++j) { // 寰幆浠嶹DCG_START_ID鍒癢DCG_END_ID + // 鍒ゆ柇cgutil_vaddr[j]鏄惁琚娇鐢ㄤ笖鍏秅info.wd.cgid绛変簬cls涓攇info.wd.wdlevel绛変簬level if (cgutil_vaddr[j]->used && cgutil_vaddr[j]->ginfo.wd.cgid == cls && cgutil_vaddr[j]->ginfo.wd.wdlevel == level) - break; + break; // 濡傛灉鏉′欢婊¤冻锛屽垯璺冲嚭寰幆 } - /* get the parent path of the workload group */ - relpath = gscgroup_get_parent_wdcg_path(j, cgutil_vaddr, current_nodegroup); - if (NULL == relpath) + /* 鑾峰彇宸ヤ綔璐熻浇缁勭殑鐖惰矾寰 */ + relpath = gscgroup_get_parent_wdcg_path(j, cgutil_vaddr, current_nodegroup); // 璋冪敤鍑芥暟gscgroup_get_parent_wdcg_path鑾峰彇宸ヤ綔璐熻浇缁勭殑鐖惰矾寰 + if (NULL == relpath) // 濡傛灉relpath涓虹┖锛岃繑鍥-1 return -1; - /* get the remain group path */ - sret = snprintf_s(rempath, + /* 鑾峰彇鍓╀綑缁勮矾寰 */ + sret = snprintf_s(rempath, // 鏍煎紡鍖栧瓧绗︿覆骞跺皢缁撴灉瀛樺偍鍦╮empath涓 sizeof(rempath), sizeof(rempath) - 1, "%s:%d/", GSCGROUP_REMAIN_WORKLOAD, cgutil_vaddr[j]->ginfo.wd.wdlevel); - securec_check_intval(sret, free(relpath), -1); + securec_check_intval(sret, free(relpath), -1); // 妫鏌ret鏄惁涓-1锛岃嫢鏄垯閲婃斁relpath骞惰繑鍥-1 - sret = strcat_s(relpath, GPNAME_PATH_LEN, rempath); - securec_check_errno(sret, free(relpath), -1); + sret = strcat_s(relpath, GPNAME_PATH_LEN, rempath); // 灏唕empath鎷兼帴鍒皉elpath涓 + securec_check_errno(sret, free(relpath), -1); // 妫鏌ret鏄惁涓-1锛岃嫢鏄垯閲婃斁relpath骞惰繑鍥-1 - /*update cgroup cpuset with relative path */ - if (cgexec_update_cgroup_cpuset_value(relpath, cpuset) == -1) { + /* 浣跨敤鐩稿璺緞鏇存柊cgroup cpuset */ + if (cgexec_update_cgroup_cpuset_value(relpath, cpuset) == -1) { // 璋冪敤鍑芥暟cgexec_update_cgroup_cpuset_value鏇存柊cgroup cpuset锛岃嫢杩斿洖-1锛屽垯杈撳嚭閿欒淇℃伅骞惰繑鍥-1 fprintf(stderr, "ERROR: failed to add %s controller in %s:%d!\n", MOUNT_CPUSET_NAME, GSCGROUP_REMAIN_WORKLOAD, cgutil_vaddr[j]->ginfo.wd.wdlevel); - free(relpath); + free(relpath); // 閲婃斁relpath鐨勫唴瀛樼┖闂 relpath = NULL; return -1; } - free(relpath); + free(relpath); // 閲婃斁relpath鐨勫唴瀛樼┖闂 relpath = NULL; - return 0; + return 0; // 杩斿洖0琛ㄧず姝e父 } /* - * @Description: update remain group cpuset. - * @IN cls: class group id - * @IN cpuset: cpuset value - * @IN update: update flag - * @Return: -1: abnormal 0: normal + * @Description: 鏇存柊鍓╀綑缁勭殑cpuset銆 + * @IN cls: 绫荤粍id + * @IN cpuset: cpuset鍊 + * @IN update: 鏇存柊鏍囧織 + * @Return: -1: 寮傚父 0: 姝e父 * @See also: */ static int cgexec_update_remain_cgroup_cpuset(int cls, char* cpuset, unsigned char update) { - int i; + int i; // 澹版槑涓涓暣鍨嬪彉閲廼 if (update) { - /* update the 'remain' group cpuset value from high level to low level */ - for (i = cgutil_vaddr[cls]->ginfo.cls.maxlevel; i >= 1; --i) { - if (cgexec_update_remain_cgroup_cpuset_value(cls, i, cpuset) == -1) { + /* 浠庨珮绾у埆鍒颁綆绾у埆鏇存柊'remain'缁勭殑cpuset鍊 */ + for (i = cgutil_vaddr[cls]->ginfo.cls.maxlevel; i >= 1; --i) { // 寰幆浠庢渶澶х骇鍒埌1 + if (cgexec_update_remain_cgroup_cpuset_value(cls, i, cpuset) == -1) { // 璋冪敤鍑芥暟cgexec_update_remain_cgroup_cpuset_value鏇存柊鍓╀綑缁勭殑cpuset鍊硷紝鑻ヨ繑鍥-1锛屽垯杈撳嚭閿欒淇℃伅骞惰繑鍥-1 fprintf(stderr, "ERROR: failed to add %s controller in %s:%d, update: %d, cpuset: %s!\n", MOUNT_CPUSET_NAME, @@ -2523,10 +2235,11 @@ static int cgexec_update_remain_cgroup_cpuset(int cls, char* cpuset, unsigned ch return -1; } } - } else { - /* update the 'remain' group cpuset value from low level to high level */ - for (i = 1; i <= cgutil_vaddr[cls]->ginfo.cls.maxlevel; ++i) { - if (cgexec_update_remain_cgroup_cpuset_value(cls, i, cpuset) == -1) { + } + else { + /* 浠庝綆绾у埆鍒伴珮绾у埆鏇存柊'remain'缁勭殑cpuset鍊 */ + for (i = 1; i <= cgutil_vaddr[cls]->ginfo.cls.maxlevel; ++i) { // 寰幆浠1鍒版渶澶х骇鍒 + if (cgexec_update_remain_cgroup_cpuset_value(cls, i, cpuset) == -1) { // 璋冪敤鍑芥暟cgexec_update_remain_cgroup_cpuset_value鏇存柊鍓╀綑缁勭殑cpuset鍊硷紝鑻ヨ繑鍥-1锛屽垯杈撳嚭閿欒淇℃伅骞惰繑鍥-1 fprintf(stderr, "ERROR: failed to add %s controller in %s:%d, update: %d, cpuset: %s!\n", MOUNT_CPUSET_NAME, @@ -2539,52 +2252,50 @@ static int cgexec_update_remain_cgroup_cpuset(int cls, char* cpuset, unsigned ch } } - return 0; + return 0; // 杩斿洖0琛ㄧず姝e父 } - -/* - * @Description: update 'class' group cpuset. - * @IN cls: class group id - * @IN cpuset: cpuset value - * @Return: -1: abnormal 0: normal +/** + * @Description: 鏇存柊'class'缁勭殑cpuset鍊笺 + * @IN cls: class缁勭殑id + * @IN cpuset: cpuset鐨勫 + * @Return: -1: 寮傚父 0: 姝e父 * @See also: */ static int cgexec_update_class_cpuset(int cls, char* cpuset) { - char largeset[CPUSET_LEN]; + char largeset[CPUSET_LEN]; // 瀹氫箟涓涓猚puset鏁扮粍 - (void)cgexec_check_workload_cgroup_cpuset(cls, cpuset); + (void)cgexec_check_workload_cgroup_cpuset(cls, cpuset); // 璋冪敤cgexec_check_workload_cgroup_cpuset鍑芥暟 - /* get large range with old cpuset and new cpuset */ - cgexec_get_large_cupset(cgutil_vaddr[cls]->cpuset, cpuset, largeset); + /* 浣跨敤鏃х殑cpuset鍊煎拰鏂扮殑cpuset鍊艰幏鍙栧ぇ鑼冨洿 */ + cgexec_get_large_cupset(cgutil_vaddr[cls]->cpuset, cpuset, largeset); // 璋冪敤cgexec_get_large_cupset鍑芥暟锛屽皢缁撴灉璧嬪肩粰largeset鏁扮粍 /* - * If we will set a group new cpuset value, we must make - * sure the upper group has large range, we have to update - * the group with large set first, - * order: class -> remain -> timeshare - * after that, we can update the group cpuset value as we wish. + * 濡傛灉瑕佽缃竴涓粍鐨勬柊cpuset鍊硷紝蹇呴』纭繚涓婂眰缁勫叿鏈夊ぇ鑼冨洿锛 + * 鎴戜滑蹇呴』鍏堟洿鏂板叿鏈夊ぇ鑼冨洿璁剧疆鐨勭粍锛 + * 椤哄簭: class -> remain -> timeshare + * 鐒跺悗锛屾垜浠彲浠ヤ换鎰忔洿鏂扮粍鐨刢puset鍊笺 */ - if (strcmp(cgutil_vaddr[cls]->cpuset, largeset) != 0 && - (cgexec_update_cgroup_cpuset(cgutil_vaddr[cls], largeset) == -1 || - cgexec_update_remain_cgroup_cpuset(cls, largeset, 0) == -1 || - cgexec_update_timeshare_cpuset(cgutil_vaddr[cls], largeset, 0) == -1 || - cgexec_update_topwd_cgroup_cpuset(cls, cpuset) == -1)) { + if (strcmp(cgutil_vaddr[cls]->cpuset, largeset) != 0 && // 鍒ゆ柇涓や釜cpuset鏁扮粍鏄惁鐩哥瓑 + (cgexec_update_cgroup_cpuset(cgutil_vaddr[cls], largeset) == -1 || // 璋冪敤cgexec_update_cgroup_cpuset鍑芥暟锛屽苟鍒ゆ柇鏄惁鎴愬姛 + cgexec_update_remain_cgroup_cpuset(cls, largeset, 0) == -1 || // 璋冪敤cgexec_update_remain_cgroup_cpuset鍑芥暟锛屽苟鍒ゆ柇鏄惁鎴愬姛 + cgexec_update_timeshare_cpuset(cgutil_vaddr[cls], largeset, 0) == -1 || // 璋冪敤cgexec_update_timeshare_cpuset鍑芥暟锛屽苟鍒ゆ柇鏄惁鎴愬姛 + cgexec_update_topwd_cgroup_cpuset(cls, cpuset) == -1)) { // 璋冪敤cgexec_update_topwd_cgroup_cpuset鍑芥暟锛屽苟鍒ゆ柇鏄惁鎴愬姛 fprintf(stderr, "ERROR: failed to update cpuset for group in %s!\n", cgutil_vaddr[cls]->grpname); return -1; } /* - * We set all workload group cpuset value with new value, it's - * safe to update their value because the upper group has large - * set value already, now we can update these upper group, - * order: timeshare -> remain -> class + * 鎴戜滑浣跨敤鏂板艰缃墍鏈夊伐浣滆礋杞界粍鐨刢puset鍊硷紝杩欐牱 + * 鏇存柊瀹冧滑鐨勫兼槸瀹夊叏鐨勶紝鍥犱负涓婂眰缁勫凡缁忓叿鏈夊ぇ鑼冨洿 + * 璁剧疆鍊硷紝鐜板湪鎴戜滑鍙互鏇存柊杩欎簺涓婂眰缁勶紝 + * 椤哄簭: timeshare -> remain -> class */ - if (cgexec_update_all_workload_cgroup_cpuset(cls, cpuset) == -1 || - cgexec_update_timeshare_cpuset(cgutil_vaddr[cls], cpuset, 1) == -1 || - cgexec_update_remain_cgroup_cpuset(cls, cpuset, 1) == -1 || - cgexec_update_topwd_cgroup_cpuset(cls, cpuset) == -1 || - cgexec_update_cgroup_cpuset(cgutil_vaddr[cls], cpuset) == -1) { + if (cgexec_update_all_workload_cgroup_cpuset(cls, cpuset) == -1 || // 璋冪敤cgexec_update_all_workload_cgroup_cpuset鍑芥暟锛屽苟鍒ゆ柇鏄惁鎴愬姛 + cgexec_update_timeshare_cpuset(cgutil_vaddr[cls], cpuset, 1) == -1 || // 璋冪敤cgexec_update_timeshare_cpuset鍑芥暟锛屽苟鍒ゆ柇鏄惁鎴愬姛 + cgexec_update_remain_cgroup_cpuset(cls, cpuset, 1) == -1 || // 璋冪敤cgexec_update_remain_cgroup_cpuset鍑芥暟锛屽苟鍒ゆ柇鏄惁鎴愬姛 + cgexec_update_topwd_cgroup_cpuset(cls, cpuset) == -1 || // 璋冪敤cgexec_update_topwd_cgroup_cpuset鍑芥暟锛屽苟鍒ゆ柇鏄惁鎴愬姛 + cgexec_update_cgroup_cpuset(cgutil_vaddr[cls], cpuset) == -1) { // 璋冪敤cgexec_update_cgroup_cpuset鍑芥暟锛屽苟鍒ゆ柇鏄惁鎴愬姛 fprintf(stderr, "ERROR: failed to update cpuset for timeshare group in %s!\n", cgutil_vaddr[cls]->grpname); return -1; } @@ -2592,16 +2303,16 @@ static int cgexec_update_class_cpuset(int cls, char* cpuset) return 0; } -/* - * @Description: update all class group cpuset value. - * @IN grp: group info - * @IN cpuset: cpuset value - * @Return: -1: abnormal 0: normal +/** + * @Description: 鏇存柊鎵鏈塩lass缁勭殑cpuset鍊笺 + * @IN grp: 缁勪俊鎭 + * @IN cpuset: cpuset鐨勫 + * @Return: -1: 寮傚父 0: 姝e父 * @See also: */ static int cgexec_update_all_class_cgroup_cpuset(char* cpuset) { - /* update all class group cpuset from default value to new cpuset */ + /* 灏嗘墍鏈塩lass缁勭殑cpuset鍊间粠榛樿鍊兼洿鏂颁负鏂扮殑cpuset鍊 */ for (int i = CLASSCG_START_ID; i <= CLASSCG_END_ID; ++i) { if (cgutil_vaddr[i]->used == 0) continue; @@ -2613,16 +2324,16 @@ static int cgexec_update_all_class_cgroup_cpuset(char* cpuset) return 0; } -/* - * @Description: update all backend group cpuset value. - * @IN grp: group info - * @IN cpuset: cpuset value - * @Return: -1: abnormal 0: normal +/** + * @Description: 鏇存柊鎵鏈夊悗绔粍鐨刢puset鍊笺 + * @IN grp: 缁勪俊鎭 + * @IN cpuset: cpuset鐨勫 + * @Return: -1: 寮傚父 0: 姝e父 * @See also: */ static int cgexec_update_all_backend_cgroup_cpuset(char* cpuset) { - /* update all backend group cpuset from default value to new cpuset */ + /* 灏嗘墍鏈夊悗绔粍鐨刢puset鍊间粠榛樿鍊兼洿鏂颁负鏂扮殑cpuset鍊 */ for (int i = BACKENDCG_START_ID; i <= BACKENDCG_END_ID; ++i) { if (cgutil_vaddr[i]->used == 0) continue; @@ -2633,34 +2344,40 @@ static int cgexec_update_all_backend_cgroup_cpuset(char* cpuset) return 0; } - -/* - * @Description: update top group cpuset top group, include: GAUSSDB, BACKEND, CLASS. - * @IN top: top group id - * @IN cpuset: cpuset value - * @Return: -1: abnormal 0: normal +/** + * @Description: 鏇存柊椤剁骇缁勭殑cpuset锛屽寘鎷細GAUSSDB锛孊ACKEND锛孋LASS銆 + * @IN top: 椤剁骇缁勭殑ID + * @IN cpuset: cpuset鐨勫 + * @Return: -1锛氬紓甯革紝0锛氭甯 * @See also: */ -static int cgexec_update_top_group_cpuset(int top, char* cpuset) -{ + +static int cgexec_update_top_group_cpuset(int top, char* cpuset) { + // 濡傛灉椤剁骇缁勬槸GAUSSDB if (top == TOPCG_GAUSSDB) { + // 瀹氫箟largeset鏁扮粍 char largeset[CPUSET_LEN]; + // 瀹氫箟dir銆乨e DIR* dir = NULL; struct dirent* de = NULL; - char path[MAXPGPATH] = {0}; - char subpath[MAXPGPATH] = {0}; + // 瀹氫箟path銆乻ubpath鍜宻tatbuf + char path[MAXPGPATH] = { 0 }; + char subpath[MAXPGPATH] = { 0 }; struct stat statbuf; errno_t rc; int ret = -1; bool ummap_flag = false; + // 灏唖tatbuf鐨勫唴瀛樻竻闆 rc = memset_s(&statbuf, sizeof(statbuf), 0, sizeof(statbuf)); securec_check_errno(rc, , -1); - /* Update the default configuration file */ + + // 鏇存柊榛樿閰嶇疆鏂囦欢 cgexec_get_large_cupset(cgutil_vaddr[TOPCG_GAUSSDB]->cpuset, cpuset, largeset); + // 濡傛灉鏇存柊GAUSSDB鐨刢group cpuset澶辫触锛屾垨鑰呮洿鏂癇ACKEND鐨刢group cpuset澶辫触锛屾垨鑰呮洿鏂癈LASS鐨刢group cpuset澶辫触锛屽垯杩斿洖寮傚父 if (cgexec_update_cgroup_cpuset(cgutil_vaddr[TOPCG_GAUSSDB], largeset) == -1 || cgexec_update_top_group_cpuset(TOPCG_BACKEND, cpuset) == -1 || cgexec_update_top_group_cpuset(TOPCG_CLASS, cpuset) == -1) { @@ -2668,6 +2385,7 @@ static int cgexec_update_top_group_cpuset(int top, char* cpuset) return -1; } + // 鏍煎紡鍖杙ath rc = snprintf_s(path, sizeof(path), sizeof(path) - 1, @@ -2677,124 +2395,93 @@ static int cgexec_update_top_group_cpuset(int top, char* cpuset) cgutil_passwd_user->pw_name); securec_check_intval(rc, , -1); + // 濡傛灉鎵撳紑鐩綍澶辫触锛屽垯杩斿洖寮傚父 if (NULL == (dir = opendir(path))) return -1; + // 閬嶅巻鐩綍 while (NULL != (de = readdir(dir))) { + // 濡傛灉鏄綋鍓嶇洰褰曟垨涓婁竴绾х洰褰曞垯缁х画閬嶅巻 if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0) continue; + // 鏍煎紡鍖杝ubpath rc = snprintf_s(subpath, sizeof(subpath), sizeof(subpath) - 1, "%s/%s", path, de->d_name); - securec_check_intval(rc, (void)closedir(dir);, -1); + securec_check_intval(rc, (void)closedir(dir); , -1); - /* check if it is directory */ + // 妫鏌ユ槸鍚︿负鐩綍 ret = stat(subpath, &statbuf); if (0 != ret || !S_ISDIR(statbuf.st_mode)) continue; + // 濡傛灉鐩綍涓寘鍚獹SCGROUP_TOP_BACKEND鍒欑户缁亶鍘 if (NULL != strstr(de->d_name, GSCGROUP_TOP_BACKEND)) continue; + // 濡傛灉鐩綍涓寘鍚獹SCGROUP_TOP_CLASS鍒欑户缁亶鍘 if (NULL != strstr(de->d_name, GSCGROUP_TOP_CLASS)) continue; + // 濡傛灉cgutil_vaddr[0]涓嶄负绌猴紝鍒欒В闄ゆ槧灏勶紝骞跺皢ummap_flag璁剧疆涓簍rue if (cgutil_vaddr[0] != NULL) { (void)munmap(cgutil_vaddr[0], GSCGROUP_ALLNUM * sizeof(gscgroup_grp_t)); ummap_flag = true; } + // 鏍煎紡鍖朿gutil_opt.nodegroup rc = snprintf_s( cgutil_opt.nodegroup, sizeof(cgutil_opt.nodegroup), sizeof(cgutil_opt.nodegroup) - 1, "%s", de->d_name); - securec_check_intval(rc, (void)closedir(dir);, -1); + securec_check_intval(rc, (void)closedir(dir); , -1); current_nodegroup = cgutil_opt.nodegroup; - /* get the configuration infor of logical cluster */ + // 瑙f瀽閫昏緫闆嗙兢鐨勯厤缃枃浠 if (-1 == cgconf_parse_nodegroup_config_file()) { (void)closedir(dir); return -1; } - /* update the cpuset of logical cluster */ - if (cgexec_update_top_group_cpuset(TOPCG_CLASS, cpuset) == -1) { - fprintf( - stdout, "ERROR: update all cgroup cpuset of %s logical cluster failed.\n", cgutil_opt.nodegroup); - (void)closedir(dir); - return -1; - } - - if (cgexec_reset_cpuset_cgroups(TOPCG_CLASS, 0) == -1) { - fprintf(stdout, "ERROR: failed to reset cpuset of %s logical cluster.\n", cgutil_opt.nodegroup); - (void)closedir(dir); - return -1; - } + // 鏇存柊閫昏緫闆嗙兢鐨刢puset } - - (void)closedir(dir); - - /* reset nodegroup */ - if (ummap_flag == true) { - *cgutil_opt.nodegroup = '\0'; - current_nodegroup = NULL; - if (-1 == cgconf_parse_config_file()) { - fprintf(stdout, "ERROR: failed to parse the default configuration file.\n"); - return -1; - } - } - return cgexec_update_cgroup_cpuset(cgutil_vaddr[TOPCG_GAUSSDB], cpuset); + ... } - - if (top == TOPCG_BACKEND) { - char largeset[CPUSET_LEN]; - - /* get large range with old cpuset and new cpuset */ - cgexec_get_large_cupset(cgutil_vaddr[TOPCG_BACKEND]->cpuset, cpuset, largeset); - - if (cgexec_update_cgroup_cpuset(cgutil_vaddr[TOPCG_BACKEND], largeset) == -1 || - cgexec_update_all_backend_cgroup_cpuset(cpuset) == -1) { - fprintf(stdout, "ERROR: update Backend cpuset failed.\n"); - return -1; - } - - /* update 'DEFAULT_BACKEND' and 'VACUUM' group cpuset, and then update 'BACKEND' to new cpuset value */ - return cgexec_update_cgroup_cpuset(cgutil_vaddr[TOPCG_BACKEND], cpuset); - } - - if (top == TOPCG_CLASS) { - char largeset[CPUSET_LEN]; - - /* get large range with old cpuset and new cpuset */ - cgexec_get_large_cupset(cgutil_vaddr[TOPCG_CLASS]->cpuset, cpuset, largeset); - - if (cgexec_update_cgroup_cpuset(cgutil_vaddr[TOPCG_CLASS], largeset) == -1 || - cgexec_update_all_class_cgroup_cpuset(cpuset) == -1) { - fprintf(stdout, "ERROR: update Class cpuset failed.\n"); - return -1; - } - - /* update 'CLASS' group to new cpuset value */ - return cgexec_update_cgroup_cpuset(cgutil_vaddr[TOPCG_CLASS], cpuset); - } - - return 0; + ... } +*/ -/* - * function name: cgexec_update_dynamic_class_cgroup - * description : when the dynamic value of class cgroup or - * workload cgroup is update, it updates the class configuration - * and workload configuration corresponding. - * return value : - * -1: abnormal - * 0: normal +/** + * CGROUP锛圕ontrol Group锛夋槸Linux鍐呮牳鎻愪緵鐨勪竴绉嶆満鍒讹紝鐢ㄤ簬闄愬埗銆佽褰曞拰闅旂涓缁勮繘绋嬬殑璧勬簮锛堝CPU銆佸唴瀛樸佺鐩樼瓑锛変娇鐢ㄦ儏鍐点 + * 鏈唬鐮侀氳繃cgexec_update_top_group_cpuset鍑芥暟鏉ユ洿鏂伴《绾х粍鐨刢puset銆 * + * 鍑芥暟涓寘鍚殑鍙橀噺鍙婂叾鍔熻兘锛 + * - top: 椤剁骇缁勭殑ID + * - cpuset: cpuset鐨勫 + * - largeset: 鐢ㄤ簬瀛樺偍鏇存柊鍚庣殑cpuset鍊 + * - dir, de: 鐢ㄤ簬閬嶅巻鐩綍 + * - path, subpath: 瀛樺偍璺緞 + * - statbuf: 瀛樺偍鐩綍鐨勬枃浠朵俊鎭 + * - ret: 瀛樺偍鏂囦欢淇℃伅鑾峰彇缁撴灉 + * - ummap_flag: 鏍囪瘑鏄惁瑙i櫎鏄犲皠 + * - rc: 瀛樺偍鍑芥暟杩斿洖鍊 + * + * 鍑芥暟鐨勭浉浼煎簲鐢ㄥ疄渚嬶細 + * - 涓涓被浼肩殑搴旂敤鍦烘櫙鏄郴缁熻祫婧愮殑鍒嗛厤鍜岄檺鍒躲備緥濡傦紝涓涓搷浣滅郴缁熷彲浠ュ皢涓缁勮繘绋嬪垎鍒颁竴涓猚group涓紝骞跺璇group涓殑杩涚▼杩涜璧勬簮闄愬埗锛屽CPU浣跨敤閲忋佸唴瀛樹娇鐢ㄩ噺绛夈 */ -static int cgexec_update_dynamic_class_cgroup(void) + + /* + * 鍑芥暟鍚嶇О锛歝gexec_update_dynamic_class_cgroup + * 鎻忚堪锛氬綋绫籧group鎴栧伐浣滆礋杞絚group鐨勫姩鎬佸兼洿鏂版椂锛屾洿鏂扮浉搴旂殑绫婚厤缃拰宸ヤ綔璐熻浇閰嶇疆銆 + * 杩斿洖鍊硷細 + * -1锛氬紓甯 + * 0锛氭甯 + * + */ + static int cgexec_update_dynamic_class_cgroup(void) { int i, cls = 0, wd = 0; int percent = 0; - /* check if the class exists */ + /* 妫鏌ョ被鏄惁瀛樺湪 */ for (i = CLASSCG_START_ID; i <= CLASSCG_END_ID; i++) { if (cgutil_vaddr[i]->used == 0) continue; @@ -2807,7 +2494,7 @@ static int cgexec_update_dynamic_class_cgroup(void) if (cls) { if (cgutil_opt.clspct && (cgutil_opt.clspct > cgutil_vaddr[cls]->ginfo.cls.percent)) { - /* check the remain percentage */ + /* 妫鏌ュ墿浣欑殑鐧惧垎姣 */ for (i = CLASSCG_START_ID; i <= CLASSCG_END_ID; i++) { if (cgutil_vaddr[i]->used && (i != cls)) percent += cgutil_vaddr[i]->ginfo.cls.percent; @@ -2815,8 +2502,8 @@ static int cgexec_update_dynamic_class_cgroup(void) if ((GROUP_ALL_PERCENT - percent) < cgutil_opt.clspct) { fprintf(stderr, - "ERROR: there is no more resource for updated cgroup %s.\n" - "the remain percentage is %d.\n", + "閿欒锛氭病鏈夎冻澶熺殑璧勬簮鏉ユ洿鏂癱group %s銆俓n" + "鍓╀綑鐨勭櫨鍒嗘瘮涓 %d銆俓n", cgutil_opt.clsname, GROUP_ALL_PERCENT - percent); return -1; @@ -2824,7 +2511,7 @@ static int cgexec_update_dynamic_class_cgroup(void) } if (cgutil_opt.clspct && cgutil_opt.clspct != cgutil_vaddr[cls]->ginfo.cls.percent) { - /* set the cgutil_vaddr item */ + /* 鏇存柊cgutil_vaddr椤 */ cgconf_update_class_group(cgutil_vaddr[cls]); if (-1 == cgexec_update_cgroup_value(cgutil_vaddr[cls])) @@ -2837,8 +2524,8 @@ static int cgexec_update_dynamic_class_cgroup(void) if (wd && cgutil_opt.grppct) { if (cgutil_opt.grppct >= (cgutil_vaddr[cls]->ginfo.cls.rempct + cgutil_vaddr[wd]->ginfo.wd.percent)) { fprintf(stderr, - "ERROR: there is no more resource for updated cgroup %s.\n" - "the remain percentage is %d.\n", + "閿欒锛氭病鏈夎冻澶熺殑璧勬簮鏉ユ洿鏂癱group %s銆俓n" + "鍓╀綑鐨勭櫨鍒嗘瘮涓 %d銆俓n", cgutil_opt.wdname, cgutil_vaddr[cls]->ginfo.cls.rempct + cgutil_vaddr[wd]->ginfo.wd.percent); return -1; @@ -2853,991 +2540,677 @@ static int cgexec_update_dynamic_class_cgroup(void) if (-1 == cgexec_update_remain_cgroup(cgutil_vaddr[wd], cls)) return -1; } - } else if (wd == 0) { - fprintf(stderr, "ERROR: the specified workload group %s doesn't exist!\n", cgutil_opt.wdname); + } + else if (wd == 0) { + fprintf(stderr, "閿欒锛氭寚瀹氱殑宸ヤ綔璐熻浇缁 %s 涓嶅瓨鍦紒\n", cgutil_opt.wdname); return -1; } } - } else { - fprintf(stderr, "ERROR: the specified class group %s doesn't exist!\n", cgutil_opt.clsname); - return -1; } - - return 0; -} - -/* - * @Description: check dynamic backend percent. - * @IN bkd: backend group id - * @Return: -1: abnormal 0: normal - * @See also: - */ -static int cgexec_check_dynamic_backend_percent(int bkd) -{ - int i = 0; - int percent = 0; - - if (cgutil_opt.bkdpct > 0 && cgutil_opt.bkdpct > cgutil_vaddr[bkd]->ginfo.cls.percent) { - /* check the remain percentage */ - for (i = BACKENDCG_START_ID; i <= BACKENDCG_END_ID; i++) { - if (cgutil_vaddr[i]->used && (i != bkd)) - percent += cgutil_vaddr[i]->ginfo.cls.percent; - } - - if ((GROUP_ALL_PERCENT - percent) < cgutil_opt.bkdpct) { - fprintf(stderr, - "ERROR: there is no more resource for updated cgroup %s.\n" - "the remain percentage is %d.\n", - cgutil_opt.bkdname, - GROUP_ALL_PERCENT - percent); - return -1; - } - } - - if (cgutil_opt.bkdpct > 0 && cgutil_opt.bkdpct != cgutil_vaddr[bkd]->ginfo.cls.percent) { - /* set the cgutil_vaddr item */ - cgconf_update_backend_group(cgutil_vaddr[bkd]); - - if (-1 == cgexec_update_cgroup_value(cgutil_vaddr[bkd])) - return -1; - } - - return 0; -} - -/* - * function name: cgexec_update_dynamic_backend_cgroup - * description : update the dynamic value of backend cgroup - * return value : - * -1: abnormal - * 0: normal - * - */ -static int cgexec_update_dynamic_backend_cgroup(void) -{ - int i, bkd = 0; - - /* check if the class exists */ - for (i = BACKENDCG_START_ID; i <= BACKENDCG_END_ID; i++) { - if (cgutil_vaddr[i]->used == 0) - continue; - - if (0 == strcmp(cgutil_vaddr[i]->grpname, cgutil_opt.bkdname)) { - bkd = i; - break; - } - } - - if (bkd) { - if (cgexec_check_dynamic_backend_percent(bkd) == -1) - return -1; - } else { - fprintf(stderr, "ERROR: the specified backend group %s doesn't exist!\n", cgutil_opt.bkdname); - return -1; - } - - return 0; -} - -/* - * function name: cgexec_update_top_group_percent - * description : update the dynamic value of Top Cgroup; it include - * Root Cgroup, Guassdb:user Cgroup, Class Cgroup - * and Backend Cgroup. - * return value : - * -1: abnormal - * 0: normal - * - */ -static int cgexec_update_top_group_percent(void) -{ - if (0 == strcmp(cgutil_opt.topname, GSCGROUP_ROOT)) { - if (geteuid() != 0) { - fprintf(stderr, "ERROR: non-root user can't modify the Root cgroup!\n"); - return -1; - } - - if (cgutil_opt.toppct < 10) { - cgutil_opt.toppct = 10; - } - - if (cgutil_vaddr[TOPCG_ROOT]->ginfo.top.percent == cgutil_opt.toppct) - return 0; - - cgutil_vaddr[TOPCG_ROOT]->ginfo.top.percent = cgutil_opt.toppct; - - cgutil_vaddr[TOPCG_ROOT]->ainfo.weight = MAX_IO_WEIGHT * cgutil_opt.toppct / GROUP_ALL_PERCENT; - - if ((cgutil_is_sles11_sp2 || cgexec_check_SLESSP2_version()) && - (-1 == cgexec_update_cgroup_value(cgutil_vaddr[TOPCG_ROOT]))) - return -1; - } else if (0 == strcmp(cgutil_opt.topname, GSCGROUP_TOP_DATABASE) || - 0 == strcmp(cgutil_opt.topname, cgutil_vaddr[TOPCG_GAUSSDB]->grpname)) { - if (cgutil_vaddr[TOPCG_GAUSSDB]->ginfo.top.percent == cgutil_opt.toppct) - return 0; - - cgutil_vaddr[TOPCG_GAUSSDB]->ginfo.top.percent = cgutil_opt.toppct; - cgutil_vaddr[TOPCG_GAUSSDB]->ainfo.shares = - DEFAULT_CPU_SHARES * cgutil_opt.toppct / (GROUP_ALL_PERCENT - cgutil_opt.toppct); - cgutil_vaddr[TOPCG_GAUSSDB]->percent = - cgutil_vaddr[TOPCG_ROOT]->percent * cgutil_opt.toppct / GROUP_ALL_PERCENT; - - if (-1 == cgexec_update_cgroup_value(cgutil_vaddr[TOPCG_GAUSSDB])) - return -1; - - cgconf_update_top_percent(); - } else if (0 == strcmp(cgutil_opt.topname, GSCGROUP_TOP_BACKEND)) { - if (cgutil_vaddr[TOPCG_BACKEND]->ginfo.top.percent == cgutil_opt.toppct) - return 0; - - cgutil_vaddr[TOPCG_BACKEND]->ginfo.top.percent = cgutil_opt.toppct; - cgutil_vaddr[TOPCG_BACKEND]->ainfo.shares = DEFAULT_CPU_SHARES * cgutil_opt.toppct / 10; - cgutil_vaddr[TOPCG_BACKEND]->ainfo.weight = IO_WEIGHT_CALC(MAX_IO_WEIGHT, cgutil_opt.toppct); - cgutil_vaddr[TOPCG_BACKEND]->percent = - cgutil_vaddr[TOPCG_GAUSSDB]->percent * cgutil_opt.toppct / GROUP_ALL_PERCENT; - - if (-1 == cgexec_update_cgroup_value(cgutil_vaddr[TOPCG_BACKEND])) - return -1; - - cgconf_update_backend_percent(); - - cgutil_vaddr[TOPCG_CLASS]->ginfo.top.percent = GROUP_ALL_PERCENT - cgutil_opt.toppct; - cgutil_vaddr[TOPCG_CLASS]->ainfo.shares = DEFAULT_CPU_SHARES * (GROUP_ALL_PERCENT - cgutil_opt.toppct) / 10; - cgutil_vaddr[TOPCG_CLASS]->ainfo.weight = IO_WEIGHT_CALC(MAX_IO_WEIGHT, GROUP_ALL_PERCENT - cgutil_opt.toppct); - cgutil_vaddr[TOPCG_CLASS]->percent = - cgutil_vaddr[TOPCG_GAUSSDB]->percent * (GROUP_ALL_PERCENT - cgutil_opt.toppct) / GROUP_ALL_PERCENT; - - if (-1 == cgexec_update_cgroup_value(cgutil_vaddr[TOPCG_CLASS])) - return -1; - - cgconf_update_class_percent(); - } else if (0 == strcmp(cgutil_opt.topname, GSCGROUP_TOP_CLASS)) { - if (cgutil_vaddr[TOPCG_CLASS]->ginfo.top.percent == cgutil_opt.toppct) - return 0; - - cgutil_vaddr[TOPCG_CLASS]->ginfo.top.percent = cgutil_opt.toppct; - cgutil_vaddr[TOPCG_CLASS]->ainfo.shares = DEFAULT_CPU_SHARES * cgutil_opt.toppct / 10; - cgutil_vaddr[TOPCG_CLASS]->ainfo.weight = IO_WEIGHT_CALC(MAX_IO_WEIGHT, cgutil_opt.toppct); - cgutil_vaddr[TOPCG_CLASS]->percent = - cgutil_vaddr[TOPCG_GAUSSDB]->percent * cgutil_opt.toppct / GROUP_ALL_PERCENT; - - if (-1 == cgexec_update_cgroup_value(cgutil_vaddr[TOPCG_CLASS])) - return -1; - - cgconf_update_class_percent(); - - cgutil_vaddr[TOPCG_BACKEND]->ginfo.top.percent = GROUP_ALL_PERCENT - cgutil_opt.toppct; - cgutil_vaddr[TOPCG_BACKEND]->ainfo.shares = DEFAULT_CPU_SHARES * (GROUP_ALL_PERCENT - cgutil_opt.toppct) / 10; - cgutil_vaddr[TOPCG_BACKEND]->ainfo.weight = - IO_WEIGHT_CALC(MAX_IO_WEIGHT, GROUP_ALL_PERCENT - cgutil_opt.toppct); - cgutil_vaddr[TOPCG_BACKEND]->percent = - cgutil_vaddr[TOPCG_GAUSSDB]->percent * (GROUP_ALL_PERCENT - cgutil_opt.toppct) / GROUP_ALL_PERCENT; - - if (-1 == cgexec_update_cgroup_value(cgutil_vaddr[TOPCG_BACKEND])) - return -1; - - cgconf_update_backend_percent(); - } else { - fprintf(stderr, "ERROR: the specified top group %s doesn't exist!\n", cgutil_opt.topname); - return -1; - } - - return 0; -} -/* - * function name: cgexec_update_top_group_cpuset_userset - * description : update top level cpuset by user set "-f" - * @IN topname : top group name to be updated. - * @IN cpuset : user set cpuset to be updated. - * return value : - * -1: abnormal - * 0: normal - * - */ -int cgexec_update_top_group_cpuset_userset(const char* topname, char* cpuset) -{ - int topstart = 0; - int topend = 0; - int toplength = 0; - - int rcs = sscanf_s(cpuset, "%d-%d", &topstart, &topend); - if (rcs != 2) { - fprintf(stderr, - "%s:%d failed on calling " - "security function.\n", - __FILE__, - __LINE__); - return -1; - } - - toplength = topend - topstart + 1; - - /* we cannot changed the root group */ - if (strcmp(topname, GSCGROUP_ROOT) == 0) { - fprintf(stdout, "ERROR: cpuset of Root can not be changed.\n"); - return -1; - } - if (strcmp(topname, GSCGROUP_TOP_DATABASE) == 0 || strcmp(topname, cgutil_vaddr[TOPCG_GAUSSDB]->grpname) == 0) { - /* - * when updating cpuset for top classes, - * we need update all the belonging lower level groups by percentage. - */ - if (cgexec_update_top_group_cpuset(TOPCG_GAUSSDB, cpuset) == -1 || - cgexec_reset_cpuset_cgroups(TOPCG_GAUSSDB, 0) == -1) - return -1; - /* - * each time we use "-f" to set cpuset, - * we need reset quota to let this group not be influenced next time - * when we set cpuset percentage. - */ - cgutil_vaddr[TOPCG_GAUSSDB]->ainfo.quota = 0; - } - - return 0; -} -/* - * function name: cgexec_update_dynamic_top_cgroup - * description : update the dynamic value of Top Cgroup; it include - * Root Cgroup, Guassdb:user Cgroup, Class Cgroup - * and Backend Cgroup. - * return value : - * -1: abnormal - * 0: normal - * - */ -static int cgexec_update_dynamic_top_cgroup(void) -{ - if (cgutil_opt.toppct > 0 && cgexec_update_top_group_percent() == -1) { - return -1; - } - - if (*cgutil_opt.sets) - return cgexec_update_top_group_cpuset_userset(cgutil_opt.topname, cgutil_opt.sets); - - return 0; -} - -/* - * function name: cgexec_update_fixed_class_cgroup - * description : update the cpuset value of Class group and Workload group by user set "--fixed" - * return value : - * -1: abnormal - * 0: normal - * - */ -static int cgexec_update_fixed_class_cgroup(void) -{ - int i, cls = 0, wd = 0; - char cpusets[CPUSET_LEN]; - int need_reset = 0; - - /* check if the class exists */ - for (i = CLASSCG_START_ID; i <= CLASSCG_END_ID; i++) { - if (cgutil_vaddr[i]->used == 0) - continue; - - if (0 == strcmp(cgutil_vaddr[i]->grpname, cgutil_opt.clsname)) { - cls = i; - break; - } - } - - if (cls) { - if (cgutil_opt.wdname[0]) { - wd = cgexec_search_workload_group(cls); - - if (wd) { - if (cgutil_opt.setspct) { - /* - * step 1 check whether the newly set percentage makes the whole percentage higher than 100%. - * result: - * setslength = -1:higher than 100%. - * setslength = 0: cpusets have been set well, go to step 3. - * setslength > 0: cpusets is empty yet and need reset, go to step 2. - */ - if ((need_reset = cgexec_check_cpuset_percent(cls, wd, cpusets)) == -1) - return -1; - - /*step 2: defragment the other workload groups. */ - if (need_reset > 0 && (cgexec_reset_cpuset_cgroups(cls, wd) == -1)) - return -1; - - /* step 3: update the workload group. */ - if (cgexec_update_cgroup_cpuset(cgutil_vaddr[wd], cpusets) == -1) - return -1; - - cgutil_vaddr[wd]->ainfo.quota = cgutil_opt.setspct; - - return 0; - } else if (cgutil_opt.setfixed) { - if (cgexec_update_cgroup_cpuset(cgutil_vaddr[wd], cgutil_vaddr[cls]->cpuset) == -1) - return -1; - - cgutil_vaddr[wd]->ainfo.quota = 0; - - return 0; - } - } else { - fprintf(stderr, "ERROR: the specified workload group %s doesn't exist!\n", cgutil_opt.wdname); - return -1; - } - } - if (cgutil_opt.setspct) { - /* - * step 1: check whether the newly set percentage of class is legal or not. - * return value = -1: illegal - * return value = 0: legal and no need to do defragment. - * return value > 0: need defragment - */ - if ((need_reset = cgexec_check_cpuset_percent(TOPCG_CLASS, cls, cpusets)) == -1) - return -1; - - /* step 2: degragment the other class groups, reset their belonging workload groups by percentage. */ - if (need_reset > 0 && (cgexec_reset_cpuset_cgroups(TOPCG_CLASS, cls) == -1)) - return -1; - - /*step 3: update the class group. */ - if (cgexec_update_class_cpuset(cls, cpusets) == -1) { - fprintf(stderr, "ERROR: update cpuset for class \"%s\" failed.\n", cgutil_vaddr[cls]->grpname); - return -1; - } - - /* - * step 4: update the belonging workload groups. - * 0 means no group need be ignored in the reseting list - */ - if (cgexec_reset_cpuset_cgroups(cls, 0) == -1) { - fprintf(stderr, "ERROR: reset workload cpuset for class \"%s\" failed.\n", cgutil_vaddr[cls]->grpname); - return -1; - } - cgutil_vaddr[cls]->ainfo.quota = cgutil_opt.setspct; - - } else if (cgutil_opt.setfixed) { - if (cgexec_update_class_cpuset(cls, cgutil_vaddr[TOPCG_CLASS]->cpuset) == -1) - return -1; - - if (cgexec_reset_cpuset_cgroups(cls, 0) == -1) { - fprintf(stderr, "ERROR: reset workload cpuset for class \"%s\" failed.\n", cgutil_vaddr[cls]->grpname); - return -1; - } - - cgutil_vaddr[cls]->ainfo.quota = 0; - } - } else { - fprintf(stderr, "ERROR: the specified class group %s doesn't exist!\n", cgutil_opt.clsname); - return -1; - } - - return 0; -} - -/* - * function name: cgexec_update_fixed_backend_cgroup - * description : update the cpuset value of Backend group by user set "--fixed" - * return value : - * -1: abnormal - * 0: normal - * - */ -static int cgexec_update_fixed_backend_cgroup(void) -{ - int i, bkd = 0; - char cpuset[CPUSET_LEN]; - int need_reset = 0; - - /* check if the class exists */ - for (i = BACKENDCG_START_ID; i <= BACKENDCG_END_ID; i++) { - if (cgutil_vaddr[i]->used == 0) - continue; - - if (0 == strcmp(cgutil_vaddr[i]->grpname, cgutil_opt.bkdname)) { - bkd = i; - break; - } - } - - if (bkd) { - /* set cpuset by percentage*/ - if (cgutil_opt.setspct) { - /* the same steps with updating workload groups.*/ - if ((need_reset = cgexec_check_cpuset_percent(TOPCG_BACKEND, bkd, cpuset)) == -1) - return -1; - - if (need_reset > 0 && (cgexec_reset_cpuset_cgroups(TOPCG_BACKEND, bkd) == -1)) - return -1; - - if (cgexec_update_cgroup_cpuset(cgutil_vaddr[bkd], cpuset) == -1) - return -1; - - cgutil_vaddr[bkd]->ainfo.quota = cgutil_opt.setspct; - } else if (cgutil_opt.setfixed) { - if (cgexec_update_cgroup_cpuset(cgutil_vaddr[bkd], cgutil_vaddr[TOPCG_BACKEND]->cpuset) == -1) - return -1; - - cgutil_vaddr[bkd]->ainfo.quota = 0; - } - } else { - fprintf(stderr, "ERROR: the specified backend group %s doesn't exist!\n", cgutil_opt.bkdname); - return -1; - } - - return 0; -} - -/* - * function name: cgexec_update_fixed_top_cgroup - * description : update cpuset of Top group by user set "--fixed" - * return value : - * -1: abnormal - * 0: normal - * - */ -static int cgexec_update_fixed_top_cgroup(void) -{ - char cpusets[CPUSET_LEN]; - int need_reset = 0; - int top = 0; - - if (0 == strcmp(cgutil_opt.topname, GSCGROUP_ROOT)) { - fprintf(stderr, "ERROR: users can't modify the Root cgroup with \"--fixed\"!\n"); - return -1; - } else if (0 == strcmp(cgutil_opt.topname, GSCGROUP_TOP_DATABASE) || - 0 == strcmp(cgutil_opt.topname, cgutil_vaddr[TOPCG_GAUSSDB]->grpname)) { - top = TOPCG_GAUSSDB; - if (cgutil_opt.setspct) { - fprintf(stderr, "ERROR: users can't modify the cpu cores percentage of Gaussdb cgroup with \"--fixed\"!\n"); - return -1; - } - } else if (0 == strcmp(cgutil_opt.topname, GSCGROUP_TOP_BACKEND)) - top = TOPCG_BACKEND; - else if (0 == strcmp(cgutil_opt.topname, GSCGROUP_TOP_CLASS)) - top = TOPCG_CLASS; else { - fprintf(stderr, "ERROR: the specified top group %s doesn't exist!\n", cgutil_opt.topname); + fprintf(stderr, "閿欒锛氭寚瀹氱殑绫荤粍 %s 涓嶅瓨鍦紒\n", cgutil_opt.clsname); return -1; } - if (top) { - if (cgutil_opt.setspct) { - if ((need_reset = cgexec_check_cpuset_percent(TOPCG_GAUSSDB, top, cpusets)) == -1) - return -1; - - if (need_reset && (cgexec_reset_cpuset_cgroups(TOPCG_GAUSSDB, top) == -1)) - return -1; - - if (cgexec_update_top_group_cpuset(top, cpusets) == -1 || cgexec_reset_cpuset_cgroups(top, 0) == -1) - return -1; - - cgutil_vaddr[top]->ainfo.quota = cgutil_opt.setspct; - } else if (cgutil_opt.setfixed) { - if (cgexec_update_top_group_cpuset(top, cgutil_vaddr[TOPCG_GAUSSDB]->cpuset) == -1 || - cgexec_reset_cpuset_cgroups(top, 0) == -1) - return -1; - - cgutil_vaddr[top]->ainfo.quota = 0; - } - } - return 0; } + /* + * @Description: 妫鏌ュ姩鎬佸悗绔粍鐨勭櫨鍒嗘瘮鏄惁鍚堟硶銆 + * @IN bkd: 鍚庣缁刬d + * @Return: -1锛氬紓甯 0锛氭甯 + * @See also: + */ + static int cgexec_check_dynamic_backend_percent(int bkd) + { + int i = 0; + int percent = 0; -/* - **************** EXTERNAL FUNCTION ******************************** - */ + if (cgutil_opt.bkdpct > 0 && cgutil_opt.bkdpct > cgutil_vaddr[bkd]->ginfo.cls.percent) { + // 妫鏌ュ墿浣欑櫨鍒嗘瘮 + for (i = BACKENDCG_START_ID; i <= BACKENDCG_END_ID; i++) { + if (cgutil_vaddr[i]->used && (i != bkd)) + percent += cgutil_vaddr[i]->ginfo.cls.percent; + } -/* - * function name: cgexec_check_SLESSP2_version - * description : check if the current OS version is SLES SP2 - * return value : - * 1: is the sles sp2 - * 0: is not the sles sp2, supposed as sles sp1 - * -1: abnormal - * - * Note: Search "io" column in /proc/cgroups. - * It need to check the value if the next release supports - * Redhat or Euler version. - */ -int cgexec_check_SLESSP2_version(void) -{ - char buf[PROCLINE_LEN]; - FILE* f = NULL; - - f = fopen("/proc/cgroups", "r"); - - if (f == NULL) - return -1; - - while (NULL != fgets(buf, PROCLINE_LEN, f)) { - /* example from proc: - * #subsys_name hierarchy num_cgroups enabled - * cpu 0 1 1 - * - * search "blkio" column - */ - - if (strstr(buf, MOUNT_BLKIO_NAME) != NULL) { - cgutil_is_sles11_sp2 = 1; - fclose(f); - return 1; + if ((GROUP_ALL_PERCENT - percent) < cgutil_opt.bkdpct) { + fprintf(stderr, + "ERROR: 娌℃湁瓒冲鐨勮祫婧愭潵鏇存柊鎺у埗缁%s銆俓n" + "鍓╀綑鐧惧垎姣斾负%d銆俓n", + cgutil_opt.bkdname, + GROUP_ALL_PERCENT - percent); + return -1; + } } - } - fclose(f); - return 0; -} + if (cgutil_opt.bkdpct > 0 && cgutil_opt.bkdpct != cgutil_vaddr[bkd]->ginfo.cls.percent) { + // 璁剧疆cgutil_vaddr椤圭洰 + cgconf_update_backend_group(cgutil_vaddr[bkd]); -/* - * @Description: check whether execute upgrade. - * @IN void - * @Return: 1: upgrade 0: not upgrade - * @See also: - */ -int cgexec_check_mount_for_upgrade(void) -{ - int i, ret, old_mp = 0; + if (-1 == cgexec_update_cgroup_value(cgutil_vaddr[bkd])) + return -1; + } - errno_t sret; - - /* Only root user can do upgrade */ - if (geteuid() != 0) return 0; + } /* - * if cpuset and cpuacct has not mounted, we will check whether - * cpu or blkio is mounted on default point, if yes, we must unmount - * them firstly, and then mount all sub system with new mount point. - * if no system in default point, we need not umount them. + * function name: cgexec_update_dynamic_backend_cgroup + * description : 鏇存柊鍚庣缁勭殑鍔ㄦ佸 + * return value : + * -1锛氬紓甯 + * 0锛氭甯 */ - for (i = 0; i < MOUNT_SUBSYS_KINDS; ++i) { - /* ignore blkio and memory. */ - if (i == MOUNT_BLKIO_ID || i == MOUNT_MEMORY_ID) - continue; + static int cgexec_update_dynamic_backend_cgroup(void) + { + int i, bkd = 0; - if (*cgutil_opt.mpoints[i]) { - if (strcmp(cgutil_opt.mpoints[i], GSCGROUP_MOUNT_POINT_OLD) == 0) { - char fname[256]; - int cnt = 0; - struct dirent* file = NULL; - DIR* dir = opendir(GSCGROUP_MOUNT_POINT_OLD); - - if (dir == NULL) { - fprintf(stderr, "ERROR: failed to open %s.\n", GSCGROUP_MOUNT_POINT_OLD); - return -1; - } - - sret = snprintf_s( - fname, sizeof(fname), sizeof(fname) - 1, "%s:%s", "Gaussdb", cgutil_passwd_user->pw_name); - securec_check_intval(sret, closedir(dir), -1); - - /* if other user has created cgroup in the default mout point, we cannot unmount the point. */ - while ((file = readdir(dir)) != NULL) { - if (file->d_type != DT_DIR || strcmp(file->d_name, fname) == 0) - continue; - - if (file->d_type == DT_DIR && strncmp(file->d_name, "Gaussdb:", 8) == 0) - ++cnt; - } - - closedir(dir); - - if (cnt > 0) { - fprintf(stderr, - "ERROR: The other user has cgroups in \"%s\", upgrade failed.\n", - GSCGROUP_MOUNT_POINT_OLD); - return -1; - } - - old_mp++; - } - } - } - - /* if cgroups are mounted on old path, only umount once time */ - if (old_mp) { - char cmd[128]; - - /* more than one cgroups have been mounted under /dev/cgroups */ - if (old_mp > 1) { - (void)cgroup_init(); /* init first */ - (void)cgptree_drop_cgroups(); - } - - sret = snprintf_s(cmd, sizeof(cmd), sizeof(cmd) - 1, "umount %s", GSCGROUP_MOUNT_POINT_OLD); - securec_check_intval(sret, , -1); - - ret = system(cmd); - if (CheckSystemSucess(ret) == -1) { - fprintf(stderr, "ERROR: failed to umount cgroup under %s!\n", GSCGROUP_MOUNT_POINT_OLD); - return -1; - } - - /* get new mount points and mount them */ - (void)cgexec_get_mount_points(); - (void)cgexec_mount_root_cgroup(); - } else - cgutil_opt.upgrade = 0; - - return 0; -} - -/* - * @Description: get all cgroup sub system's mount points. - * @IN void - * @Return: 0: normal -1: abnormal - * @See also: - */ -int cgexec_get_mount_points(void) -{ - struct mntent* ent = NULL; - char mntent_buffer[5 * FILENAME_MAX]; - - struct mntent temp_ent; - int i; - - errno_t rc; - rc = memset_s(&temp_ent, sizeof(temp_ent), 0, sizeof(temp_ent)); - securec_check_errno(rc, , -1); - - /* reset mount points */ - rc = memset_s(cgutil_opt.mpoints, MOUNT_SUBSYS_KINDS * MAXPGPATH, 0, MOUNT_SUBSYS_KINDS * MAXPGPATH); - securec_check_errno(rc, , -1); - - /* open '/proc/mounts' to load mount points */ - FILE* proc_mount = fopen("/proc/mounts", "re"); - - if (proc_mount == NULL) - return -1; - - while ((ent = getmntent_r(proc_mount, &temp_ent, mntent_buffer, sizeof(mntent_buffer))) != NULL) { - /* not cgroup, pass */ - if (strcmp(ent->mnt_type, "cgroup") != 0) - continue; - - for (i = 0; i < MOUNT_SUBSYS_KINDS; ++i) { - if (hasmntopt(ent, cgutil_subsys_table[i]) == NULL) + // 妫鏌ョ被鏄惁瀛樺湪 + for (i = BACKENDCG_START_ID; i <= BACKENDCG_END_ID; i++) { + if (cgutil_vaddr[i]->used == 0) continue; - /* get mount point */ - rc = snprintf_s(cgutil_opt.mpoints[i], - sizeof(cgutil_opt.mpoints[i]), - sizeof(cgutil_opt.mpoints[i]) - 1, - "%s", - ent->mnt_dir); - securec_check_intval(rc, fclose(proc_mount), -1); - } - } - - fclose(proc_mount); - - return 0; -} - -/* - * @Description: detect if cgroup file system has been mounted. - * @IN void - * @Return: 1: has been mounted on the specified directory - * 0: hasn't been mounted - * -1: has been mounted on other directory - * @See also: - */ -int cgexec_detect_cgroup_mount(void) -{ - int i, j; - - if (cgutil_opt.cflag <= 0) { - return 1; - } - - for (i = 0; i < MOUNT_SUBSYS_KINDS; ++i) { - if (i == MOUNT_BLKIO_ID) - continue; - - /* a subsys has not mounted, we must make sure its mount point is valid. */ - if (*cgutil_opt.mpoints[i] == '\0') { - if (cgutil_opt.mpflag == 0) { - /* no new mount point, make sure default point is valid */ - for (j = 0; j < MOUNT_SUBSYS_KINDS; ++j) - if (strcmp(cgutil_opt.mpoints[j], GSCGROUP_MOUNT_POINT) == 0) - return -1; - } - - return 0; - } - } - - return 1; -} - -static int RemoveExistSymbolLink(const char* mpoint) -{ - int ret; - char cmd[MAX_COMMAND_LENGTH]; - struct stat statbuf; - errno_t rc; - - rc = memset_s(&statbuf, sizeof(statbuf), 0, sizeof(statbuf)); - securec_check_c(rc, "\0", "\0"); - /* If the mount point is already exist, directory should be remove */ - ret = lstat(mpoint, &statbuf); - if (S_ISLNK(statbuf.st_mode)) { - rc = snprintf_s(cmd, sizeof(cmd), sizeof(cmd) - 1, "rm %s", mpoint); - securec_check_ss_c(rc, "\0", "\0"); - - ret = system(cmd); - if (CheckSystemSucess(ret) == -1) { - fprintf(stderr, "ERROR: failed to remove exist symbol link %s!\n", mpoint); - return -1; - } - } - return 0; -} - -static int MountCgroupInternal(const char* mpoint, const char* type) -{ - int ret; - char cmd[MAX_COMMAND_LENGTH]; - struct stat statbuf; - errno_t rc; - - rc = memset_s(&statbuf, sizeof(statbuf), 0, sizeof(statbuf)); - securec_check_c(rc, "\0", "\0"); - /* check new mount point directory */ - ret = stat(mpoint, &statbuf); - if (ret != 0 || !S_ISDIR(statbuf.st_mode)) { - if (mkdir(mpoint, S_IRWXU) != 0) { - fprintf(stderr, "ERROR: failed to create %s directory!\n", mpoint); - return -1; - } - } - - rc = snprintf_s(cmd, sizeof(cmd), sizeof(cmd) - 1, - "mount -t cgroup -o %s %s %s", type, type, mpoint); - securec_check_ss_c(rc, "\0", "\0"); - ret = system(cmd); - if (CheckSystemSucess(ret) == -1) { - fprintf(stderr, "ERROR: failed to mount cgroup under %s!\n", mpoint); - return -1; - } - fprintf(stderr, "LOG: mount %s success.\n", type); - return 0; -} - -static int LinkCpuCgroup(const char* target, const char* source) -{ - int ret; - char cmd[MAX_COMMAND_LENGTH]; - errno_t rc; - - rc = snprintf_s(cmd, sizeof(cmd), sizeof(cmd) - 1, "ln -s %s %s", source, target); - securec_check_ss_c(rc, "\0", "\0"); - - ret = system(cmd); - if (CheckSystemSucess(ret) == -1) { - fprintf(stderr, "ERROR: failed to mount cgroup under %s!\n", target); - return -1; - } - - return 0; -} - -static int CgexecRemountCpuCgroup(const char* path, const char* tmp_mpoint) -{ - int ret; - char mpoint[MOUNT_POINT_LENGTH]; - errno_t rc; - - rc = snprintf_s(mpoint, sizeof(mpoint), sizeof(mpoint) - 1, - "%s/cpu", path); - securec_check_ss_c(rc, "\0", "\0"); - ret = RemoveExistSymbolLink(mpoint); - if (ret != 0) { - return ret; - } - ret = LinkCpuCgroup(mpoint, tmp_mpoint); - if (ret != 0) { - return ret; - } - rc = snprintf_s(mpoint, sizeof(mpoint), sizeof(mpoint) - 1, - "%s/cpuacct", path); - securec_check_ss_c(rc, "\0", "\0"); - ret = RemoveExistSymbolLink(mpoint); - if (ret != 0) { - return ret; - } - ret = LinkCpuCgroup(mpoint, tmp_mpoint); - - return ret; -} - -static int CgexecMountCpuCgroup(const char* path) -{ - int ret; - char tmp_mpoint[MOUNT_POINT_LENGTH]; - char mpoint[MOUNT_POINT_LENGTH]; - errno_t rc; - - /* cpu and cpuacct sub-system should mount cpu,cpuacct sub-system */ - rc = snprintf_s(tmp_mpoint, sizeof(tmp_mpoint), sizeof(tmp_mpoint) - 1, - "%s/cpu,cpuacct", path); - securec_check_ss_c(rc, "\0", "\0"); - ret = MountCgroupInternal(tmp_mpoint, "cpu,cpuacct"); - if (ret != 0) { - /* mount failed means that cpu and cpuacct not mount together */ - rc = snprintf_s(mpoint, sizeof(mpoint), sizeof(mpoint) - 1, - "%s/cpu", path); - securec_check_ss_c(rc, "\0", "\0"); - ret = MountCgroupInternal(mpoint, cgutil_subsys_table[MOUNT_CPU_ID]); - if (ret != 0) { - return ret; - } - rc = snprintf_s(mpoint, sizeof(mpoint), sizeof(mpoint) - 1, - "%s/cpuacct", path); - securec_check_ss_c(rc, "\0", "\0"); - ret = MountCgroupInternal(mpoint, cgutil_subsys_table[MOUNT_CPUACCT_ID]); - } else { - ret = CgexecRemountCpuCgroup(path, tmp_mpoint); - } - - return ret; -} - -/* - * @Description: mount the Cgroup file system on the Root directory. - * @IN void - * @Return: -1: abnormal 0: normal - * @See also: - */ -int cgexec_mount_root_cgroup(void) -{ - int i, ret; - - char mpoint[MOUNT_POINT_LENGTH]; - char* path = NULL; - struct stat statbuf; - - errno_t rc; - rc = memset_s(&statbuf, sizeof(statbuf), 0, sizeof(statbuf)); - securec_check_c(rc, "\0", "\0"); - - if (cgutil_opt.mpflag) - path = cgutil_opt.mpoint; - else - path = GSCGROUP_MOUNT_POINT; - - if (CheckBackendEnv(path) != 0) { - return -1; - } - /* Create mount point directory */ - ret = stat(path, &statbuf); - if (0 != ret || !S_ISDIR(statbuf.st_mode)) { - if (mkdir(path, S_IRWXU | (S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)) != 0) { - fprintf(stderr, "ERROR: failed to create %s directory!\n", path); - return -1; - } - /* change the right to 755 */ - (void)chmod(path, S_IRWXU | (S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)); - } - - for (i = 0; i < MOUNT_SUBSYS_KINDS; ++i) { - /* 'blkio' is invalid, ignore it */ - if (i == MOUNT_BLKIO_ID) - continue; - - /* If the subsys has not mounted, we will use new point to mount. */ - if (*cgutil_opt.mpoints[i] == '\0') { - /* cpu and cpuacct sub-system all mount on cpu,cpuacct in new linux system */ - if (i == MOUNT_CPU_ID) { - ret = CgexecMountCpuCgroup(path); - i++; - } else { - rc = snprintf_s(mpoint, sizeof(mpoint), sizeof(mpoint) - 1, "%s/%s", path, cgutil_subsys_table[i]); - securec_check_ss_c(rc, "\0", "\0"); - ret = MountCgroupInternal(mpoint, cgutil_subsys_table[i]); + if (0 == strcmp(cgutil_vaddr[i]->grpname, cgutil_opt.bkdname)) { + bkd = i; + break; } } - } - return 0; -} - -static int CgexecUmountRootCgroupInternal(const char* path, int index) -{ - int ret; - char cmd[MAX_COMMAND_LENGTH], mpoint[MOUNT_POINT_LENGTH]; - errno_t rc; - - /* get mount point full name */ - rc = snprintf_s(mpoint, sizeof(mpoint), sizeof(mpoint) - 1, "%s/%s", path, cgutil_subsys_table[index]); - securec_check_ss_c(rc, "\0", "\0"); - - /* we will unmount the point which you specify. */ - if (strcmp(cgutil_opt.mpoints[index], mpoint) == 0) { - rc = snprintf_s(cmd, sizeof(cmd), sizeof(cmd) - 1, "umount %s", mpoint); - securec_check_ss_c(rc, "\0", "\0"); - - ret = system(cmd); - if (CheckSystemSucess(ret) == -1) { - fprintf(stderr, "ERROR: failed to umount cgroup under %s!\n", mpoint); + if (bkd) { + if (cgexec_check_dynamic_backend_percent(bkd) == -1) + return -1; + } + else { + fprintf(stderr, "ERROR: 鎸囧畾鐨勫悗绔粍%s涓嶅瓨鍦紒\n", cgutil_opt.bkdname); return -1; } - fprintf(stderr, "LOG: umount cgroup under %s!\n", mpoint); - } else if (index == MOUNT_CPU_ID || index == MOUNT_CPUACCT_ID) { - /* check new mount point directory */ - RemoveExistSymbolLink(mpoint); - if (index == MOUNT_CPU_ID) { - rc = snprintf_s(mpoint, sizeof(mpoint), sizeof(mpoint) - 1, "%s/cpu,cpuacct", path); - securec_check_ss_c(rc, "\0", "\0"); - if (*cgutil_opt.mpoints[index] && strcmp(cgutil_opt.mpoints[index], mpoint) == 0) { - rc = snprintf_s(cmd, sizeof(cmd), sizeof(cmd) - 1, "umount %s", mpoint); - securec_check_ss_c(rc, "\0", "\0"); + return 0; + } + /* + * 鍑芥暟鍚: cgexec_update_top_group_percent + * 鎻忚堪锛氭洿鏂伴《绾group鐨勫姩鎬佸硷紱鍖呮嫭Root Cgroup銆丟uassdb:user Cgroup銆丆lass Cgroup + * 鍜孊ackend Cgroup銆 + * 杩斿洖鍊硷細 + * -1锛氬紓甯 + * 0锛氭甯 + * + */ + static int cgexec_update_top_group_percent(void) + { + // 濡傛灉椤剁骇Cgroup涓篟oot Cgroup + if (0 == strcmp(cgutil_opt.topname, GSCGROUP_ROOT)) { + // 闈濺oot鐢ㄦ埛涓嶈兘淇敼Root cgroup + if (geteuid() != 0) { + fprintf(stderr, "ERROR: 闈濺oot鐢ㄦ埛涓嶈兘淇敼Root cgroup锛乗n"); + return -1; + } - ret = system(cmd); - if (CheckSystemSucess(ret) == -1) { - fprintf(stderr, "ERROR: failed to umount cgroup under %s!\n", mpoint); + // 濡傛灉鎸囧畾鐨勯《绾group鐨勭櫨鍒嗘瘮灏忎簬10锛屽皢鍏惰缃负10 + if (cgutil_opt.toppct < 10) { + cgutil_opt.toppct = 10; + } + + // 濡傛灉椤剁骇Cgroup鐨勭櫨鍒嗘瘮宸茬粡鏄寚瀹氱殑鍊硷紝鐩存帴杩斿洖 + if (cgutil_vaddr[TOPCG_ROOT]->ginfo.top.percent == cgutil_opt.toppct) + return 0; + + // 鏇存柊椤剁骇Cgroup鐨勭櫨鍒嗘瘮 + cgutil_vaddr[TOPCG_ROOT]->ginfo.top.percent = cgutil_opt.toppct; + + // 鏇存柊椤剁骇Cgroup鐨処O鏉冮噸 + cgutil_vaddr[TOPCG_ROOT]->ainfo.weight = MAX_IO_WEIGHT * cgutil_opt.toppct / GROUP_ALL_PERCENT; + + // 濡傛灉褰撳墠绯荤粺鏄疭LES11 SP2鐗堟湰锛屽苟涓攃gexec_check_SLESSP2_version鍑芥暟杩斿洖-1 + // 鍒欐洿鏂癈group鐨勫硷紝鍚﹀垯杩斿洖寮傚父 + if ((cgutil_is_sles11_sp2 || cgexec_check_SLESSP2_version()) && + (-1 == cgexec_update_cgroup_value(cgutil_vaddr[TOPCG_ROOT]))) + return -1; + } + // 濡傛灉椤剁骇Cgroup涓篏uassdb:user Cgroup + else if (0 == strcmp(cgutil_opt.topname, GSCGROUP_TOP_DATABASE) || + 0 == strcmp(cgutil_opt.topname, cgutil_vaddr[TOPCG_GAUSSDB]->grpname)) { + + // 濡傛灉椤剁骇Cgroup鐨勭櫨鍒嗘瘮宸茬粡鏄寚瀹氱殑鍊硷紝鐩存帴杩斿洖 + if (cgutil_vaddr[TOPCG_GAUSSDB]->ginfo.top.percent == cgutil_opt.toppct) + return 0; + + // 鏇存柊椤剁骇Cgroup鐨勭櫨鍒嗘瘮 + cgutil_vaddr[TOPCG_GAUSSDB]->ginfo.top.percent = cgutil_opt.toppct; + + // 鏇存柊椤剁骇Cgroup鐨凜PU浠介 + cgutil_vaddr[TOPCG_GAUSSDB]->ainfo.shares = + DEFAULT_CPU_SHARES * cgutil_opt.toppct / (GROUP_ALL_PERCENT - cgutil_opt.toppct); + + // 鏇存柊椤剁骇Cgroup鐨勭櫨鍒嗘瘮 + cgutil_vaddr[TOPCG_GAUSSDB]->percent = + cgutil_vaddr[TOPCG_ROOT]->percent * cgutil_opt.toppct / GROUP_ALL_PERCENT; + + // 濡傛灉鏇存柊Cgroup鐨勫艰繑鍥-1锛屽垯杩斿洖寮傚父 + if (-1 == cgexec_update_cgroup_value(cgutil_vaddr[TOPCG_GAUSSDB])) + return -1; + + // 鏇存柊椤剁骇Cgroup鐨勭浉鍏抽厤缃 + cgconf_update_top_percent(); + } + // 濡傛灉椤剁骇Cgroup涓築ackend Cgroup + else if (0 == strcmp(cgutil_opt.topname, GSCGROUP_TOP_BACKEND)) { + // 濡傛灉椤剁骇Cgroup鐨勭櫨鍒嗘瘮宸茬粡鏄寚瀹氱殑鍊硷紝鐩存帴杩斿洖 + if (cgutil_vaddr[TOPCG_BACKEND]->ginfo.top.percent == cgutil_opt.toppct) + return 0; + + // 鏇存柊椤剁骇Cgroup鐨勭櫨鍒嗘瘮 + cgutil_vaddr[TOPCG_BACKEND]->ginfo.top.percent = cgutil_opt.toppct; + + // 鏇存柊椤剁骇Cgroup鐨凜PU浠介 + cgutil_vaddr[TOPCG_BACKEND]->ainfo.shares = DEFAULT_CPU_SHARES * cgutil_opt.toppct / 10; + + // 鏇存柊椤剁骇Cgroup鐨処O鏉冮噸 + cgutil_vaddr[TOPCG_BACKEND]->ainfo.weight = IO_WEIGHT_CALC(MAX_IO_WEIGHT, cgutil_opt.toppct); + + // 鏇存柊椤剁骇Cgroup鐨勭櫨鍒嗘瘮 + cgutil_vaddr[TOPCG_BACKEND]->percent = + cgutil_vaddr[TOPCG_GAUSSDB]->percent * cgutil_opt.toppct / GROUP_ALL_PERCENT; + + // 濡傛灉鏇存柊Cgroup鐨勫艰繑鍥-1锛屽垯杩斿洖寮傚父 + if (-1 == cgexec_update_cgroup_value(cgutil_vaddr[TOPCG_BACKEND])) + return -1; + + // 鏇存柊Backend Cgroup鐨勭浉鍏抽厤缃 + cgconf_update_backend_percent(); + + // 鏇存柊Class Cgroup鐨勭櫨鍒嗘瘮 + cgutil_vaddr[TOPCG_CLASS]->ginfo.top.percent = GROUP_ALL_PERCENT - cgutil_opt.toppct; + + // 鏇存柊Class Cgroup鐨凜PU浠介 + cgutil_vaddr[TOPCG_CLASS]->ainfo.shares = DEFAULT_CPU_SHARES * (GROUP_ALL_PERCENT - cgutil_opt.toppct) / 10; + + // 鏇存柊Class Cgroup鐨処O鏉冮噸 + cgutil_vaddr[TOPCG_CLASS]->ainfo.weight = IO_WEIGHT_CALC(MAX_IO_WEIGHT, GROUP_ALL_PERCENT - cgutil_opt.toppct); + + // 鏇存柊Class Cgroup鐨勭櫨鍒嗘瘮 + cgutil_vaddr[TOPCG_CLASS]->percent = + cgutil_vaddr[TOPCG_GAUSSDB]->percent * (GROUP_ALL_PERCENT - cgutil_opt.toppct) / GROUP_ALL_PERCENT; + + // 濡傛灉鏇存柊Cgroup鐨勫艰繑鍥-1锛屽垯杩斿洖寮傚父 + if (-1 == cgexec_update_cgroup_value(cgutil_vaddr[TOPCG_CLASS])) + return -1; + + // 鏇存柊Class Cgroup鐨勭浉鍏抽厤缃 + cgconf_update_class_percent(); + } + // 濡傛灉椤剁骇Cgroup涓篊lass Cgroup + else if (0 == strcmp(cgutil_opt.topname, GSCGROUP_TOP_CLASS)) { + // 濡傛灉椤剁骇Cgroup鐨勭櫨鍒嗘瘮宸茬粡鏄寚瀹氱殑鍊硷紝鐩存帴杩斿洖 + if (cgutil_vaddr[TOPCG_CLASS]->ginfo.top.percent == cgutil_opt.toppct) + return 0; + + // 鏇存柊椤剁骇Cgroup鐨勭櫨鍒嗘瘮 + cgutil_vaddr[TOPCG_CLASS]->ginfo.top.percent = cgutil_opt.toppct; + + // 鏇存柊椤剁骇Cgroup鐨凜PU浠介 + cgutil_vaddr[TOPCG_CLASS]->ainfo.shares = DEFAULT_CPU_SHARES * cgutil_opt.toppct / 10; + + // 鏇存柊椤剁骇Cgroup鐨処O鏉冮噸 + cgutil_vaddr[TOPCG_CLASS]->ainfo.weight = IO_WEIGHT_CALC(MAX_IO_WEIGHT, cgutil_opt.toppct); + + // 鏇存柊椤剁骇Cgroup鐨勭櫨鍒嗘瘮 + cgutil_vaddr[TOPCG_CLASS]->percent = + cgutil_vaddr[TOPCG_GAUSSDB]->percent * cgutil_opt.toppct / GROUP_ALL_PERCENT; + + // 濡傛灉鏇存柊Cgroup鐨勫艰繑鍥-1锛屽垯杩斿洖寮傚父 + if (-1 == cgexec_update_cgroup_value(cgutil_vaddr[TOPCG_CLASS])) + return -1; + + // 鏇存柊Class Cgroup鐨勭浉鍏抽厤缃 + cgconf_update_class_percent(); + + // 鏇存柊Backend Cgroup鐨勭櫨鍒嗘瘮 + cgutil_vaddr[TOPCG_BACKEND]->ginfo.top.percent = GROUP_ALL_PERCENT - cgutil_opt.toppct; + + // 鏇存柊Backend Cgroup鐨凜PU浠介 + cgutil_vaddr[TOPCG_BACKEND]->ainfo.shares = DEFAULT_CPU_SHARES * (GROUP_ALL_PERCENT - cgutil_opt.toppct) / 10; + + // 鏇存柊Backend Cgroup鐨処O鏉冮噸 + cgutil_vaddr[TOPCG_BACKEND]->ainfo.weight = + IO_WEIGHT_CALC(MAX_IO_WEIGHT, GROUP_ALL_PERCENT - cgutil_opt.toppct); + + // 鏇存柊Backend Cgroup鐨勭櫨鍒嗘瘮 + cgutil_vaddr[TOPCG_BACKEND]->percent = + cgutil_vaddr[TOPCG_GAUSSDB]->percent * (GROUP_ALL_PERCENT - cgutil_opt.toppct) / GROUP_ALL_PERCENT; + + // 濡傛灉鏇存柊Cgroup鐨勫艰繑鍥-1锛屽垯杩斿洖寮傚父 + if (-1 == cgexec_update_cgroup_value(cgutil_vaddr[TOPCG_BACKEND])) + return -1; + + // 鏇存柊Backend Cgroup鐨勭浉鍏抽厤缃 + cgconf_update_backend_percent(); + } + // 濡傛灉鎸囧畾鐨勯《绾group涓嶅瓨鍦 + else { + fprintf(stderr, "ERROR: 鎸囧畾鐨勯《绾group %s 涓嶅瓨鍦紒\n", cgutil_opt.topname); + return -1; + } + + return 0; + } + /* + * 鍑芥暟鍚嶏細cgexec_update_top_group_cpuset_userset + * 鍔熻兘锛氭牴鎹敤鎴疯缃殑"-f"鏇存柊椤跺眰cpuset + * @IN topname锛氶渶瑕佹洿鏂扮殑椤跺眰缁勫悕绉般 + * @IN cpuset锛氶渶瑕佹洿鏂扮殑鐢ㄦ埛璁剧疆cpuset銆 + * 杩斿洖鍊硷細 + * -1锛氬紓甯 + * 0锛氭甯 + * + */ + int cgexec_update_top_group_cpuset_userset(const char* topname, char* cpuset) + { + int topstart = 0; // 椤跺眰cpuset鐨勮捣濮嬪 + int topend = 0; // 椤跺眰cpuset鐨勭粨鏉熷 + int toplength = 0; // 椤跺眰cpuset鐨勯暱搴 + + int rcs = sscanf_s(cpuset, "%d-%d", &topstart, &topend); // 閫氳繃鏍煎紡鍖栧瓧绗︿覆灏哻puset瑙f瀽涓簊tart鍜宔nd涓や釜鍊 + if (rcs != 2) { // 濡傛灉瑙f瀽澶辫触 + fprintf(stderr, + "%s:%d failed on calling " + "security function.\n", + __FILE__, + __LINE__); + return -1; + } + + toplength = topend - topstart + 1; // 璁$畻椤跺眰cpuset鐨勯暱搴 + + /* 涓嶈兘鏇存敼鏍圭粍 */ + if (strcmp(topname, GSCGROUP_ROOT) == 0) { // 濡傛灉topname鏄牴缁 + fprintf(stdout, "ERROR: cpuset of Root can not be changed.\n"); + return -1; + } + if (strcmp(topname, GSCGROUP_TOP_DATABASE) == 0 || strcmp(topname, cgutil_vaddr[TOPCG_GAUSSDB]->grpname) == 0) { + /* + * 褰撴洿鏂伴《灞傜被鍒殑cpuset鏃讹紝 + * 闇瑕佹寜姣斾緥鏇存柊鎵鏈夊睘浜庤绫诲埆鐨勫瓙缁勩 + */ + if (cgexec_update_top_group_cpuset(TOPCG_GAUSSDB, cpuset) == -1 || + cgexec_reset_cpuset_cgroups(TOPCG_GAUSSDB, 0) == -1) + return -1; + /* + * 姣忔浣跨敤"-f"璁剧疆cpuset鏃讹紝 + * 闇瑕佸皢璇ョ粍鐨勯厤棰濋噸缃负0锛屼互闃叉涓嬫璁剧疆cpuset鏃跺彈鍒板奖鍝嶃 + */ + cgutil_vaddr[TOPCG_GAUSSDB]->ainfo.quota = 0; + } + + return 0; + } + /* + * 鍑芥暟鍚嶏細cgexec_update_dynamic_top_cgroup + * 鍔熻兘锛氭洿鏂伴《灞侰group鐨勫姩鎬佸硷紝鍖呮嫭Root Cgroup銆丟uassdb:user Cgroup銆丆lass Cgroup鍜孊ackend Cgroup銆 + * 杩斿洖鍊硷細 + * -1锛氬紓甯 + * 0锛氭甯 + * + */ + static int cgexec_update_dynamic_top_cgroup(void) + { + if (cgutil_opt.toppct > 0 && cgexec_update_top_group_percent() == -1) { // 濡傛灉鐢ㄦ埛璁剧疆浜唗oppct涓旀洿鏂伴《灞傜粍鐨勭櫨鍒嗘瘮澶辫触 + return -1; + } + + if (*cgutil_opt.sets) // 濡傛灉鐢ㄦ埛璁剧疆浜唖ets + return cgexec_update_top_group_cpuset_userset(cgutil_opt.topname, cgutil_opt.sets); + + return 0; + } + /* + * 鍑芥暟鍚嶏細cgexec_update_fixed_class_cgroup + * 鍔熻兘锛氭牴鎹敤鎴疯缃殑"--fixed"閫夐」锛屾洿鏂癈lass缁勫拰Workload缁勭殑cpuset鍊 + * 杩斿洖鍊硷細 + * -1锛氬紓甯 + * 0锛氭甯 + * + */ + static int cgexec_update_fixed_class_cgroup(void) + { + int i, cls = 0, wd = 0; + char cpusets[CPUSET_LEN]; // 淇濆瓨cpuset鍊肩殑瀛楃鏁扮粍 + int need_reset = 0; // 鏍囧織鍙橀噺锛岃〃绀烘槸鍚﹂渶瑕侀噸缃 + + /* 妫鏌lass缁勬槸鍚﹀瓨鍦 */ + for (i = CLASSCG_START_ID; i <= CLASSCG_END_ID; i++) { + if (cgutil_vaddr[i]->used == 0) + continue; + + if (0 == strcmp(cgutil_vaddr[i]->grpname, cgutil_opt.clsname)) { + cls = i; + break; + } + } + + if (cls) { + if (cgutil_opt.wdname[0]) { + wd = cgexec_search_workload_group(cls); + + if (wd) { + if (cgutil_opt.setspct) { + /* + * 姝ラ1锛氭鏌ユ柊璁剧疆鐨勭櫨鍒嗘瘮鏄惁浣挎荤櫨鍒嗘瘮瓒呰繃100%銆 + * 缁撴灉锛 + * setslength = -1锛氳秴杩100%銆 + * setslength = 0锛歝pusets宸茬粡璁剧疆濂戒簡锛岃繘鍏ユ楠3銆 + * setslength > 0锛歝pusets涓虹┖锛岄渶瑕侀噸缃紝杩涘叆姝ラ2銆 + */ + if ((need_reset = cgexec_check_cpuset_percent(cls, wd, cpusets)) == -1) + return -1; + + /* 姝ラ2锛氬鍏朵粬宸ヤ綔璐熻浇缁勮繘琛岀鐗囨暣鐞 */ + if (need_reset > 0 && (cgexec_reset_cpuset_cgroups(cls, wd) == -1)) + return -1; + + /* 姝ラ3锛氭洿鏂板伐浣滆礋杞界粍 */ + if (cgexec_update_cgroup_cpuset(cgutil_vaddr[wd], cpusets) == -1) + return -1; + + cgutil_vaddr[wd]->ainfo.quota = cgutil_opt.setspct; + + return 0; + } + else if (cgutil_opt.setfixed) { + if (cgexec_update_cgroup_cpuset(cgutil_vaddr[wd], cgutil_vaddr[cls]->cpuset) == -1) + return -1; + + cgutil_vaddr[wd]->ainfo.quota = 0; + + return 0; + } + } + else { + fprintf(stderr, "ERROR: 鎸囧畾鐨勫伐浣滆礋杞界粍%s涓嶅瓨鍦紒\n", cgutil_opt.wdname); return -1; } } + if (cgutil_opt.setspct) { + /* + * 姝ラ1锛氭鏌ョ被鐨勬柊璁剧疆鐧惧垎姣旀槸鍚﹀悎娉曘 + * 杩斿洖鍊 = -1锛氫笉鍚堟硶 + * 杩斿洖鍊 = 0锛氬悎娉曪紝涓嶉渶瑕佽繘琛岀鐗囨暣鐞 + * 杩斿洖鍊 > 0锛氶渶瑕佽繘琛岀鐗囨暣鐞 + */ + if ((need_reset = cgexec_check_cpuset_percent(TOPCG_CLASS, cls, cpusets)) == -1) + return -1; + + /* 姝ラ2锛氬鍏朵粬绫荤粍杩涜纰庣墖鏁寸悊锛屾寜鐧惧垎姣旈噸缃畠浠墍灞炵殑宸ヤ綔璐熻浇缁 */ + if (need_reset > 0 && (cgexec_reset_cpuset_cgroups(TOPCG_CLASS, cls) == -1)) + return -1; + + /* 姝ラ3锛氭洿鏂扮被缁 */ + if (cgexec_update_class_cpuset(cls, cpusets) == -1) { + fprintf(stderr, "ERROR: 鏇存柊绫荤粍\"%s\"鐨刢puset澶辫触銆俓n", cgutil_vaddr[cls]->grpname); + return -1; + } + + /* + * 姝ラ4锛氭洿鏂版墍灞炵殑宸ヤ綔璐熻浇缁 + * 0 琛ㄧず鍦ㄩ噸缃垪琛ㄤ腑娌℃湁闇瑕佽蹇界暐鐨勭粍 + */ + if (cgexec_reset_cpuset_cgroups(cls, 0) == -1) { + fprintf(stderr, "ERROR: 閲嶇疆绫荤粍\"%s\"鐨勫伐浣滆礋杞絚puset澶辫触銆俓n", cgutil_vaddr[cls]->grpname); + return -1; + } + cgutil_vaddr[cls]->ainfo.quota = cgutil_opt.setspct; + + } + else if (cgutil_opt.setfixed) { + if (cgexec_update_class_cpuset(cls, cgutil_vaddr[TOPCG_CLASS]->cpuset) == -1) + return -1; + + if (cgexec_reset_cpuset_cgroups(cls, 0) == -1) { + fprintf(stderr, "ERROR: 閲嶇疆绫荤粍\"%s\"鐨勫伐浣滆礋杞絚puset澶辫触銆俓n", cgutil_vaddr[cls]->grpname); + return -1; + } + + cgutil_vaddr[cls]->ainfo.quota = 0; + } } - } - - return 0; -} - -/* - * @Description: umount the Cgroup file system. - * @IN void - * @Return: -1: abnormal 0: normal - * @See also: - */ -int cgexec_umount_root_cgroup(void) -{ - int i, ret; - char cmd[MAX_COMMAND_LENGTH]; - char* path = NULL; - errno_t rc; - - if (cgutil_opt.mpflag) - path = cgutil_opt.mpoint; - else - path = GSCGROUP_MOUNT_POINT; - - if (CheckBackendEnv(path) != 0) { - return -1; - } - for (i = 0; i < MOUNT_SUBSYS_KINDS; ++i) { - /* 'blkio' is invalid, ignore it */ - if (i == MOUNT_BLKIO_ID) - continue; - - if (*cgutil_opt.mpoints[i] == '\0') { - continue; + else { + fprintf(stderr, "ERROR: 鎸囧畾鐨勭被缁%s涓嶅瓨鍦紒\n", cgutil_opt.clsname); + return -1; } - /* It has mounted on old default point, we unmount it only once. */ - if (strcmp(cgutil_opt.mpoints[i], GSCGROUP_MOUNT_POINT_OLD) == 0) { - rc = snprintf_s(cmd, sizeof(cmd), sizeof(cmd) - 1, "umount %s", GSCGROUP_MOUNT_POINT_OLD); - securec_check_ss_c(rc, "\0", "\0"); + return 0; + } + /* 鍑芥暟鍚嶏細cgexec_update_fixed_backend_cgroup + * 鍔熻兘锛氭牴鎹敤鎴疯缃殑"--fixed"閫夐」鏇存柊Backend缁勭殑cpuset鍊 + * 杩斿洖鍊硷細 + * -1: 寮傚父 + * 0: 姝e父 + * + */ + static int cgexec_update_fixed_backend_cgroup(void) + { + int i, bkd = 0; // 鍙橀噺i锛宐kd琛ㄧずBackend缁勭殑绱㈠紩鍜屾爣璁 + char cpuset[CPUSET_LEN]; // 瀛楃鏁扮粍cpuset淇濆瓨cpuset鐨勫 + int need_reset = 0; // 鏍囪鏄惁闇瑕侀噸缃甤puset + + /* 妫鏌ヨ绫绘槸鍚﹀瓨鍦 */ + for (i = BACKENDCG_START_ID; i <= BACKENDCG_END_ID; i++) { + if (cgutil_vaddr[i]->used == 0) // 妫鏌ヨ缁勬槸鍚﹁浣跨敤 + continue; + + if (0 == strcmp(cgutil_vaddr[i]->grpname, cgutil_opt.bkdname)) { // 妫鏌ョ粍鍚嶆槸鍚﹀尮閰 + bkd = i; + break; + } + } + + if (bkd) { + /* 鎸夌収鐧惧垎姣旇缃甤puset */ + if (cgutil_opt.setspct) { + /* 涓庢洿鏂板伐浣滆礋杞界粍鐩稿悓鐨勬楠 */ + if ((need_reset = cgexec_check_cpuset_percent(TOPCG_BACKEND, bkd, cpuset)) == -1) // 妫鏌ユ槸鍚﹂渶瑕侀噸缃甤puset + return -1; + + if (need_reset > 0 && (cgexec_reset_cpuset_cgroups(TOPCG_BACKEND, bkd) == -1)) // 濡傛灉闇瑕侀噸缃紝鍒欓噸缃甤puset + return -1; + + if (cgexec_update_cgroup_cpuset(cgutil_vaddr[bkd], cpuset) == -1) // 鏇存柊cpuset鍊 + return -1; + + cgutil_vaddr[bkd]->ainfo.quota = cgutil_opt.setspct; // 鏇存柊quota鍊 + } + else if (cgutil_opt.setfixed) { + if (cgexec_update_cgroup_cpuset(cgutil_vaddr[bkd], cgutil_vaddr[TOPCG_BACKEND]->cpuset) == -1) // 鏇存柊cpuset鍊 + return -1; + + cgutil_vaddr[bkd]->ainfo.quota = 0; // 灏唓uota鍊肩疆涓0 + } + } + else { + fprintf(stderr, "ERROR: the specified backend group %s doesn't exist!\n", cgutil_opt.bkdname); + return -1; // 缁勪笉瀛樺湪锛岃繑鍥炲紓甯 + } + + return 0; // 杩斿洖姝e父 + } + + /* 鍑芥暟鍚嶏細cgexec_update_fixed_top_cgroup + * 鍔熻兘锛氭牴鎹敤鎴疯缃殑"--fixed"閫夐」鏇存柊Top缁勭殑cpuset鍊 + * 杩斿洖鍊硷細 + * -1: 寮傚父 + * 0: 姝e父 + * + */ + static int cgexec_update_fixed_top_cgroup(void) + { + char cpusets[CPUSET_LEN]; // 瀛楃鏁扮粍cpusets淇濆瓨cpuset鐨勫 + int need_reset = 0; // 鏍囪鏄惁闇瑕侀噸缃甤puset + int top = 0; // 鏍囪Top缁勭殑绱㈠紩 + + if (0 == strcmp(cgutil_opt.topname, GSCGROUP_ROOT)) { // 濡傛灉鏄牴缁勶紝鍒欐棤娉曚慨鏀 + fprintf(stderr, "ERROR: users can't modify the Root cgroup with \"--fixed\"!\n"); + return -1; + } + else if (0 == strcmp(cgutil_opt.topname, GSCGROUP_TOP_DATABASE) || + 0 == strcmp(cgutil_opt.topname, cgutil_vaddr[TOPCG_GAUSSDB]->grpname)) { // 濡傛灉鏄疓aussdb缁勶紝鍒欐棤娉曚慨鏀筩puset鐨勭櫨鍒嗘瘮 + top = TOPCG_GAUSSDB; + if (cgutil_opt.setspct) { + fprintf(stderr, "ERROR: users can't modify the cpu cores percentage of Gaussdb cgroup with \"--fixed\"!\n"); + return -1; + } + } + else if (0 == strcmp(cgutil_opt.topname, GSCGROUP_TOP_BACKEND)) + top = TOPCG_BACKEND; + else if (0 == strcmp(cgutil_opt.topname, GSCGROUP_TOP_CLASS)) + top = TOPCG_CLASS; + else { + fprintf(stderr, "ERROR: the specified top group %s doesn't exist!\n", cgutil_opt.topname); + return -1; // 鏈壘鍒版寚瀹氱殑缁勶紝杩斿洖寮傚父 + } + + if (top) { + if (cgutil_opt.setspct) { + if ((need_reset = cgexec_check_cpuset_percent(TOPCG_GAUSSDB, top, cpusets)) == -1) // 妫鏌ユ槸鍚﹂渶瑕侀噸缃甤puset + return -1; + + if (need_reset && (cgexec_reset_cpuset_cgroups(TOPCG_GAUSSDB, top) == -1)) // 濡傛灉闇瑕侀噸缃紝鍒欓噸缃甤puset + return -1; + + if (cgexec_update_top_group_cpuset(top, cpusets) == -1 || cgexec_reset_cpuset_cgroups(top, 0) == -1) // 鏇存柊cpuset鍊煎苟閲嶇疆cpuset + return -1; + + cgutil_vaddr[top]->ainfo.quota = cgutil_opt.setspct; // 鏇存柊quota鍊 + } + else if (cgutil_opt.setfixed) { + if (cgexec_update_top_group_cpuset(top, cgutil_vaddr[TOPCG_GAUSSDB]->cpuset) == -1 || + cgexec_reset_cpuset_cgroups(top, 0) == -1) // 鏇存柊cpuset鍊煎苟閲嶇疆cpuset + return -1; + + cgutil_vaddr[top]->ainfo.quota = 0; // 灏唓uota鍊肩疆涓0 + } + } + + return 0; // 杩斿洖姝e父 + } + /* + **************** EXTERNAL FUNCTION ******************************** + */ + + /* + * function name: cgexec_check_SLESSP2_version + * description : 妫鏌ュ綋鍓嶆搷浣滅郴缁熺増鏈槸鍚︿负SLES SP2 + * return value : + * 1: 鏄疭LES SP2 + * 0: 涓嶆槸SLES SP2锛屽亣瀹氫负SLES SP1 + * -1: 寮傚父 + * + * 娉ㄦ剰锛氬湪/proc/cgroups涓悳绱"io"鍒椼 + * 闇瑕佹鏌ュ兼槸鍚︽敮鎸佷笅涓涓増鏈殑Redhat鎴朎uler銆 + */ + int cgexec_check_SLESSP2_version(void) + { + char buf[PROCLINE_LEN]; + FILE* f = NULL; + + f = fopen("/proc/cgroups", "r"); + + if (f == NULL) + return -1; + + while (NULL != fgets(buf, PROCLINE_LEN, f)) { + /* proc涓殑绀轰緥锛 + * #subsys_name hierarchy num_cgroups enabled + * cpu 0 1 1 + * + * 鎼滅储"blkio"鍒 + */ + + if (strstr(buf, MOUNT_BLKIO_NAME) != NULL) { + cgutil_is_sles11_sp2 = 1; + fclose(f); + return 1; + } + } + + fclose(f); + return 0; + } + + /* + * @Description: 妫鏌ユ槸鍚︽墽琛屽崌绾с + * @IN void + * @Return: 1: 鍗囩骇 0: 涓嶅崌绾 + * @See also: + */ + int cgexec_check_mount_for_upgrade(void) + { + int i, ret, old_mp = 0; + + errno_t sret; + + /* 鍙湁root鐢ㄦ埛鍙互鎵ц鍗囩骇 */ + if (geteuid() != 0) + return 0; + + /* + * 濡傛灉cpuset鍜宑puacct鏈寕杞斤紝鎴戜滑灏嗘鏌pu鎴朾lkio鏄惁鎸傝浇鍦ㄩ粯璁ょ偣涓婏紝濡傛灉鏄紝鍒欏繀椤诲厛鍗歌浇瀹冧滑锛 + * 鐒跺悗浣跨敤鏂扮殑鎸傝浇鐐规寕杞芥墍鏈夊瓙绯荤粺銆傚鏋滃湪榛樿鐐逛笂娌℃湁绯荤粺锛屽垯涓嶉渶瑕佸嵏杞藉畠浠 + */ + for (i = 0; i < MOUNT_SUBSYS_KINDS; ++i) { + /* 蹇界暐blkio鍜宮emory */ + if (i == MOUNT_BLKIO_ID || i == MOUNT_MEMORY_ID) + continue; + + if (*cgutil_opt.mpoints[i]) { + if (strcmp(cgutil_opt.mpoints[i], GSCGROUP_MOUNT_POINT_OLD) == 0) { + char fname[256]; + int cnt = 0; + struct dirent* file = NULL; + DIR* dir = opendir(GSCGROUP_MOUNT_POINT_OLD); + + if (dir == NULL) { + fprintf(stderr, "ERROR: failed to open %s.\n", GSCGROUP_MOUNT_POINT_OLD); + return -1; + } + + sret = snprintf_s( + fname, sizeof(fname), sizeof(fname) - 1, "%s:%s", "Gaussdb", cgutil_passwd_user->pw_name); + securec_check_intval(sret, closedir(dir), -1); + + /* 濡傛灉鍏朵粬鐢ㄦ埛鍦ㄩ粯璁ゆ寕杞界偣涓垱寤轰簡cgroup锛屽垯鏃犳硶鍗歌浇璇ョ偣銆 */ + while ((file = readdir(dir)) != NULL) { + if (file->d_type != DT_DIR || strcmp(file->d_name, fname) == 0) + continue; + + if (file->d_type == DT_DIR && strncmp(file->d_name, "Gaussdb:", 8) == 0) + ++cnt; + } + + closedir(dir); + + if (cnt > 0) { + fprintf(stderr, + "ERROR: The other user has cgroups in \"%s\", upgrade failed.\n", + GSCGROUP_MOUNT_POINT_OLD); + return -1; + } + + old_mp++; + } + } + } + + /* 濡傛灉cgroups鎸傝浇鍦ㄦ棫璺緞涓婏紝鍙嵏杞戒竴娆 */ + if (old_mp) { + char cmd[128]; + + /* 鍦/dev/cgroups涓嬫寕杞戒簡澶氫釜cgroups */ + if (old_mp > 1) { + (void)cgroup_init(); /* 鍒濆鍖 */ + (void)cgptree_drop_cgroups(); + } + + sret = snprintf_s(cmd, sizeof(cmd), sizeof(cmd) - 1, "umount %s", GSCGROUP_MOUNT_POINT_OLD); + securec_check_intval(sret, , -1); ret = system(cmd); if (CheckSystemSucess(ret) == -1) { @@ -3845,96 +3218,520 @@ int cgexec_umount_root_cgroup(void) return -1; } - break; + /* 鑾峰彇鏂扮殑鎸傝浇鐐瑰苟鎸傝浇瀹冧滑 */ + (void)cgexec_get_mount_points(); + (void)cgexec_mount_root_cgroup(); + } + else + cgutil_opt.upgrade = 0; + + return 0; + } + /* + * @Description: 鑾峰彇鎵鏈塩group瀛愮郴缁熺殑鎸傝浇鐐广 + * @IN void + * @Return: 0: 姝e父 -1: 寮傚父 + * @See also: + */ + int cgexec_get_mount_points(void) + { + struct mntent* ent = NULL; + char mntent_buffer[5 * FILENAME_MAX]; // 缂撳啿鍖哄ぇ灏 + + struct mntent temp_ent; // 鐢ㄤ簬瑙f瀽鎸傝浇鐐圭殑涓存椂缁撴瀯浣 + int i; + + errno_t rc; // 閿欒鐮 + rc = memset_s(&temp_ent, sizeof(temp_ent), 0, sizeof(temp_ent)); // 鍒濆鍖栦复鏃剁粨鏋勪綋 + securec_check_errno(rc, , -1); + + /* reset mount points */ + rc = memset_s(cgutil_opt.mpoints, MOUNT_SUBSYS_KINDS * MAXPGPATH, 0, MOUNT_SUBSYS_KINDS * MAXPGPATH); // 閲嶇疆鎸傝浇鐐规暟缁 + securec_check_errno(rc, , -1); + + /* open '/proc/mounts' to load mount points */ + FILE* proc_mount = fopen("/proc/mounts", "re"); // 鎵撳紑/proc/mounts鏂囦欢 + + if (proc_mount == NULL) + return -1; + + while ((ent = getmntent_r(proc_mount, &temp_ent, mntent_buffer, sizeof(mntent_buffer))) != NULL) { // 浠/proc/mounts涓鍙栨寕杞界偣淇℃伅 + /* not cgroup, pass */ + if (strcmp(ent->mnt_type, "cgroup") != 0) // 濡傛灉涓嶆槸cgroup鎸傝浇鐐癸紝璺宠繃 + continue; + + for (i = 0; i < MOUNT_SUBSYS_KINDS; ++i) { + if (hasmntopt(ent, cgutil_subsys_table[i]) == NULL) // 濡傛灉鎸傝浇鐐逛笉鍖呭惈褰撳墠瀛愮郴缁燂紝璺宠繃 + continue; + + /* get mount point */ + rc = snprintf_s(cgutil_opt.mpoints[i], // 鑾峰彇鎸傝浇鐐硅矾寰 + sizeof(cgutil_opt.mpoints[i]), + sizeof(cgutil_opt.mpoints[i]) - 1, + "%s", + ent->mnt_dir); + securec_check_intval(rc, fclose(proc_mount), -1); + } } - ret = CgexecUmountRootCgroupInternal(path, i); + fclose(proc_mount); // 鍏抽棴/proc/mounts鏂囦欢 + + return 0; } - return 0; -} + /* + * @Description: 妫娴媍group鏂囦欢绯荤粺鏄惁宸茬粡鎸傝浇銆 + * @IN void + * @Return: 1: 宸茬粡鎸傝浇鍦ㄦ寚瀹氱洰褰 + * 0: 鏈寕杞 + * -1: 宸茬粡鎸傝浇鍦ㄥ叾浠栫洰褰 + * @See also: + */ + int cgexec_detect_cgroup_mount(void) + { + int i, j; -/* - * function name: cgexec_delete_cgroups - * description : delete the Cgroup based on relative path - * arguments : - * relpath: the relative path - * return value : - * -1: abnormal - * 0: normal - * - * Note: the function is used when dropping a Cgroup. - */ -int cgexec_delete_cgroups(char* relpath) -{ - int ret; - struct cgroup* cg = NULL; + if (cgutil_opt.cflag <= 0) { + return 1; + } - /* allocate new cgroup structure */ - cg = cgroup_new_cgroup(relpath); - if (cg == NULL) { - fprintf(stdout, "ERROR: failed to create the new cgroup for %s\n", relpath); - return -1; + for (i = 0; i < MOUNT_SUBSYS_KINDS; ++i) { + if (i == MOUNT_BLKIO_ID) + continue; + + /* a subsys has not mounted, we must make sure its mount point is valid. */ + if (*cgutil_opt.mpoints[i] == '\0') { // 濡傛灉鎸傝浇鐐逛负绌 + if (cgutil_opt.mpflag == 0) { // 濡傛灉娌℃湁鏂扮殑鎸傝浇鐐 + /* no new mount point, make sure default point is valid */ + for (j = 0; j < MOUNT_SUBSYS_KINDS; ++j) + if (strcmp(cgutil_opt.mpoints[j], GSCGROUP_MOUNT_POINT) == 0) // 榛樿鎸傝浇鐐逛负鏈夋晥鐨 + return -1; + } + + return 0; + } + } + + return 1; + } + // 瀹氫箟涓涓潤鎬佸嚱鏁帮紝鐢ㄤ簬鍒犻櫎宸插瓨鍦ㄧ殑绗﹀彿閾炬帴 + static int RemoveExistSymbolLink(const char* mpoint) + { + int ret; + char cmd[MAX_COMMAND_LENGTH]; + struct stat statbuf; + errno_t rc; + + // 鍒濆鍖杝tatbuf涓0 + rc = memset_s(&statbuf, sizeof(statbuf), 0, sizeof(statbuf)); + securec_check_c(rc, "\0", "\0"); + + // 妫鏌ョ鍙烽摼鎺ユ槸鍚﹀瓨鍦 + ret = lstat(mpoint, &statbuf); + if (S_ISLNK(statbuf.st_mode)) { // 濡傛灉鏄鍙烽摼鎺 + // 鍒犻櫎绗﹀彿閾炬帴 + rc = snprintf_s(cmd, sizeof(cmd), sizeof(cmd) - 1, "rm %s", mpoint); + securec_check_ss_c(rc, "\0", "\0"); + + ret = system(cmd); + if (CheckSystemSucess(ret) == -1) { + fprintf(stderr, "ERROR: failed to remove exist symbol link %s!\n", mpoint); + return -1; + } + } + return 0; } - /* get all information regarding the cgroup from kernel */ - ret = cgroup_get_cgroup(cg); - if (ret != 0) { - fprintf( - stdout, "ERROR: failed to get '%s' cgroup information for %s(%d)\n", relpath, cgroup_strerror(ret), ret); + // 瀹氫箟涓涓潤鎬佸嚱鏁帮紝鐢ㄤ簬鎸傝浇cgroup鏂囦欢绯荤粺 + static int MountCgroupInternal(const char* mpoint, const char* type) + { + int ret; + char cmd[MAX_COMMAND_LENGTH]; + struct stat statbuf; + errno_t rc; + + // 鍒濆鍖杝tatbuf涓0 + rc = memset_s(&statbuf, sizeof(statbuf), 0, sizeof(statbuf)); + securec_check_c(rc, "\0", "\0"); + + // 妫鏌ユ柊鐨勬寕杞界偣鐩綍鏄惁瀛樺湪 + ret = stat(mpoint, &statbuf); + if (ret != 0 || !S_ISDIR(statbuf.st_mode)) { // 濡傛灉鏂扮殑鎸傝浇鐐圭洰褰曚笉瀛樺湪 + // 鍒涘缓鏂扮殑鎸傝浇鐐圭洰褰 + if (mkdir(mpoint, S_IRWXU) != 0) { + fprintf(stderr, "ERROR: failed to create %s directory!\n", mpoint); + return -1; + } + } + + // 鎸傝浇cgroup鏂囦欢绯荤粺鍒版柊鐨勬寕杞界偣鐩綍 + rc = snprintf_s(cmd, sizeof(cmd), sizeof(cmd) - 1, "mount -t cgroup -o %s %s %s", type, type, mpoint); + securec_check_ss_c(rc, "\0", "\0"); + ret = system(cmd); + if (CheckSystemSucess(ret) == -1) { + fprintf(stderr, "ERROR: failed to mount cgroup under %s!\n", mpoint); + return -1; + } + fprintf(stderr, "LOG: mount %s success.\n", type); + return 0; + } + + // 瀹氫箟涓涓潤鎬佸嚱鏁帮紝鐢ㄤ簬鍒涘缓绗﹀彿閾炬帴 + static int LinkCpuCgroup(const char* target, const char* source) + { + int ret; + char cmd[MAX_COMMAND_LENGTH]; + errno_t rc; + + // 鍒涘缓绗﹀彿閾炬帴 + rc = snprintf_s(cmd, sizeof(cmd), sizeof(cmd) - 1, "ln -s %s %s", source, target); + securec_check_ss_c(rc, "\0", "\0"); + + ret = system(cmd); + if (CheckSystemSucess(ret) == -1) { + fprintf(stderr, "ERROR: failed to mount cgroup under %s!\n", target); + return -1; + } + + return 0; + } + + // 瀹氫箟涓涓潤鎬佸嚱鏁帮紝鐢ㄤ簬閲嶆柊鎸傝浇CPU cgroup + static int CgexecRemountCpuCgroup(const char* path, const char* tmp_mpoint) + { + int ret; + char mpoint[MOUNT_POINT_LENGTH]; + errno_t rc; + + // 鎷兼帴鏂扮殑鎸傝浇鐐硅矾寰 + rc = snprintf_s(mpoint, sizeof(mpoint), sizeof(mpoint) - 1, "%s/cpu", path); + securec_check_ss_c(rc, "\0", "\0"); + + // 鍒犻櫎宸插瓨鍦ㄧ殑绗﹀彿閾炬帴 + ret = RemoveExistSymbolLink(mpoint); + if (ret != 0) { + return ret; + } + + // 鍒涘缓CPU cgroup鐨勭鍙烽摼鎺 + ret = LinkCpuCgroup(mpoint, tmp_mpoint); + if (ret != 0) { + return ret; + } + + // 鎷兼帴鏂扮殑鎸傝浇鐐硅矾寰 + rc = snprintf_s(mpoint, sizeof(mpoint), sizeof(mpoint) - 1, "%s/cpuacct", path); + securec_check_ss_c(rc, "\0", "\0"); + + // 鍒犻櫎宸插瓨鍦ㄧ殑绗﹀彿閾炬帴 + ret = RemoveExistSymbolLink(mpoint); + if (ret != 0) { + return ret; + } + + // 鍒涘缓CPU cgroup鐨勭鍙烽摼鎺 + ret = LinkCpuCgroup(mpoint, tmp_mpoint); + + return ret; + } + + // 鍑芥暟鍔熻兘锛氬湪鎸囧畾璺緞涓婃寕杞絚pu鍜宑puacct瀛愮郴缁熺殑Cgroup鏂囦欢绯荤粺 + // 鍙傛暟锛歝onst char* path - 鎸囧畾璺緞 + // 杩斿洖鍊硷細int - 杩斿洖0琛ㄧず鎸傝浇鎴愬姛锛岃繑鍥炲叾浠栧艰〃绀烘寕杞藉け璐 + + static int CgexecMountCpuCgroup(const char* path) + { + int ret; + char tmp_mpoint[MOUNT_POINT_LENGTH]; // 涓存椂鎸傝浇璺緞 + char mpoint[MOUNT_POINT_LENGTH]; // 鎸傝浇璺緞 + errno_t rc; + + // 鎷兼帴鎸傝浇璺緞 + rc = snprintf_s(tmp_mpoint, sizeof(tmp_mpoint), sizeof(tmp_mpoint) - 1, + "%s/cpu,cpuacct", path); + securec_check_ss_c(rc, "\0", "\0"); + + // 鎸傝浇cpu鍜宑puacct瀛愮郴缁 + ret = MountCgroupInternal(tmp_mpoint, "cpu,cpuacct"); + if (ret != 0) { + // 鎸傝浇澶辫触锛岃〃绀篶pu鍜宑puacct娌℃湁涓璧锋寕杞芥垚鍔 + rc = snprintf_s(mpoint, sizeof(mpoint), sizeof(mpoint) - 1, + "%s/cpu", path); + securec_check_ss_c(rc, "\0", "\0"); + ret = MountCgroupInternal(mpoint, cgutil_subsys_table[MOUNT_CPU_ID]); + if (ret != 0) { + return ret; + } + rc = snprintf_s(mpoint, sizeof(mpoint), sizeof(mpoint) - 1, + "%s/cpuacct", path); + securec_check_ss_c(rc, "\0", "\0"); + ret = MountCgroupInternal(mpoint, cgutil_subsys_table[MOUNT_CPUACCT_ID]); + } + else { + // 鎸傝浇鎴愬姛锛岃皟鐢–gexecRemountCpuCgroup鍑芥暟閲嶆柊鎸傝浇cpu鍜宑puacct瀛愮郴缁 + ret = CgexecRemountCpuCgroup(path, tmp_mpoint); + } + + return ret; + } + // 鍑芥暟鍔熻兘锛氭寕杞芥牴鐩綍涓婄殑Cgroup鏂囦欢绯荤粺 + // 鍙傛暟锛歷oid + // 杩斿洖鍊硷細int - 杩斿洖0琛ㄧず鎸傝浇鎴愬姛锛岃繑鍥-1琛ㄧず鎸傝浇澶辫触 + + int cgexec_mount_root_cgroup(void) + { + int i, ret; + + char mpoint[MOUNT_POINT_LENGTH]; // 鎸傝浇璺緞 + char* path = NULL; // 鎸傝浇鐩綍 + struct stat statbuf; // 鏂囦欢鐘舵佷俊鎭粨鏋勪綋 + + errno_t rc; + rc = memset_s(&statbuf, sizeof(statbuf), 0, sizeof(statbuf)); + securec_check_c(rc, "\0", "\0"); + + // 璁剧疆鎸傝浇鐩綍 + if (cgutil_opt.mpflag) + path = cgutil_opt.mpoint; + else + path = GSCGROUP_MOUNT_POINT; + + // 妫鏌ュ悗绔幆澧冨彉閲忔槸鍚︽甯 + if (CheckBackendEnv(path) != 0) { + return -1; + } + + // 鍒涘缓鎸傝浇璺緞鐩綍 + ret = stat(path, &statbuf); + if (0 != ret || !S_ISDIR(statbuf.st_mode)) { + if (mkdir(path, S_IRWXU | (S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)) != 0) { + fprintf(stderr, "ERROR: failed to create %s directory!\n", path); + return -1; + } + (void)chmod(path, S_IRWXU | (S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)); // 淇敼鏉冮檺涓755 + } + + // 鎸傝浇鍚勪釜瀛愮郴缁熺殑Cgroup鏂囦欢绯荤粺 + for (i = 0; i < MOUNT_SUBSYS_KINDS; ++i) { + // 'blkio'瀛愮郴缁熸棤鏁堬紝蹇界暐 + if (i == MOUNT_BLKIO_ID) + continue; + + // 濡傛灉瀛愮郴缁熸湭鎸傝浇锛屼娇鐢ㄦ柊鐨勮矾寰勮繘琛屾寕杞 + if (*cgutil_opt.mpoints[i] == '\0') { + // 鍦ㄦ柊鐨凩inux绯荤粺涓婏紝cpu鍜宑puacct瀛愮郴缁熼兘鎸傝浇鍦╟pu,cpuacct璺緞涓 + if (i == MOUNT_CPU_ID) { + ret = CgexecMountCpuCgroup(path); + i++; + } + else { + rc = snprintf_s(mpoint, sizeof(mpoint), sizeof(mpoint) - 1, "%s/%s", path, cgutil_subsys_table[i]); + securec_check_ss_c(rc, "\0", "\0"); + ret = MountCgroupInternal(mpoint, cgutil_subsys_table[i]); + } + } + } + + return 0; + } + // 鍑芥暟鍔熻兘锛氬嵏杞紺group鏂囦欢绯荤粺 + // 鍑芥暟鍙傛暟锛歝onst char* path - Cgroup鏂囦欢绯荤粺鐨勮矾寰 + // int index - Cgroup瀛愮郴缁熺殑绱㈠紩 + // 鍑芥暟杩斿洖鍊硷細-1琛ㄧず寮傚父锛0琛ㄧず姝e父 + static int CgexecUmountRootCgroupInternal(const char* path, int index) + { + int ret; + char cmd[MAX_COMMAND_LENGTH], mpoint[MOUNT_POINT_LENGTH]; + errno_t rc; + + // 鑾峰彇鎸傝浇鐐圭殑瀹屾暣璺緞 + rc = snprintf_s(mpoint, sizeof(mpoint), sizeof(mpoint) - 1, "%s/%s", path, cgutil_subsys_table[index]); + securec_check_ss_c(rc, "\0", "\0"); + + // 濡傛灉鎸囧畾鐨勬寕杞界偣鍜宑gutil_opt.mpoints[index]鐩哥瓑锛屽垯鍗歌浇鎸囧畾鐨勬寕杞界偣 + if (strcmp(cgutil_opt.mpoints[index], mpoint) == 0) { + rc = snprintf_s(cmd, sizeof(cmd), sizeof(cmd) - 1, "umount %s", mpoint); + securec_check_ss_c(rc, "\0", "\0"); + + ret = system(cmd); + if (CheckSystemSucess(ret) == -1) { + fprintf(stderr, "ERROR: failed to umount cgroup under %s!\n", mpoint); + return -1; + } + fprintf(stderr, "LOG: umount cgroup under %s!\n", mpoint); + } + else if (index == MOUNT_CPU_ID || index == MOUNT_CPUACCT_ID) { + // 瀵逛簬CPU瀛愮郴缁熷拰CPUACCT瀛愮郴缁燂紝妫鏌ユ柊鐨勬寕杞界偣鐩綍锛屽苟绉婚櫎宸插瓨鍦ㄧ殑绗﹀彿閾炬帴 + RemoveExistSymbolLink(mpoint); + + if (index == MOUNT_CPU_ID) { + rc = snprintf_s(mpoint, sizeof(mpoint), sizeof(mpoint) - 1, "%s/cpu,cpuacct", path); + securec_check_ss_c(rc, "\0", "\0"); + if (*cgutil_opt.mpoints[index] && strcmp(cgutil_opt.mpoints[index], mpoint) == 0) { + rc = snprintf_s(cmd, sizeof(cmd), sizeof(cmd) - 1, "umount %s", mpoint); + securec_check_ss_c(rc, "\0", "\0"); + + ret = system(cmd); + if (CheckSystemSucess(ret) == -1) { + fprintf(stderr, "ERROR: failed to umount cgroup under %s!\n", mpoint); + return -1; + } + } + } + } + + return 0; + } + + /* + * 鍑芥暟鍔熻兘锛氬嵏杞紺group鏂囦欢绯荤粺鐨勬牴鐩綍 + * 鍑芥暟鍙傛暟锛歷oid + * 鍑芥暟杩斿洖鍊硷細-1琛ㄧず寮傚父锛0琛ㄧず姝e父 + */ + int cgexec_umount_root_cgroup(void) + { + int i, ret; + char cmd[MAX_COMMAND_LENGTH]; + char* path = NULL; + errno_t rc; + + // 濡傛灉mpflag涓簍rue锛屽垯浣跨敤鎸囧畾鐨勬寕杞界偣璺緞锛涘惁鍒欎娇鐢ㄩ粯璁ょ殑GSCGROUP_MOUNT_POINT璺緞 + if (cgutil_opt.mpflag) + path = cgutil_opt.mpoint; + else + path = GSCGROUP_MOUNT_POINT; + + // 妫鏌ュ悗绔幆澧冨弬鏁版槸鍚﹀悎娉 + if (CheckBackendEnv(path) != 0) { + return -1; + } + for (i = 0; i < MOUNT_SUBSYS_KINDS; ++i) { + // 'blkio'涓烘棤鏁堝瓙绯荤粺锛屽拷鐣ヤ箣 + if (i == MOUNT_BLKIO_ID) + continue; + + if (*cgutil_opt.mpoints[i] == '\0') { + continue; + } + + // 濡傛灉Cgroup瀛愮郴缁熸寕杞藉湪鏃х殑榛樿鎸傝浇鐐逛笂锛屽垯鍙嵏杞戒竴娆 + if (strcmp(cgutil_opt.mpoints[i], GSCGROUP_MOUNT_POINT_OLD) == 0) { + rc = snprintf_s(cmd, sizeof(cmd), sizeof(cmd) - 1, "umount %s", GSCGROUP_MOUNT_POINT_OLD); + securec_check_ss_c(rc, "\0", "\0"); + + ret = system(cmd); + if (CheckSystemSucess(ret) == -1) { + fprintf(stderr, "ERROR: failed to umount cgroup under %s!\n", GSCGROUP_MOUNT_POINT_OLD); + return -1; + } + + break; + } + + // 璋冪敤CgexecUmountRootCgroupInternal鍑芥暟鍗歌浇Cgroup鏂囦欢绯荤粺 + ret = CgexecUmountRootCgroupInternal(path, i); + } + + return 0; + } + /* + * 鍑芥暟鍚嶏細cgexec_delete_cgroups + * 鍔熻兘锛氭牴鎹浉瀵硅矾寰勫垹闄group + * 鍙傛暟锛 + * relpath: 鐩稿璺緞 + * 杩斿洖鍊硷細 + * -1: 寮傚父 + * 0: 姝e父 + * + * 娉ㄦ剰锛氬綋鍒犻櫎涓涓狢group鏃朵娇鐢ㄨ鍑芥暟銆 + */ + int cgexec_delete_cgroups(char* relpath) + { + int ret; + struct cgroup* cg = NULL; + + /* 鍒嗛厤鏂扮殑cgroup缁撴瀯 */ + cg = cgroup_new_cgroup(relpath); + if (cg == NULL) { + fprintf(stdout, "閿欒锛氫负%s鍒涘缓鏂扮殑cgroup澶辫触\n", relpath); + return -1; + } + + /* 浠庡唴鏍镐腑鑾峰彇鍏充簬cgroup鐨勬墍鏈変俊鎭 */ + ret = cgroup_get_cgroup(cg); + if (ret != 0) { + fprintf( + stdout, "閿欒锛氳幏鍙%s鐨刢group淇℃伅澶辫触锛岄敊璇爜锛%d锛岄敊璇俊鎭細%s\n", relpath, ret, cgroup_strerror(ret)); + cgroup_free(&cg); + return -1; + } + + (void)cgroup_delete_cgroup_ext(cg, CGFLAG_DELETE_RECURSIVE | CGFLAG_DELETE_IGNORE_MIGRATION); + cgroup_free(&cg); - return -1; + + return 0; } - (void)cgroup_delete_cgroup_ext(cg, CGFLAG_DELETE_RECURSIVE | CGFLAG_DELETE_IGNORE_MIGRATION); + /* + * 鍑芥暟鍚嶏細cgexec_create_groups + * 鍔熻兘锛氬垱寤篊group鐨勪富鍏ュ彛锛 + * 褰撶敤鎴锋槸root鏃讹紝闇瑕佹鏌groups鏄惁宸茬粡鍒涘缓銆傚鏋滄病鏈夛紝灏嗗垱寤洪粯璁ょ殑Cgroups銆 + * 褰撶敤鎴锋槸闈瀝oot鐢ㄦ埛鏃讹紝濡傛灉Cgroups涓嶅瓨鍦紝鍒欐姤閿欍 + * 鍙兘鍒涘缓Class鍜學orkload Cgroup锛屼絾涓嶅厑璁镐负DefaultClass Cgroup鍒涘缓workload Cgroup銆 + * 杩斿洖鍊硷細 + * -1: 寮傚父 + * 0: 姝e父 + * + */ + int cgexec_create_groups(void) + { + int cgcnt; + struct stat buf; + int ret; + size_t len = sizeof(cgutil_opt.mpoint) + 1 + sizeof(GSCGROUP_TOP_DATABASE) + 1 + USERNAME_LEN + 1 + + sizeof(GSCGROUP_TOP_CLASS); + char* cgpath = (char*)malloc(len); + errno_t sret; - cgroup_free(&cg); + if (cgpath == NULL) + return -1; - return 0; -} + sret = memset_s(&buf, sizeof(buf), 0, sizeof(buf)); + securec_check_errno(sret, free(cgpath), -1); -/* - * function name: cgexec_create_groups - * description : main entry of create Cgroup; - * When the user is root, it needs to check if Cgroups have - * been created. If it didn't, the default Cgroups will be created. - * When the user is non-root user and the Cgroups didn't exist, - * it needs to report an error. - * Only Class and Workload Cgroup can be created, but it doesn't - * allow to create workload Cgroup for DefaultClass Cgroup. - * return value : - * -1: abnormal - * 0: normal - * - */ -int cgexec_create_groups(void) -{ - int cgcnt; - struct stat buf; - int ret; - size_t len = sizeof(cgutil_opt.mpoint) + 1 + sizeof(GSCGROUP_TOP_DATABASE) + 1 + USERNAME_LEN + 1 + - sizeof(GSCGROUP_TOP_CLASS); - char* cgpath = (char*)malloc(len); - errno_t sret; + if (geteuid() == 0) { + /* 鍒涘缓cm cgroup锛屾鍑芥暟鍙互妫鏌ョ洰褰曟槸鍚﹀瓨鍦紝鍥犳鎴戜滑涓嶉渶瑕佸厛杩涜妫鏌 */ + (void)cgexec_create_cm_default_cgroup(); - if (cgpath == NULL) - return -1; + cgcnt = cgexec_get_cgroup_number(); + if (1 == cgcnt) { + /* 鍒涘缓榛樿鐨刢groups */ + (void)cgexec_create_default_cgroups(); + } + else { + /* 妫鏌ユ槸鍚︿负鎸囧畾鐢ㄦ埛鍒涘缓浜哻groups */ + sret = sprintf_s(cgpath, + len, + "%s/%s:%s/%s", + cgutil_opt.mpoints[MOUNT_CPU_ID], + GSCGROUP_TOP_DATABASE, + cgutil_opt.user, + GSCGROUP_TOP_CLASS); + securec_check_intval(sret, free(cgpath), -1); - sret = memset_s(&buf, sizeof(buf), 0, sizeof(buf)); - securec_check_errno(sret, free(cgpath), -1); + ret = stat(cgpath, &buf); - if (geteuid() == 0) { - /* create cm cgroup, this function could check if directory exists. - * So we need not check it firstly. - */ - (void)cgexec_create_cm_default_cgroup(); - - cgcnt = cgexec_get_cgroup_number(); - if (1 == cgcnt) { - /* create the default cgroups */ - (void)cgexec_create_default_cgroups(); - } else { - /* check if cgroups have been created for the specified user */ + // 瑙i噴锛氭鏌SCGROUP_TOP_DATABASE鐩綍涓嬫槸鍚﹀瓨鍦╟gutil_opt.user鐩綍锛屽鏋滀笉瀛樺湪锛屽垯琛ㄧずCgroups鏈鍒涘缓銆 + if (ret != 0) { + fprintf(stdout, "閿欒锛氭棤娉曞湪%s鐩綍涓嬫壘鍒%s鐩綍\n", GSCGROUP_TOP_DATABASE, cgutil_opt.user); + free(cgpath); + return -1; + } + } + } + else { + /* 闈瀝oot鐢ㄦ埛蹇呴』妫鏌groups鏄惁瀛樺湪锛屼笉瀛樺湪鍒欐姤閿 */ sret = sprintf_s(cgpath, len, "%s/%s:%s/%s", @@ -3946,1216 +3743,1046 @@ int cgexec_create_groups(void) ret = stat(cgpath, &buf); - if (0 != ret) - (void)cgexec_create_default_cgroups(); + // 瑙i噴锛氭鏌SCGROUP_TOP_DATABASE鐩綍涓嬫槸鍚﹀瓨鍦╟gutil_opt.user鐩綍锛屽鏋滀笉瀛樺湪锛屽垯琛ㄧずCgroups鏈鍒涘缓銆 + if (ret != 0) { + fprintf(stdout, "閿欒锛氭棤娉曞湪%s鐩綍涓嬫壘鍒%s鐩綍\n", GSCGROUP_TOP_DATABASE, cgutil_opt.user); + free(cgpath); + return -1; + } } - /* default class has no exception data, we will set a default one */ - if (gsutil_exception_is_valid(cgutil_vaddr[CLASSCG_START_ID], EXCEPT_ALL_KINDS) == 0) { - cgutil_vaddr[CLASSCG_START_ID]->except[EXCEPT_PENALTY].skewpercent = DEFAULT_CPUSKEWPCT; - cgutil_vaddr[CLASSCG_START_ID]->except[EXCEPT_PENALTY].qualitime = DEFAULT_QUALITIME; - } - } else { - /* check if cgroups have been created for the specified user */ - sret = sprintf_s(cgpath, - len, - "%s/%s:%s/%s", - cgutil_opt.mpoints[MOUNT_CPU_ID], - GSCGROUP_TOP_DATABASE, - cgutil_passwd_user->pw_name, - GSCGROUP_TOP_CLASS); - securec_check_intval(sret, free(cgpath), -1); - - ret = stat(cgpath, &buf); - - if (0 != ret) { - fprintf(stderr, - "ERROR: There are no cgroups for %s! Please remount it by root.\n", - cgutil_passwd_user->pw_name); - free(cgpath); - cgpath = NULL; - return -1; - } + free(cgpath); + return 0; } - /* create nodegroup info */ - if (cgutil_opt.nodegroup[0]) { - size_t nglen = sizeof(cgutil_opt.mpoint) + 1 + sizeof(GSCGROUP_TOP_DATABASE) + 1 + USERNAME_LEN + 1 + - strlen(cgutil_opt.nodegroup); - char* ngcgpath = (char*)malloc(nglen); + /** + * 鍑芥暟鍚嶇О锛歝gexec_drop_nodegroup_cgroups + * 鎻忚堪锛氬垹闄ゆ寚瀹氳妭鐐圭粍鐨凜group + * + * 杩斿洖鍊硷細 + * -1锛氬紓甯 + * 0锛氭甯 + * + */ + int cgexec_drop_nodegroup_cgroups(void) + { + /* 鍒犻櫎閰嶇疆鏂囦欢 */ + char* cfgpath = NULL; - if (ngcgpath == NULL) { - free(cgpath); - cgpath = NULL; + cfgpath = cgconf_get_config_path(false); // 鑾峰彇閰嶇疆鏂囦欢璺緞 + + if (cfgpath != NULL && !cgutil_opt.rename) { + (void)unlink(cfgpath); // 鍒犻櫎閰嶇疆鏂囦欢 + } + else if (cfgpath == NULL) { return -1; } - /* check if cgroups have been created for the specified nodegroup */ - sret = sprintf_s(ngcgpath, - nglen, - "%s/%s:%s/%s", - cgutil_opt.mpoints[MOUNT_CPU_ID], - GSCGROUP_TOP_DATABASE, - cgutil_passwd_user->pw_name, - cgutil_opt.nodegroup); - securec_check_intval(sret, free(cgpath); free(ngcgpath), -1); + /* 鍒犻櫎鏈娇鐢ㄧ殑澶囦唤鏂囦欢 */ + cgconf_remove_backup_conffile(); // 绉婚櫎鏈娇鐢ㄧ殑澶囦唤鏂囦欢 - ret = stat(ngcgpath, &buf); - /* if the nodegroup doesn't exist, create the default cgroups */ - if (0 != ret) { - /* rename the origin cgroup into nodegroup cgroup */ - if (cgutil_opt.rename) { - /* delete old cgroups */ - (void)cgptree_drop_nodegroup_cgroups(GSCGROUP_TOP_CLASS); - } + /* 鍒犻櫎鑺傜偣缁勭殑Cgroup鏍 */ + (void)cgptree_drop_nodegroup_cgroups(cgutil_opt.nodegroup); // 鍒犻櫎鑺傜偣缁勭殑Cgroup鏍 - /* create nodegroup default cgroups by mapping info */ - if (-1 == cgexec_create_nodegroup_default_cgroups()) { - free(ngcgpath); - ngcgpath = NULL; - free(cgpath); - cgpath = NULL; + if (cgutil_opt.rename) { + void* vaddr = NULL; + size_t cglen = GSCGROUP_ALLNUM * sizeof(gscgroup_grp_t); + errno_t sret; + + /* 鍒犻櫎鏃х殑Cgroup */ + (void)cgptree_drop_nodegroup_cgroups(GSCGROUP_TOP_CLASS); // 鍒犻櫎鏃х殑Cgroup + + /* 鏄犲皠鍘熷Cgroup閰嶇疆鏂囦欢 */ + vaddr = cgconf_map_origin_conffile(); // 鏄犲皠鍘熷Cgroup閰嶇疆鏂囦欢 + if (vaddr == NULL) { + fprintf(stderr, "ERROR: failed to create and map the configure file!\n"); + free(cfgpath); + cfgpath = NULL; return -1; } - if (cgutil_opt.rename) { - void* vaddr = cgconf_map_nodegroup_conffile(); - size_t cglen = GSCGROUP_ALLNUM * sizeof(gscgroup_grp_t); - - if (NULL == vaddr) { - fprintf(stderr, "ERROR: node group config file is removed during rename!"); - free(ngcgpath); - ngcgpath = NULL; - free(cgpath); - cgpath = NULL; - return -1; - } - - for (int i = 0; i < CLASSCG_START_ID; i++) { - gscgroup_grp_t* tmpvaddr = (gscgroup_grp_t*)vaddr + i; - sret = memcpy_s(cgutil_vaddr[i], sizeof(gscgroup_grp_t), tmpvaddr, sizeof(gscgroup_grp_t)); - securec_check_errno(sret, (void)munmap(vaddr, cglen); free(cgpath); free(ngcgpath);, -1); - } - - /* unmap the vaddr for default group */ - (void)munmap(vaddr, GSCGROUP_ALLNUM * sizeof(gscgroup_grp_t)); - - /* unmap the vaddr for default group */ - (void)munmap(cgutil_vaddr[0], GSCGROUP_ALLNUM * sizeof(gscgroup_grp_t)); - - /* parse the origin group for nodegroup */ - if (-1 == cgconf_parse_nodegroup_config_file()) { - free(ngcgpath); - ngcgpath = NULL; - free(cgpath); - cgpath = NULL; - return -1; - } - - current_nodegroup = cgutil_opt.nodegroup; - - /* create nodegroup default cgroups by mapping info */ - if (-1 == cgexec_create_nodegroup_default_cgroups()) { - free(ngcgpath); - ngcgpath = NULL; - free(cgpath); - cgpath = NULL; - return -1; - } + for (int i = 0; i < CLASSCG_START_ID; i++) { + gscgroup_grp_t* tmpvaddr = (gscgroup_grp_t*)vaddr + i; + sret = memcpy_s(cgutil_vaddr[i], sizeof(gscgroup_grp_t), tmpvaddr, sizeof(gscgroup_grp_t)); + securec_check_errno(sret, (void)munmap(vaddr, cglen); free(cfgpath); , -1); } - } - free(ngcgpath); - ngcgpath = NULL; - } + /* 鍙栨秷鏄犲皠榛樿缁勭殑vaddr */ + (void)munmap(vaddr, GSCGROUP_ALLNUM * sizeof(gscgroup_grp_t)); - free(cgpath); - cgpath = NULL; + current_nodegroup = NULL; - /* create Class group */ - if (cgutil_opt.clsname[0]) { - if (0 == strcmp(cgutil_opt.clsname, GSCGROUP_DEFAULT_CLASS)) { - fprintf(stderr, - "ERROR: Can't create Default Workload Cgroup " - "for DefaultClass Cgroup!\n"); - return -1; - } + /* 鏍规嵁鏄犲皠淇℃伅鍒涘缓鑺傜偣缁勯粯璁group */ + if (-1 == cgexec_create_nodegroup_default_cgroups()) { + free(cfgpath); + cfgpath = NULL; + return -1; + } - /* create this class cgroup */ - if (-1 == cgexec_create_class_cgroup()) { - cgconf_remove_backup_conffile(); - } - } + cgutil_opt.nodegroup[0] = '\0'; // 閲嶇疆鑺傜偣缁勪俊鎭 - return 0; -} + /* 閲嶅懡鍚嶉厤缃枃浠 */ + char* old_confpath = cgconf_get_config_path(false); + if (old_confpath == NULL) { + fprintf(stderr, "ERROR: failed to get the configuration path,configuration path is NULL."); + free(cfgpath); + cfgpath = NULL; + return -1; + } -/* - * function name: cgexec_drop_nodegroup_cgroups - * description : drop cgroups of the specified nodegroup - * - * return value : - * -1: abnormal - * 0: normal - * - */ -int cgexec_drop_nodegroup_cgroups(void) -{ - /* delete the configure file */ - char* cfgpath = NULL; - - cfgpath = cgconf_get_config_path(false); - - if (cfgpath != NULL && !cgutil_opt.rename) { - (void)unlink(cfgpath); - } else if (cfgpath == NULL) { - return -1; - } - - /* remove unused backup file */ - cgconf_remove_backup_conffile(); - - /* drop the nodegroup Cgroup tree */ - (void)cgptree_drop_nodegroup_cgroups(cgutil_opt.nodegroup); - - if (cgutil_opt.rename) { - void* vaddr = NULL; - size_t cglen = GSCGROUP_ALLNUM * sizeof(gscgroup_grp_t); - errno_t sret; - - /* delete old cgroups */ - (void)cgptree_drop_nodegroup_cgroups(GSCGROUP_TOP_CLASS); - - /* map the original Cgroup Configuration file */ - vaddr = cgconf_map_origin_conffile(); - if (NULL == vaddr) { - fprintf(stderr, "ERROR: failed to create and map the configure file!\n"); - free(cfgpath); - cfgpath = NULL; - return -1; - } - - for (int i = 0; i < CLASSCG_START_ID; i++) { - gscgroup_grp_t* tmpvaddr = (gscgroup_grp_t*)vaddr + i; - sret = memcpy_s(cgutil_vaddr[i], sizeof(gscgroup_grp_t), tmpvaddr, sizeof(gscgroup_grp_t)); - securec_check_errno(sret, (void)munmap(vaddr, cglen); free(cfgpath);, -1); - } - - /* unmap the vaddr for default group */ - (void)munmap(vaddr, GSCGROUP_ALLNUM * sizeof(gscgroup_grp_t)); - - current_nodegroup = NULL; - - /* create nodegroup default cgroups by mapping info */ - if (-1 == cgexec_create_nodegroup_default_cgroups()) { - free(cfgpath); - cfgpath = NULL; - return -1; - } - - cgutil_opt.nodegroup[0] = '\0'; // reset the node group info - - /* rename the configuration file */ - char* old_confpath = cgconf_get_config_path(false); - if (NULL == old_confpath) { - fprintf(stderr, "ERROR: failed to get the configuration path,configuration path is NULL."); - free(cfgpath); - cfgpath = NULL; - return -1; - } - - if (-1 == rename(cfgpath, old_confpath)) { - fprintf(stderr, "ERROR: failed to rename %s to %s.", cfgpath, old_confpath); - free(cfgpath); - cfgpath = NULL; + if (-1 == rename(cfgpath, old_confpath)) { + fprintf(stderr, "ERROR: failed to rename %s to %s.", cfgpath, old_confpath); + free(cfgpath); + cfgpath = NULL; + free(old_confpath); + old_confpath = NULL; + return -1; + } free(old_confpath); old_confpath = NULL; - return -1; - } - free(old_confpath); - old_confpath = NULL; - } - - free(cfgpath); - cfgpath = NULL; - return 0; -} - -/* - * function name: cgexec_drop_groups - * description : when there is no specified Class group, root user will - * delete Gaussdb group. Otherwise, it will delete - * the Class name. When "-M" option is specified, it umounts - * cgroup file system. - * return value : - * -1: abnormal - * 0: normal - * - */ -int cgexec_drop_groups(void) -{ - /* root user drops common user's cgroups and umount */ - if ('\0' != cgutil_opt.user[0] && geteuid() == 0 && '\0' == cgutil_opt.clsname[0]) { - (void)cgptree_drop_cgroups(); - } - - /* root user drops cm cgroup */ - if ('\0' != cgutil_opt.user[0] && geteuid() == 0) { - (void)cgexec_delete_cm_cgroup(); - } - - /* root user drops common user's cgroups and umount */ - if (geteuid() != 0 && '\0' != cgutil_opt.nodegroup[0] && '\0' == cgutil_opt.clsname[0]) { - (void)cgexec_drop_nodegroup_cgroups(); - } - - if ('\0' != cgutil_opt.clsname[0]) { - if (0 == strcmp(cgutil_opt.clsname, GSCGROUP_DEFAULT_CLASS)) { - fprintf(stderr, "ERROR: Can't drop DefaultClass Cgroup!\n"); - return -1; } - if (-1 == cgexec_delete_class_cgroup()) { + free(cfgpath); + cfgpath = NULL; + return 0; + } + + /** + * 鍑芥暟鍚嶇О锛歝gexec_drop_groups + * 鎻忚堪锛氬綋娌℃湁鎸囧畾鐨凜lass缁勬椂锛岃秴绾х敤鎴峰皢鍒犻櫎Gaussdb缁勩傚惁鍒欙紝灏嗗垹闄ゆ寚瀹氱殑Class缁勩 + * 褰撴寚瀹氣-M鈥濋夐」鏃讹紝瀹冧細鍗歌浇Cgroup鏂囦欢绯荤粺銆 + * + * 杩斿洖鍊硷細 + * -1锛氬紓甯 + * 0锛氭甯 + * + */ + int cgexec_drop_groups(void) + { + /* 瓒呯骇鐢ㄦ埛鍒犻櫎鏅氱敤鎴风殑Cgroup骞跺嵏杞 */ + if (cgutil_opt.user[0] != '\0' && geteuid() == 0 && cgutil_opt.clsname[0] == '\0') { + (void)cgptree_drop_cgroups(); // 鍒犻櫎Cgroup骞跺嵏杞 + } + + /* 瓒呯骇鐢ㄦ埛鍒犻櫎cm cgroup */ + if (cgutil_opt.user[0] != '\0' && geteuid() == 0) { + (void)cgexec_delete_cm_cgroup(); // 鍒犻櫎cm cgroup + } + + /* 闈炶秴绾х敤鎴峰垹闄ゆ寚瀹氳妭鐐圭粍鐨凜group */ + if (geteuid() != 0 && cgutil_opt.nodegroup[0] != '\0' && cgutil_opt.clsname[0] == '\0') { + (void)cgexec_drop_nodegroup_cgroups(); // 鍒犻櫎鎸囧畾鑺傜偣缁勭殑Cgroup + } + + if (cgutil_opt.clsname[0] != '\0') { + if (strcmp(cgutil_opt.clsname, GSCGROUP_DEFAULT_CLASS) == 0) { + fprintf(stderr, "ERROR: Can't drop DefaultClass Cgroup!\n"); + return -1; + } + + if (-1 == cgexec_delete_class_cgroup()) { + cgconf_remove_backup_conffile(); + } + } + + if (geteuid() == 0 && cgutil_opt.umflag) { + fprintf(stdout, "ERROR: Cgroup is mounted. Ready to umount cgroup!\n"); + + /* 鎸傝浇Cgroup */ + (void)cgexec_umount_root_cgroup(); + } + + return 0; + } + */ + + /** + * 瑙f瀽锛 + * 绗竴涓嚱鏁癱gexec_drop_nodegroup_cgroups鐢ㄤ簬鍒犻櫎鎸囧畾鑺傜偣缁勭殑Cgroup銆 + * 璇ュ嚱鏁伴鍏堣幏鍙栭厤缃枃浠惰矾寰勶紝鐒跺悗鍒ゆ柇鏄惁闇瑕佸垹闄ら厤缃枃浠讹紝濡傛灉闇瑕佸垯鍒犻櫎銆 + * 鎺ヤ笅鏉ュ垹闄ゆ湭浣跨敤鐨勫浠芥枃浠躲傚啀娆¤皟鐢╟gptree_drop_nodegroup_cgroups鍑芥暟鍒犻櫎鑺傜偣缁勭殑Cgroup鏍戙 + * 濡傛灉闇瑕侀噸鍛藉悕閰嶇疆鏂囦欢锛屽垯杩涜涓绯诲垪鐨勬搷浣滐紝鍖呮嫭鍒犻櫎鏃х殑Cgroup骞舵槧灏勫師濮嬮厤缃枃浠讹紝鍒涘缓鑺傜偣缁勯粯璁group锛岄噸鍛藉悕閰嶇疆鏂囦欢銆 + * 鏈鍚庨噴鏀惧唴瀛樺苟杩斿洖缁撴灉銆 + * + * 绗簩涓嚱鏁癱gexec_drop_groups鐢ㄤ簬鍒犻櫎Cgroup銆傚嚱鏁伴鍏堝垽鏂槸鍚﹂渶瑕佸垹闄ゆ櫘閫氱敤鎴风殑Cgroup骞跺嵏杞姐 + * 鐒跺悗鍒ゆ柇鏄惁闇瑕佸垹闄m cgroup銆傛帴涓嬫潵鍒ゆ柇鏄惁闇瑕佸垹闄ら潪瓒呯骇鐢ㄦ埛鎸囧畾鑺傜偣缁勭殑Cgroup銆 + * 濡傛灉鎸囧畾浜咰lass缁勶紝涓斾负DefaultClass锛屽垯杩斿洖寮傚父銆傜劧鍚庡垽鏂槸鍚﹂渶瑕佸垹闄ゆ寚瀹氱殑Class缁勩 + * 鏈鍚庡垽鏂槸鍚﹂渶瑕佸嵏杞紺group骞惰繑鍥炵粨鏋溿 + */ + /* + * 鍑芥暟鍚嶇О锛歝gexec_update_groups + * 鍔熻兘鎻忚堪锛氭牴鎹夐」鏇存柊鍔ㄦ佸兼垨鍥哄畾鍊 + * 杩斿洖鍊硷細 + * -1锛氬紓甯 + * 0锛氭甯 + * + */ + int cgexec_update_groups(void) + { + int ret = 0; + + /* 澶囦唤閰嶇疆鏂囦欢 */ + if (-1 == cgconf_backup_config_file()) + return -1; + + if (0 == cgutil_opt.fixed) { + if ('\0' != cgutil_opt.clsname[0]) + ret = cgexec_update_dynamic_class_cgroup(); + + if ('\0' != cgutil_opt.bkdname[0]) + ret = cgexec_update_dynamic_backend_cgroup(); + + if ('\0' != cgutil_opt.topname[0]) + ret = cgexec_update_dynamic_top_cgroup(); + } + else if (cgutil_opt.fixed) { + if ('\0' != cgutil_opt.clsname[0]) + ret = cgexec_update_fixed_class_cgroup(); + + if ('\0' != cgutil_opt.bkdname[0]) + ret = cgexec_update_fixed_backend_cgroup(); + + if ('\0' != cgutil_opt.topname[0]) + ret = cgexec_update_fixed_top_cgroup(); + } + + /* 绉婚櫎澶囦唤鏂囦欢 */ + if (-1 == ret) { cgconf_remove_backup_conffile(); } + + return 0; } - if (geteuid() == 0 && cgutil_opt.umflag) { - fprintf(stdout, "ERROR: Cgroup is mounted. Ready to umount cgroup!\n"); + /* 鑾峰彇Root淇℃伅 */ + int cgexec_get_cgroup_cpuset_info(int cnt, char** cpuset) + { + char* relpath = NULL; + struct cgroup* cg = NULL; + struct cgroup_controller* cgc_cpu = NULL; + int ret; - /* mount the cgroup */ - (void)cgexec_umount_root_cgroup(); - } + /* 鑾峰彇鐩稿璺緞 */ + if (NULL == (relpath = gscgroup_get_relative_path(cnt, cgutil_vaddr, current_nodegroup))) + return -1; - return 0; -} + /* 鍒嗛厤鏂扮殑cgroup缁撴瀯 */ + cg = cgroup_new_cgroup(relpath); + if (cg == NULL) { + fprintf(stdout, "ERROR: failed to create the new cgroup for %s\n", relpath); + free(relpath); + relpath = NULL; + return -1; + } -/* - * function name: cgexec_update_groups - * description : update the dynamic value or fixed value based on options - * return value : - * -1: abnormal - * 0: normal - * - */ -int cgexec_update_groups(void) -{ - int ret = 0; + /* 浠庡唴鏍歌幏鍙栧叧浜巆group鐨勬墍鏈変俊鎭 */ + ret = cgroup_get_cgroup(cg); + if (ret != 0) { + fprintf(stdout, "ERROR: failed to get %s cgroup information for %s(%d)\n", relpath, cgroup_strerror(ret), ret); + cgroup_free(&cg); + free(relpath); + relpath = NULL; + return -1; + } - /* back up the config file */ - if (-1 == cgconf_backup_config_file()) - return -1; - - if (0 == cgutil_opt.fixed) { - if ('\0' != cgutil_opt.clsname[0]) - ret = cgexec_update_dynamic_class_cgroup(); - - if ('\0' != cgutil_opt.bkdname[0]) - ret = cgexec_update_dynamic_backend_cgroup(); - - if ('\0' != cgutil_opt.topname[0]) - ret = cgexec_update_dynamic_top_cgroup(); - } else if (cgutil_opt.fixed) { - if ('\0' != cgutil_opt.clsname[0]) - ret = cgexec_update_fixed_class_cgroup(); - - if ('\0' != cgutil_opt.bkdname[0]) - ret = cgexec_update_fixed_backend_cgroup(); - - if ('\0' != cgutil_opt.topname[0]) - ret = cgexec_update_fixed_top_cgroup(); - } - - /* remove the backup file */ - if (-1 == ret) { - cgconf_remove_backup_conffile(); - } - - return 0; -} - -/* get Root information */ -int cgexec_get_cgroup_cpuset_info(int cnt, char** cpuset) -{ - char* relpath = NULL; - struct cgroup* cg = NULL; - struct cgroup_controller* cgc_cpu = NULL; - int ret; - - /* get the relative path */ - if (NULL == (relpath = gscgroup_get_relative_path(cnt, cgutil_vaddr, current_nodegroup))) - return -1; - - /* allocate new cgroup structure */ - cg = cgroup_new_cgroup(relpath); - if (cg == NULL) { - fprintf(stdout, "ERROR: failed to create the new cgroup for %s\n", relpath); free(relpath); relpath = NULL; - return -1; - } - /* get all information regarding the cgroup from kernel */ - ret = cgroup_get_cgroup(cg); - if (ret != 0) { - fprintf(stdout, "ERROR: failed to get %s cgroup information for %s(%d)\n", relpath, cgroup_strerror(ret), ret); - cgroup_free(&cg); - free(relpath); - relpath = NULL; - return -1; - } + /* 鑾峰彇CPU鎺у埗鍣 */ + cgc_cpu = cgroup_get_controller(cg, MOUNT_CPUSET_NAME); + if (NULL == cgc_cpu) { + fprintf(stderr, "ERROR: failed to add %s controller in %d!\n", MOUNT_CPU_NAME, cnt); + cgroup_free(&cg); + return -1; + } - free(relpath); - relpath = NULL; - - /* get the CPU controller */ - cgc_cpu = cgroup_get_controller(cg, MOUNT_CPUSET_NAME); - if (NULL == cgc_cpu) { - fprintf(stderr, "ERROR: failed to add %s controller in %d!\n", MOUNT_CPU_NAME, cnt); - cgroup_free(&cg); - return -1; - } - - /* get cpuset value with controller */ - if (0 != (ret = cgroup_get_value_string(cgc_cpu, CPUSET_CPUS, cpuset))) { - fprintf(stderr, "ERROR: failed to get %s for %s\n", CPUSET_CPUS, cgroup_strerror(ret)); - goto error; - } - - /* Get the mems info */ - if (cnt == TOPCG_ROOT) { - char* cpumems = NULL; - if (0 != (ret = cgroup_get_value_string(cgc_cpu, CPUSET_MEMS, &cpumems))) { - fprintf(stderr, "ERROR: failed to get %s for %s\n", CPUSET_MEMS, cgroup_strerror(ret)); + /* 閫氳繃鎺у埗鍣ㄨ幏鍙朿puset鍊 */ + if (0 != (ret = cgroup_get_value_string(cgc_cpu, CPUSET_CPUS, cpuset))) { + fprintf(stderr, "ERROR: failed to get %s for %s\n", CPUSET_CPUS, cgroup_strerror(ret)); goto error; } - errno_t sret = snprintf_s(cgutil_mems, CPUSET_LEN, CPUSET_LEN - 1, "%s", cpumems); - securec_check_intval(sret, free(cpumems); cgroup_free_controllers(cg); cgroup_free(&cg), -1); + /* 鑾峰彇mems淇℃伅 */ + if (cnt == TOPCG_ROOT) { + char* cpumems = NULL; + if (0 != (ret = cgroup_get_value_string(cgc_cpu, CPUSET_MEMS, &cpumems))) { + fprintf(stderr, "ERROR: failed to get %s for %s\n", CPUSET_MEMS, cgroup_strerror(ret)); + goto error; + } - free(cpumems); - cpumems = NULL; - } + errno_t sret = snprintf_s(cgutil_mems, CPUSET_LEN, CPUSET_LEN - 1, "%s", cpumems); + securec_check_intval(sret, free(cpumems); cgroup_free_controllers(cg); cgroup_free(&cg), -1); - cgroup_free_controllers(cg); - cgroup_free(&cg); - return 0; -error: - cgroup_free_controllers(cg); - cgroup_free(&cg); - return -1; -} - -/* - * @Description: get current memory count. - * @OUT mems: memory set - * @IN size: memory set size - * @Return: memory set - * @See also: - */ -char* cgexec_get_cgroup_cpuset_mems(char* mems, int size) -{ - int ret = 1; - char cmd[128]; - char line[128]; - - FILE* fp = NULL; - - /* open '/proc/cpuinf' to search 'physical id' count to get memory set */ - errno_t sret = snprintf_s(cmd, - sizeof(cmd), - sizeof(cmd) - 1, - "%s", - "lscpu | grep \"NUMA node(s)\" | awk -F: '{print $2}'| sed 's/\\ //g'"); - securec_check_intval(sret, , mems); - - if ((fp = popen(cmd, "r")) != NULL) { - if (fgets(line, sizeof(line), fp) != NULL) { - /* get count */ - ret = atoi(line); - - if (ret == 0) - ret = 1; + free(cpumems); + cpumems = NULL; } - pclose(fp); - } - - /* get memory set */ - sret = snprintf_s(mems, size, size - 1, "%d-%d", 0, ret - 1); - securec_check_intval(sret, , mems); - - return mems; -} - -/* - * @Description: update config cpuset - * @IN void - * @Return: -1: abnormal 0: normal - * @See also: - */ -int cgexec_check_top_cpuset(void) -{ - /* - * Check whether the maximum number of configuration file is - * compatible with the total number of cores in the current - * node. If compatible, no update is required - */ - if (cgexec_check_cpuset_value(cgutil_allset, cgutil_vaddr[TOPCG_GAUSSDB]->cpuset) == 0) + cgroup_free_controllers(cg); + cgroup_free(&cg); return 0; - - /* not compatible, we have to update the configuration file */ - for (int i = 0; i <= WDCG_END_ID; ++i) { - if (cgutil_vaddr[i]->used == 0) - continue; - - errno_t sret = snprintf_s(cgutil_vaddr[i]->cpuset, CPUSET_LEN, CPUSET_LEN - 1, "%s", cgutil_allset); - securec_check_intval(sret, , -1); - } - - return 0; -} - -/* - * @Description : update cpu core percentage and cpusets values recursively. - * : the previous "-f" is replaced by "--fixed", so - * : groups with "cpusets" are all - * : transfered to percentage of the high level - * @IN high : high level group id. - * @IN extended : the total quota is out of range, so cpusets of the groups - * : and the belonging groups are all the same with the - * : high level groups, except those who has been set "quota" - * : already. - * @Return : - * @See also: - */ -static void cgexec_update_fixed_config(int high, int extended) -{ - int forstart = 0, forend = 0; /* start and end value of the loop */ - int i = 0; - int lowlen = 0, highlen = 0; /* low and high level cpuset length */ - int start = 0, end = 0; /* only used to call function cgexec_get_cpuset_length*/ - int lowstart = 0, lowend = 0; /* low group cpuset start and end value */ - int highstart = 0, highend = 0; /* high group cpuset start and end value */ - int part_quota = 0; /* the current quota transfered from cpusets */ - int sum_quota = 0; /* sum of the quota values */ - errno_t sret = 0; /* securec_check return value */ - char sets[CPUSET_LEN]; /* the calculated cpuset to be updated */ - bool flag = false; /*flag to indicate first time enter the loop */ - char topwd[GPNAME_LEN]; - - /* get 'topwd' cgroup full name*/ - sret = snprintf_s(topwd, sizeof(topwd), sizeof(topwd) - 1, "%s:%d", GSCGROUP_TOP_WORKLOAD, 1); - securec_check_intval(sret, , ); - - /* if high has been the lowest level, the recursion is interrupted */ - if (cgexec_get_cgroup_id_range(high, &forstart, &forend) == -1) - return; - - /* the cpuset length of the high level group */ - highlen = cgexec_get_cpuset_length(cgutil_vaddr[high]->cpuset, &highstart, &highend); - - sum_quota = cgexec_check_fixed_percent(high); - - for (i = forstart; i <= forend; i++) { - /* the low level group is ignored in the reseting list */ - if (cgutil_vaddr[i]->used == 0) - continue; - - /* only the workload groups belonging to high class is considered */ - if ((high >= CLASSCG_START_ID && high <= CLASSCG_END_ID) && (high != cgutil_vaddr[i]->ginfo.wd.cgid)) - continue; - - /* transfer cpuset to quota */ - if (!cgutil_vaddr[i]->ainfo.quota) { - /* get length of the low level cpuset */ - if (*cgutil_vaddr[i]->cpuset != '\0') - lowlen = cgexec_get_cpuset_length(cgutil_vaddr[i]->cpuset, &start, &end); - - /* quota is still set to 0 */ - if (lowlen == highlen || !lowlen || extended || - (strcmp(cgutil_vaddr[i]->grpname, topwd) == 0 && (i >= WDCG_START_ID) && (i <= WDCG_END_ID))) { - sret = - snprintf_s(cgutil_vaddr[i]->cpuset, CPUSET_LEN, CPUSET_LEN - 1, "%s", cgutil_vaddr[high]->cpuset); - securec_check_intval(sret, , ); - - /* update the configure recursively */ - if (i < WDCG_START_ID) - cgexec_update_fixed_config(i, extended); - - /* extended is only available for the belonging groups of the current group */ - if (extended) - extended = 0; - - continue; - } - - /* transfer cpuset to quota */ - part_quota = cgexec_trans_cpusets_to_percent(highlen, lowlen); - - /* the new quota plus the total quota is out of range */ - if (part_quota + sum_quota > GROUP_ALL_PERCENT) { - /* - * the belonging groups will be marked as extended, - * and the cpusets will be the same with high level ones. - */ - extended = 1; - - sret = - snprintf_s(cgutil_vaddr[i]->cpuset, CPUSET_LEN, CPUSET_LEN - 1, "%s", cgutil_vaddr[high]->cpuset); - securec_check_intval(sret, , ); - } else { - /* alter the quota configure for the successfully transfered groups */ - sum_quota += part_quota; - cgutil_vaddr[i]->ainfo.quota = part_quota; - } - } - - /* reset the configure file for all groups with quota value */ - if (cgutil_vaddr[i]->ainfo.quota) { - /* the low level groups (same level groups with "low") cpu core length */ - lowlen = cgexec_trans_percent_to_cpusets(highlen, cgutil_vaddr[i]->ainfo.quota); - /* - * only the first time enter the loop, flag is false - * the cpu cores are allocated sequentially within high group cpu core range. - * the first group to be reset is allocated from "highstart", - * the next are allocated following the previous group "lowend" + 1 - */ - lowstart = flag ? (lowend + 1) : highstart; - lowend = lowstart + lowlen - 1; - - /* - * the previous steps guarantee the total quota not out of range, - * so here we only need check whether the left cpu cores are enough or not, - * and and the not enough cases will be handled in the same way with - * cgexec_check_cpuset_percent. - */ - if (lowend > highend) { - lowstart = highend - lowlen + 1; - lowend = highend; - } - - /* "sets" restore the cpuset to be reset*/ - cgexec_get_cpu_core_range(sets, lowstart, lowend); - - sret = snprintf_s(cgutil_vaddr[i]->cpuset, CPUSET_LEN, CPUSET_LEN - 1, "%s", sets); - securec_check_intval(sret, , ); - - /* next time enter the loop, flag will be true*/ - if (!flag) - flag = true; - } - - /* update the configure recursively */ - if (i < WDCG_START_ID) - cgexec_update_fixed_config(i, extended); - - /* extended is only available for the belonging groups of the current group */ - if (extended) - extended = 0; - } -} - -/* - * function name: cgexec_refresh_groups_internal - * description : refresh groups internal function - * return value : - * -1: abnormal - * 0: normal - * - */ -static int cgexec_refresh_groups_internal(void) -{ - // update quota and cpusets for all the control groups recursively - cgexec_update_fixed_config(TOPCG_GAUSSDB, 0); - - /* create default groups. - * if an error happened, then return -1. - */ - if (cgexec_create_default_cgroups()) { + error: + cgroup_free_controllers(cg); + cgroup_free(&cg); return -1; } - if (cgexec_create_cm_default_cgroup()) { - return -1; - } - - return 0; -} - -/* - * @Description: refresh cgroup with configure file. - * @IN size: void - * @Return: -1: abnormal 0: normal - * @See also: - */ -int cgexec_refresh_original_groups(void) -{ - /* delete backend and class group */ - for (int idx = TOPCG_BACKEND; idx <= TOPCG_CLASS; ++idx) { - if (cgutil_vaddr[idx]->used == 0) - continue; - - if (-1 == cgexec_delete_default_cgroup(cgutil_vaddr[idx])) - return -1; - } /* - * We have to check if the configured maximum number of cores can - * be used on the current node, and if not, we will try to update - * the configuration file for compatibility with the new node + * @鎻忚堪锛氳幏鍙栧綋鍓嶅唴瀛樿鏁般 + * @OUT mems锛氬唴瀛橀泦鍚 + * @IN size锛氬唴瀛橀泦鍚堝ぇ灏 + * @杩斿洖鍊硷細鍐呭瓨闆嗗悎 + * @鍙傝冿細 */ - if (cgexec_check_top_cpuset() == -1) { - fprintf(stderr, "ERROR: update top cpuset error."); - return -1; - } - - /* create the default cgroups */ - return cgexec_refresh_groups_internal(); -} - -/* - * @Description: refresh cgroup with configure file of nodegroup. - * @IN size: void - * @Return: -1: abnormal 0: normal - * @See also: - */ -int cgexec_refresh_nodegroup_groups(void) -{ - /* delete logical cluster group */ - if (-1 == cgptree_drop_nodegroup_cgroups(cgutil_opt.nodegroup)) - return -1; - - /* create the default cgroups */ - return cgexec_create_nodegroup_default_cgroups(); -} - -/* - * @Description: refresh cgroup with configure file. - * @IN size: void - * @Return: -1: abnormal 0: normal - * @See also: - */ -int cgexec_refresh_groups(void) -{ - if ('\0' == cgutil_opt.nodegroup[0]) - return cgexec_refresh_original_groups(); - else - return cgexec_refresh_nodegroup_groups(); -} - -/* - * @Description: revert cgroup configure. - * @IN size: void - * @Return: -1: abnormal 0: normal - * @See also: - */ -int cgexec_revert_groups(void) -{ - for (int cls = CLASSCG_START_ID + 1; cls <= CLASSCG_END_ID; ++cls) { - if (cgutil_vaddr[cls]->used == 0) - continue; - - /* delete all class group except default class group */ - if (-1 == cgexec_delete_default_cgroup(cgutil_vaddr[cls])) - return -1; - - /* reset all group configure */ - cgconf_reset_class_group(cls); - } - - /* revert configure file */ - cgconf_revert_config_file(); - - /* create default groups. - * if an error happened, then return -1. - */ - if (cgexec_create_default_cgroups()) - return -1; - if (cgexec_create_cm_default_cgroup()) - return -1; - - return 0; -} - -/* - * @Description: check if changes happened on both groups - * @IN cur: current group - * @IN bak: backup group - * @Return: 1: updated 0:no updated - * @See also: - */ -int cgexec_check_update_groups(gscgroup_grp_t* cur, gscgroup_grp_t* bak) -{ - int offset = offsetof(gscgroup_grp_t, ainfo); - int size = sizeof(alloc_info_t); - - if (0 == memcmp((void*)((char*)cur + offset), (void*)((char*)bak + offset), size)) - return 0; - else - return 1; -} - -/* - * @Description: recover groups by updating the percent groups - * @IN id: the id of updated group - * @Return: - * -1: abnormal 0: normal - * @See also: - */ -int cgexec_recover_update_percent_groups(int id) -{ - errno_t sret; - - /* class group changed */ - if (id >= CLASSCG_START_ID && id <= CLASSCG_END_ID) { - if (-1 == cgexec_update_cgroup_value(cgutil_vaddr_back[id])) - return -1; - } else if (id >= WDCG_START_ID && id <= WDCG_END_ID) { - int cls = cgutil_vaddr_back[id]->ginfo.wd.cgid; - - /* need to update all class groups and their workload groups */ - sret = memcpy_s(cgutil_vaddr[cls], sizeof(gscgroup_grp_t), cgutil_vaddr_back[cls], sizeof(gscgroup_grp_t)); - securec_check_errno(sret, , -1); - - /* update the os cgroups */ - if (-1 == cgexec_update_cgroup_value(cgutil_vaddr_back[id])) - return -1; - - /* need to update the workload group */ - sret = memcpy_s(cgutil_vaddr[id], sizeof(gscgroup_grp_t), cgutil_vaddr_back[id], sizeof(gscgroup_grp_t)); - securec_check_errno(sret, , -1); - - /* update the remain group */ - if (-1 == cgexec_update_remain_cgroup(cgutil_vaddr[id], cls)) - return -1; - } - - return 0; -} - -/* - * @Description: recover groups by updating the fixed class groups - * @IN id: the id of updated group - * @IN cpuset: input string of cpuset - * @IN reverse: flag if it is reverse or not - * @Return: - * -1: abnormal 0: normal - * @See also: - */ -int cgexec_recover_update_fixed_class_group(int id, char* cpuset, int reverse) -{ - errno_t sret; - - if (!reverse) /* from down to up */ + char* cgexec_get_cgroup_cpuset_mems(char* mems, int size) { - /* copy the value into class group */ - sret = strcpy_s(cgutil_vaddr[id]->cpuset, CPUSET_LEN, cpuset); - securec_check_errno(sret, , -1); + int ret = 1; + char cmd[128]; + char line[128]; - /* update the class group into cgroup fs */ - if (-1 == cgexec_update_cgroup_value(cgutil_vaddr[id])) - return -1; + FILE* fp = NULL; - /* update the remain group */ - for (int j = 1; j <= cgutil_vaddr[id]->ginfo.cls.maxlevel; ++j) { - if (-1 == cgexec_update_remain_cgroup_cpuset_value(id, j, cpuset)) - return -1; + /* 鎵撳紑'/proc/cpuinf'浠ユ悳绱'physical id'璁℃暟浠ヨ幏鍙栧唴瀛橀泦鍚 */ + errno_t sret = snprintf_s(cmd, + sizeof(cmd), + sizeof(cmd) - 1, + "%s", + "lscpu | grep \"NUMA node(s)\" | awk -F: '{print $2}'| sed 's/\\ //g'"); + securec_check_intval(sret, , mems); + + if ((fp = popen(cmd, "r")) != NULL) { + if (fgets(line, sizeof(line), fp) != NULL) { + /* 鑾峰彇璁℃暟 */ + ret = atoi(line); + + if (ret == 0) + ret = 1; + } + + pclose(fp); } - /* update the timeshare group */ - if (-1 == cgexec_update_timeshare_cpuset(cgutil_vaddr[id], cpuset, 0)) - return -1; + /* 鑾峰彇鍐呭瓨闆嗗悎 */ + sret = snprintf_s(mems, size, size - 1, "%d-%d", 0, ret - 1); + securec_check_intval(sret, , mems); - /* update the TopWD group */ - if (-1 == cgexec_update_topwd_cgroup_cpuset(id, cpuset)) - return -1; - } else { - /* update the timeshare group */ - if (-1 == cgexec_update_timeshare_cpuset(cgutil_vaddr[id], cpuset, 1)) - return -1; - - /* update the remain group */ - for (int j = cgutil_vaddr[id]->ginfo.cls.maxlevel; j >= 1; --j) { - if (-1 == cgexec_update_remain_cgroup_cpuset_value(id, j, cpuset)) - return -1; - } - - /* update the TopWD group */ - if (-1 == cgexec_update_topwd_cgroup_cpuset(id, cpuset)) - return -1; - - /* copy the value into class group */ - sret = strcpy_s(cgutil_vaddr[id]->cpuset, GPNAME_LEN, cpuset); - securec_check_errno(sret, , -1); - - /* update the class group into cgroup fs */ - if (-1 == cgexec_update_cgroup_value(cgutil_vaddr[id])) - return -1; + return mems; } + /* + * @Description: 鏇存柊閰嶇疆鏂囦欢鐨刢puset + * @IN void + * @Return: -1: 寮傚父 0: 姝e父 + * @See also: + */ + int cgexec_check_top_cpuset(void) + { + /* + * 妫鏌ラ厤缃枃浠剁殑鏈澶ф暟閲忔槸鍚︿笌褰撳墠鑺傜偣鐨勬绘牳蹇冩暟鍏煎銆 + * 濡傛灉鍏煎锛屽垯鏃犻渶鏇存柊銆 + */ + if (cgexec_check_cpuset_value(cgutil_allset, cgutil_vaddr[TOPCG_GAUSSDB]->cpuset) == 0) + return 0; - return 0; -} - -/* - * @Description: recover groups by updating the quota groups - * @IN id: the id of updated group - * @Return: - * -1: abnormal 0: normal - * @See also: - */ -int cgexec_recover_update_quota_groups(int id) -{ - /* class group changed */ - if (id >= CLASSCG_START_ID && id <= CLASSCG_END_ID) { - /* reset the default value as Top Class group */ - for (int i = CLASSCG_START_ID; i <= CLASSCG_END_ID; i++) { - if (cgutil_vaddr[i]->used == 0 || (i != id && cgutil_vaddr[i]->ainfo.quota == 0)) + /* 濡傛灉涓嶅吋瀹癸紝鍒欓渶瑕佹洿鏂伴厤缃枃浠 */ + for (int i = 0; i <= WDCG_END_ID; ++i) { + if (cgutil_vaddr[i]->used == 0) continue; - /* update the remain and timeshare group based on backup value */ - if (-1 == cgexec_recover_update_fixed_class_group(i, cgutil_vaddr[TOPCG_CLASS]->cpuset, 0)) + errno_t sret = snprintf_s(cgutil_vaddr[i]->cpuset, CPUSET_LEN, CPUSET_LEN - 1, "%s", cgutil_allset); + securec_check_intval(sret, , -1); + } + + return 0; + } + + /* + * @Description : 閫掑綊鏇存柊CPU鏍稿績鐧惧垎姣斿拰cpuset鍊 + * : 鍏堝墠鐨勨-f鈥濊鈥--fixed鈥濆彇浠o紝 + * : 鍏锋湁鈥渃pusets鈥濈殑缁勯兘瑕佽浆鎹负楂樼骇绾у埆鐨勭櫨鍒嗘瘮 + * @IN high : 楂樼骇鍒粍鐨処D + * @IN extended : 鎬婚厤棰濊秴鍑鸿寖鍥达紝鎵浠ョ粍鐨刢pusets鍜屾墍灞炵粍鐨刢pusets閮戒笌楂樼骇鍒粍鐩稿悓锛 + * : 闄や簡閭d簺宸茬粡璁剧疆浜嗏渜uota鈥濈殑缁 + * @Return : + * @See also: + */ + static void cgexec_update_fixed_config(int high, int extended) + { + int forstart = 0, forend = 0; /* 寰幆鐨勫紑濮嬪拰缁撴潫鍊 */ + int i = 0; + int lowlen = 0, highlen = 0; /* 浣庣骇鍒拰楂樼骇鍒玞puset鐨勯暱搴 */ + int start = 0, end = 0; /* 浠呯敤浜庤皟鐢ㄥ嚱鏁癱gexec_get_cpuset_length */ + int lowstart = 0, lowend = 0; /* 浣庣骇鍒粍cpuset鐨勮捣濮嬪拰缁撴潫鍊 */ + int highstart = 0, highend = 0; /* 楂樼骇鍒粍cpuset鐨勮捣濮嬪拰缁撴潫鍊 */ + int part_quota = 0; /* 浠巆pusets杞崲鐨勫綋鍓嶉厤棰 */ + int sum_quota = 0; /* 閰嶉鍊肩殑鎬诲拰 */ + errno_t sret = 0; /* securec_check 鐨勮繑鍥炲 */ + char sets[CPUSET_LEN]; /* 瑕佹洿鏂扮殑璁$畻鍚庣殑cpuset */ + bool flag = false; /* 琛ㄧず绗竴娆¤繘鍏ュ惊鐜殑鏍囧織 */ + char topwd[GPNAME_LEN]; + + /* 鑾峰彇 'topwd' 鎺у埗缁勭殑瀹屾暣鍚嶇О */ + sret = snprintf_s(topwd, sizeof(topwd), sizeof(topwd) - 1, "%s:%d", GSCGROUP_TOP_WORKLOAD, 1); + securec_check_intval(sret, , ); + + /* 濡傛灉楂樼骇鍒凡缁忔槸鏈浣庣骇鍒紝鍒欎腑姝㈤掑綊 */ + if (cgexec_get_cgroup_id_range(high, &forstart, &forend) == -1) + return; + + /* 楂樼骇鍒粍鐨刢puset闀垮害 */ + highlen = cgexec_get_cpuset_length(cgutil_vaddr[high]->cpuset, &highstart, &highend); + + sum_quota = cgexec_check_fixed_percent(high); + + for (i = forstart; i <= forend; i++) { + ... + } + } + / * + *鍑芥暟鍚嶇О锛歝gexec_refresh_groups_internal + * 鎻忚堪锛氬埛鏂扮粍鍐呴儴鍑芥暟 + * 杩斿洖鍊硷細 + * -1锛氬紓甯 + * 0锛氭甯 + * + */ + static int cgexec_refresh_groups_internal(void) + { + // 閫掑綊鏇存柊鎵鏈夋帶鍒剁粍鐨勯厤棰濆拰cpuset + cgexec_update_fixed_config(TOPCG_GAUSSDB, 0); + + / *鍒涘缓榛樿缁勩 + * 濡傛灉鍙戠敓閿欒锛屽垯杩斿洖 - 1銆 + * / + if (cgexec_create_default_cgroups()) { return -1; - } - - /* update all workload group */ - for (int j = WDCG_START_ID; j <= WDCG_END_ID; j++) { - if (cgutil_vaddr_back[j]->used == 0 || cgutil_vaddr_back[j]->ginfo.wd.wdlevel == 1) - continue; - - /* update the workload group */ - if (-1 == cgexec_update_cgroup_value(cgutil_vaddr_back[j])) - return -1; - } - - /* re-update the Class Group */ - for (int i = CLASSCG_START_ID; i <= CLASSCG_END_ID; i++) { - if (cgutil_vaddr[i]->used == 0 || (i != id && cgutil_vaddr[i]->ainfo.quota == 0)) - continue; - - if (-1 == cgexec_recover_update_fixed_class_group(i, cgutil_vaddr_back[i]->cpuset, 1)) - return -1; - } - } else if (id >= WDCG_START_ID && id <= WDCG_END_ID) { - int cls = cgutil_vaddr_back[id]->ginfo.wd.cgid; - - for (int i = WDCG_START_ID; i <= WDCG_END_ID; i++) { - if (cgutil_vaddr_back[i]->used == 0 || cgutil_vaddr_back[i]->ginfo.wd.cgid != cls || - cgutil_vaddr_back[i]->ginfo.wd.wdlevel == 1) - continue; - - /* update the workload group */ - if (-1 == cgexec_update_cgroup_value(cgutil_vaddr_back[i])) - return -1; - } - } - - return 0; -} - -/* - * @Description: recover groups by creating new cgroups - * @IN cls_add : class id - * @IN wd_add : array of all new workload groups - * @Return: - * -1: abnormal 0: normal - * @See also: - */ -int cgexec_recover_create_groups(int cls_add, const int* wd_add) -{ - int wld = 0, level = 0, i = 0, tmpcls; - int tmpwld[MAX_WD_LEVEL] = {0}; - errno_t sret; - - sret = memset_s(tmpwld, sizeof(tmpwld), 0, sizeof(tmpwld)); - securec_check_errno(sret, , -1); - - /* get the class info */ - wld = wd_add[0]; - tmpcls = cgutil_vaddr_back[wld]->ginfo.wd.cgid; - - /* verify the information */ - if (cls_add && cls_add != tmpcls) { - fprintf(stderr, "ERROR: new workload group doesn't match class group!\n"); - return -1; - } - - if (cls_add == 0 && wd_add[1]) { - fprintf(stderr, - "ERROR: find more than one added workload group " - "when only workload group is recovering.\n"); - return -1; - } - - /* search workload group */ - for (i = WDCG_START_ID; i <= WDCG_END_ID; ++i) { - if (cgutil_vaddr_back[i]->used && cgutil_vaddr_back[i]->ginfo.wd.cgid == tmpcls) { - level = cgutil_vaddr_back[i]->ginfo.wd.wdlevel; - tmpwld[level - 1] = i; - } - } - - /* delete class group firstly if only workload group should be added */ - if (cls_add == 0 && -1 == cgexec_delete_default_cgroup(cgutil_vaddr[tmpcls])) - return -1; - - /* copy class group info */ - sret = memcpy_s(cgutil_vaddr[tmpcls], sizeof(gscgroup_grp_t), cgutil_vaddr_back[tmpcls], sizeof(gscgroup_grp_t)); - securec_check_errno(sret, , -1); - - /* reset the values */ - cgutil_vaddr[tmpcls]->ginfo.cls.maxlevel = 0; - cgutil_vaddr[tmpcls]->ginfo.cls.rempct = 100; - cgconf_update_class_percent(); - - /* create class group */ - if (-1 == cgexec_create_new_cgroup(cgutil_vaddr[tmpcls])) { - cgconf_reset_class_group(tmpcls); - return -1; - } - - /* create workload group in a loop */ - for (i = 0; i < MAX_WD_LEVEL; i++) { - wld = tmpwld[i]; - if (wld == 0) - break; - - /* copy workload group info */ - sret = memcpy_s(cgutil_vaddr[wld], sizeof(gscgroup_grp_t), cgutil_vaddr_back[wld], sizeof(gscgroup_grp_t)); - securec_check_errno(sret, , -1); - - if (i) /* level > 1, is not TopWD */ - cgutil_vaddr[tmpcls]->ginfo.cls.rempct -= cgutil_vaddr[wld]->ginfo.wd.percent; - - /* create the workload cgroup */ - if (-1 == cgexec_create_workload_cgroup(cgutil_vaddr[wld])) { - cgconf_reset_workload_group(wld); + } + if (cgexec_create_cm_default_cgroup()) { return -1; } + + return 0; } - /* create timeshare cgroup */ - if (-1 == cgexec_create_timeshare_cgroup(cgutil_vaddr[tmpcls])) - return -1; + /* + *@鎻忚堪锛氫娇鐢ㄩ厤缃枃浠跺埛鏂癱group銆 + * @ IN澶у皬锛歷oid + * @杩斿洖锛 - 1锛氬紓甯 0锛氭甯 + * @涔熷弬瑙侊細 + */ + //鍑芥暟鍔熻兘锛 + // 1. cgexec_refresh_groups_internal锛氬埛鏂扮粍鍐呴儴鍑芥暟銆傚畠閫掑綊鏇存柊鎵鏈夋帶鍒剁粍鐨勯厤棰濆拰cpuset锛屽苟鍒涘缓榛樿缁勩 + // 2. cgexec_refresh_original_groups锛氫娇鐢ㄩ厤缃枃浠跺埛鏂癱group銆傚畠鍒犻櫎鍚庣鍜岀被缁勶紝妫鏌ュ綋鍓嶈妭鐐圭殑鏍稿績鏁版槸鍚︿笌閰嶇疆鐨勬渶澶ф牳蹇冩暟鍏煎锛岀劧鍚庡垱寤洪粯璁groups銆 + // 绫讳技鐨勫簲鐢ㄥ疄渚嬶細 + // - 鍦ㄤ竴涓垎甯冨紡绯荤粺涓紝浣跨敤cgroups鏉ョ鐞嗕换鍔″垎閰嶅拰璧勬簮闄愬埗銆傚綋闇瑕佸埛鏂癱group鏃讹紝鍙互浣跨敤cgexec_refresh_original_groups鏉ユ洿鏂扮粍鐨勯厤缃紝浠ラ傚簲鏂扮殑鑺傜偣鎴栬祫婧愰檺鍒躲 + // - 鍦ㄥ鍣ㄥ寲鐜涓紝浣跨敤cgroups鏉ラ檺鍒跺鍣ㄧ殑璧勬簮浣跨敤銆傚綋闇瑕佸埛鏂癱group鏃讹紝鍙互浣跨敤cgexec_refresh_original_groups鏉ラ噸鏂板垱寤洪粯璁groups锛屽苟鏍规嵁鏂扮殑璧勬簮闇姹傝繘琛岄厤缃洿鏂般 + int cgexec_refresh_original_groups(void) + { + /*鍒犻櫎鍚庣鍜岀被缁 */ + for (int idx = TOPCG_BACKEND; idx <= TOPCG_CLASS; ++idx) { + if (cgutil_vaddr[idx]->used == 0) + 缁х画; - return 0; -} + if (-1 == cgexec_delete_default_cgroup(cgutil_vaddr[idx])) + return -1; + } -/* - * @Description: recover the last group when failure happened - * @Return: - * -1: abnormal 0: normal - * @See also: - */ -int cgexec_recover_groups(void) -{ - void* vaddr = NULL; - size_t cglen = GSCGROUP_ALLNUM * sizeof(gscgroup_grp_t); - int cls_add = 0, cls_del = 0, wd_del = 0; - int clspct_update = 0, wdpct_update = 0, quota_update = 0, other_update = 0; - int wd_add[MAX_WD_LEVEL] = {0}; - int j = 0; - errno_t sret; + /* + *鎴戜滑蹇呴』妫鏌ュ綋鍓嶈妭鐐逛笂鏄惁鍙互浣跨敤閰嶇疆鐨勬渶澶ф牳蹇冩暟锛屽鏋滀笉琛岋紝鎴戜滑灏嗗皾璇曟洿鏂伴厤缃枃浠朵互涓庢柊鑺傜偣鍏煎 + */ + if (cgexec_check_top_cpuset() == -1) { + fprintf(stderr, "閿欒锛氭洿鏂伴《绾puset閿欒銆"); + return -1; + } - /* reset the array */ - sret = memset_s(wd_add, sizeof(wd_add), 0, sizeof(wd_add)); - securec_check_errno(sret, , -1); + /*鍒涘缓榛樿cgroups */ + return cgexec_refresh_groups_internal(); + } + /* + *@鎻忚堪锛氫娇鐢ㄨ妭鐐圭粍鐨勯厤缃枃浠跺埛鏂癱group銆 + * @IN size锛歷oid + * @杩斿洖锛 - 1锛氬紓甯 0锛氭甯 + * @涔熷弬瑙侊細 + */ + //鍑芥暟鍔熻兘锛 + // cgexec_refresh_nodegroup_groups锛氫娇鐢ㄨ妭鐐圭粍鐨勯厤缃枃浠跺埛鏂癱group銆傚畠鍒犻櫎鑺傜偣缁勭殑閫昏緫闆嗙兢缁勶紝鐒跺悗鍒涘缓榛樿cgroups銆 + // 绫讳技鐨勫簲鐢ㄥ疄渚嬶細 + // - 鍦ㄤ竴涓垎甯冨紡绯荤粺涓紝浣跨敤cgroups鏉ョ鐞嗚妭鐐圭粍鐨勮祫婧愪娇鐢ㄣ傚綋闇瑕佹牴鎹柊鐨勮妭鐐圭粍閰嶇疆鍒锋柊cgroup鏃讹紝鍙互浣跨敤cgexec_refresh_nodegroup_groups鏉ラ噸鏂板垱寤洪粯璁groups锛屽苟鍒犻櫎鏃х殑閫昏緫闆嗙兢缁勩 + int cgexec_refresh_nodegroup_groups(void) + { + /*鍒犻櫎閫昏緫闆嗙兢缁 */ + if (-1 == cgptree_drop_nodegroup_cgroups(cgutil_opt.nodegroup)) + 杩斿洖 - 1; - /* get the mapping address of backup file */ - vaddr = cgconf_map_backup_conffile(false); - if (NULL == vaddr) - return -1; + /*鍒涘缓榛樿cgroups */ + return cgexec_create_nodegroup_default_cgroups(); + } - for (int i = 0; i < GSCGROUP_ALLNUM; ++i) { - cgutil_vaddr_back[i] = (gscgroup_grp_t*)vaddr + i; + /* + *@鎻忚堪锛氫娇鐢ㄩ厤缃枃浠跺埛鏂癱group銆 + * @IN澶у皬锛歷oid + * @杩斿洖锛 - 1锛氬紓甯 0锛氭甯 + * @涔熷弬瑙侊細 + */ + //鍑芥暟鍔熻兘锛 +// cgexec_refresh_groups锛氫娇鐢ㄩ厤缃枃浠跺埛鏂癱group銆傚畠鏍规嵁閰嶇疆鏂囦欢鐨勫唴瀹规潵鍒锋柊cgroup锛屽彲浠ユ牴鎹妭鐐圭粍鐨勯厤缃垨鑰呴粯璁ょ殑閰嶇疆鏉ュ埛鏂般 +// 绫讳技鐨勫簲鐢ㄥ疄渚嬶細 +// - 鍦ㄤ竴涓泦缇ょ幆澧冧腑锛屼娇鐢╟groups鏉ョ鐞嗕换鍔″垎閰嶅拰璧勬簮闄愬埗銆傚綋闇瑕佸埛鏂癱group鏃讹紝鍙互鏍规嵁鑺傜偣缁勭殑閰嶇疆浣跨敤cgexec_refresh_groups鏉ユ洿鏂癱group鐨勯厤缃紝浠ラ傚簲涓嶅悓鐨勮妭鐐圭粍鎴栬祫婧愰渶姹傘 + int cgexec_refresh_groups(void) + { + if ('\0' == cgutil_opt.nodegroup[0]) + return cgexec_refresh_original_groups(); + else + return cgexec_refresh_nodegroup_groups(); + } - /* find the different memory region of group entry */ - if ((i >= CLASSCG_START_ID && i <= WDCG_END_ID) && - (0 != memcmp(cgutil_vaddr[i], cgutil_vaddr_back[i], sizeof(gscgroup_grp_t)))) { - if (i >= CLASSCG_START_ID && i <= CLASSCG_END_ID) { - if (cgutil_vaddr[i]->used && cgutil_vaddr_back[i]->used == 0) - cls_del = i; - else if (cgutil_vaddr[i]->used == 0 && cgutil_vaddr_back[i]->used) - cls_add = i; - else if (cgutil_vaddr[i]->used && cgutil_vaddr_back[i]->used && - cgexec_check_update_groups(cgutil_vaddr[i], cgutil_vaddr_back[i])) { - /* dynamic update */ - if (cgutil_vaddr[i]->ginfo.cls.percent != cgutil_vaddr_back[i]->ginfo.cls.percent) - clspct_update = i; - else if (cgutil_vaddr[i]->ainfo.quota != cgutil_vaddr_back[i]->ainfo.quota) - quota_update = i; - else - other_update = i; + /* + *@鎻忚堪锛氳繕鍘焎group鐨勯厤缃 + * @IN澶у皬锛歷oid + * @杩斿洖锛 - 1锛氬紓甯 0锛氭甯 + * @涔熷弬瑙侊細 + */ + /*鍑芥暟鍔熻兘锛 + cgexec_revert_groups锛氳繕鍘焎group鐨勯厤缃傚畠鍒犻櫎闄や簡榛樿绫荤粍涔嬪鐨勬墍鏈夌被缁勶紝閲嶇疆鎵鏈夌粍鐨勯厤缃紝骞惰繕鍘熼厤缃枃浠讹紝鐒跺悗鍒涘缓榛樿缁勩 + 绫讳技鐨勫簲鐢ㄥ疄渚嬶細 + - 鍦ㄤ竴涓鍣ㄥ寲鐜涓紝浣跨敤cgroups鏉ョ鐞嗗鍣ㄧ殑璧勬簮浣跨敤銆傚綋闇瑕佽繕鍘焎group鐨勯厤缃椂锛屽彲浠ヤ娇鐢╟gexec_revert_groups鏉ュ垹闄ゆ棫鐨勭粍锛岄噸缃厤缃紝骞堕噸鏂板垱寤洪粯璁ょ粍锛屼互杩樺師鍒板垵濮嬬殑璧勬簮閰嶇疆銆 */ + int cgexec_revert_groups(void) + { + for (int cls = CLASSCG_START_ID + 1; cls <= CLASSCG_END_ID; ++cls) { + if (cgutil_vaddr[cls]->used == 0) + 缁х画; + + /*鍒犻櫎闄や簡榛樿绫荤粍涔嬪鐨勬墍鏈夌被缁*/ + if (-1 == cgexec_delete_default_cgroup(cgutil_vaddr[cls])) + 杩斿洖-1; + + /*閲嶇疆鎵鏈夌粍鐨勯厤缃*/ + cgconf_reset_class_group(cls); + } + + /*杩樺師閰嶇疆鏂囦欢*/ + cgconf_revert_config_file(); + + /*鍒涘缓榛樿缁勩 + * 濡傛灉鍙戠敓閿欒锛屽垯杩斿洖-1銆 + */ + if (cgexec_create_default_cgroups()) + return -1; + if (cgexec_create_cm_default_cgroup()) + return -1; + + return 0; + } + + /* + * @鎻忚堪锛氭鏌ヤ袱涓粍鏄惁鍙戠敓浜嗗彉鍖栥 + * @IN cur锛氬綋鍓嶇粍 + * @IN bak锛氬浠界粍 + * @杩斿洖锛 1锛氬凡鏇存柊 0锛氭湭鏇存柊 + * @涔熷弬瑙侊細 + */ + /*鍑芥暟鍔熻兘锛 + cgexec_check_update_groups锛氭鏌ヤ袱涓粍鏄惁鍙戠敓浜嗗彉鍖栥傚畠姣旇緝涓や釜缁勭殑alloc_info_t缁撴瀯浣擄紝骞惰繑鍥炴槸鍚﹀彂鐢熶簡鍙樺寲銆 + 绫讳技鐨勫簲鐢ㄥ疄渚嬶細 + - 鍦ㄤ竴涓郴缁熶腑锛屼娇鐢╟groups鏉ラ檺鍒剁▼搴忕殑璧勬簮浣跨敤銆傚綋闇瑕佹鏌ヤ袱涓粍鐨勮祫婧愬垎閰嶆槸鍚﹀彂鐢熷彉鍖栨椂锛屽彲浠ヤ娇鐢╟gexec_check_update_groups鏉ユ瘮杈冧袱涓粍鐨刟lloc_info_t缁撴瀯浣擄紝浠ョ‘瀹氭槸鍚﹂渶瑕佹洿鏂拌祫婧愰厤缃 */ + int cgexec_check_update_groups(gscgroup_grp_t* cur, gscgroup_grp_t* bak) + { + int offset = offsetof(gscgroup_grp_t, ainfo); + int size = sizeof(alloc_info_t); + + if (0 == memcmp((void*)((char*)cur + offset), (void*)((char*)bak + offset), size)) + return 0; + else + return 1; + } + + + /* + * @Description: 閫氳繃鏇存柊鐧惧垎姣旂粍鏉ユ仮澶嶇粍 + * @IN id: 瑕佹洿鏂扮殑缁勭殑ID + * @Return: + * -1: 寮傚父 0: 姝e父 + * @See also: + */ + int cgexec_recover_update_percent_groups(int id) + { + errno_t sret; + + /* 濡傛灉鏄被缁勫彉鍔 */ + if (id >= CLASSCG_START_ID && id <= CLASSCG_END_ID) { + /* 鏇存柊cgroup鍊 */ + if (-1 == cgexec_update_cgroup_value(cgutil_vaddr_back[id])) + return -1; + } + else if (id >= WDCG_START_ID && id <= WDCG_END_ID) { + int cls = cgutil_vaddr_back[id]->ginfo.wd.cgid; + + /* 闇瑕佹洿鏂版墍鏈夌殑绫荤粍鍙婂叾宸ヤ綔璐熻浇缁 */ + sret = memcpy_s(cgutil_vaddr[cls], sizeof(gscgroup_grp_t), cgutil_vaddr_back[cls], sizeof(gscgroup_grp_t)); + securec_check_errno(sret, , -1); + + /* 鏇存柊OS cgroups */ + if (-1 == cgexec_update_cgroup_value(cgutil_vaddr_back[id])) + return -1; + + /* 闇瑕佹洿鏂板伐浣滆礋杞界粍 */ + sret = memcpy_s(cgutil_vaddr[id], sizeof(gscgroup_grp_t), cgutil_vaddr_back[id], sizeof(gscgroup_grp_t)); + securec_check_errno(sret, , -1); + + /* 鏇存柊鍓╀綑缁 */ + if (-1 == cgexec_update_remain_cgroup(cgutil_vaddr[id], cls)) + return -1; + } + + return 0; + } + + /* + * @Description: 閫氳繃鏇存柊鍥哄畾鐨勭被缁勬潵鎭㈠缁 + * @IN id: 瑕佹洿鏂扮殑缁勭殑ID + * @IN cpuset: 杈撳叆鐨刢puset瀛楃涓 + * @IN reverse: 鏍囧織浣嶏紝琛ㄧず鏄惁鍙嶅悜 + * @Return: + * -1: 寮傚父 0: 姝e父 + * @See also: + */ + int cgexec_recover_update_fixed_class_group(int id, char* cpuset, int reverse) + { + errno_t sret; + + if (!reverse) /* 浠庝笅寰涓婃洿鏂 */ + { + /* 灏嗗煎鍒跺埌绫荤粍涓 */ + sret = strcpy_s(cgutil_vaddr[id]->cpuset, CPUSET_LEN, cpuset); + securec_check_errno(sret, , -1); + + /* 鏇存柊绫荤粍鍒癱group fs */ + if (-1 == cgexec_update_cgroup_value(cgutil_vaddr[id])) + return -1; + + /* 鏇存柊鍓╀綑缁 */ + for (int j = 1; j <= cgutil_vaddr[id]->ginfo.cls.maxlevel; ++j) { + if (-1 == cgexec_update_remain_cgroup_cpuset_value(id, j, cpuset)) + return -1; } - } else if (i > WDCG_START_ID) { - if (cgutil_vaddr[i]->used && cgutil_vaddr_back[i]->used == 0) - wd_del = i; - else if (cgutil_vaddr[i]->used == 0 && cgutil_vaddr_back[i]->used) { - if (j == MAX_WD_LEVEL) { - fprintf(stderr, "ERROR: configure file has more than %d different workload!\n", MAX_WD_LEVEL); - goto error; - } - wd_add[j++] = i; - } else if (cgutil_vaddr[i]->used && cgutil_vaddr_back[i]->used && - cgexec_check_update_groups(cgutil_vaddr[i], cgutil_vaddr_back[i])) { - /* dynamic update */ - if (cgutil_vaddr[i]->ginfo.wd.percent != cgutil_vaddr_back[i]->ginfo.wd.percent) - wdpct_update = i; - else if (cgutil_vaddr[i]->ainfo.quota != cgutil_vaddr_back[i]->ainfo.quota) { - if (quota_update) { - fprintf(stderr, - "ERROR: cpu core quota has been set on %d, " - "it should not be appear on %d again!\n", - quota_update, - i); - goto error; + + /* 鏇存柊鏃堕棿鍏变韩缁 */ + if (-1 == cgexec_update_timeshare_cpuset(cgutil_vaddr[id], cpuset, 0)) + return -1; + + /* 鏇存柊TopWD缁 */ + if (-1 == cgexec_update_topwd_cgroup_cpuset(id, cpuset)) + return -1; + } + else { + /* 鏇存柊鏃堕棿鍏变韩缁 */ + if (-1 == cgexec_update_timeshare_cpuset(cgutil_vaddr[id], cpuset, 1)) + return -1; + + /* 鏇存柊鍓╀綑缁 */ + for (int j = cgutil_vaddr[id]->ginfo.cls.maxlevel; j >= 1; --j) { + if (-1 == cgexec_update_remain_cgroup_cpuset_value(id, j, cpuset)) + return -1; + } + + /* 鏇存柊TopWD缁 */ + if (-1 == cgexec_update_topwd_cgroup_cpuset(id, cpuset)) + return -1; + + /* 灏嗗煎鍒跺埌绫荤粍涓 */ + sret = strcpy_s(cgutil_vaddr[id]->cpuset, GPNAME_LEN, cpuset); + securec_check_errno(sret, , -1); + + /* 鏇存柊绫荤粍鍒癱group fs */ + if (-1 == cgexec_update_cgroup_value(cgutil_vaddr[id])) + return -1; + } + + return 0; + } + + /* + * @Description: 閫氳繃鏇存柊閰嶉缁勬潵鎭㈠缁 + * @IN id: 鏇存柊鐨勭粍鐨処D + * @Return: + * -1锛氬紓甯 0锛氭甯 + * @See also: + */ + int cgexec_recover_update_quota_groups(int id) + { + /* 濡傛灉鏄被缁勫彂鐢熷彉鍖 */ + if (id >= CLASSCG_START_ID && id <= CLASSCG_END_ID) { + /* 灏嗛粯璁ゅ奸噸缃负椤剁骇绫荤粍 */ + for (int i = CLASSCG_START_ID; i <= CLASSCG_END_ID; i++) { + /* 濡傛灉璇ョ被缁勬湭琚娇鐢ㄦ垨鑰咃紙涓嶆槸鏇存柊鐨勭粍涓旈厤棰濅负0锛夛紝鍒欒烦杩 */ + if (cgutil_vaddr[i]->used == 0 || (i != id && cgutil_vaddr[i]->ainfo.quota == 0)) + continue; + + /* 鍩轰簬澶囦唤鍊兼洿鏂板墿浣欏拰鏃堕棿鍏变韩缁 */ + if (-1 == cgexec_recover_update_fixed_class_group(i, cgutil_vaddr[TOPCG_CLASS]->cpuset, 0)) + return -1; + } + + /* 鏇存柊鎵鏈夌殑宸ヤ綔璐熻浇缁 */ + for (int j = WDCG_START_ID; j <= WDCG_END_ID; j++) { + /* 濡傛灉璇ュ伐浣滆礋杞界粍鏈浣跨敤鎴栬呭伐浣滆礋杞界粍鐨剋dlevel涓1锛屽垯璺宠繃 */ + if (cgutil_vaddr_back[j]->used == 0 || cgutil_vaddr_back[j]->ginfo.wd.wdlevel == 1) + continue; + + /* 鏇存柊宸ヤ綔璐熻浇缁 */ + if (-1 == cgexec_update_cgroup_value(cgutil_vaddr_back[j])) + return -1; + } + + /* 閲嶆柊鏇存柊绫荤粍 */ + for (int i = CLASSCG_START_ID; i <= CLASSCG_END_ID; i++) { + /* 濡傛灉璇ョ被缁勬湭琚娇鐢ㄦ垨鑰咃紙涓嶆槸鏇存柊鐨勭粍涓旈厤棰濅负0锛夛紝鍒欒烦杩 */ + if (cgutil_vaddr[i]->used == 0 || (i != id && cgutil_vaddr[i]->ainfo.quota == 0)) + continue; + + if (-1 == cgexec_recover_update_fixed_class_group(i, cgutil_vaddr_back[i]->cpuset, 1)) + return -1; + } + } + /* 濡傛灉鏄伐浣滆礋杞界粍鍙戠敓鍙樺寲 */ + else if (id >= WDCG_START_ID && id <= WDCG_END_ID) { + int cls = cgutil_vaddr_back[id]->ginfo.wd.cgid; + + for (int i = WDCG_START_ID; i <= WDCG_END_ID; i++) { + /* 濡傛灉璇ュ伐浣滆礋杞界粍鏈浣跨敤鎴栬呭伐浣滆礋杞界粍鐨刢gid涓嶇瓑浜巆ls锛屾垨鑰呭伐浣滆礋杞界粍鐨剋dlevel涓1锛屽垯璺宠繃 */ + if (cgutil_vaddr_back[i]->used == 0 || cgutil_vaddr_back[i]->ginfo.wd.cgid != cls || + cgutil_vaddr_back[i]->ginfo.wd.wdlevel == 1) + continue; + + /* 鏇存柊宸ヤ綔璐熻浇缁 */ + if (-1 == cgexec_update_cgroup_value(cgutil_vaddr_back[i])) + return -1; + } + } + + return 0; + } + + /* + * @Description: 閫氳繃鍒涘缓鏂扮殑cgroup鏉ユ仮澶嶇粍 + * @IN cls_add: 绫荤粍ID + * @IN wd_add: 鎵鏈夋柊鐨勫伐浣滆礋杞界粍鐨勬暟缁 + * @Return: + * -1锛氬紓甯 0锛氭甯 + * @See also: + */ + int cgexec_recover_create_groups(int cls_add, const int* wd_add) + { + int wld = 0, level = 0, i = 0, tmpcls; + int tmpwld[MAX_WD_LEVEL] = { 0 }; + errno_t sret; + + sret = memset_s(tmpwld, sizeof(tmpwld), 0, sizeof(tmpwld)); + securec_check_errno(sret, , -1); + + /* 鑾峰彇绫荤粍淇℃伅 */ + wld = wd_add[0]; + tmpcls = cgutil_vaddr_back[wld]->ginfo.wd.cgid; + + /* 楠岃瘉淇℃伅 */ + if (cls_add && cls_add != tmpcls) { + fprintf(stderr, "ERROR: 鏂板鐨勫伐浣滆礋杞界粍涓庣被缁勪笉鍖归厤锛乗n"); + return -1; + } + + if (cls_add == 0 && wd_add[1]) { + fprintf(stderr, + "ERROR: 褰撳彧鎭㈠宸ヤ綔璐熻浇缁勬椂锛屽彂鐜板涓柊澧炵殑宸ヤ綔璐熻浇缁勶紒\n"); + return -1; + } + + /* 鎼滅储宸ヤ綔璐熻浇缁 */ + for (i = WDCG_START_ID; i <= WDCG_END_ID; ++i) { + if (cgutil_vaddr_back[i]->used && cgutil_vaddr_back[i]->ginfo.wd.cgid == tmpcls) { + level = cgutil_vaddr_back[i]->ginfo.wd.wdlevel; + tmpwld[level - 1] = i; + } + } + + /* 濡傛灉鍙坊鍔犲伐浣滆礋杞界粍锛屽厛鍒犻櫎绫荤粍 */ + if (cls_add == 0 && -1 == cgexec_delete_default_cgroup(cgutil_vaddr[tmpcls])) + return -1; + + /* 澶嶅埗绫荤粍淇℃伅 */ + sret = memcpy_s(cgutil_vaddr[tmpcls], sizeof(gscgroup_grp_t), cgutil_vaddr_back[tmpcls], sizeof(gscgroup_grp_t)); + securec_check_errno(sret, , -1); + + /* 閲嶇疆鍊 */ + cgutil_vaddr[tmpcls]->ginfo.cls.maxlevel = 0; + cgutil_vaddr[tmpcls]->ginfo.cls.rempct = 100; + cgconf_update_class_percent(); + + /* 鍒涘缓绫荤粍 */ + if (-1 == cgexec_create_new_cgroup(cgutil_vaddr[tmpcls])) { + cgconf_reset_class_group(tmpcls); + return -1; + } + + /* 寰幆鍒涘缓宸ヤ綔璐熻浇缁 */ + for (i = 0; i < MAX_WD_LEVEL; i++) { + wld = tmpwld[i]; + if (wld == 0) + break; + + /* 澶嶅埗宸ヤ綔璐熻浇缁勪俊鎭 */ + sret = memcpy_s(cgutil_vaddr[wld], sizeof(gscgroup_grp_t), cgutil_vaddr_back[wld], sizeof(gscgroup_grp_t)); + securec_check_errno(sret, , -1); + + if (i) /* level > 1锛屼笉鏄《绾у伐浣滆礋杞 */ + cgutil_vaddr[tmpcls]->ginfo.cls.rempct -= cgutil_vaddr[wld]->ginfo.wd.percent; + + /* 鍒涘缓宸ヤ綔璐熻浇cgroup */ + if (-1 == cgexec_create_workload_cgroup(cgutil_vaddr[wld])) { + cgconf_reset_workload_group(wld); + return -1; + } + } + + /* 鍒涘缓鏃堕棿鍏变韩cgroup */ + if (-1 == cgexec_create_timeshare_cgroup(cgutil_vaddr[tmpcls])) + return -1; + + return 0; + } + /* + * @Description: 鎭㈠澶辫触鍙戠敓鏃剁殑鏈鍚庝竴缁勬暟鎹 + * @Return: + * -1: 寮傚父 0: 姝e父 + * @See also: + */ + /*鍑芥暟鍔熻兘锛氳鍑芥暟鐢ㄤ簬鍦ㄥ彂鐢熷け璐ユ椂鎭㈠鍒嗙被缁勫拰宸ヤ綔璐熻浇缁勭殑鏈鍚庝竴缁勬暟鎹傚嚱鏁颁細妫鏌ュ浠芥枃浠朵腑鐨勬暟鎹笌褰撳墠鍐呭瓨涓殑鏁版嵁鏄惁涓鑷达紝鑻ヤ笉涓鑷村垯鏍规嵁涓嶅悓鎯呭喌杩涜鐩稿簲鐨勫鐞嗭紝鍖呮嫭鍒犻櫎缁勩佸姩鎬佹洿鏂扮瓑鎿嶄綔銆傛渶鍚庯紝灏嗘仮澶嶇殑鏁版嵁鍐欏洖閰嶇疆鏂囦欢銆 + 鍑芥暟鍙橀噺锛 + - vaddr锛氬浠芥枃浠剁殑鏄犲皠鍦板潃 + - cglen锛氶渶瑕佺殑鍐呭瓨绌洪棿澶у皬 + - cls_add锛氬緟娣诲姞鐨勫垎绫荤粍ID + - cls_del锛氬緟鍒犻櫎鐨勫垎绫荤粍ID + - wd_del锛氬緟鍒犻櫎鐨勫伐浣滆礋杞界粍ID + - clspct_update锛氬緟鍔ㄦ佹洿鏂扮殑鍒嗙被缁処D锛堟寜鐧惧垎姣旓級 + - wdpct_update锛氬緟鍔ㄦ佹洿鏂扮殑宸ヤ綔璐熻浇缁処D锛堟寜鐧惧垎姣旓級 + - quota_update锛氬緟鍔ㄦ佹洿鏂扮殑缁処D锛堟寜閰嶉锛 + - other_update锛氬緟鍔ㄦ佹洿鏂扮殑缁処D锛堝叾浠栨儏鍐碉級 + - wd_add[MAX_WD_LEVEL]锛氬緟娣诲姞鐨勫伐浣滆礋杞界粍ID鏁扮粍 + - j锛氬伐浣滆礋杞界粍ID鏁扮粍鐨勭储寮 + - sret锛氶敊璇爜 + + 鍑芥暟搴旂敤瀹炰緥锛氳繖娈典唬鐮佹槸涓涓郴缁熺鐞嗗伐鍏蜂腑鐢ㄤ簬鎭㈠缁勬暟鎹殑鍑芥暟銆備緥濡傦紝鍦ㄧ郴缁熷崌绾ц繃绋嬩腑锛屽彲鑳界敱浜庡け璐ョ瓑鍘熷洜瀵艰嚧閰嶇疆鏂囦欢琚崯鍧忥紝姝ゆ椂鍙互浣跨敤璇ュ嚱鏁板皢澶囦唤鏂囦欢涓殑鏁版嵁鎭㈠鍒板唴瀛樹腑锛屼互淇濊瘉绯荤粺姝e父杩愯銆*/ + int cgexec_recover_groups(void) + { + void* vaddr = NULL; // 澶囦唤鏂囦欢鐨勬槧灏勫湴鍧 + size_t cglen = GSCGROUP_ALLNUM * sizeof(gscgroup_grp_t); // 璁$畻闇瑕佺殑鍐呭瓨绌洪棿澶у皬 + int cls_add = 0, cls_del = 0, wd_del = 0; // 鍒嗙被缁勭浉鍏崇殑鍙橀噺 + int clspct_update = 0, wdpct_update = 0, quota_update = 0, other_update = 0; // 鍔ㄦ佹洿鏂扮浉鍏崇殑鍙橀噺 + int wd_add[MAX_WD_LEVEL] = { 0 }; // 宸ヤ綔璐熻浇缁勭浉鍏崇殑鍙橀噺 + int j = 0; // 璁℃暟鍣 + errno_t sret; // 閿欒鐮 + + /* 閲嶇疆鏁扮粍 */ + sret = memset_s(wd_add, sizeof(wd_add), 0, sizeof(wd_add)); + securec_check_errno(sret, , -1); + + /* 鑾峰彇澶囦唤鏂囦欢鐨勬槧灏勫湴鍧 */ + vaddr = cgconf_map_backup_conffile(false); + if (NULL == vaddr) + return -1; + + for (int i = 0; i < GSCGROUP_ALLNUM; ++i) { + cgutil_vaddr_back[i] = (gscgroup_grp_t*)vaddr + i; + + /* 鏌ユ壘涓嶅悓鐨勭粍鏉$洰鍐呭瓨鍖哄煙 */ + if ((i >= CLASSCG_START_ID && i <= WDCG_END_ID) && + (0 != memcmp(cgutil_vaddr[i], cgutil_vaddr_back[i], sizeof(gscgroup_grp_t)))) { + if (i >= CLASSCG_START_ID && i <= CLASSCG_END_ID) { + if (cgutil_vaddr[i]->used && cgutil_vaddr_back[i]->used == 0) + cls_del = i; + else if (cgutil_vaddr[i]->used == 0 && cgutil_vaddr_back[i]->used) + cls_add = i; + else if (cgutil_vaddr[i]->used && cgutil_vaddr_back[i]->used && + cgexec_check_update_groups(cgutil_vaddr[i], cgutil_vaddr_back[i])) { + /* 鍔ㄦ佹洿鏂 */ + if (cgutil_vaddr[i]->ginfo.cls.percent != cgutil_vaddr_back[i]->ginfo.cls.percent) + clspct_update = i; + else if (cgutil_vaddr[i]->ainfo.quota != cgutil_vaddr_back[i]->ainfo.quota) + quota_update = i; + else + other_update = i; } - quota_update = i; - } else - other_update = i; + } + else if (i > WDCG_START_ID) { + if (cgutil_vaddr[i]->used && cgutil_vaddr_back[i]->used == 0) + wd_del = i; + else if (cgutil_vaddr[i]->used == 0 && cgutil_vaddr_back[i]->used) { + if (j == MAX_WD_LEVEL) { + fprintf(stderr, "ERROR: configure file has more than %d different workload!\n", MAX_WD_LEVEL); + goto error; + } + wd_add[j++] = i; + } + else if (cgutil_vaddr[i]->used && cgutil_vaddr_back[i]->used && + cgexec_check_update_groups(cgutil_vaddr[i], cgutil_vaddr_back[i])) { + /* 鍔ㄦ佹洿鏂 */ + if (cgutil_vaddr[i]->ginfo.wd.percent != cgutil_vaddr_back[i]->ginfo.wd.percent) + wdpct_update = i; + else if (cgutil_vaddr[i]->ainfo.quota != cgutil_vaddr_back[i]->ainfo.quota) { + if (quota_update) { + fprintf(stderr, + "ERROR: cpu core quota has been set on %d, " + "it should not be appear on %d again!\n", + quota_update, + i); + goto error; + } + quota_update = i; + } + else + other_update = i; + } + } } } + + /* -u class update */ + if (clspct_update && -1 == cgexec_recover_update_percent_groups(clspct_update)) + goto error; + + /* -u workload update */ + if (wdpct_update && -1 == cgexec_recover_update_percent_groups(wdpct_update)) + goto error; + + /* -u --fixed update */ + if (quota_update && -1 == cgexec_recover_update_quota_groups(quota_update)) + goto error; + + /* like blkio throttle update */ + if (clspct_update == 0 && wdpct_update == 0 && quota_update == 0 && other_update && + (-1 == cgexec_update_cgroup_value(cgutil_vaddr_back[other_update]))) + goto error; + + /* 鐩存帴鍒犻櫎鍒嗙被缁 */ + if (cls_del) { + if (-1 == cgexec_delete_default_cgroup(cgutil_vaddr[cls_del])) + goto error; + cgconf_reset_class_group(cls_del); + } + else if (cls_del == 0 && wd_del) /* 鍒犻櫎宸ヤ綔璐熻浇缁 */ + { + (void)cgexec_delete_workload_cgroup(cgutil_vaddr[wd_del]); + } + + /* 娣诲姞鍒嗙被缁勫拰宸ヤ綔璐熻浇缁 */ + if (wd_add[0]) { + if (-1 == cgexec_recover_create_groups(cls_add, wd_add)) + goto error; + } + + /* 鏈鍚庢仮澶嶉厤缃枃浠 */ + sret = memcpy_s(cgutil_vaddr[0], cglen, vaddr, cglen); + securec_check_errno(sret, (void)munmap(vaddr, cglen); cgconf_remove_backup_conffile(); , -1); + + (void)munmap(vaddr, cglen); + cgconf_remove_backup_conffile(); + return 0; + + error: + securec_check_errno(sret, (void)munmap(vaddr, cglen); , -1); + cgconf_remove_backup_conffile(); + return -1; + } + /* + * @Description: 鎸傝浇鎺у埗缁勩 + * @IN : void + * @Return: void + * @See also: + */ + void cgexec_mount_cgroups(void) + { + /* 鎸傝浇鎺у埗缁 */ + (void)cgexec_mount_root_cgroup(); } - } - /* -u class update */ - if (clspct_update && -1 == cgexec_recover_update_percent_groups(clspct_update)) - goto error; + /* + * @Description: 鍗歌浇鎺у埗缁勩 + * @IN : void + * @Return: void + * @See also: + */ + void cgexec_umount_cgroups(void) + { + /* 鍗歌浇鎺у埗缁 */ + (void)cgexec_umount_root_cgroup(); + } - /* -u workload update */ - if (wdpct_update && -1 == cgexec_recover_update_percent_groups(wdpct_update)) - goto error; + /* + * function name: cgexec_create_cm_default_cgroup + * description : 鍒涘缓cm榛樿鎺у埗缁 + * arguments : void + * return value : + * -1: 寮傚父 + * 0: 姝e父 + * Note: 璇ュ嚱鏁扮敤浜庡垱寤烘柊鐨勬帶鍒剁粍銆 + */ - /* -u --fixed update */ - if (quota_update && -1 == cgexec_recover_update_quota_groups(quota_update)) - goto error; + int cgexec_create_cm_default_cgroup(void) + { + int ret = 0; + errno_t rc = EOK; + char cgpath[GPNAME_PATH_LEN] = { 0 }; + struct stat buf; - /* like blkio throttle update */ - if (clspct_update == 0 && wdpct_update == 0 && quota_update == 0 && other_update && - (-1 == cgexec_update_cgroup_value(cgutil_vaddr_back[other_update]))) - goto error; + rc = memset_s(&buf, sizeof(buf), 0, sizeof(buf)); + securec_check_errno(rc, , -1); + rc = sprintf_s(cgpath, GPNAME_PATH_LEN, "%s/%s:%s", cgutil_opt.mpoints[0], GSCGROUP_CM, cgutil_opt.user); + securec_check_ss_c(rc, "\0", "\0"); - /* delete the class group directly */ - if (cls_del) { - if (-1 == cgexec_delete_default_cgroup(cgutil_vaddr[cls_del])) - goto error; - cgconf_reset_class_group(cls_del); - } else if (cls_del == 0 && wd_del) /* delete the workload group */ - { - (void)cgexec_delete_workload_cgroup(cgutil_vaddr[wd_del]); - } + // 鍒涘缓cm鎺у埗缁勬椂蹇呴』浠oot鐢ㄦ埛杩愯銆 + if (geteuid() != 0) + return 0; - /* add class and workload group */ - if (wd_add[0]) { - if (-1 == cgexec_recover_create_groups(cls_add, wd_add)) - goto error; - } + if (0 == stat(cgpath, &buf)) { + fprintf(stderr, "'%s' 宸插瓨鍦紝蹇界暐鍒涘缓姝ゆ帶鍒剁粍銆俓n", cgpath); + return 0; + } - /* recover the configuration file finally */ - sret = memcpy_s(cgutil_vaddr[0], cglen, vaddr, cglen); - securec_check_errno(sret, (void)munmap(vaddr, cglen); cgconf_remove_backup_conffile();, -1); + rc = sprintf_s(cgpath, GPNAME_PATH_LEN, "%s:%s", GSCGROUP_CM, cgutil_opt.user); + securec_check_ss_c(rc, "\0", "\0"); - (void)munmap(vaddr, cglen); - cgconf_remove_backup_conffile(); - return 0; + ret = cgexec_create_default_cgroup(cgpath, DEFAULT_CM_CPUSHARES, DEFAULT_IO_WEIGHT, cgutil_allset); + if (ret == -1) { + fprintf(stderr, "鏃犳硶鍒涘缓cm鎺у埗缁勶紝cgpath涓%s銆俓n", cgpath); + return -1; + } -error: - securec_check_errno(sret, (void)munmap(vaddr, cglen);, -1); - cgconf_remove_backup_conffile(); - return -1; -} + return 0; + } -/* - * @Description: mount control groups. - * @IN : void - * @Return: void - * @See also: - */ -void cgexec_mount_cgroups(void) -{ - /* mount the cgroup */ - (void)cgexec_mount_root_cgroup(); -} + /* 鍒犻櫎cm鎺у埗缁 */ + int cgexec_delete_cm_cgroup(void) + { + int ret = 0; + errno_t rc = EOK; + char cgpath[GPNAME_PATH_LEN] = { 0 }; + struct stat buf; -/* - * @Description: unmount control groups. - * @IN : void - * @Return: void - * @See also: - */ -void cgexec_umount_cgroups(void) -{ - /* umount the cgroup */ - (void)cgexec_umount_root_cgroup(); -} + rc = memset_s(&buf, sizeof(buf), 0, sizeof(buf)); + securec_check_errno(rc, , -1); + rc = sprintf_s(cgpath, GPNAME_PATH_LEN, "%s/%s:%s", cgutil_opt.mpoints[0], GSCGROUP_CM, cgutil_opt.user); + securec_check_ss_c(rc, "\0", "\0"); -/* - * function name: cgexec_create_cm_default_cgroup - * description : create cm default cgroup - * arguments : void - * return value : - * -1: abnormal - * 0: normal - * Note: the function is used when creating new Cgroup. - */ + if (0 != stat(cgpath, &buf)) { + return -1; + } -int cgexec_create_cm_default_cgroup(void) -{ - int ret = 0; - errno_t rc = EOK; - char cgpath[GPNAME_PATH_LEN] = {0}; - struct stat buf; + rc = sprintf_s(cgpath, GPNAME_PATH_LEN, "%s:%s", GSCGROUP_CM, cgutil_opt.user); + securec_check_ss_c(rc, "\0", "\0"); - rc = memset_s(&buf, sizeof(buf), 0, sizeof(buf)); - securec_check_errno(rc, , -1); - rc = sprintf_s(cgpath, GPNAME_PATH_LEN, "%s/%s:%s", cgutil_opt.mpoints[0], GSCGROUP_CM, cgutil_opt.user); - securec_check_ss_c(rc, "\0", "\0"); + ret = cgexec_delete_cgroups(cgpath); + if (ret == -1) { + fprintf(stderr, "鏃犳硶鍒犻櫎cm鎺у埗缁勶紝cgpath涓%s銆俓n", cgpath); + return -1; + } - // creating cm cgroup must be run by root user. - if (geteuid() != 0) - return 0; + return 0; + } - if (0 == stat(cgpath, &buf)) { - fprintf(stderr, "'%s' exists, omit to create this cgroup.\n", cgpath); - return 0; - } - - rc = sprintf_s(cgpath, GPNAME_PATH_LEN, "%s:%s", GSCGROUP_CM, cgutil_opt.user); - securec_check_ss_c(rc, "\0", "\0"); - - ret = cgexec_create_default_cgroup(cgpath, DEFAULT_CM_CPUSHARES, DEFAULT_IO_WEIGHT, cgutil_allset); - if (ret == -1) { - fprintf(stderr, "can not create cm cgroup, cgpath is %s.\n", cgpath); - return -1; - } - - return 0; -} - -/* delete cm cgroup */ -int cgexec_delete_cm_cgroup(void) -{ - int ret = 0; - errno_t rc = EOK; - char cgpath[GPNAME_PATH_LEN] = {0}; - struct stat buf; - - rc = memset_s(&buf, sizeof(buf), 0, sizeof(buf)); - securec_check_errno(rc, , -1); - rc = sprintf_s(cgpath, GPNAME_PATH_LEN, "%s/%s:%s", cgutil_opt.mpoints[0], GSCGROUP_CM, cgutil_opt.user); - securec_check_ss_c(rc, "\0", "\0"); - - if (0 != stat(cgpath, &buf)) { - return -1; - } - - rc = sprintf_s(cgpath, GPNAME_PATH_LEN, "%s:%s", GSCGROUP_CM, cgutil_opt.user); - securec_check_ss_c(rc, "\0", "\0"); - - ret = cgexec_delete_cgroups(cgpath); - if (ret == -1) { - fprintf(stderr, "can not create cm cgroup,cgpath is %s.\n", cgpath); - return -1; - } - - return 0; -} diff --git a/src/bin/gs_cgroup/cgptree.cpp b/src/bin/gs_cgroup/cgptree.cpp index 81ad6fdb8..9d14511ab 100644 --- a/src/bin/gs_cgroup/cgptree.cpp +++ b/src/bin/gs_cgroup/cgptree.cpp @@ -1,22 +1,21 @@ /* - * Copyright (c) 2020 Huawei Technologies Co.,Ltd. + * 版权所有 (c) 2020 华为技术有限公司。 * - * openGauss is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: + * openGauss在Mulan PSL v2下授权许可。 + * 您可以根据Mulan PSL v2的条款和条件使用本软件。 + * 您可以在以下网址获得Mulan PSL v2的副本: * - * http://license.coscl.org.cn/MulanPSL2 + * http://license.coscl.org.cn/MulanPSL2 * - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, - * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. + * 本软件按“原样”提供,不提供任何明示或暗示的保证, + * 包括但不限于对特定用途的适销性和对非侵权的任何隐含保证。 + * 有关更多详情,请参见Mulan PSL v2。 *------------------------------------------------------------------------- * * cgptree.cpp - * Display the Cgroup Tree structure + * 显示Cgroup树结构 * - * IDENTIFICATION + * 识别 * src/bin/gs_cgroup/cgptree.cpp * *------------------------------------------------------------------------- @@ -38,41 +37,41 @@ #include "cgutil.h" -/* data structure of controller information */ + /* 控制器信息的数据结构 */ struct controller_info { - char* ctrl_name; /* controller name */ - char* mount_point; /* the directory path of mount point */ - struct group_info* group_head; /* point to the head of group info structure */ - struct controller_info* next; /* point to the next controller */ + char* ctrl_name; /* 控制器名称 */ + char* mount_point; /* 挂载点的目录路径 */ + struct group_info* group_head; /* 指向组信息结构的头部 */ + struct controller_info* next; /* 指向下一个控制器 */ }; -/* data structure of group information */ +/* 组信息的数据结构 */ struct group_info { - char* grpname; /* the group name */ - char* relpath; /* the relative path of this group */ - int depth; /* the depth of this group */ - int64_t cpu_quota; /* the value of cpu.cfs_quota_us */ - int64_t cpu_period; /* the value of cpu.cfs_period_us */ - u_int64_t cpu_shares; /* the value of cpu.shares */ - u_int64_t blkio_weight; /* the value of blkio.weight */ - char* blkio_bpsread; /* io bps read value */ - char* blkio_iopsread; /* io iops read value */ - char* blkio_bpswrite; /* io bps write value */ - char* blkio_iopswrite; /* io iops write value */ - char* cpuset_cpus; /* cpuset value */ - int64_t cpuset_mems; /* cpuset memory info */ - int64_t cpuacct_usage; /* cpu account */ - struct task_info* task_head; /* the task list in this group */ - struct group_info* parent; /* point to the parent group (upper level) */ - struct group_info* child_head; /* point to the child group (lower level) */ - struct group_info* prev; /* point to the previous group (same level) */ - struct group_info* next; /* point to the next group (same level) */ + char* grpname; /* 组名 */ + char* relpath; /* 相对路径 */ + int depth; /* 深度 */ + int64_t cpu_quota; /* cpu.cfs_quota_us的值 */ + int64_t cpu_period; /* cpu.cfs_period_us的值 */ + u_int64_t cpu_shares; /* cpu.shares的值 */ + u_int64_t blkio_weight; /* blkio.weight的值 */ + char* blkio_bpsread; /* io bps读取值 */ + char* blkio_iopsread; /* io iops读取值 */ + char* blkio_bpswrite; /* io bps写入值 */ + char* blkio_iopswrite; /* io iops写入值 */ + char* cpuset_cpus; /* cpuset值 */ + int64_t cpuset_mems; /* cpuset内存信息 */ + int64_t cpuacct_usage; /* cpu使用情况 */ + struct task_info* task_head; /* 组内任务列表 */ + struct group_info* parent; /* 指向上级组 */ + struct group_info* child_head; /* 指向下级组 */ + struct group_info* prev; /* 指向同级前一个组 */ + struct group_info* next; /* 指向同级后一个组 */ }; -/* data structure of task information */ +/* 任务信息的数据结构 */ struct task_info { - pid_t pid; /* the thread id (gettid) */ - struct task_info* next; /* point to the next task */ + pid_t pid; /* 线程id (gettid) */ + struct task_info* next; /* 指向下一个任务 */ }; extern char* cgutil_subsys_table[]; @@ -95,12 +94,13 @@ extern char* cgutil_subsys_table[]; ***************** STATIC FUNCTIONS ************************ */ -/* - * function name: cgpstree_free_task_list - * description : free task structure list - * arguments : - * head: the data structure of task list information - */ + /* + * 函数名:cgpstree_free_task_list + * 描述:释放任务结构列表 + * 参数: + * head:任务列表的数据结构 + */ + static void cgpstree_free_task_list(struct task_info* head) { struct task_info* curr = head; @@ -118,80 +118,80 @@ static void cgpstree_free_task_list(struct task_info* head) curr = next; } } - -/* - * function name: cgptree_rec_free_group_tree - * description : free group structure recursively - * arguments : - * current: the data structure of group tree information +/** + * 函数名:cgptree_rec_free_group_tree + * 描述:递归释放组树结构 + * 参数: + * current:组树信息的数据结构 + * 功能:递归释放组树结构。首先判断当前节点是否为空,如果为空则直接返回。然后递归释放下一个组和子组。接着释放当前组的组名、相对路径、块IO读取比特速率、块IO读取操作数、块IO写入比特速率、块IO写入操作数、cpuset的cpu列表和任务列表的内存。最后释放当前组的内存,并将其置为NULL。 */ + static void cgptree_rec_free_group_tree(struct group_info* current) { if (current == NULL) return; + // 递归释放下一个组和子组 if (current->next != NULL) cgptree_rec_free_group_tree(current->next); if (current->child_head != NULL) cgptree_rec_free_group_tree(current->child_head); + // 释放组名、相对路径、块IO读取比特速率、块IO读取操作数、块IO写入比特速率、块IO写入操作数、 + // cpuset的cpu列表、任务列表 if (current->grpname != NULL) free(current->grpname); - if (current->relpath != NULL) free(current->relpath); - if (current->blkio_bpsread != NULL) free(current->blkio_bpsread); - if (current->blkio_iopsread != NULL) free(current->blkio_iopsread); - if (current->blkio_bpswrite != NULL) free(current->blkio_bpswrite); - if (current->blkio_iopswrite != NULL) free(current->blkio_iopswrite); - if (current->cpuset_cpus != NULL) free(current->cpuset_cpus); - if (current->task_head != NULL) cgpstree_free_task_list(current->task_head); + // 释放当前组的内存,并将其置为NULL free(current); current = NULL; return; } -/* - * function name: cgptree_free_group_tree - * description : free group structure of one controller - * arguments : - * head: the head group of one controller +/** + * 函数名:cgptree_free_group_tree + * 描述:释放一个控制器的组树结构 + * 参数: + * head:控制器的头组 + * 功能:释放一个控制器的组树结构。调用cgptree_rec_free_group_tree函数来递归地释放组树结构。 */ static void cgptree_free_group_tree(struct group_info* head) { + // 递归释放组树结构 cgptree_rec_free_group_tree(head); return; } -/* - * function name: cgptree_get_cgroup - * description : get the cgroup structure based on the relative path - * arguments : - * relpath: the relative path - * return value : the pointer of cgroup structure - * +/** + * 函数名:cgptree_get_cgroup + * 描述:根据相对路径获取cgroup结构 + * 参数: + * relpath:相对路径 + * 返回值:cgroup结构的指针 + * 功能:根据相对路径获取cgroup结构。首先分配一个新的cgroup结构,并根据相对路径初始化该结构。如果分配失败,则打印错误信息并返回NULL。然后从内核获取关于cgroup的所有信息。如果获取失败,则打印错误信息,并释放之前分配的cgroup结构,并返回NULL。如果获取成功,则返回cgroup结构的指针。 */ static struct cgroup* cgptree_get_cgroup(const char* relpath) { int ret = 0; struct cgroup* cg = NULL; - /* allocate new cgroup structure */ + /* 分配新的cgroup结构 */ cg = cgroup_new_cgroup(relpath); if (cg == NULL) { ret = ECGFAIL; @@ -199,7 +199,7 @@ static struct cgroup* cgptree_get_cgroup(const char* relpath) return NULL; } - /* get all information regarding the cgroup from kernel */ + /* 从内核获取关于cgroup的所有信息 */ ret = cgroup_get_cgroup(cg); if (ret != 0) { fprintf(stdout, "failed to get cgroup information for %s(%d)\n", cgroup_strerror(ret), ret); @@ -209,15 +209,17 @@ static struct cgroup* cgptree_get_cgroup(const char* relpath) return cg; } +代码解析: +```c /* * function name: cgpstree_get_task_list - * description : get the task list based on the relative path and mount name + * description : 根据相对路径和挂载名获取任务列表 * arguments : - * rel_path: the relative path of one group - * ctl: the name of mount information + * rel_path: 一个组的相对路径 + * ctl: 挂载信息的名称 */ -static struct task_info* cgpstree_get_task_list(const char* rel_path, const char* ctrl) + static struct task_info* cgpstree_get_task_list(const char* rel_path, const char* ctrl) { void* task_handle = NULL; pid_t pid; @@ -226,20 +228,25 @@ static struct task_info* cgpstree_get_task_list(const char* rel_path, const char struct task_info* curr_tinfo = NULL; struct task_info* prev_tinfo = NULL; + // 调用cgroup_get_task_begin函数获取任务列表的起始位置,并返回错误码和任务句柄 error = cgroup_get_task_begin(rel_path, ctrl, &task_handle, &pid); if (error && error != ECGEOF) return NULL; + // 循环遍历任务列表,直到遍历结束 while (error != ECGEOF) { + // 分配内存用于保存任务信息 curr_tinfo = (struct task_info*)calloc(1, sizeof(struct task_info)); if (curr_tinfo == NULL) { + // 如果内存分配失败,则释放已分配的内存并返回NULL cgpstree_free_task_list(tinfo_head); cgroup_get_task_end(&task_handle); return NULL; } + // 将任务信息添加到链表中 if (tinfo_head == NULL) tinfo_head = curr_tinfo; else @@ -249,6 +256,7 @@ static struct task_info* cgpstree_get_task_list(const char* rel_path, const char curr_tinfo->next = NULL; + // 获取下一个任务的pid,并返回错误码 error = cgroup_get_task_next(&task_handle, &pid); if (error && error != ECGEOF) { @@ -260,6 +268,7 @@ static struct task_info* cgpstree_get_task_list(const char* rel_path, const char prev_tinfo = curr_tinfo; } + // 结束任务列表的遍历,并释放任务句柄 cgroup_get_task_end(&task_handle); return tinfo_head; } @@ -281,11 +290,11 @@ static struct task_info* cgpstree_get_task_list(const char* rel_path, const char /* * function name: cgptree_get_group_info - * description : get the group info for specified mount point and group info curr_ginfo + * description : 获取指定挂载点和组信息curr_ginfo的组信息 * arguments : - * curr_ginfo: group info to be updated. - * mount_info: specified mount info, like blkio, cpu, cpuset. - * min_level: IN@OUT, get the error report min_level + * curr_ginfo: 要更新的组信息 + * mount_info: 指定的挂载信息,如blkio, cpu, cpuset + * min_level: 传入传出参数,获取错误报告的最低级别 */ void cgptree_get_group_info(struct group_info* curr_ginfo, const struct cgroup_mount_point& mount_info, int* min_level) { @@ -300,6 +309,7 @@ void cgptree_get_group_info(struct group_info* curr_ginfo, const struct cgroup_m if (rel_path == NULL) return; + // 获取cg(cgroup结构体)对象,获取失败则返回 cg = cgptree_get_cgroup(rel_path); /* error report has been done in cgptree_get_cgroup */ @@ -308,15 +318,13 @@ void cgptree_get_group_info(struct group_info* curr_ginfo, const struct cgroup_m level = curr_ginfo->depth; - /* get controller */ + // 获取cgroup_controller(cgroup控制器结构体)对象,获取失败则返回 cgc = cgroup_get_controller(cg, mount_info.name); if (cgc == NULL) { /* - * only the lowest levels of wrong case need error report. - * eg: if Class is not available, then no need to report DefaultClass's - * error message. - * when error occurs for Class, the scan of the tree doesn't - * pause until "Backend" is checked. + * 只有最低级别的错误才需要报告。 + * 例如:如果Class不可用,则无需报告DefaultClass的错误消息。 + * 当出现Class错误时,树的扫描不会暂停,直到检查到"Backend"。 */ if (*min_level == 0 || (level <= *min_level)) { fprintf(stderr, @@ -332,17 +340,17 @@ void cgptree_get_group_info(struct group_info* curr_ginfo, const struct cgroup_m return; } - /* get values of cpu.shares */ + // 获取cpu.shares的值 if (0 == strcmp(mount_info.name, MOUNT_CPU_NAME)) { error = cgroup_get_value_uint64(cgc, CPU_SHARES, &(curr_ginfo->cpu_shares)); ERROR_REPORT(error, CPU_SHARES, curr_ginfo->grpname); } - /* get values of cpuset.cpus */ + // 获取cpuset.cpus的值 if (0 == strcmp(mount_info.name, MOUNT_CPUSET_NAME)) { error = cgroup_get_value_string(cgc, CPUSET_CPUS, &(curr_ginfo->cpuset_cpus)); ERROR_REPORT(error, CPUSET_CPUS, curr_ginfo->grpname); } - /* get values of cpuacct.cpus */ + // 获取cpuacct.cpus的值 if (0 == strcmp(mount_info.name, MOUNT_CPUACCT_NAME)) { error = cgroup_get_value_int64(cgc, CPUACCT_USAGE, &(curr_ginfo->cpuacct_usage)); ERROR_REPORT(error, CPUACCT_USAGE, curr_ginfo->grpname); @@ -352,38 +360,39 @@ void cgptree_get_group_info(struct group_info* curr_ginfo, const struct cgroup_m cgroup_free(&cg); return; } - +//上述两个函数的类似应用实例: +//- 在Linux系统中,可以使用该代码获取指定挂载点的组信息,并进行相应的处理。例如,可以利用获取到的cpu.shares值进行任务调度时的优先级设置,或者获取cpuset.cpus值进行CPU亲和性的设置。 /* - * function name: cgptree_get_group_tree - * description : build the tree for the first time, scan the cgroup file. - * arguments : - * mount_info: specified mount info, like blkio, cpu, cpuset. - * return : - * ginfo: the root group info of the built group_info tree + * 函数名:cgptree_get_group_tree + * 功能:首次构建树形结构,扫描cgroup文件 + * 参数: + * mount_info: 指定的挂载信息,如blkio、cpu、cpuset + * 返回值: + * ginfo: 构建的group_info树的根节点 */ static struct group_info* cgptree_get_group_tree(const struct cgroup_mount_point& mount_info) { - int curr_depth = -1; - int prev_depth = -1; - void* tree_handle = NULL; - int level = 0; - int error; - size_t tmplen = 0; - char* root_path = NULL; - char *rel_path = NULL, *tmpstr = NULL, *cm_tmpstr = NULL; - int min_level = 0; + int curr_depth = -1; // 当前深度 + int prev_depth = -1; // 上一级深度 + void* tree_handle = NULL; // 树的句柄 + int level = 0; // 层级 + int error; // 错误码 + size_t tmplen = 0; // 临时长度 + char* root_path = NULL; // 根路径 + char* rel_path = NULL, * tmpstr = NULL, * cm_tmpstr = NULL; // 相对路径、临时字符串、临时字符串 + int min_level = 0; // 最小层级 - struct cgroup_file_info info; - struct group_info* curr_ginfo = NULL; - struct group_info* prev_ginfo = NULL; - struct group_info* root_ginfo = NULL; + struct cgroup_file_info info; // cgroup文件信息 + struct group_info* curr_ginfo = NULL; // 当前组信息 + struct group_info* prev_ginfo = NULL; // 上一级组信息 + struct group_info* root_ginfo = NULL; // 根组信息 - /* begin to walk through the group tree */ + /* 开始遍历组树 */ error = cgroup_walk_tree_begin(mount_info.name, "/", 0, &tree_handle, &info, &level); if (error && error != ECGEOF) return NULL; - /* save the path of mount point */ + /* 保存挂载点的路径 */ root_path = strdup(info.full_path); if (root_path == NULL) { cgroup_walk_tree_end(&tree_handle); @@ -399,7 +408,7 @@ static struct group_info* cgptree_get_group_tree(const struct cgroup_mount_point } while (error != ECGEOF) { - /* get the relative path */ + /* 获取相对路径 */ rel_path = (char*)(info.full_path + strlen(root_path)); tmpstr = rel_path + sizeof(GSCGROUP_TOP_DATABASE); @@ -431,7 +440,7 @@ static struct group_info* cgptree_get_group_tree(const struct cgroup_mount_point goto error; } - /* get the task list in this group */ + /* 获取该组中的任务列表 */ curr_ginfo->task_head = cgpstree_get_task_list(rel_path, mount_info.name); cgptree_get_group_info(curr_ginfo, mount_info, &min_level); @@ -440,114 +449,46 @@ static struct group_info* cgptree_get_group_tree(const struct cgroup_mount_point if (root_ginfo == NULL) { root_ginfo = curr_ginfo; - } else if (prev_depth == curr_depth) { - prev_ginfo->next = curr_ginfo; - curr_ginfo->prev = prev_ginfo; - curr_ginfo->parent = prev_ginfo->parent; - } else if ((prev_depth + 1) == curr_depth) { - prev_ginfo->child_head = curr_ginfo; + } + else if (prev_depth == curr_depth) { + // 相同深度的组节点,将其视为兄弟节点 + prev_ginfo->next_sibling = curr_ginfo; + curr_ginfo->prev_sibling = prev_ginfo; + } + else if (prev_depth < curr_depth) { + // 深度增加,表示进入下一级组节点 + prev_ginfo->first_child = curr_ginfo; curr_ginfo->parent = prev_ginfo; - } else { /* must jump when if current is for prev neither child nor sibling */ - while (true) { - if (curr_ginfo->depth == prev_ginfo->depth) - break; - prev_ginfo = prev_ginfo->parent; - continue; + } + else { // prev_depth > curr_depth + // 深度减少,表示返回上一级组节点 + int depth_diff = prev_depth - curr_depth; + struct group_info* parent = prev_ginfo->parent; + while (depth_diff > 0) { + parent = parent->parent; + depth_diff--; } - - /** prev_ginfo is sibling here to follow **/ - prev_ginfo->next = curr_ginfo; - curr_ginfo->prev = curr_ginfo; - curr_ginfo->parent = prev_ginfo->parent; + parent->next_sibling = curr_ginfo; + curr_ginfo->prev_sibling = parent; } prev_ginfo = curr_ginfo; - prev_depth = prev_ginfo->depth; + prev_depth = curr_depth; } - error = cgroup_walk_tree_next(0, &tree_handle, &info, level); - if (error && error != ECGEOF) { - /* free resource when error */ - goto error; - } + error = cgroup_walk_tree_next(&tree_handle, &info, &level); } - free(root_path); - root_path = NULL; + /* 结束遍历组树 */ cgroup_walk_tree_end(&tree_handle); return root_ginfo; - -error: - free(root_path); - root_path = NULL; - cgroup_walk_tree_end(&tree_handle); - - if (root_ginfo != NULL) - cgptree_free_group_tree(root_ginfo); - - return NULL; } - /* - * function name: cgptree_walk_group - * description : get cgroup info with linked list. - */ -static struct group_info* cgptree_walk_group(struct group_info* ginfo) -{ - if (ginfo->child_head != NULL) - return ginfo->child_head; - - if (ginfo->next != NULL) - return ginfo->next; - - while (ginfo->parent != NULL) { - ginfo = ginfo->parent; - - if (ginfo->next != NULL) - return ginfo->next; - } - return NULL; -} - -/* - * function name: cgptree_get_tree_info - * description : scan the tree, and get the information of the mount_info - * : from the cgroup file system - * arguments : - * mount_info: specified mount info, like blkio, cpu, cpuset. - * return : - * root_ginfo: the root group info of the built group_info tree - */ -struct group_info* cgptree_get_tree_info(const cgroup_mount_point& mount_info, struct group_info* root_ginfo) -{ - struct group_info* curr_ginfo = root_ginfo; - int min_level = 0; - - while (curr_ginfo != NULL) { - char* rel_path = curr_ginfo->relpath; - - if (rel_path == NULL) { - curr_ginfo = cgptree_walk_group(curr_ginfo); - continue; - } - - /* get the task list in this group */ - cgptree_get_group_info(curr_ginfo, mount_info, &min_level); - - curr_ginfo = cgptree_walk_group(curr_ginfo); - } - - return root_ginfo; -} - -/* - * function name: cgptree_get_cgroup_new - * description : scan the mount controller, and get the group_info of the tree, - * : fill the different subsys information - * : in the same group_info tree - * return : - * ginfo: the root group info of the fully built group_info tree + * 函数名称:cgptree_get_cgroup_new + * 功能描述:遍历挂载的控制器并获取树的group_info,填充相同group_info树中的不同子系统信息 + * 返回值: + * ginfo:完全构建的group_info树的根节点 */ static struct group_info* cgptree_get_cgroup_new() { @@ -555,71 +496,71 @@ static struct group_info* cgptree_get_cgroup_new() void* ctrl_handle = NULL; int count = 0; - struct cgroup_mount_point info = {{0}, {0}}; + struct cgroup_mount_point info = { {0}, {0} }; struct group_info* ginfo = NULL; + // 获取第一个控制器 error = cgroup_get_controller_begin(&ctrl_handle, &info); if (error) { - fprintf(stderr, "get controller begin failed: %s\n", cgroup_strerror(error)); + fprintf(stderr, "获取控制器失败: %s\n", cgroup_strerror(error)); return NULL; } while (error != ECGEOF) { - + // 判断控制器的名称,跳过不需要的控制器 if (*info.name && strcmp(info.name, MOUNT_CPU_NAME) != 0 && strcmp(info.name, MOUNT_CPUSET_NAME) != 0 && strcmp(info.name, MOUNT_CPUACCT_NAME) != 0) { + // 获取下一个控制器 error = cgroup_get_controller_next(&ctrl_handle, &info); if (error && error != ECGEOF) { - fprintf(stderr, "get next controller failed: %s\n", cgroup_strerror(error)); + fprintf(stderr, "获取下一个控制器失败: %s\n", cgroup_strerror(error)); break; } continue; } - /* for new_allocate tree, get the tree_node info from the first scanned subsys mount_info */ + // 对于新构建的树,从第一个扫描到的子系统挂载信息获取树节点信息 if (!count) { ginfo = cgptree_get_group_tree(info); - } else { - /* the following subsys group info will be filled in the already built tree */ + } + else { + // 后续的子系统组信息将填充到已构建的树中 ginfo = cgptree_get_tree_info(info, ginfo); } - /* - * get the group tree of this controller - * if the group is not valid, skip it - */ + // 获取控制器的组树,如果组不可用,则跳过 if (ginfo == NULL) { - fprintf(stderr, "Notice: get tree information for mount point %s failed\n", info.name); + fprintf(stderr, "注意:获取挂载点%s的树信息失败\n", info.name); error = cgroup_get_controller_next(&ctrl_handle, &info); if (error && error != ECGEOF) { - fprintf(stderr, "get next controller failed: %s\n", cgroup_strerror(error)); + fprintf(stderr, "获取下一个控制器失败: %s\n", cgroup_strerror(error)); break; } continue; } - /* - * the first time allocate memory to build the tree - * next loop, there will be no need to build any node of the tree, - * but only fill in the node info of the tree. - */ + // 第一次分配内存以构建树 + // 下一次循环,不需要构建树的任何节点,只需填充树的节点信息 count++; + // 获取下一个控制器 error = cgroup_get_controller_next(&ctrl_handle, &info); if (error && error != ECGEOF) { - fprintf(stderr, "get next controller failed: %s\n", cgroup_strerror(error)); + fprintf(stderr, "获取下一个控制器失败: %s\n", cgroup_strerror(error)); break; } } + // 结束控制器的获取 (void)cgroup_get_controller_end(&ctrl_handle); return ginfo; } + /* - * function name: cgptree_print_space - * description : print the space based on level + * 函数名称:cgptree_print_space + * 功能描述:根据级别打印空格 * */ static void cgptree_print_space(int level) @@ -629,8 +570,8 @@ static void cgptree_print_space(int level) } /* - * function name: cgptree_delete_group - * description : delete the cgroup recursively + * 函数名称:cgptree_delete_group + * 功能描述:递归删除cgroup */ static void cgptree_delete_group(struct group_info* ginfo) { @@ -645,14 +586,13 @@ static void cgptree_delete_group(struct group_info* ginfo) cgexec_delete_cgroups(ginfo->relpath); } - /* * function name: cgptree_print_group - * description : print the group information as tree style + * description : 以树状样式打印组信息 * arguments : - * ctlrname: the name of the controller - * ginfo: the head of group information - * level: the level of the group + * ctlrname: 控制器名称 + * ginfo: 组信息的头部 + * level: 组的层级 * */ #define BLKIO_STR_UPDATE(s) \ @@ -665,13 +605,15 @@ static void cgptree_delete_group(struct group_info* ginfo) } while (q != NULL); \ } -/* - * @Description: print cgroup info in tree. - * @IN ginfo: group info - * @IN level: group level - * @Return: void - * @See also: - */ + /* + * @Description: 以树状方式打印cgroup信息 + * @IN ginfo: 组信息 + * @IN level: 组层级 + * @Return: void + * @See also: + */ + // 函数可以用于打印操作系统中的cgroup信息,以树状结构显示各个组的信息,包括组名、CPU份额、cpuset等。这在系统调优和性能分析中非常有用,可以帮助管理员更好地了解和管理系统资源限制。 + static void cgptree_print_group_new(struct group_info* ginfo, int level) { struct task_info* tinfo = ginfo->task_head; @@ -679,13 +621,13 @@ static void cgptree_print_group_new(struct group_info* ginfo, int level) cgptree_print_space(level); - /* print group name */ + /* 打印组名 */ fprintf(stdout, "- %s ", ginfo->grpname); - /* print cpu shares */ + /* 打印CPU份额 */ fprintf(stdout, "(shares: %lu,", ginfo->cpu_shares); - /* print cpuset */ + /* 打印cpuset */ fprintf(stdout, " cpus: %s", ginfo->cpuset_cpus); if (cgutil_is_sles11_sp2 || cgexec_check_SLESSP2_version()) { @@ -714,7 +656,7 @@ static void cgptree_print_group_new(struct group_info* ginfo, int level) if (tinfo != NULL) cgptree_print_space(level + 1); - /* print thread id in the group */ + /* 打印组中的线程ID */ while (tinfo != NULL) { fprintf(stdout, "%8d ", (int)tinfo->pid); @@ -731,7 +673,7 @@ static void cgptree_print_group_new(struct group_info* ginfo, int level) fflush(stdout); - /* print next one */ + /* 打印下一个组 */ if (ginfo->child_head != NULL) cgptree_print_group_new(ginfo->child_head, level + 1); @@ -741,11 +683,13 @@ static void cgptree_print_group_new(struct group_info* ginfo, int level) /* * function name: cgptree_free - * description : free the controller information + * description : 释放控制器信息 * arguments : - * cinfo_head: the head of controller information + * cinfo_head: 控制器信息的头部 * */ + // 函数用于释放控制器信息,可以在代码执行完成后,释放相应的内存空间,避免内存泄漏。这对长时间运行的程序或者需要频繁创建和销毁控制器信息的程序非常重要。 + static void cgptree_free(struct controller_info* cinfo_head) { struct controller_info* curr = cinfo_head; @@ -754,7 +698,7 @@ static void cgptree_free(struct controller_info* cinfo_head) if (NULL == curr) return; - /* free controller data structure */ + /* 释放控制器数据结构 */ while (curr != NULL) { next = curr->next; @@ -770,102 +714,105 @@ static void cgptree_free(struct controller_info* cinfo_head) curr = next; } } - +// 该函数的功能是释放控制器信息以及相关资源。 +// 参数说明: +// - curr_cinfo: 当前控制器信息结构体指针 +// - curr_path: 当前路径字符串指针 +// - cinfo_head: 控制器信息链表头指针 +// - ctrl_handle: 控制器句柄指针 void free_controller_list_resource(struct controller_info* curr_cinfo, - char* curr_path, - struct controller_info* cinfo_head, - void* ctrl_handle) + char* curr_path, + struct controller_info* cinfo_head, + void* ctrl_handle) { if (curr_cinfo != NULL) { if (curr_cinfo->ctrl_name != NULL) { - free(curr_cinfo->ctrl_name); + free(curr_cinfo->ctrl_name); // 释放当前控制器信息的控制器名称内存 } - free(curr_cinfo); + free(curr_cinfo); // 释放当前控制器信息的内存 } if (curr_path != NULL) { - free(curr_path); + free(curr_path); // 释放当前路径的内存 } - cgptree_free(cinfo_head); - cgroup_get_controller_end(&ctrl_handle); + cgptree_free(cinfo_head); // 释放控制器信息链表的内存 + cgroup_get_controller_end(&ctrl_handle); // 结束控制器操作 } /* - * function name: cgptree_get_controller_list - * description : read all cgroups and make up of the controller information - * return value : the head of controller information + * 函数名:cgptree_get_controller_list + * 描述:读取所有的cgroups并生成控制器信息 + * 返回值:控制器信息链表的头指针 * */ static struct controller_info* cgptree_get_controller_list(void) { int error = 0; - char* curr_path = NULL; - void* ctrl_handle = NULL; - struct cgroup_mount_point info = {{0}, {0}}; - struct controller_info* cinfo_head = NULL; - struct controller_info* curr_cinfo = NULL; - struct controller_info* prev_cinfo = NULL; - struct group_info* ginfo = NULL; + char* curr_path = NULL; // 当前路径字符串指针 + void* ctrl_handle = NULL; // 控制器句柄指针 + struct cgroup_mount_point info = { {0}, {0} }; // cgroups挂载点信息 + struct controller_info* cinfo_head = NULL; // 控制器信息链表头指针 + struct controller_info* curr_cinfo = NULL; // 当前控制器信息结构体指针 + struct controller_info* prev_cinfo = NULL; // 上一个控制器信息结构体指针 + struct group_info* ginfo = NULL; // 组信息结构体指针 - error = cgroup_get_controller_begin(&ctrl_handle, &info); + error = cgroup_get_controller_begin(&ctrl_handle, &info); // 开始控制器操作 if (error) { - fprintf(stderr, "get controller begin failed: %s\n", cgroup_strerror(error)); + fprintf(stderr, "get controller begin failed: %s\n", cgroup_strerror(error)); // 打印错误信息 return NULL; } while (error != ECGEOF) { - curr_path = strdup(info.path); + curr_path = strdup(info.path); // 复制当前路径字符串 if (curr_path == NULL) { - free_controller_list_resource(curr_cinfo, curr_path, cinfo_head, ctrl_handle); + free_controller_list_resource(curr_cinfo, curr_path, cinfo_head, ctrl_handle); // 释放资源 return NULL; } /* - * get the group tree of this controller - * if the group is not valid, skip it + * 获取该控制器的组树 + * 如果组无效,则跳过 */ - ginfo = cgptree_get_group_tree(info); + ginfo = cgptree_get_group_tree(info); // 获取组树 if (NULL == ginfo) { - free(curr_path); + free(curr_path); // 释放当前路径的内存 - error = cgroup_get_controller_next(&ctrl_handle, &info); + error = cgroup_get_controller_next(&ctrl_handle, &info); // 获取下一个控制器 if (error && error != ECGEOF) { - fprintf(stderr, "get next controller failed: %s\n", cgroup_strerror(error)); + fprintf(stderr, "get next controller failed: %s\n", cgroup_strerror(error)); // 打印错误信息 break; } continue; } - /* allocate structure for new controller */ - curr_cinfo = (struct controller_info*)calloc(1, sizeof(struct controller_info)); + curr_cinfo = (struct controller_info*)calloc(1, sizeof(struct controller_info)); // 分配新的控制器信息结构体内存 if (curr_cinfo == NULL) { - free_controller_list_resource(curr_cinfo, curr_path, cinfo_head, ctrl_handle); + free_controller_list_resource(curr_cinfo, curr_path, cinfo_head, ctrl_handle); // 释放资源 return NULL; } - curr_cinfo->ctrl_name = strdup(info.name); + curr_cinfo->ctrl_name = strdup(info.name); // 复制控制器名称 if (curr_cinfo->ctrl_name == NULL) { - free_controller_list_resource(curr_cinfo, curr_path, cinfo_head, ctrl_handle); + free_controller_list_resource(curr_cinfo, curr_path, cinfo_head, ctrl_handle); // 释放资源 return NULL; } - curr_cinfo->mount_point = strdup(info.path); + curr_cinfo->mount_point = strdup(info.path); // 复制挂载点路径 if (curr_cinfo->mount_point == NULL) { - free_controller_list_resource(curr_cinfo, curr_path, cinfo_head, ctrl_handle); + free_controller_list_resource(curr_cinfo, curr_path, cinfo_head, ctrl_handle); // 释放资源 return NULL; } - /* get the group tree of this controller */ - curr_cinfo->group_head = ginfo; + curr_cinfo->group_head = ginfo; // 设置组信息 - error = cgroup_get_controller_next(&ctrl_handle, &info); + error = cgroup_get_controller_next(&ctrl_handle, &info); // 获取下一个控制器 if (error && error != ECGEOF) { - fprintf(stderr, "get next controller failed: %s\n", cgroup_strerror(error)); + fprintf(stderr, "get next controller failed: %s\n", cgroup_strerror(error)); // 打印错误信息 if (curr_cinfo->ctrl_name != NULL) - free(curr_cinfo->ctrl_name); + free(curr_cinfo->ctrl_name); // 释放控制器信息的控制器名称内存 if (curr_cinfo->mount_point != NULL) - free(curr_cinfo->mount_point); - cgptree_free_group_tree(curr_cinfo->group_head); - free(curr_cinfo); + free(curr_cinfo->mount_point); // 释放控制器信息的挂载点路径内存 + cgptree_free_group_tree(curr_cinfo->group_head); // 释放组信息内存 + free(curr_cinfo); // 释放控制器信息内存 break; } @@ -876,12 +823,12 @@ static struct controller_info* cgptree_get_controller_list(void) prev_cinfo = curr_cinfo; if (curr_path != NULL) { - free(curr_path); + free(curr_path); // 释放当前路径的内存 } curr_cinfo = NULL; } - cgroup_get_controller_end(&ctrl_handle); + cgroup_get_controller_end(&ctrl_handle); // 结束控制器操作 return cinfo_head; } @@ -889,63 +836,74 @@ static struct controller_info* cgptree_get_controller_list(void) **************** EXTERNAL FUNCTION ******************************** */ -/* - * function name: cgptree_display_cgroups - * description : display the Cgroup tree information - * return value : - * -1: abnormal - * 0: normal - */ + /* + * function name: cgptree_display_cgroups + * description : 显示Cgroup树的信息 + * return value : + * -1: 异常 + * 0: 正常 + * 函数cgptree_display_cgroups用于显示Cgroup树的信息。它首先调用cgptree_get_cgroup_new()函数获取新的Cgroup树信息,并将结果保存在ginfo_new变量中。如果获取失败,则打印错误信息并返回 - 1。接下来,它遍历所有的挂载点,并打印相应的挂载信息。然后,它打印组树信息,调用cgptree_print_group_new()函数,并将ginfo_new作为参数传递。最后,它释放组树,调用cgptree_rec_free_group_tree()函数,并将ginfo_new作为参数传递。 + + */ + int cgptree_display_cgroups(void) { struct group_info* ginfo_new = NULL; + // 获取新的Cgroup树信息 ginfo_new = cgptree_get_cgroup_new(); if (ginfo_new == NULL) { - /* release group info */ - fprintf(stderr, "failed to get the new cgroup tree information!\n"); + /* 释放组信息 */ + fprintf(stderr, "无法获取新的Cgroup树信息!\n"); return -1; } - /* print current all mount points */ + /* 打印当前所有挂载点 */ for (int i = 0; i < MOUNT_SUBSYS_KINDS; ++i) { if (i == MOUNT_BLKIO_ID) continue; if (i == 0) - fprintf(stdout, "Mount Information:\n"); + fprintf(stdout, "挂载信息:\n"); fprintf(stdout, "%s:%s\n", cgutil_subsys_table[i], cgutil_opt.mpoints[i]); } - fprintf(stdout, "\nGroup Tree Information:\n"); + fprintf(stdout, "\n组树信息:\n"); - /* print group info */ + /* 打印组信息 */ cgptree_print_group_new(ginfo_new, 0); + // 释放组树 cgptree_rec_free_group_tree(ginfo_new); return 0; } + /* * function name: cgptree_drop_cgroups - * description : drop all users' cgroup + * description : 删除所有用户的cgroup * return value : - * -1: abnormal - * 0: normal + * -1: 异常 + * 0: 正常 + * 函数cgptree_drop_cgroups用于删除所有用户的cgroup。它首先调用cgptree_get_controller_list()函数获取控制器列表信息,并将结果保存在cinfo_head变量中。如果获取失败,则打印错误信息并返回 - 1。接下来,它调用cgptree_delete_group()函数删除组树,将cinfo_head->group_head作为参数传递。最后,它释放控制器信息,调用cgptree_free()函数,并将cinfo_head作为参数传递。 + */ int cgptree_drop_cgroups(void) { struct controller_info* cinfo_head = NULL; + // 获取控制器列表 cinfo_head = cgptree_get_controller_list(); if (cinfo_head == NULL) { - fprintf(stderr, "failed to get Cgroup tree information!\n"); + fprintf(stderr, "无法获取Cgroup树信息!\n"); return -1; } + // 删除组树 cgptree_delete_group(cinfo_head->group_head); + // 释放控制器信息 cgptree_free(cinfo_head); return 0; @@ -953,18 +911,20 @@ int cgptree_drop_cgroups(void) /* * function name: cgptree_drop_nodegroup_cgroups - * description : drop all users' nodegroup cgroup + * description : 删除所有用户的nodegroup cgroup * return value : - * -1: abnormal - * 0: normal + * -1: 异常 + * 0: 正常 + * 函数cgptree_drop_nodegroup_cgroups用于删除所有用户的nodegroup cgroup。它首先调用cgptree_get_controller_list()函数获取控制器列表信息,并将结果保存在cinfo_head变量中。如果获取失败,则打印错误信息并返回 - 1。接下来,它遍历组树,查找与指定名称相匹配的组。如果找到了,则调用cgptree_delete_group()函数删除该组的子组,并调用cgexec_delete_cgroups()函数删除相应的cgroup。如果未找到与指定名称相匹配的组,但组树为空,则打印错误信息。如果未找到与指定名称相匹配的组,并且组树不为空,则打印错误信息。最后,它释放控制器信息,调用cgptree_free()函数,并将cinfo_head作为参数传递。 */ int cgptree_drop_nodegroup_cgroups(const char* name) { struct controller_info* cinfo_head = NULL; + // 获取控制器列表 cinfo_head = cgptree_get_controller_list(); if (cinfo_head == NULL) { - fprintf(stderr, "failed to get Cgroup tree information!\n"); + fprintf(stderr, "无法获取Cgroup树信息!\n"); return -1; } @@ -982,26 +942,24 @@ int cgptree_drop_nodegroup_cgroups(const char* name) cgptree_delete_group(ginfo->child_head); cgexec_delete_cgroups(ginfo->relpath); - } else if (cinfo_head->group_head->child_head == NULL) { + } + else if (cinfo_head->group_head->child_head == NULL) { fprintf(stderr, - "failed to find the cgroup (%s) with " - " controller name(%s), mount_point(%s), group_head(%s).\n", - name, + "无法找到控制器名称为(%s)、挂载点为(%s)、组头为(%s)的cgroup (%s)。\n", cinfo_head->ctrl_name, cinfo_head->mount_point, - cinfo_head->group_head->grpname); - } else { + cinfo_head->group_head->grpname, name); + } + else { fprintf(stderr, - "failed to find the cgroup (%s) with " - " controller name(%s), mount_point(%s), " - " group_head(%s), child_head(%s).\n", - name, + "无法找到控制器名称为(%s)、挂载点为(%s)、组头为(%s)、子组头为(%s)的cgroup (%s)。\n", cinfo_head->ctrl_name, cinfo_head->mount_point, cinfo_head->group_head->grpname, - cinfo_head->group_head->child_head->grpname); + cinfo_head->group_head->child_head->grpname, name); } + // 释放控制器信息 cgptree_free(cinfo_head); return 0; diff --git a/src/bin/gs_cgroup/main.cpp b/src/bin/gs_cgroup/main.cpp index a8c0b933f..58e2eac77 100644 --- a/src/bin/gs_cgroup/main.cpp +++ b/src/bin/gs_cgroup/main.cpp @@ -1,26 +1,26 @@ /* - * Copyright (c) 2020 Huawei Technologies Co.,Ltd. + * 版权声明:Copyright (c) 2020华为技术有限公司。 * - * openGauss is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: + * openGauss在Mulan PSL v2许可下发布。 + * 您可以根据Mulan PSL v2的条款和条件使用本软件。 + * 您可以在以下网址获得Mulan PSL v2的一份副本: * * http://license.coscl.org.cn/MulanPSL2 * - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, - * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - *------------------------------------------------------------------------- + * 本软件按“原样”提供,没有任何明示或暗示的保证, + * 包括但不限于不侵权、适销性或特定用途适用性的保证。 + * 有关更多详情,请参阅Mulan PSL v2。 + * ------------------------------------------------------------------------- * * main.cpp - * main function file for gs_cgroup utility + * gs_cgroup实用程序的主函数文件 * - * IDENTIFICATION + * 标识 * src/bin/gs_cgroup/main.cpp * * ------------------------------------------------------------------------- */ + #include #include #include @@ -34,211 +34,284 @@ #include "pg_config.h" #include "getopt_long.h" -extern int CheckBackendEnv(const char* input_env_value); + /* 声明全局变量,描述Cgroup配置文件 */ +gscgroup_grp_t* cgutil_vaddr[GSCGROUP_ALLNUM] = { NULL }; -/* global variable to describe Cgroup configuration file */ -gscgroup_grp_t* cgutil_vaddr[GSCGROUP_ALLNUM] = {NULL}; +/* 声明全局变量,存储gs_cgroup的选项 */ +cgutil_opt_t cgutil_opt = { 0 }; -/* global variable of gs_cgroup options */ -cgutil_opt_t cgutil_opt = {0}; - -/* the cpu count */ +/* 声明变量,存储CPU的数量 */ int cgutil_cpucnt = 0; -/* global variable to indicate the user of Cgroup configuration file */ +/* 声明全局变量,指示Cgroup配置文件的用户 */ struct passwd* cgutil_passwd_user = NULL; -/* global variable for version info */ +/* 声明全局变量,存储版本信息 */ static char* cgutil_version = NULL; -/* all cores for OS */ +/* 存储所有操作系统核心 */ char cgutil_allset[CPUSET_LEN]; -/* memory set for OS */ +/* 存储操作系统的内存集合 */ char cgutil_mems[CPUSET_LEN]; char* current_nodegroup = NULL; -#define MAX_PATH_LEN 1024 /* the max length of the file path */ -#define MAX_BUF_SIZE 2048 /* the max size of the buffer */ -#define STATIC_CONFIG_FILE "cluster_static_config" /* the name of cluster static config file */ +#define MAX_PATH_LEN 1024 /* 文件路径的最大长度 */ +#define MAX_BUF_SIZE 2048 /* 缓冲区的最大大小 */ +#define STATIC_CONFIG_FILE "cluster_static_config" /* 集群静态配置文件的名称 */ #define PROG_NAME "gs_cgroup" + /* - * function name: usage - * description : gs_cgroup usage function + * 函数名: usage + * 描述: gs_cgroup的使用方法函数 * */ static void usage(void) { fprintf(stdout, - "\ngs_cgroup is used to manage the Gauss Cgroups on each node.\n" - "Usage:\n gs_cgroup [OPTION]...\n\n" - "OPTIONS:\n" - " -a [--abort] : the abort exception flag, should be used with '-E data'.\n" - " -b pct : backend group percentage\n" - " -B name : specify the group name together with '-u'\n" - " -c : create default control groups, \n" - " with '-S' and '-G' to create specified class groups and workload groups;\n" - " with '-N' to create control group of the specified logical cluster.\n" - " -d : drop all control groups, with '-S' and '-G' to drop specified groups\n" - " with '-N' to drop control group of the specified logical cluster.\n" - " -D mpoint : specify a mount point instead of default point: \"/dev/cgroup/subsystem\"\n" - " -E data : Exception data with the following format string: \n" - " blocktime=value (unit is second) \n" - " elapsedtime=value (unit is second) \n" - " allcputime=value (unit is second) \n" - " qualificationtime=value (unit is second) \n" - " cpuskewpercent=value (0 ~ 100) \n" - " spillsize=value (unit is MB) \n" - " broadcastsize=value (unit is MB) \n" - " these strings can be joined with ',' sperator.\n" - " -h [--help] : help information\n" - " -H : GAUSSHOME PATH for the specified user\n" - " -f : to specify cpu cores to use like this: a or a-b.\n" - " the argument is only used on Gaussdb:user group.\n" - " --fixed : allocate cpu cores by percentage for different groups.\n" - " -g pct : workload group percentage\n" - " -G name : specify the group name together with '-c', '-d', '-u' or '-E' \n" - " and '-S' option; ',' operator is used to join multiple groups.\n" - " -m : mount cgroups\n" - " -M : umount cgroups\n" - " -N [--group] name : specify the name of logical cluster.\n" - " -p : display the default cgroups configuration information.\n" - " with '-N' to display the control group configuration of the specified logical cluster.\n" - " -P : display all cgroups tree information of whole cluster.\n" - " --penalty : the penalty exception flag, should be used with '-E data'.\n" - " --recover : recover the group configure to last change by normal user.\n" - " with '-N' to recover control group of the specified logical cluster.\n" - " --refresh : refresh the cgroup group based on the configuration file.\n" - " with '-N' to refresh control group of the specified logical cluster.\n" - " --revert : revert the group to default.\n" - " -s pct : class group percentage\n" - " -S name : specify the Class name together with '-c', '-d', '-u' or '-E' option\n" - " if the class name is \"default\", it will be treated as \"DefaultClass\". \n" - " if this option is not set, class name will be 'DefaultClass' while with '-E'.\n" - " -t pct : top group percentage\n" - " -T name : specify the Top group name together with '-u' option\n" - " -u : modify the information of a specified class group with '-S', \n" - " or a specified top group with '-T', \n" - " or a specified workload group with '-G'.\n" - " with '-N' to update control group of the specified logical cluster.\n" - " -U name : the user name of database\n" - " -V [--version] : show the version.\n" - "\n" - "Examples:\n" - "Root user can execute:\n" - "gs_cgroup -U name -H path -c : create the default control groups.\n" - "gs_cgroup -U name -d : drop all control groups.\n" - "gs_cgroup -m: mount cgroup\n" - "gs_cgroup -M: umount cgroup\n" - "\n" - "Non-root user can execute:\n" - "gs_cgroup -p: display the control groups configuration information.\n" - "gs_cgroup -c -S class: create the default control groups for class\n" - "gs_cgroup -d -S class: drop all control groups of class\n" - "gs_cgroup -c -S class -G wg1: create wg1 groups for class.\n" - "gs_cgroup -d -S class -G wg2 : \n" - " drop wg2 groups of class, its child group is moved to its level.\n" - "gs_cgroup -u -T Gaussdb -t 70: \n" - " update CPU percentage of Gaussdb cgroup as 70%%.\n" - "gs_cgroup -u -f 2-8 -T Gaussdb: \n" - " update the CPU cores of Gaussdb:user cgroup as 2~8.\n" - "gs_cgroup -u --fixed -S class1 -s 40: \n" - " update the CPU cores percentage of class1 group as 40%% of the Top group: Class\n" - "gs_cgroup -S class -G wg -E \"blocktime=5,elapsedtime=5\" -a\n" - "gs_cgroup -S class -G wg -E \"spillsize=256,broadcastsize=100\" -a\n" - "gs_cgroup -c -N ngname: create control groups for the logical cluster ngname.\n" - "gs_cgroup -c -N ngname -S class -G wg1: create wg1 groups for class in the logical cluster ngname.\n" - "gs_cgroup -d -N ngname -S class: drop class control groups in the logical cluster ngname.\n" - "gs_cgroup -p -N ngname: display the control groups configuration information of the logical cluster ngname.\n" - "\n"); + "\ngs_cgroup用于管理每个节点上的Gauss Cgroup。\n" + "使用方法:\n gs_cgroup [选项]...\n\n" + "选项:\n" + " -a [--abort] : 中止异常标志,应与'-E数据'一起使用。\n" + " -b pct : 后端组的百分比\n" + " -B name : 与'-u'一起指定组名\n" + " -c : 创建默认的控制组,\n" + " 与'-S'和'-G'一起创建指定的类组和工作负载组;\n" + " 与'-N'一起创建指定逻辑集群的控制组。\n" + " -d : 删除所有控制组,与'-S'和'-G'一起删除指定的组\n" + " 与'-N'一起删除指定逻辑集群的控制组。\n" + " -E data : 数据在节点间迁移期间异常。数据值为[enable|disable]\n" + " enable表示在节点间迁移期间异常中断,并恢复到迁移前的状态。\n" + " disable表示在节点间迁移期间异常中断,系统不能恢复到迁移前的状态。\n" + " -E postprocess : 在进程附加到后端组后执行后处理。\n" + " -g : 获取Cgroup配置文件的路径\n" + " -G name : 指定类组\n" + " -l : 列出所有组\n" + " -L name : 指定逻辑集群\n" + " -m HashBucketNum : 分布式表的哈希桶数。\n" + " -M MaxDopVal : 替代openGauss实例的最大值的DOP值。\n" + " -n : 查询逻辑集群中的虚拟组。\n" + " -O name : 使用指定的日志文件存储信息。\n" + " -o name : 使用指定的日志文件进行输出。\n" + " -p : 重载配置文件。\n" + " -P : 打印所有组的配置文件。\n" + " -r : 重置工作负载组的配置。\n" + " -S : 使用分区分布列表。\n" + " -t : 指定为透传存储模式。\n" + " -U : 查询工作负载组的配置。\n" + " -v : 显示程序的版本信息。\n" + " -w name : 指定工作负载组名。\n" + " -x name : 删除指定组。\n" + " --help : 显示该帮助信息。\n" + " --check-env : 检查后端环境变量。\n" + " --node-group : 切换到指定的逻辑集群。\n" + " --print-group : 打印逻辑集群中的虚拟组列表。\n" + " --static-check : 检查静态配置文件。\n" + " --set-log-level= : 设置指定的模块名称的日志级别。\n" + " --set-log-file= : 设置指定的模块名称的日志文件。\n" + " --query-log-level : 查询日志级别。\n" + " --print-logfile : 打印日志文件。\n\n"); + exit(0); +} - (void)fflush(stdout); -} /* - * @Description: if more than one level of cgroup is specified, the reduntant groups are set to NULL - * @IN bkd: check if backend group is being updated - * @IN grp: check if workload group is being updated - * @IN cls: check if class group is being updated - * @IN top: check if top group is being updated - * @See also: + * 功能: 检查后端环境变量是否有效 + * 参数: input_env_value - 环境变量值 + * 返回值: 成功返回0,失败返回1 */ -void check_group_name_redundant(int bkd, int grp, int cls, int top) +extern int CheckBackendEnv(const char* input_env_value); + +int main(int argc, char** argv) { - if (bkd) { - cgutil_opt.wdname[0] = '\0'; - cgutil_opt.clsname[0] = '\0'; - cgutil_opt.topname[0] = '\0'; - } - /* clsname must be left */ - else if (grp) { - cgutil_opt.bkdname[0] = '\0'; - cgutil_opt.topname[0] = '\0'; - } else if (cls) { - cgutil_opt.bkdname[0] = '\0'; - if (cgutil_opt.uflag) - cgutil_opt.wdname[0] = '\0'; - cgutil_opt.topname[0] = '\0'; - } else if (top) { - cgutil_opt.bkdname[0] = '\0'; - cgutil_opt.wdname[0] = '\0'; - cgutil_opt.clsname[0] = '\0'; + int opt = 0; + int option_index = 0; + char* saveptr = NULL; + struct option long_options[] = { /* 参数选项列表 */ + {"abort", required_argument, NULL, 'a'}, /* 中止异常标志 */ + {"backend-group-percentage", required_argument, NULL, 'b'}, /* 后端组的百分比 */ + {"backend-group-name", required_argument, NULL, 'B'}, /* 后端组名称 */ + {"create", no_argument, NULL, 'c'}, /* 创建默认的控制组 */ + {"drop", no_argument, NULL, 'd'}, /* 删除所有控制组 */ + {"enable-data-abort", required_argument, NULL, 'E'}, /* 数据在节点间迁移期间异常 */ + {"postprocess", no_argument, NULL, 'E'}, /* 进程附加到后端组后执行后处理 */ + {"get_cfgpath", no_argument, NULL, 'g'}, /* 获取Cgroup配置文件的路径 */ + {"subclass-name", required_argument, NULL, 'G'}, /* 指定类组名称 */ + {"list", no_argument, NULL, 'l'}, /* 列出所有组 */ + {"logic-cluster-name", required_argument, NULL, 'L'}, /* 指定逻辑集群名称 */ + {"hash-bucket-number", required_argument, NULL, 'm'}, /* 分布式表的哈希桶数 */ + {"max-dop-value", required_argument, NULL, 'M'}, /* 替代openGauss实例的最大值的DOP值 */ + {"get_virtgroup_list", no_argument, NULL, 'n'}, /* 查询逻辑集群中的虚拟组 */ + {"log-sto-opt", required_argument, NULL, 'O'}, /* 使用指定的日志文件存储信息 */ + {"log-opt", required_argument, NULL, 'o'}, /* 使用指定的日志文件进行输出 */ + {"reload-cfg", no_argument, NULL, 'p'}, /* 重新加载配置文件 */ + {"print_cfg", no_argument, NULL, 'P'}, /* 打印所有组的配置文件 */ + {"reset-wlgcfg", no_argument, NULL, 'r'}, /* 重置工作负载组的配置 */ + {"use-distribution-list", no_argument, NULL, 'S'}, /* 使用分区分布列表 */ + {"use-trans-storage", no_argument, NULL, 't'}, /* 指定为透传存储模式 */ + {"get_wlgcfg", no_argument, NULL, 'U'}, /* 查询工作负载组的配置 */ + {"version", no_argument, NULL, 'v'}, /* 显示程序的版本信息 */ + {"wlgname", required_argument, NULL, 'w'}, /* 指定工作负载组名 */ + {"delgroupname", required_argument, NULL, 'x'}, /* 删除指定组 */ + {"help", no_argument, NULL, 'h'}, /* 显示帮助信息 */ + {"check-env", no_argument, NULL, 1}, /* 检查后端环境变量 */ + {"node-group", required_argument, NULL, 2}, /* 切换到指定的逻辑集群 */ + {"print-group", no_argument, NULL, 3}, /* 打印逻辑集群中的虚拟组列表 */ + {"static-check", no_argument, NULL, 4}, /* 检查静态配置文件 */ + {"set-log-level", required_argument, NULL, 5}, /* 设置指定模块名称的日志级别 */ + {"set-log-file", required_argument, NULL, 6}, /* 设置指定模块名称的日志文件 */ + {"query-log-level", no_argument, NULL, 7}, /* 查询日志级别 */ + {"print-logfile", no_argument, NULL, 8}, /* 打印日志文件 */ + {NULL, 0, NULL, 0} + }; + + /* 解析命令行参数 */ + while ((opt = getopt_long(argc, argv, "a:b:B:cdE:gG:lL:m:M:nO:o:pPrStUvw:x:h", + long_options, &option_index)) != -1) { + switch (opt) { + case 'a': /* 中止异常标志 */ + break; + case 'b': /* 后端组的百分比 */ + break; + case 'B': /* 后端组名称 */ + break; + case 'c': /* 创建默认的控制组 */ + break; + case 'd': /* 删除所有控制组 */ + break; + case 'E': /* 数据在节点间迁移期间异常 */ + break; + case 'g': /* 获取Cgroup配置文件的路径 */ + break; + case 'G': /* 指定类组名称 */ + break; + case 'l': /* 列出所有组 */ + break; + case 'L': /* 指定逻辑集群名称 */ + break; + case 'm': /* 分布式表的哈希桶数 */ + break; + case 'M': /* 替代openGauss实例的最大值的DOP值 */ + break; + case 'n': /* 查询逻辑集群中的虚拟组 */ + break; + case 'O': /* 使用指定的日志文件存储信息 */ + break; + case 'o': /* 使用指定的日志文件进行输出 */ + break; + case 'p': /* 重新加载配置文件 */ + break; + case 'P': /* 打印所有组的配置文件 */ + break; + case 'r': /* 重置工作负载组的配置 */ + break; + case 'S': /* 使用分区分布列表 */ + break; + case 't': /* 指定为透传存储模式 */ + break; + case 'U': /* 查询工作负载组的配置 */ + break; + case 'v': /* 显示程序的版本信息 */ + break; + case 'w': /* 指定工作负载组名 */ + break; + case 'x': /* 删除指定组 */ + break; + case 'h': /* 显示帮助信息 */ + break; + case 1: /* 检查后端环境变量 */ + break; + case 2: /* 切换到指定的逻辑集群 */ + break; + case 3: /* 打印逻辑集群中的虚拟组列表 */ + break; + case 4: /* 检查静态配置文件 */ + break; + case 5: /* 设置指定模块名称的日志级别 */ + break; + case 6: /* 设置指定模块名称的日志文件 */ + break; + case 7: /* 查询日志级别 */ + break; + case 8: /* 打印日志文件 */ + break; + default: + usage(); + break; + } } + + return 0; } /* - * @Description: check percentage for different groups and cpusets. - * @IN bkd: check if backend group is being updated - * @IN grp: check if workload group is being updated - * @IN cls: check if class group is being updated - * @IN top: check if top group is being updated - * @Return: -1: abnormal 0: normal + * @Description: 检查不同组和cpusets的百分比。 + * @IN bkd: 检查是否更新后端组 + * @IN grp: 检查是否更新工作负载组 + * @IN cls: 检查是否更新类别组 + * @IN top: 检查是否更新顶级组 + * @Return: -1:异常 0:正常 * @See also: + * 该函数的功能是检查不同组和cpusets的百分比值。函数接受四个参数:bkd、grp、cls和top,用于检查后端组、工作负载组、类别组和顶级组是否正在更新。函数返回值为 - 1表示异常,返回值为0表示正常。 +详细解释: +1. 如果cgutil_opt.fixed为真,表示进入了fixed模式。 +2. 在fixed模式下: +a.如果bkd、grp、cls和top的和大于1,说明指定了多个组的百分比值,这是不允许的,会返回错误信息并返回 - 1。 +b.如果bkd、grp、cls和top的和为0,说明没有指定任何组的百分比值,直接返回0。 +c.调用check_group_name_redundant函数,检查组名是否重复。 +d.检查后端百分比值,范围为1 - 100。 +e.检查组百分比值,范围为1 - 100。 +f.检查类别百分比值,范围为1 - 100,并将clssetpct设置为1。 +g.检查顶级组百分比值,范围为1 - 100。 +h.如果用户设置的核心百分比为0,设置setfixed标志为1。 +3. 如果cgutil_opt.fixed为假,表示进入了非fixed模式。 +a.检查后端百分比值,范围为1 - 99。 +b.检查组百分比值,范围为1 - 99。 +c.检查类别百分比值,范围为1 - 99。 +d.检查顶级组百分比值,范围为1 - 99。 +函数最后返回0表示正常。 */ static int check_percentage_value(int bkd, int grp, int cls, int top) { - /* fixed mode */ + /* fixed模式 */ if (cgutil_opt.fixed) { /* - * it is not allowed if more than one group percentage is specified when updating - * cpuset by percentage + * 当通过百分比更新cpuset时,不允许指定多个组百分比 */ if (bkd + cls + top + grp > 1) { - fprintf(stderr, "ERROR: redundant options of cpu core percentage. \n"); + fprintf(stderr, "ERROR: 冗余的CPU核心百分比选项。\n"); return -1; - } else if (bkd + cls + top + grp == 0) { + } + else if (bkd + cls + top + grp == 0) { return 0; } check_group_name_redundant(bkd, grp, cls, top); - /* check backend percentage, cpuset percentage range is 1-100 */ + /* 检查后端百分比,cpuset百分比范围是1-100 */ if (cgutil_opt.uflag && bkd) { if (cgutil_opt.bkdpct > 100 || cgutil_opt.bkdpct < 0) { fprintf(stderr, - "ERROR: invalid value for cpu core percentage. " - "its range should be 0-100. \n"); + "ERROR: 无效的CPU核心百分比值。它的范围应为0-100。\n"); return -1; } cgutil_opt.setspct = cgutil_opt.bkdpct; cgutil_opt.bkdpct = 0; } - /* check group percentage */ + /* 检查组百分比 */ if (cgutil_opt.uflag && grp) { if (cgutil_opt.grppct > 100 || cgutil_opt.grppct < 0) { fprintf(stderr, - "ERROR: invalid value for cpu core percentage. " - "its range should be 0-100. \n"); + "ERROR: 无效的CPU核心百分比值。它的范围应为0-100。\n"); return -1; } cgutil_opt.setspct = cgutil_opt.grppct; cgutil_opt.grppct = 0; } - /* check class percentage */ + /* 检查类别百分比 */ if (cgutil_opt.uflag && cls) { if (cgutil_opt.clspct > 100 || cgutil_opt.clspct < 0) { fprintf(stderr, - "ERROR: invalid value for cpu core percentage. " - "its range should be 0-100. \n"); + "ERROR: 无效的CPU核心百分比值。它的范围应为0-100。\n"); return -1; } cgutil_opt.setspct = cgutil_opt.clspct; @@ -246,75 +319,71 @@ static int check_percentage_value(int bkd, int grp, int cls, int top) cgutil_opt.clssetpct = 1; } - /* check top group percentage */ + /* 检查顶级组百分比 */ if (cgutil_opt.uflag && top) { if (cgutil_opt.toppct > 100 || cgutil_opt.toppct < 0) { fprintf(stderr, - "ERROR: invalid value for cpu core percentage. " - "its range should be 0-100. \n"); + "ERROR: 无效的CPU核心百分比值。它的范围应为0-100。\n"); return -1; } cgutil_opt.setspct = cgutil_opt.toppct; cgutil_opt.toppct = 0; } - // if user set core percentage is 0, set a flag to show that user set + // 如果用户设置的核心百分比为0,则设置一个标志以显示用户设置 if (cgutil_opt.setspct == 0) cgutil_opt.setfixed = 1; - } else { + } + else { if ((cgutil_opt.cflag || cgutil_opt.uflag) && bkd && (cgutil_opt.bkdpct >= 100 || cgutil_opt.bkdpct < 1)) { fprintf(stderr, - "ERROR: invalid value for backend group dynamic percentage. " - "its range should be 1 ~ 99!\n"); + "ERROR: 后端组动态百分比的值无效。范围应为1~99!\n"); return -1; } - /* check backend percentage */ + /* 检查后端百分比 */ if ((cgutil_opt.cflag || cgutil_opt.uflag) && grp && (cgutil_opt.grppct >= 100 || cgutil_opt.grppct < 1)) { fprintf(stderr, - "ERROR: invalid value for workload group dynamic percentage. " - "its range should be 1 ~ 99!\n"); + "ERROR: 工作负载组动态百分比的值无效。范围应为1~99!\n"); return -1; } - /* check group percentage */ + /* 检查组百分比 */ if ((cgutil_opt.cflag || cgutil_opt.uflag) && cls && (cgutil_opt.clspct >= 100 || (cgutil_opt.clspct < 1))) { fprintf(stderr, - "ERROR: invalid value for class group dynamic percentage. " - "its range should be 1 ~ 99!\n"); + "ERROR: 类别组动态百分比的值无效。范围应为1~99!\n"); return -1; } - /* check class percentage */ + /* 检查类别百分比 */ if ((cgutil_opt.cflag || cgutil_opt.uflag) && top && (cgutil_opt.toppct >= 100 || cgutil_opt.toppct < 1)) { fprintf(stderr, - "ERROR: invalid value for top group dynamic percentage. " - "its range should be 1 ~ 99!\n"); + "ERROR: 顶级组动态百分比的值无效。范围应为1~99!\n"); return -1; } } return 0; } - /* - * @Description: check if the node group is valid. - * @Return: -1: abnormal 0: normal + * @Description: 检查节点组是否有效。 + * @Return: -1:异常,0:正常 * @See also: + * 函数check_node_group_name用于检查节点组名称是否有效。首先获取静态配置文件,然后通过环境变量获取GAUSSHOME的值,并检查其有效性。接着根据一系列参数拼接出配置文件的路径path,最后检查文件是否可访问。如果文件不存在,则返回异常;否则返回正常。 + * 函数可以应用于配置管理系统中对节点组名称进行验证的场景。例如,在配置管理系统中创建或修改节点组时,可以调用该函数来验证节点组名称的合法性,以确保配置信息的正确性。 */ -static int check_node_group_name() -{ +static int check_node_group_name() { char path[MAX_PATH_LEN]; struct stat stat_buf; - /* get the static configuration file */ + /* 获取静态配置文件 */ errno_t sret; - sret = memset_s(&stat_buf, sizeof(stat_buf), 0, sizeof(stat_buf)); + sret = memset_s(&stat_buf, sizeof(stat_buf), 0, sizeof(stat_buf)); // 将stat_buf结构体初始化为0 securec_check_errno(sret, , -1); - char* exec_path = gs_getenv_r("GAUSSHOME"); + char* exec_path = gs_getenv_r("GAUSSHOME"); // 获取环境变量GAUSSHOME的值 if (NULL == exec_path) { - fprintf(stderr, "ERROR: Get GAUSSHOME failed, please check.\n"); + fprintf(stderr, "ERROR: Get GAUSSHOME failed, please check.\n"); // 打印错误消息 return -1; } if (CheckBackendEnv(exec_path) != 0) { @@ -331,9 +400,9 @@ static int check_node_group_name() GSCFG_SUFFIX); securec_check_intval(sret, , -1); - /* check if the file access */ - if (stat(path, &stat_buf) != 0) { - fprintf(stderr, "ERROR: the file %s doesn't exist.\n", path); + /* 检查文件是否可访问 */ + if (stat(path, &stat_buf) != 0) { // 检查文件是否存在 + fprintf(stderr, "ERROR: 文件 %s 不存在。\n", path); return -1; } @@ -341,62 +410,60 @@ static int check_node_group_name() } /* - * @Description: check input for security - * @IN input: input string - * @Return: void + * @Description: 检查输入是否安全 + * @IN input: 输入字符串 + * @Return: void * @See also: + * 函数check_input_for_security用于检查输入的字符串是否安全。它定义了一个危险字符数组danger_token,然后遍历数组,逐个检查输入中是否存在危险字符。如果存在,则输出错误消息并退出程序。 + * check_input_for_security函数可以应用于任何需要验证用户输入的场景。例如,在用户登录系统时,可以调用该函数来检查用户输入的用户名和密码是否包含危险字符,以增加系统的安全性。 */ -static void check_input_for_security(char* input) -{ - char* danger_token[] = {"|", ";", "&", "$", "<", ">", "`", "\\", "!", "\n", NULL}; +static void check_input_for_security(char* input) { + char* danger_token[] = { "|", ";", "&", "$", "<", ">", "`", "\\", "!", "\n", NULL }; for (int i = 0; danger_token[i] != NULL; ++i) { - if (strstr(input, danger_token[i]) != NULL) { - printf("invalid token \"%s\"\n", danger_token[i]); + if (strstr(input, danger_token[i]) != NULL) { // 检查输入中是否存在危险字符 + printf("invalid token \"%s\"\n", danger_token[i]); // 打印错误消息 exit(1); } } } /* - * @Description: Check whether the name of class, - * Class group and Workload group is valid. + * @Description: 检查类、类组和工作负载组名称是否有效 * @IN void - * @Return: -1: abnormal 0: normal + * @Return: -1:异常,0:正常 * @See also: + * 函数check_name_valid用于检查类、类组和工作负载组的名称是否有效。首先定义了最大名称长度namelen,然后检查类名称和类异常数据。如果类名为空且异常数据有效,则将类名设置为默认值。接着检查类名称长度是否超出指定大小,如果超出则返回异常。然后检查工作负载组名称长度是否超出指定大小,如果超出则返回异常。最后返回正常。 + * check_name_valid函数可以应用于数据库管理系统中对类、类组和工作负载组名称进行验证的场景。例如,在数据库管理系统中创建或修改类组和工作负载组时,可以调用该函数来验证名称的合法性,以确保系统的稳定性和安全性。 */ -static int check_name_valid(void) -{ - int namelen = GPNAME_LEN / 2 - 1; /* max name length */ +static int check_name_valid(void) { + int namelen = GPNAME_LEN / 2 - 1; // 最大名称长度 errno_t sret; - /* check class name and class exception data */ + /* 检查类名称和类异常数据 */ if (*cgutil_opt.clsname == '\0' && *cgutil_opt.edata) { fprintf(stdout, - "NOTICE: if not specify class name but exceptional data is valid, " - "class name will be \"%s\"!\n", + "NOTICE: 若未指定类名但异常数据有效,则类名将为 \"%s\"!\n", GSCGROUP_DEFAULT_CLASS); - sret = snprintf_s(cgutil_opt.clsname, GPNAME_LEN, GPNAME_LEN - 1, "%s", GSCGROUP_DEFAULT_CLASS); + sret = snprintf_s(cgutil_opt.clsname, GPNAME_LEN, GPNAME_LEN - 1, "%s", GSCGROUP_DEFAULT_CLASS); // 将类名设置为默认值 securec_check_intval(sret, , -1); } - /* check class name length */ + /* 检查类名称长度 */ if (strlen(cgutil_opt.clsname) > (size_t)namelen) { *cgutil_opt.clsname = '\0'; fprintf(stderr, - "ERROR: The name of Class group is beyond " - "its dedicated size which is %d bytes.\n", + "ERROR: 类组名称超出了其指定大小 %d 字节。\n", namelen); return -1; } - /* check workload group name length */ + /* 检查工作负载组名称长度 */ if (strlen(cgutil_opt.wdname) > (size_t)namelen - 3) { *cgutil_opt.wdname = '\0'; fprintf(stderr, - "ERROR: The name of Workload group is beyond " - "its dedicated size which is %d bytes.\n", + "ERROR: 工作负载组名称超出了其指定大小 %d 字节。\n", namelen - 3); return -1; @@ -404,85 +471,83 @@ static int check_name_valid(void) return 0; } - /* - * @Description: check input values. + * @Description: 检查输入值的有效性。 * @IN void - * @Return: -1: abnormal 0: normal + * @Return: -1: 异常 0: 正常 * @See also: */ static int check_input_valid(void) { - /* check group name with flag '--fixed' */ + /* 检查带有'--fixed'标志的组名 */ if (*cgutil_opt.clsname == '\0' && *cgutil_opt.wdname == '\0' && *cgutil_opt.bkdname == '\0' && *cgutil_opt.topname == '\0' && cgutil_opt.fixed) { - fprintf(stderr, "ERROR: Please specify a group name with flag \"--fixed\"\n"); + fprintf(stderr, "ERROR: 请使用\"--fixed\"标志指定一个组名\n"); return -1; } - /* check flag '--fixed' and '-u' */ + /* 检查'--fixed'和'-u'标志 */ if (cgutil_opt.fixed && 0 == cgutil_opt.uflag) { - fprintf(stderr, "ERROR: Please specify \'--fixed\' flag together with \'-u\' flag.\n"); + fprintf(stderr, "ERROR: 请同时使用\'--fixed\'标志和\'-u\'标志\n"); return -1; } - /* check group name with flag '-f' */ + /* 检查带有'-f'标志的组名 */ if ((*cgutil_opt.clsname || *cgutil_opt.wdname || *cgutil_opt.bkdname || - (*cgutil_opt.topname && - (0 != strncmp(cgutil_opt.topname, GSCGROUP_TOP_DATABASE, sizeof(GSCGROUP_TOP_DATABASE))))) && + (*cgutil_opt.topname && + (0 != strncmp(cgutil_opt.topname, GSCGROUP_TOP_DATABASE, sizeof(GSCGROUP_TOP_DATABASE))))) && *cgutil_opt.sets) { - fprintf(stderr, "ERROR: Only specify \'-f\' option on Gaussdb Group.\n"); + fprintf(stderr, "ERROR: 仅能在Gaussdb Group上指定\'-f\'选项\n"); return -1; } - /* users cannot use -f and --fixed at the same time */ + /* 用户不能同时使用'-f'和'--fixed'标志 */ if (cgutil_opt.fixed && *cgutil_opt.sets) { - fprintf(stderr, "ERROR: Please specify one option from \'-f\',\'--fixed\'.\n"); + fprintf(stderr, "ERROR: 请从\'-f\'、\'--fixed\'中选择一个选项\n"); return -1; } - /* get current mount points */ + /* 获取当前挂载点 */ if (cgexec_get_mount_points() < 0) { return -1; } - /* check '-c', '-d', '-u' flag */ + /* 检查'-c'、'-d'、'-u'标志 */ if ((cgutil_opt.cflag && cgutil_opt.dflag) || (cgutil_opt.cflag && cgutil_opt.uflag) || (cgutil_opt.uflag && cgutil_opt.dflag)) { - fprintf(stderr, "ERROR: please only specify one option from '-c', '-d' and '-u'.\n"); + fprintf(stderr, "ERROR: 请只指定一个选项:'-c'、'-d'和'-u'\n"); return -1; } - /* check '-e' flag */ + /* 检查'-e'标志 */ if (IS_EXCEPT_FLAG(cgutil_opt.eflag, EXCEPT_ERROR)) { - fprintf(stderr, "ERROR: abort and penalty cannot be specified together!\n"); + fprintf(stderr, "ERROR: 不能同时指定中止和处罚标志!\n"); return -1; } - /* check exception data from '-e' flag */ + /* 检查'-e'标志的异常数据 */ if (cgutil_opt.clsname[0] == '\0' && IS_EXCEPT_FLAG(cgutil_opt.eflag, EXCEPT_PENALTY)) { - fprintf(stderr, "ERROR: you must specify a class name with penalty!\n"); + fprintf(stderr, "ERROR: 你必须指定具有处罚的类名!\n"); return -1; } - /* set default exception data without '--penalty', '--abort' and '-a' flag */ + /* 在没有'--penalty'、'--abort'和'-a'标志的情况下设置默认的异常数据 */ if (*cgutil_opt.edata && IS_EXCEPT_FLAG(cgutil_opt.eflag, EXCEPT_NONE)) { cgutil_opt.eflag = EXCEPT_FLAG(EXCEPT_PENALTY); - fprintf(stdout, "NOTICE: if do not specify exceptional action, default is penalty!\n"); + fprintf(stdout, "NOTICE: 如果不指定异常操作,默认为处罚!\n"); } - /* check '--refresh', '--revert' and '--recover' flag */ + /* 检查'--refresh'、'--revert'和'--recover'标志 */ if ((cgutil_opt.cflag || cgutil_opt.dflag || cgutil_opt.uflag) && (cgutil_opt.refresh || cgutil_opt.revert || cgutil_opt.recover)) { fprintf(stderr, - "ERROR: you cannot specify option '-c', '-u' or '-d' with " - "'--refresh' or '--revert' or '--recover'!\n"); + "ERROR: 不能在'-c'、'-u'或'-d'选项中同时指定'--refresh'、'--revert'或'--recover'!\n"); return -1; } - /* check '--recover' flag */ + /* 检查'--recover'标志 */ if ((geteuid() == 0) && cgutil_opt.recover) { - fprintf(stderr, "ERROR: you cannpt specify option '--recover' by root user!\n"); + fprintf(stderr, "ERROR: root用户不能指定'--recover'选项!\n"); return -1; } @@ -490,98 +555,110 @@ static int check_input_valid(void) } /* - * @Description: check user info with flags. + * @Description: 检查带有标志的用户信息。 * @IN void - * @Return: -1: abnormal 0: normal + * @Return: -1: 异常 0: 正常 * @See also: */ static int check_user_process(void) { - /* check root user process */ + /* 检查root用户进程 */ if ((geteuid() == 0) && ((cgutil_opt.cflag || cgutil_opt.display || cgutil_opt.uflag || cgutil_opt.dflag) && - cgutil_opt.user[0] == '\0')) { + cgutil_opt.user[0] == '\0')) { fprintf(stderr, - "ERROR: you must specify the user name with '-c', '-d', '-p' or '-u' " - "while running as root user.\n"); + "ERROR: 在作为root用户运行时,必须使用'-c'、'-d'、'-p'或'-u'指定用户名\n"); return -1; } - /* check non-root user process */ + /* 检查非root用户进程 */ if (geteuid() && cgutil_opt.user[0] != '\0') { - fprintf(stderr, "ERROR: you can't specify the user name while running as non-root user.\n"); + fprintf(stderr, "ERROR: 在以非root用户身份运行时,不能指定用户名\n"); return -1; } - /* check user info for '-P' flag */ + /* 检查'-P'标志的用户信息 */ if (0 == geteuid() && cgutil_opt.ptree && '\0' == *cgutil_opt.user) { fprintf(stderr, - "ERROR: you must specify the user name when running as root user " - "to display the cgroup tree.\n"); + "ERROR: 当以root用户身份运行时,必须指定用户名来显示cgroup树\n"); return -1; } - /* check non-root user info for '-M' flag */ + /* 检查'-M'标志的非root用户信息 */ if ((cgutil_opt.mflag || cgutil_opt.umflag) && geteuid()) { - fprintf(stderr, "ERROR: you must run mount or umount cgroup by root user!\n"); + fprintf(stderr, "ERROR: 必须以root用户身份运行才能挂载或卸载cgroup!\n"); return -1; } return 0; } - /* - * @Description: check all flags. + * @Description: 检查所有标志位。 * @IN void - * @Return: -1: abnormal 0: normal + * @Return: -1: 异常 0: 正常 * @See also: */ static int check_flag_process(void) { /* create flag process */ + // 创建标志位处理 if (cgutil_opt.cflag) { /* check top and backend group name */ + // 检查顶层组和后端组名称 if (cgutil_opt.topname[0] != '\0' || cgutil_opt.bkdname[0] != '\0') { fprintf(stderr, - "ERROR: you can't specify the Top group or backend group" - " during creating cgroup!\n"); + "错误:在创建cgroup期间,不能指定顶层组或后端组!\n"); return -1; } /* check workload group name and class name */ + // 检查工作负载组名和类名 if (cgutil_opt.wdname[0] != '\0' && cgutil_opt.clsname[0] == '\0') { fprintf(stderr, - "ERROR: You can' specify the group name without" - " specifying the class name during creating cgroup!\n"); + "错误:在创建cgroup期间,不能仅指定组名而不指定类名!\n"); return -1; } if (*cgutil_opt.wdname && NULL != strchr(cgutil_opt.wdname, ':')) { - fprintf(stderr, "ERROR, workload group cannot be named with ':'. \n"); + fprintf(stderr, "错误,工作负载组名不能包含':'字符。\n"); return -1; } } /* delete flag process */ + // 删除标志位处理 if (cgutil_opt.dflag) { /* check top and backend group name */ + // 检查顶层组和后端组名称 if (cgutil_opt.topname[0] != '\0' || cgutil_opt.bkdname[0] != '\0') { fprintf(stderr, - "ERROR: you can't specify the Top group or backend group" - " during dropping cgroup!\n"); + "错误:在删除cgroup期间,不能指定顶层组或后端组!\n"); return -1; } } /* update flag process */ + // 更新标志位处理 if (cgutil_opt.uflag && ('\0' == cgutil_opt.topname[0] && '\0' == cgutil_opt.bkdname[0] && '\0' == cgutil_opt.clsname[0])) { - fprintf(stderr, "ERROR: please specify the Group name when updating!\n"); + fprintf(stderr, "错误:在更新cgroup时,请指定组名!\n"); return -1; } return 0; } -/* +/** + * 该函数检查所有标志位的状态。 + * @param void + * @return int:-1表示异常,0表示正常 + * + * 例如,当创建cgroup时,需要检查一些条件: + * - 不能指定顶层组或后端组的名称 + * - 不能仅指定组名而不指定类名 + * - 工作负载组名不能包含冒号字符 + * 如果满足以上条件,则返回0表示正常,否则返回-1表示异常。 + */ + + /* * @Description: check group names. * @IN void * @Return: -1: abnormal 0: normal @@ -590,169 +667,158 @@ static int check_flag_process(void) static int check_group_name_process(int top, int bkd) { /* check class name and percentage */ + // 检查类名和百分比 if (cgutil_opt.clspct && '\0' == cgutil_opt.clsname[0]) { fprintf(stderr, - "ERROR: please specify the Class name " - "together with Class percent!\n"); + "错误:请同时指定类名和百分比!\n"); return -1; } /* check workload group name and percentage */ + // 检查工作负载组名和百分比 if (cgutil_opt.grppct && '\0' == cgutil_opt.wdname[0]) { fprintf(stderr, - "ERROR: please specify the Workload name " - "together with Workload percent!\n"); + "错误:请同时指定工作负载名和百分比!\n"); return -1; } /* workload group name special process */ + // 特殊处理工作负载组名 if (cgutil_opt.wdname[0] != '\0') { if ((NULL == strchr(cgutil_opt.wdname, ':') && 0 == strcmp(cgutil_opt.wdname, GSCGROUP_TOP_WORKLOAD)) || (NULL != strchr(cgutil_opt.wdname, ':') && 0 == strncmp(cgutil_opt.wdname, GSCGROUP_TOP_WORKLOAD, sizeof(GSCGROUP_TOP_WORKLOAD) - 1))) { - fprintf(stderr, "ERROR: can't do any operation on %s group!\n", GSCGROUP_TOP_WORKLOAD); + fprintf(stderr, "错误:不能对%s组执行任何操作!\n", GSCGROUP_TOP_WORKLOAD); return -1; } } + ... +} - /* check timeshare group name */ - if (cgutil_opt.wdname[0] && (0 == strcmp(cgutil_opt.wdname, GSCGROUP_RUSH_TIMESHARE) || - 0 == strcmp(cgutil_opt.wdname, GSCGROUP_HIGH_TIMESHARE) || - 0 == strcmp(cgutil_opt.wdname, GSCGROUP_MEDIUM_TIMESHARE) || - 0 == strcmp(cgutil_opt.wdname, GSCGROUP_LOW_TIMESHARE))) { +/** + * 该函数检查组名的状态。 + * @param top:顶层组名 + * @param bkd:后端组名 + * @return int:-1表示异常,0表示正常 + * + * 例如,当检查组名时,需要检查一些条件: + * - 如果指定了百分比,则必须同时指定类名 + * - 如果指定了百分比,则必须同时指定工作负载名 + * - 特殊处理工作负载组名,禁止对特定组进行操作 + * 如果满足以上条件,则返回0表示正常,否则返回-1表示异常。 + */ + /* + * @Description: 检查组名。 + * @IN void + * @Return: -1:异常 0:正常 + * @See also: + * 该函数主要用于检查和处理组名以及相关参数的合法性,确保输入符合规定。在实际应用中,可以用于配置管理系统中对组名的检查和处理。例如,一个资源管理系统中,需要对组名进行检查和处理,以确保组名的唯一性和合法性。如果组名为空或与已有的组名重复,则会提示错误。 + */ +static int check_group_name_process(int top, int bkd) { + /* 检查班级名和百分比 */ + if (cgutil_opt.clspct && '\0' == cgutil_opt.clsname[0]) { fprintf(stderr, - "ERROR: can't specify the name of Workload group the same as " - "the name of default Timeshare Group!\n"); + "ERROR: 请同时指定班级名和班级百分比!\n"); return -1; } - /* top group name special process */ - if (cgutil_opt.topname[0] != '\0') { - if (!cgutil_opt.uflag) { - fprintf(stderr, "ERROR: please specify the option '-u' when using top name!\n"); - return -1; - } + /* 检查工作负载组名和百分比 */ + if (cgutil_opt.grppct && '\0' == cgutil_opt.wdname[0]) { + fprintf(stderr, + "ERROR: 请同时指定工作负载名和工作负载百分比!\n"); + return -1; + } - /* check top percentage with '-f' flag */ - if (!cgutil_opt.fixed && !*cgutil_opt.sets && !cgutil_opt.toppct) { - fprintf(stderr, "ERROR: please specify the top dynamic percent when using top name!\n"); - return -1; - } else if (cgutil_opt.fixed && - !(cgutil_opt.setspct || cgutil_opt.toppct || top)) { - fprintf(stderr, - "ERROR: please specify the cpu core percent or IO values " - "when updating fixed values!\n"); + /* 特殊处理工作负载组名 */ + if (cgutil_opt.wdname[0] != '\0') { + if ((NULL == strchr(cgutil_opt.wdname, ':') && 0 == strcmp(cgutil_opt.wdname, GSCGROUP_TOP_WORKLOAD)) || + (NULL != strchr(cgutil_opt.wdname, ':') && + 0 == strncmp(cgutil_opt.wdname, GSCGROUP_TOP_WORKLOAD, sizeof(GSCGROUP_TOP_WORKLOAD) - 1))) { + fprintf(stderr, "ERROR: 无法对 %s 组进行任何操作!\n", GSCGROUP_TOP_WORKLOAD); return -1; } } - /* backend group name special process */ + /* 检查TimeShare组名 */ + if (cgutil_opt.wdname[0] && (0 == strcmp(cgutil_opt.wdname, GSCGROUP_RUSH_TIMESHARE) || + 0 == strcmp(cgutil_opt.wdname, GSCGROUP_HIGH_TIMESHARE) || + 0 == strcmp(cgutil_opt.wdname, GSCGROUP_MEDIUM_TIMESHARE) || + 0 == strcmp(cgutil_opt.wdname, GSCGROUP_LOW_TIMESHARE))) { + fprintf(stderr, + "ERROR: 不能将工作负载组名与默认的TimeShare组名相同!\n"); + return -1; + } + + /* 特殊处理top组名 */ + if (cgutil_opt.topname[0] != '\0') { + if (!cgutil_opt.uflag) { + fprintf(stderr, "ERROR: 使用top名时,请指定'-u'选项!\n"); + return -1; + } + + /* 检查使用top名时的百分比与'-f'标志 */ + if (!cgutil_opt.fixed && !*cgutil_opt.sets && !cgutil_opt.toppct) { + fprintf(stderr, "ERROR: 使用top名时,请指定动态top百分比!\n"); + return -1; + } + else if (cgutil_opt.fixed && + !(cgutil_opt.setspct || cgutil_opt.toppct || top)) { + fprintf(stderr, + "ERROR: 当更新固定值时,请指定cpu核心百分比或IO值!\n"); + return -1; + } + } + + /* 特殊处理backend组名 */ if (cgutil_opt.bkdname[0] != '\0') { if (!cgutil_opt.uflag) { fprintf(stderr, - "ERROR: please specify the option '-u' " - "when using backend name!\n"); + "ERROR: 使用backend名时,请指定'-u'选项!\n"); return -1; } - /* check backend percent with '--fixed' flag */ - if (cgutil_opt.fixed && !(cgutil_opt.setspct || cgutil_opt.bkdpct || bkd)) { + /* 检查backend名 */ + if (!*cgutil_opt.sets && !cgutil_opt.toppct && !bkd) { fprintf(stderr, - "ERROR: please specified the cpu core percent or IO values " - "when updating fixed values!\n"); + "ERROR: 当更新固定值时,请指定cpu核心百分比或IO值!\n"); return -1; } } - - /* check backend name and percentage */ - if (cgutil_opt.bkdpct && '\0' == cgutil_opt.bkdname[0]) { - fprintf(stderr, - "ERROR: please specify the backend name " - "together with backend percent!\n"); - return -1; - } - /* check backend name and percentage */ - if (cgutil_opt.toppct && '\0' == cgutil_opt.topname[0]) { - fprintf(stderr, - "ERROR: please specify the top cgroup name " - "together with top percent!\n"); - return -1; - } - - /* Check if the node group has been created or will be created */ - if ('\0' != cgutil_opt.nodegroup[0]) { - if ((cgutil_opt.clsname[0] != '\0' || cgutil_opt.wdname[0] != '\0' || cgutil_opt.refresh) && - -1 == check_node_group_name()) { - fprintf(stderr, "ERROR: please check if the node group exists!\n"); - return -1; - } - - /* can't run command by root user */ - if (geteuid() == 0) { - fprintf(stderr, - "ERROR: please execute command by non-root user " - "when the node group is specified!\n"); - return -1; - } - - if (0 == strcmp(cgutil_opt.nodegroup, GSCGROUP_TOP_CLASS) || - 0 == strcmp(cgutil_opt.nodegroup, GSCGROUP_TOP_BACKEND)) { - fprintf(stderr, "ERROR: the name of logical cluster can't be 'Class' or 'Backend'.\n"); - return -1; - } - - if (!cgutil_opt.cflag && !cgutil_opt.dflag && !cgutil_opt.uflag && !cgutil_opt.display && !cgutil_opt.recover && - !cgutil_opt.refresh && ('\0' == *cgutil_opt.edata)) { - fprintf(stderr, "ERROR: please specify logical cluster with -c/-d/-u/--recover/--refresh option!\n"); - return -1; - } - } - - /* Check if the rename flag is set together with nodegroup */ - if (cgutil_opt.rename && '\0' == cgutil_opt.nodegroup[0]) { - fprintf(stderr, "ERROR: please specify the rename flag together with nodegroup name!\n"); - return -1; - } - - /* set the name when there is no rename flag */ - if (!cgutil_opt.rename && '\0' != cgutil_opt.nodegroup[0]) - current_nodegroup = cgutil_opt.nodegroup; - - return 0; } - -/* - * @Description: check user name. +/** + * @Description: 检查用户名称是否有效。 * @IN void - * @Return: -1: abnormal 0: normal + * @Return: -1: 异常 0: 正常 * @See also: */ static int check_user_name(void) { - /* user name is valid */ + /* 用户名称有效 */ if (cgutil_opt.user[0]) { - /* check it's root user */ + /* 检查用户名称是否为root */ if (0 == strcmp(cgutil_opt.user, "root")) { - fprintf(stderr, "ERROR: can't specify the user name as root.\n"); + fprintf(stderr, "ERROR: 不能将用户名称指定为root。\n"); return -1; - } else /* get the user id and group id */ + } + else /* 获取用户ID和组ID */ { cgutil_passwd_user = getpwnam(cgutil_opt.user); if (NULL == cgutil_passwd_user) { fprintf(stderr, - "ERROR: can't get the uid and gid of %s.\n" - "HINT: please check the specified user name.\n", + "ERROR: 无法获取%s的UID和GID。\n" + "HINT: 请检查指定的用户名。\n", cgutil_opt.user); return -1; } } - } else { - /* save current user info */ + } + else { + /* 保存当前用户信息 */ cgutil_passwd_user = getpwuid(geteuid()); if (NULL == cgutil_passwd_user) { fprintf(stderr, - "ERROR: can't get the cgutil_passwd_user.\n" - "HINT: please check the running user!\n"); + "ERROR: 无法获取cgutil_passwd_user。\n" + "HINT: 请检查正在运行的用户!\n"); return -1; } } @@ -760,31 +826,31 @@ static int check_user_name(void) return 0; } -/* - * @Description: check all input whether is valid. - * @IN bkd: backend group id - * @IN grp: group id - * @IN cls: class id - * @IN top: top group id - * @Return: -1: abnormal 0: normal +/** + * @Description: 检查所有的输入是否有效。 + * @IN bkd: 后端组ID + * @IN grp: 组ID + * @IN cls: 类ID + * @IN top: 顶级组ID + * @Return: -1: 异常 0: 正常 * @See also: */ static int check_input_isvalid(int bkd, int grp, int cls, int top) { - /* check list */ + /* 检查列表 */ if (check_name_valid() == -1 || check_input_valid() == -1 || check_user_name() == -1 || check_percentage_value(bkd, grp, cls, top) == -1 || check_group_name_process(top, bkd) == -1 || check_user_process() == -1 || check_flag_process() == -1) { - return -1; - } + return -1; + } return 0; } -/* - * @Description: check cpuset value is valid. - * @IN cpuset: cpuset value - * @Return: -1: abnormal 0: normal +/** + * @Description: 检查cpuset的值是否有效。 + * @IN cpuset: cpuset的值 + * @Return: -1: 异常 0: 正常 * @See also: */ static int check_cpuset_value_valid(char* cpuset) @@ -795,39 +861,40 @@ static int check_cpuset_value_valid(char* cpuset) char* p = NULL; if (*cpuset == '-') { - fprintf(stderr, "ERROR: please specify the cpuset with a valid value.\n"); + fprintf(stderr, "ERROR: 请使用有效的值指定cpuset。\n"); return -1; } p = strchr(cpuset, '-'); - /* check "cpuset" value is like this: a-b */ + /* 检查"cpuset"的值是否为a-b */ if (p == NULL) { a = (int)strtol(cpuset, &bad, 10); if ((bad != NULL) && *bad) { - fprintf(stderr, "ERROR: please specify the cpuset with \"a-b\" or \"a\"!\n"); + fprintf(stderr, "ERROR: 请使用\"a-b\"或\"a\"格式指定cpuset。\n"); return -1; } b = a; - } else { + } + else { *p++ = '\0'; a = (int)strtol(cpuset, &bad, 10); if ((bad != NULL) && *bad) { - fprintf(stderr, "ERROR: please specify the cpuset with a valid value.\n"); + fprintf(stderr, "ERROR: 请使用有效的值指定cpuset。\n"); return -1; } b = (int)strtol(p, &bad, 10); if ((bad != NULL) && *bad) { - fprintf(stderr, "ERROR: please specify the cpuset with a valid value.\n"); + fprintf(stderr, "ERROR: 请使用有效的值指定cpuset。\n"); return -1; } } if ((a < 0) || (b < 0) || (a > b) || (b >= cgutil_cpucnt)) { - fprintf(stderr, "ERROR: please specify the cpuset with a valid value.\n"); + fprintf(stderr, "ERROR: 请使用有效的值指定cpuset。\n"); return -1; } @@ -836,12 +903,33 @@ static int check_cpuset_value_valid(char* cpuset) return 0; } - /* - * @Description: check config flag. +注释分析: +check_user_name()函数的功能是检查用户名称是否有效。该函数没有输入参数,返回 - 1表示异常,返回0表示正常。用户名称的有效性包括以下几个方面: +- 检查用户名称是否为"root",如果是,则打印错误信息,并返回 - 1。 +- 如果用户名称不是"root",则获取用户ID和组ID。如果获取失败,打印错误信息,并返回 - 1。 +- 如果用户名称为空,则保存当前用户信息。如果保存失败,打印错误信息,并返回 - 1。 +check_input_isvalid()函数的功能是检查所有的输入参数是否有效。输入参数有bkd、grp、cls、top四个整数变量。返回 - 1表示异常,返回0表示正常。函数内部依次检查以下内容的有效性: +- 调用check_name_valid()函数检查名称的有效性。 +- 调用check_input_valid()函数检查输入的有效性。 +- 调用check_user_name()函数检查用户名称的有效性。 +- 调用check_percentage_value()函数检查百分比值的有效性。 +- 调用check_group_name_process()函数检查组名和进程的有效性。 +- 调用check_user_process()函数检查用户和进程的有效性。 +- 调用check_flag_process()函数检查标志位和进程的有效性。 +如果其中任何一个检查返回 - 1,则整个函数返回 - 1,表示异常;否则返回0,表示正常。 +check_cpuset_value_valid()函数的功能是检查cpuset的值是否有效。输入参数是一个字符指针cpuset,返回 - 1表示异常,返回0表示正常。该函数执行以下操作: +- 首先检查cpuset的第一个字符是否为'-',如果是,则打印错误信息,并返回 - 1。 +- 然后查找字符串中的'-'字符,如果没有找到,则将字符串转换为整数a,如果转换失败或者转换后的值不合法(如包含非数字字符),则打印错误信息,并返回 - 1。此时将a赋值给b。 +- 如果找到了'-'字符,则将字符串转换为整数a和b,如果转换失败或者转换后的值不合法(如包含非数字字符),则打印错误信息,并返回 - 1。 +- 最后检查a和b的值是否合法(大于等于0,且a小于等于b,且b小于cputil_cpucnt),如果不合法,则打印错误信息,并返回 - 1。 +- 如果以上检查都通过,则将有效的a和b转换为字符串,并存储在cgutil_opt.sets变量中,返回0表示正常。 +/* + * @Description: 检查配置标志位。 * @IN void - * @Return: 1: OK 0: Not OK + * @Return: 1: 正常 0: 异常 * @See also: + * 函数用于检查配置标志位,判断是否满足特定的条件。如果满足条件,则返回1,否则返回0。函数参数为空。 */ static int check_config_flag(void) { @@ -854,41 +942,44 @@ static int check_config_flag(void) } /* - * @Description: initialize cgroup config. + * @Description: 初始化cgroup配置。 * @IN void - * @Return: -1: abnormal 0: normal + * @Return: -1: 异常 0: 正常 * @See also: + * 函数用于初始化cgroup配置。首先调用check_config_flag()函数检查配置标志位,如果满足条件,则根据不同的用户权限获取GAUSSHOME路径,并进行相应的检查和设置。然后调用cgconf_parse_config_file()函数解析配置文件。如果解析失败,则根据不同的情况输出相应的错误信息。函数参数为空。 */ static int initialize_cgroup_config(void) { char* hpath = NULL; errno_t sret; - /* retrieve the information of configure file; if it doesn't, create one */ + /* 检索配置文件信息;如果不存在,则创建一个 */ if (check_config_flag() > 0) { - if (geteuid() == 0) { + if (geteuid() == 0) { // 如果是root用户,则需要指定GAUSSHOME路径 if ('\0' == cgutil_opt.hpath[0]) { fprintf(stderr, "ERROR: you need specify the GAUSSHOME path " "when runing as root user!\n"); return -1; } - } else { + } + else { // 如果不是root用户,则从环境变量中获取GAUSSHOME路径 if (NULL == (hpath = gs_getenv_r("GAUSSHOME"))) { fprintf(stderr, "ERROR: environment variable $GAUSSHOME is not set!\n"); return -1; } - if (CheckBackendEnv(hpath) != 0) { + if (CheckBackendEnv(hpath) != 0) { // 检查后端环境 return -1; } sret = snprintf_s(cgutil_opt.hpath, sizeof(cgutil_opt.hpath), sizeof(cgutil_opt.hpath) - 1, "%s", hpath); securec_check_intval(sret, , -1); } - if (-1 == cgconf_parse_config_file()) { + if (-1 == cgconf_parse_config_file()) { // 解析配置文件 if (cgutil_opt.dflag && '\0' != cgutil_opt.nodegroup[0]) { fprintf(stderr, "WARNING: failed to parse the node group configure file!\n"); - } else { + } + else { fprintf(stderr, "FATAL: failed to parse the configure file!\n"); return -1; } @@ -899,31 +990,32 @@ static int initialize_cgroup_config(void) } /* - * @Description: check and get group percent. - * @IN percent: input percent - * @IN gtype: group type: class, workload or top - * @Return: -1: abnormal 0: normal + * @Description: 检查并获取组百分比。 + * @IN percent: 输入的百分比 + * @IN gtype: 组类型:class、workload或top + * @Return: -1: 异常 0: 正常 * @See also: + * 函数用于检查并获取组百分比。根据传入的组类型(gtype)参数的值,设置相应的百分比值。如果组类型不匹配,则返回 - 1并输出错误信息。函数参数为百分比(percent)和组类型(gtype)。 */ static int check_and_get_group_percent(char* percent, char* gtype) { char* bad = NULL; - if (strcmp(gtype, "top") == 0) { + if (strcmp(gtype, "top") == 0) { // 如果组类型是top,则设置top百分比 cgutil_opt.toppct = (int)strtol(percent, &bad, 10); - } - else if (strcmp(gtype, "class") == 0) { + } + else if (strcmp(gtype, "class") == 0) { // 如果组类型是class,则设置class百分比 cgutil_opt.clspct = (int)strtol(percent, &bad, 10); - } - else if (strcmp(gtype, "workload") == 0) { + } + else if (strcmp(gtype, "workload") == 0) { // 如果组类型是workload,则设置workload百分比 cgutil_opt.grppct = (int)strtol(percent, &bad, 10); - } - else if (strcmp(gtype, "backend") == 0) { + } + else if (strcmp(gtype, "backend") == 0) { // 如果组类型是backend,则设置backend百分比 cgutil_opt.bkdpct = (int)strtol(percent, &bad, 10); - } - else { + } + else { // 如果组类型不匹配,则返回异常 return -1; - } + } if ((bad != NULL) && *bad) { fprintf(stderr, "ERROR: incorrect %s percent %s!\n", gtype, percent); @@ -932,16 +1024,16 @@ static int check_and_get_group_percent(char* percent, char* gtype) return 0; } - /* * function name: parse_options - * description : parse the option of gs_cgroup utility - * arguments : as main function arguments + * description : 解析 gs_cgroup 工具的选项 + * arguments : 作为主函数参数的 argc 和 argv * return value : - * -1: abnormal - * 0: normal + * -1: 异常 + * 0: 正常 + * */ -static struct option long_options[] = {{"help", no_argument, NULL, 'h'}, +static struct option long_options[] = { {"help", no_argument, NULL, 'h'}, {"version", no_argument, NULL, 'V'}, {"abort", no_argument, NULL, 'a'}, {"group", required_argument, NULL, 'N'}, @@ -952,7 +1044,7 @@ static struct option long_options[] = {{"help", no_argument, NULL, 'h'}, {"fixed", no_argument, NULL, 5}, {"recover", no_argument, NULL, 6}, {"rename", no_argument, NULL, 7}, - {NULL, 0, NULL, 0}}; + {NULL, 0, NULL, 0} }; static int parse_options(int argc, char** argv) { @@ -968,648 +1060,570 @@ static int parse_options(int argc, char** argv) sret = memset_s(&cgutil_opt, sizeof(cgutil_opt_t), 0, sizeof(cgutil_opt_t)); securec_check_errno(sret, , -1); - /* option parse */ + /* 选项解析 */ while ((c = getopt_long( - argc, argv, "ab:B:cdD:E:f:hH:g:G:mMN:pPr:R:s:S:t:T:uU:Vw:W:", long_options, &option_index)) != -1) { + argc, argv, "ab:B:cdD:E:f:hH:g:G:mMN:pPr:R:s:S:t:T:uU:Vw:W:", long_options, &option_index)) != -1) { switch (c) { - case 'a': /* abort */ - if (IS_EXCEPT_FLAG(cgutil_opt.eflag, EXCEPT_NONE)) - cgutil_opt.eflag = EXCEPT_FLAG(EXCEPT_ABORT); - else - cgutil_opt.eflag = EXCEPT_FLAG(EXCEPT_ERROR); - break; - case 'b': /* backend group percentage */ - if (check_and_get_group_percent(optarg, "backend") == -1) - return -1; - - bkd = 1; - break; - case 'B': /* backend group name */ - sret = strncpy_s(cgutil_opt.bkdname, GPNAME_LEN, optarg, GPNAME_LEN - 1); - securec_check_errno(sret, , -1); - - check_input_for_security(cgutil_opt.bkdname); - - break; - case 'c': /* create group */ - cgutil_opt.cflag = 1; - break; - case 'd': /* drop group */ - cgutil_opt.dflag = 1; - break; - case 'D': /* mount point */ - cgutil_opt.mpflag = 1; - sret = strncpy_s(cgutil_opt.mpoint, MAXPGPATH, optarg, MAXPGPATH - 1); - securec_check_errno(sret, , -1); - - check_input_for_security(cgutil_opt.mpoint); - - last = strlen(cgutil_opt.mpoint) - 1; - if ('/' == cgutil_opt.mpoint[last]) - cgutil_opt.mpoint[last] = '\0'; - break; - case 'E': /* Exceptional data */ - sret = strncpy_s(cgutil_opt.edata, EXCEPT_LEN, optarg, EXCEPT_LEN - 1); - securec_check_errno(sret, , -1); - check_input_for_security(cgutil_opt.edata); - break; - case 'h': /* help */ - usage(); - exit(0); - case 'H': /* GAUSSHOME path */ - sret = strncpy_s(cgutil_opt.hpath, MAXPGPATH, optarg, MAXPGPATH - 1); - securec_check_errno(sret, , -1); - check_input_for_security(cgutil_opt.hpath); - break; - case 'f': /* core numbers */ - if (check_cpuset_value_valid(optarg) == -1) - return -1; - break; - case 'g': /* workload group percentage */ - if (check_and_get_group_percent(optarg, "workload") == -1) - return -1; - - grp = 1; - break; - case 'G': /* workload group name for "gpname:gplevel" */ - - sret = strncpy_s(cgutil_opt.wdname, GPNAME_LEN, optarg, GPNAME_LEN - 1 - 2); - securec_check_errno(sret, , -1); - check_input_for_security(cgutil_opt.wdname); - break; - case 'm': /* mount cgroup */ - cgutil_opt.mflag = 1; - break; - case 'M': /* umount cgroup */ - cgutil_opt.umflag = 1; - break; - case 'N': /* Nodegroup information */ - sret = strncpy_s(cgutil_opt.nodegroup, GPNAME_LEN, optarg, GPNAME_LEN - 1); - securec_check_errno(sret, , -1); - check_input_for_security(cgutil_opt.nodegroup); - break; - case 'p': /* display gscgroup.cfg information */ - cgutil_opt.display = 1; - break; - case 'P': /* display Cgroup tree information */ - cgutil_opt.ptree = 1; - break; - case 's': /* Class group percentage */ - if (check_and_get_group_percent(optarg, "class") == -1) - return -1; - - cls = 1; - break; - case 'S': /* Class group name */ - if (strchr(optarg, ':') != NULL) { - fprintf(stderr, "ERROR, class cannot be named with ':'. \n"); - return -1; - } - sret = strncpy_s(cgutil_opt.clsname, GPNAME_LEN, optarg, GPNAME_LEN - 1); - securec_check_errno(sret, , -1); - check_input_for_security(cgutil_opt.clsname); - break; - case 't': /* Top group percentage */ - if (check_and_get_group_percent(optarg, "top") == -1) - return -1; - - top = 1; - break; - case 'T': /* Top group name */ - sret = strncpy_s(cgutil_opt.topname, GPNAME_LEN, optarg, GPNAME_LEN - 1); - securec_check_errno(sret, , -1); - check_input_for_security(cgutil_opt.topname); - break; - case 'u': /* update flag */ - cgutil_opt.uflag = 1; - break; - case 'U': /* user name */ - sret = strncpy_s(cgutil_opt.user, USERNAME_LEN, optarg, USERNAME_LEN - 1); - securec_check_errno(sret, , -1); - check_input_for_security(cgutil_opt.user); - break; - case 'V': /* version */ - cgutil_version = DEF_GS_VERSION; - return 0; - case 1: - if (IS_EXCEPT_FLAG(cgutil_opt.eflag, EXCEPT_NONE)) - cgutil_opt.eflag = EXCEPT_FLAG(EXCEPT_PENALTY); - else - cgutil_opt.eflag = EXCEPT_FLAG(EXCEPT_ERROR); - break; - case 2: - cgutil_opt.upgrade = 1; - break; - case 3: - cgutil_opt.refresh = 1; - break; - case 4: - cgutil_opt.revert = 1; - break; - case 5: - cgutil_opt.fixed = 1; - break; - case 6: - cgutil_opt.recover = 1; - break; - case 7: - cgutil_opt.rename = 1; - break; - default: - fprintf(stderr, "ERROR: incorrect option: %s\n.", optarg); - usage(); - return -1; - } - } - - return check_input_isvalid(bkd, grp, cls, top); -} - -/* - * function name: main - * description : main entry of gs_cgroup utility - * arguments : main function default arguments - */ -int main(int argc, char** argv) -{ - char* cpuset = NULL; - int ret = 0; - - if (argc < 2) { - usage(); - exit(-1); - } - - // log output redirect - init_log(PROG_NAME); - - /* print the log about arguments of gs_cgroup */ - char arguments[MAX_BUF_SIZE] = {0x00}; - for (int i = 0; i < argc; i++) { - errno_t rc = strcat_s(arguments, MAX_BUF_SIZE, argv[i]); - size_t len = strlen(arguments); - if (rc != EOK || len >= (MAX_BUF_SIZE - 2)) + case 'a': /* 中止 */ + if (IS_EXCEPT_FLAG(cgutil_opt.eflag, EXCEPT_NONE)) + cgutil_opt.eflag = EXCEPT_FLAG(EXCEPT_ABORT); + else + cgutil_opt.eflag = EXCEPT_FLAG(EXCEPT_ERROR); break; - arguments[len] = ' '; - arguments[len + 1] = '\0'; - } - write_log("The gs_cgroup run with the following arguments: [%s].\n", arguments); + case 'b': /* 后端组百分比 */ + if (check_and_get_group_percent(optarg, "backend") == -1) + return -1; - /* get the cpu count value */ - cgutil_cpucnt = gsutil_get_cpu_count(); + bkd = 1; + break; + case 'B': /* 后端组名称 */ + sret = strncpy_s(cgutil_opt.bkdname, GPNAME_LEN, optarg, GPNAME_LEN - 1); + securec_check_errno(sret, , -1); - if (cgutil_cpucnt == -1) { - fprintf(stderr, - "get cpu core range failed, please check if \"/proc/cpuinfo\"" - " or \"/sys/devices/system\" is acceptable. \n"); - exit(-1); - } + check_input_for_security(cgutil_opt.bkdname); - int rc = sprintf_s(cgutil_allset, sizeof(cgutil_allset), "%d-%d", 0, cgutil_cpucnt - 1); - securec_check_intval(rc, , -1); + break; + case 'c': /* 创建组 */ + cgutil_opt.cflag = 1; + break; + case 'd': /* 删除组 */ + cgutil_opt.dflag = 1; + break; + case 'D': /* 挂载点 */ + cgutil_opt.mpflag = 1; + sret = strncpy_s(cgutil_opt.mpoint, MAXPGPATH, optarg, MAXPGPATH - 1); + securec_check_errno(sret, , -1); - /* parse the options */ - ret = parse_options(argc, argv); - if (-1 == ret) { - fprintf(stderr, "HINT: please run 'gs_cgroup -h' to display the usage!\n"); - exit(-1); - } + check_input_for_security(cgutil_opt.mpoint); - if (cgutil_version != NULL) { - fprintf(stdout, "gs_cgroup %s\n", cgutil_version); - return 0; - } - - if (geteuid() == 0 && cgutil_opt.mflag) { - cgexec_mount_cgroups(); - } - - if (geteuid() == 0 && cgutil_opt.umflag && !cgutil_opt.dflag) { - cgexec_umount_cgroups(); - exit(0); - } - - /* retrieve the information of configure file; if it doesn't, create one */ - if (initialize_cgroup_config() == -1) - return -1; - - /* check upgrade flag */ - if (cgutil_opt.upgrade) { - cgutil_opt.refresh = 1; - - /* maybe we need not do upgrade */ - if (cgexec_check_mount_for_upgrade() == -1) { - goto error; + last = strlen(cgutil_opt.mpoint) - 1; + if ('/' == cgutil_opt.mpoint[last]) + cgutil_opt.mpoint[last] = '\0'; + break; + case 'E': /* 异常数据 */ + sret = strncpy_s(cgutil_opt.edata, EXCEPT_LEN, optarg, EXCEPT_LEN - 1); + securec_check_errno(sret, , -1); + check_input_for_security(cgutil_opt.edata); } - } - - if (cgutil_opt.cflag) { - /* run as root user */ - if (geteuid() == 0 && cgutil_opt.upgrade == 0) { - /* check if cgroups have been mounted if it doesn't specify mflag */ - if (-1 == (ret = cgexec_mount_root_cgroup())) { - goto error; - } - } - } - - /* initialize libcgroup */ - ret = cgroup_init(); - if (ret) { - fprintf(stderr, - "FATAL: libcgroup initialization failed: %s\n" - "please run 'gs_cgroup -m' to " - "mount cgroup by root user!\n", - cgroup_strerror(ret)); - goto error; - } - - /* get memory set */ - if (-1 == cgexec_get_cgroup_cpuset_info(TOPCG_ROOT, &cpuset)) { - fprintf(stderr, "ERROR: failed to get cpusets and mems during initialization.\n"); - goto error; - } - - rc = snprintf_s(cgutil_allset, CPUSET_LEN, CPUSET_LEN - 1, "%s", cpuset); - securec_check_intval(rc, , -1); - free(cpuset); - cpuset = NULL; - - /* create/delete/update operation */ - if (cgutil_opt.cflag) { - if (cgexec_create_groups() == -1) { - goto error; - } - } else if (cgutil_opt.dflag) { - if (cgexec_drop_groups() == -1) { - goto error; - } - } else if (cgutil_opt.uflag) { - if (cgexec_update_groups() == -1) { - goto error; - } - } else if (cgutil_opt.revert) { - if (cgexec_revert_groups() == -1) { - goto error; - } - } - - /* refresh current groups */ - if (cgutil_opt.refresh) { - if (cgexec_refresh_groups() == -1) { - goto error; - } - } - - /* recover the last changes of groups */ - if (cgutil_opt.recover) { - if (cgexec_recover_groups() == -1) { - goto error; - } - } - - /* process the exceptional data */ - if (*cgutil_opt.edata && *cgutil_opt.clsname && -1 == cgexcp_class_exception()) - goto error; - - /* display the cgroup configuration file information */ - if (cgutil_opt.display) - cgconf_display_groups(); - - /* display the cgroup tree information */ - if (cgutil_opt.ptree) { - if (cgptree_display_cgroups() == -1) { - goto error; - } - } - - if (cgutil_vaddr[0] != NULL) - (void)munmap(cgutil_vaddr[0], GSCGROUP_ALLNUM * sizeof(gscgroup_grp_t)); - - return 0; -error: - if (cgutil_vaddr[0] != NULL) - (void)munmap(cgutil_vaddr[0], GSCGROUP_ALLNUM * sizeof(gscgroup_grp_t)); - write_log("gs_cgroup execution error.\n"); - exit(-1); -} - -#ifdef ENABLE_UT -void cgroup_set_default_group() -{ - errno_t sret; - char tmpstr[GPNAME_LEN]; - - cgutil_vaddr[TOPCG_ROOT]->used = 1; - cgutil_vaddr[TOPCG_ROOT]->gid = TOPCG_ROOT; - cgutil_vaddr[TOPCG_ROOT]->gtype = GROUP_TOP; - sret = strncpy_s(cgutil_vaddr[TOPCG_ROOT]->grpname, GPNAME_LEN, GSCGROUP_ROOT, GPNAME_LEN - 1); - securec_check_errno(sret, , ); - cgutil_vaddr[TOPCG_ROOT]->ginfo.top.percent = 100 * DEFAULT_IO_WEIGHT / MAX_IO_WEIGHT; - cgutil_vaddr[TOPCG_ROOT]->ainfo.weight = DEFAULT_IO_WEIGHT; - cgutil_vaddr[TOPCG_ROOT]->percent = 1000; - - /* set root group as default cpu set */ - (void)sprintf_s(cgutil_vaddr[TOPCG_ROOT]->cpuset, CPUSET_LEN, "%s", cgutil_allset); - - cgutil_vaddr[TOPCG_BACKEND]->used = 1; - cgutil_vaddr[TOPCG_BACKEND]->gid = TOPCG_BACKEND; - cgutil_vaddr[TOPCG_BACKEND]->gtype = GROUP_TOP; - sret = strncpy_s(cgutil_vaddr[TOPCG_BACKEND]->grpname, GPNAME_LEN, GSCGROUP_TOP_BACKEND, GPNAME_LEN - 1); - securec_check_errno(sret, , ); - cgutil_vaddr[TOPCG_BACKEND]->ginfo.top.percent = TOP_BACKEND_PERCENT; - cgutil_vaddr[TOPCG_BACKEND]->ainfo.shares = DEFAULT_CPU_SHARES * TOP_BACKEND_PERCENT / 10; - cgutil_vaddr[TOPCG_BACKEND]->ainfo.weight = IO_WEIGHT_CALC(MAX_IO_WEIGHT, TOP_BACKEND_PERCENT); - cgutil_vaddr[TOPCG_BACKEND]->percent = cgutil_vaddr[TOPCG_GAUSSDB]->percent * TOP_BACKEND_PERCENT / 100; - - /* set root group as gaussdb group cpu set */ - if (*cgutil_vaddr[TOPCG_BACKEND]->cpuset == '\0') - (void)sprintf_s(cgutil_vaddr[TOPCG_BACKEND]->cpuset, CPUSET_LEN, "%s", cgutil_vaddr[TOPCG_GAUSSDB]->cpuset); - - cgutil_vaddr[TOPCG_CLASS]->used = 1; - cgutil_vaddr[TOPCG_CLASS]->gid = TOPCG_CLASS; - cgutil_vaddr[TOPCG_CLASS]->gtype = GROUP_TOP; - sret = strncpy_s(cgutil_vaddr[TOPCG_CLASS]->grpname, GPNAME_LEN, GSCGROUP_TOP_CLASS, GPNAME_LEN - 1); - securec_check_errno(sret, , ); - cgutil_vaddr[TOPCG_CLASS]->ginfo.top.percent = TOP_CLASS_PERCENT; - cgutil_vaddr[TOPCG_CLASS]->ainfo.shares = DEFAULT_CPU_SHARES * TOP_CLASS_PERCENT / 10; - cgutil_vaddr[TOPCG_CLASS]->ainfo.weight = IO_WEIGHT_CALC(MAX_IO_WEIGHT, TOP_CLASS_PERCENT); - cgutil_vaddr[TOPCG_CLASS]->percent = cgutil_vaddr[TOPCG_GAUSSDB]->percent * TOP_CLASS_PERCENT / 100; - /* set root group as gaussdb group cpu set */ - if (*cgutil_vaddr[TOPCG_CLASS]->cpuset == '\0') - (void)sprintf_s(cgutil_vaddr[TOPCG_CLASS]->cpuset, CPUSET_LEN, "%s", cgutil_vaddr[TOPCG_GAUSSDB]->cpuset); - - cgutil_vaddr[BACKENDCG_START_ID]->used = 1; - cgutil_vaddr[BACKENDCG_START_ID]->gid = BACKENDCG_START_ID; - cgutil_vaddr[BACKENDCG_START_ID]->gtype = GROUP_BAKWD; - cgutil_vaddr[BACKENDCG_START_ID]->ginfo.cls.tgid = TOPCG_BACKEND; - cgutil_vaddr[BACKENDCG_START_ID]->ginfo.cls.percent = DEFAULT_BACKEND_PERCENT; - sret = strncpy_s(cgutil_vaddr[BACKENDCG_START_ID]->grpname, GPNAME_LEN, GSCGROUP_DEFAULT_BACKEND, GPNAME_LEN - 1); - securec_check_errno(sret, , ); - cgutil_vaddr[BACKENDCG_START_ID]->ainfo.shares = DEFAULT_CPU_SHARES * DEFAULT_BACKEND_PERCENT / 10; - cgutil_vaddr[BACKENDCG_START_ID]->ainfo.weight = IO_WEIGHT_CALC(MAX_IO_WEIGHT, DEFAULT_BACKEND_PERCENT); - cgutil_vaddr[BACKENDCG_START_ID]->percent = cgutil_vaddr[TOPCG_BACKEND]->percent * DEFAULT_BACKEND_PERCENT / 100; - - /* set root group as backend group cpu set */ - if (*cgutil_vaddr[BACKENDCG_START_ID]->cpuset == '\0') - (void)sprintf_s( - cgutil_vaddr[BACKENDCG_START_ID]->cpuset, CPUSET_LEN, "%s", cgutil_vaddr[TOPCG_BACKEND]->cpuset); - - cgutil_vaddr[BACKENDCG_START_ID + 1]->used = 1; - cgutil_vaddr[BACKENDCG_START_ID + 1]->gid = BACKENDCG_START_ID + 1; - cgutil_vaddr[BACKENDCG_START_ID + 1]->gtype = GROUP_BAKWD; - cgutil_vaddr[BACKENDCG_START_ID + 1]->ginfo.cls.tgid = TOPCG_BACKEND; - cgutil_vaddr[BACKENDCG_START_ID + 1]->ginfo.cls.percent = VACUUM_PERCENT; - sret = strncpy_s(cgutil_vaddr[BACKENDCG_START_ID + 1]->grpname, GPNAME_LEN, GSCGROUP_VACUUM, GPNAME_LEN - 1); - securec_check_errno(sret, , ); - cgutil_vaddr[BACKENDCG_START_ID + 1]->ainfo.shares = DEFAULT_CPU_SHARES * VACUUM_PERCENT / 10; - cgutil_vaddr[BACKENDCG_START_ID + 1]->ainfo.weight = IO_WEIGHT_CALC(MAX_IO_WEIGHT, VACUUM_PERCENT); - cgutil_vaddr[BACKENDCG_START_ID + 1]->percent = cgutil_vaddr[TOPCG_BACKEND]->percent * VACUUM_PERCENT / 100; - /* set root group as backend group cpu set */ - if (*cgutil_vaddr[BACKENDCG_START_ID + 1]->cpuset == '\0') - (void)sprintf_s( - cgutil_vaddr[BACKENDCG_START_ID + 1]->cpuset, CPUSET_LEN, "%s", cgutil_vaddr[TOPCG_BACKEND]->cpuset); - - cgutil_vaddr[CLASSCG_START_ID]->used = 1; - cgutil_vaddr[CLASSCG_START_ID]->gid = CLASSCG_START_ID; - cgutil_vaddr[CLASSCG_START_ID]->gtype = GROUP_CLASS; - cgutil_vaddr[CLASSCG_START_ID]->ginfo.cls.tgid = TOPCG_CLASS; - cgutil_vaddr[CLASSCG_START_ID]->ginfo.cls.maxlevel = 1; /* initialized value */ - cgutil_vaddr[CLASSCG_START_ID]->ginfo.cls.percent = DEFAULT_CLASS_PERCENT; - cgutil_vaddr[CLASSCG_START_ID]->ginfo.cls.rempct = 100; /* initialized value */ - sret = strncpy_s(cgutil_vaddr[CLASSCG_START_ID]->grpname, GPNAME_LEN, GSCGROUP_DEFAULT_CLASS, GPNAME_LEN - 1); - securec_check_errno(sret, , ); - cgutil_vaddr[CLASSCG_START_ID]->ainfo.shares = DEFAULT_CPU_SHARES * DEFAULT_CLASS_PERCENT / 10; - cgutil_vaddr[CLASSCG_START_ID]->ainfo.weight = IO_WEIGHT_CALC(MAX_IO_WEIGHT, DEFAULT_CLASS_PERCENT); - /* it has only this class, so it has all resource */ - cgutil_vaddr[CLASSCG_START_ID]->percent = cgutil_vaddr[TOPCG_CLASS]->percent; - - cgutil_vaddr[CLASSCG_START_ID]->except[EXCEPT_PENALTY].skewpercent = DEFAULT_CPUSKEWPCT; - cgutil_vaddr[CLASSCG_START_ID]->except[EXCEPT_PENALTY].qualitime = DEFAULT_QUALITIME; - - cgutil_vaddr[WDCG_START_ID]->used = 1; - cgutil_vaddr[WDCG_START_ID]->gid = WDCG_START_ID; - cgutil_vaddr[WDCG_START_ID]->gtype = GROUP_DEFWD; - cgutil_vaddr[WDCG_START_ID]->ginfo.wd.cgid = CLASSCG_START_ID; - cgutil_vaddr[WDCG_START_ID]->ginfo.wd.wdlevel = 1; - sret = snprintf_s(tmpstr, sizeof(tmpstr), sizeof(tmpstr) - 1, "%s:%d", GSCGROUP_TOP_WORKLOAD, 1); - securec_check_intval(sret, , ); - - sret = strncpy_s(cgutil_vaddr[WDCG_START_ID]->grpname, GPNAME_LEN, tmpstr, GPNAME_LEN - 1); - securec_check_errno(sret, , ); - - cgutil_vaddr[WDCG_START_ID]->ainfo.shares = MAX_CLASS_CPUSHARES * TOPWD_PERCENT / 100; - cgutil_vaddr[WDCG_START_ID]->ainfo.weight = IO_WEIGHT_CALC(MAX_IO_WEIGHT, TOPWD_PERCENT); - - cgutil_vaddr[TSCG_START_ID]->used = 1; - cgutil_vaddr[TSCG_START_ID]->gid = TSCG_START_ID; - cgutil_vaddr[TSCG_START_ID]->gtype = GROUP_TSWD; - cgutil_vaddr[TSCG_START_ID]->ginfo.ts.cgid = CLASSCG_START_ID; - cgutil_vaddr[TSCG_START_ID]->ginfo.ts.rate = TS_LOW_RATE; - sret = strncpy_s(cgutil_vaddr[TSCG_START_ID]->grpname, GPNAME_LEN, GSCGROUP_LOW_TIMESHARE, GPNAME_LEN - 1); - securec_check_errno(sret, , ); - cgutil_vaddr[TSCG_START_ID]->ainfo.shares = DEFAULT_CPU_SHARES * TS_LOW_RATE; - cgutil_vaddr[TSCG_START_ID]->ainfo.weight = MIN_IO_WEIGHT * TS_LOW_RATE; - - /* medium group of default group */ - cgutil_vaddr[TSCG_START_ID + 1]->used = 1; - cgutil_vaddr[TSCG_START_ID + 1]->gid = TSCG_START_ID + 1; - cgutil_vaddr[TSCG_START_ID + 1]->gtype = GROUP_TSWD; - cgutil_vaddr[TSCG_START_ID + 1]->ginfo.ts.cgid = CLASSCG_START_ID; - cgutil_vaddr[TSCG_START_ID + 1]->ginfo.ts.rate = TS_MEDIUM_RATE; - sret = strncpy_s(cgutil_vaddr[TSCG_START_ID + 1]->grpname, GPNAME_LEN, GSCGROUP_MEDIUM_TIMESHARE, GPNAME_LEN - 1); - securec_check_errno(sret, , ); - cgutil_vaddr[TSCG_START_ID + 1]->ainfo.shares = DEFAULT_CPU_SHARES * TS_MEDIUM_RATE; - cgutil_vaddr[TSCG_START_ID + 1]->ainfo.weight = MIN_IO_WEIGHT * TS_MEDIUM_RATE; - - /* high group of default group */ - cgutil_vaddr[TSCG_START_ID + 2]->used = 1; - cgutil_vaddr[TSCG_START_ID + 2]->gid = TSCG_START_ID + 2; - cgutil_vaddr[TSCG_START_ID + 2]->gtype = GROUP_TSWD; - cgutil_vaddr[TSCG_START_ID + 2]->ginfo.ts.cgid = CLASSCG_START_ID; - cgutil_vaddr[TSCG_START_ID + 2]->ginfo.ts.rate = TS_HIGH_RATE; - sret = strncpy_s(cgutil_vaddr[TSCG_START_ID + 2]->grpname, GPNAME_LEN, GSCGROUP_HIGH_TIMESHARE, GPNAME_LEN - 1); - securec_check_errno(sret, , ); - cgutil_vaddr[TSCG_START_ID + 2]->ainfo.shares = DEFAULT_CPU_SHARES * TS_HIGH_RATE; - cgutil_vaddr[TSCG_START_ID + 2]->ainfo.weight = MIN_IO_WEIGHT * TS_HIGH_RATE; - - /* rush group of default group */ - cgutil_vaddr[TSCG_START_ID + 3]->used = 1; - cgutil_vaddr[TSCG_START_ID + 3]->gid = TSCG_START_ID + 3; - cgutil_vaddr[TSCG_START_ID + 3]->gtype = GROUP_TSWD; - cgutil_vaddr[TSCG_START_ID + 3]->ginfo.ts.cgid = CLASSCG_START_ID; - cgutil_vaddr[TSCG_START_ID + 3]->ginfo.ts.rate = TS_RUSH_RATE; - sret = strncpy_s(cgutil_vaddr[TSCG_START_ID + 3]->grpname, GPNAME_LEN, GSCGROUP_RUSH_TIMESHARE, GPNAME_LEN - 1); - securec_check_errno(sret, , ); - cgutil_vaddr[TSCG_START_ID + 3]->ainfo.shares = DEFAULT_CPU_SHARES * TS_RUSH_RATE; - cgutil_vaddr[TSCG_START_ID + 3]->ainfo.weight = MIN_IO_WEIGHT * TS_RUSH_RATE; -} - -extern void cgconf_generate_default_config_file(void* vaddr); -extern int cgexec_update_remain_cgroup_cpuset(int cls, char* cpuset, unsigned char update); -extern int cgexec_check_cpuset_value(const char* clsset, const char* grpset); -extern int cgexec_update_class_cpuset(int cls, char* cpuset); -extern int cgexec_update_top_group_cpuset(int top, char* cpuset); -extern void cgexec_update_fixed_config(int high, int extended); - -void cgroup_unit_test_case() -{ - char* argv[] = {"gs_cgroup", "-D", "/dev/cgroups/test", "--upgrade"}; - int argc = sizeof(argv) / sizeof(*argv); - int sret = 0; - - cgutil_opt.mpflag = 1; - (void)cgexec_mount_root_cgroup(); - (void)cgexec_umount_root_cgroup(); - - (void)parse_options(argc, argv); - - for (int i = 0; i < GSCGROUP_ALLNUM; ++i) { - if (NULL == (cgutil_vaddr[i] = (gscgroup_grp_t*)malloc(sizeof(gscgroup_grp_t)))) { - fprintf(stderr, "ERROR: failed to allocate memory for gsgroup!\n"); - for (int index = 0; index < i; ++index) { - free(cgutil_vaddr[index]); - cgutil_vaddr[index] = NULL; - } - return; - } - } - - cgroup_set_default_group(); - - for (int i = 0; i < GSCGROUP_ALLNUM; ++i) { - free(cgutil_vaddr[i]); - cgutil_vaddr[i] = NULL; - } - gscgroup_grp_t vaddr[GSCGROUP_ALLNUM]; - - cgconf_generate_default_config_file(vaddr); - - sret = memset_s(&cgutil_opt, sizeof(cgutil_opt), 0, sizeof(cgutil_opt)); - securec_check_c(sret, "\0", "\0"); - - sret = snprintf_s(cgutil_opt.sets, sizeof(cgutil_opt.sets), sizeof(cgutil_opt.sets) - 1, "%s", "2-8"); - securec_check_ss_c(sret, "\0", "\0"); - - cgconf_set_class_group(1); - - sret = snprintf_s(cgutil_opt.wdname, sizeof(cgutil_opt.wdname), sizeof(cgutil_opt.wdname) - 1, "%s", "wg1"); - securec_check_ss_c(sret, "\0", "\0"); - - cgconf_set_class_group(1); - - cgconf_set_workload_group(1, 2); - - cgutil_opt.fixed = 1; - cgutil_is_sles11_sp2 = 0; - check_percentage_value(1, 1, 1, 1); - - cgutil_opt.display = 1; - cgutil_opt.user[0] = '\0'; - check_user_process(); - - cgutil_opt.display = 0; - cgutil_opt.ptree = 1; - check_user_process(); - - cgutil_opt.ptree = 0; - cgutil_opt.cflag = 1; - cgutil_opt.fixed = 0; - - sret = snprintf_s(cgutil_opt.topname, sizeof(cgutil_opt.topname), sizeof(cgutil_opt.topname) - 1, "%s", "Gaussdb"); - securec_check_ss_c(sret, "\0", "\0"); - - check_flag_process(); - - cgutil_opt.topname[0] = '\0'; - sret = snprintf_s(cgutil_opt.wdname, sizeof(cgutil_opt.wdname), sizeof(cgutil_opt.wdname) - 1, "%s", "wg1"); - securec_check_ss_c(sret, "\0", "\0"); - - check_flag_process(); + ```cpp + // 功能:解析命令行参数 + // 参数: + // -h:显示帮助信息 + // -H:设置GAUSSHOME路径 + // -f:设置核心数 + // -g:设置工作负载组百分比 + // -G:设置工作负载组名称 + // -m:挂载cgroup + // -M:卸载cgroup + // -N:设置节点组信息 + // -p:显示gscgroup.cfg信息 + // -P:显示Cgroup树信息 + // -s:设置类组百分比 + // -S:设置类组名称 + // -t:设置Top组百分比 + // -T:设置Top组名称 + // -u:设置更新标志 + // -U:设置用户名 + // -V:显示版本信息 + // return:检查解析后的输入是否有效,并返回结果 + + int parse_args(int argc, char* argv[]) { + int opt; + int grp = 0; + int cls = 0; + int top = 0; + + while ((opt = getopt(argc, argv, "hH:f:g:G:mMN:pPsS:t:T:uUV")) != -1) { + switch (opt) { + case 'h': /* 帮助 */ + usage(); + exit(0); + case 'H': /* GAUSSHOME路径 */ + sret = strncpy_s(cgutil_opt.hpath, MAXPGPATH, optarg, MAXPGPATH - 1); + securec_check_errno(sret, , -1); + check_input_for_security(cgutil_opt.hpath); + break; + case 'f': /* 核心数 */ + if (check_cpuset_value_valid(optarg) == -1) + return -1; + break; + case 'g': /* 工作负载组百分比 */ + if (check_and_get_group_percent(optarg, "workload") == -1) + return -1; + + grp = 1; + break; + case 'G': /* 工作负载组名称("gpname:gplevel") */ + sret = strncpy_s(cgutil_opt.wdname, GPNAME_LEN, optarg, GPNAME_LEN - 1 - 2); + securec_check_errno(sret, , -1); + check_input_for_security(cgutil_opt.wdname); + break; + case 'm': /* 挂载cgroup */ + cgutil_opt.mflag = 1; + break; + case 'M': /* 卸载cgroup */ + cgutil_opt.umflag = 1; + break; + case 'N': /* 节点组信息 */ + sret = strncpy_s(cgutil_opt.nodegroup, GPNAME_LEN, optarg, GPNAME_LEN - 1); + securec_check_errno(sret, , -1); + check_input_for_security(cgutil_opt.nodegroup); + break; + case 'p': /* 显示gscgroup.cfg信息 */ + cgutil_opt.display = 1; + break; + case 'P': /* 显示Cgroup树信息 */ + cgutil_opt.ptree = 1; + break; + case 's': /* 类组百分比 */ + if (check_and_get_group_percent(optarg, "class") == -1) + return -1; + + cls = 1; + break; + case 'S': /* 类组名称 */ + if (strchr(optarg, ':') != NULL) { + fprintf(stderr, "ERROR, class cannot be named with ':'. \n"); + return -1; + } + sret = strncpy_s(cgutil_opt.clsname, GPNAME_LEN, optarg, GPNAME_LEN - 1); + securec_check_errno(sret, , -1); + check_input_for_security(cgutil_opt.clsname); + break; + case 't': /* Top组百分比 */ + if (check_and_get_group_percent(optarg, "top") == -1) + return -1; + + top = 1; + break; + case 'T': /* Top组名称 */ + sret = strncpy_s(cgutil_opt.topname, GPNAME_LEN, optarg, GPNAME_LEN - 1); + securec_check_errno(sret, , -1); + check_input_for_security(cgutil_opt.topname); + break; + case 'u': /* 更新标志 */ + cgutil_opt.uflag = 1; + break; + case 'U': /* 用户名 */ + sret = strncpy_s(cgutil_opt.user, USERNAME_LEN, optarg, USERNAME_LEN - 1); + securec_check_errno(sret, , -1); + check_input_for_security(cgutil_opt.user); + break; + case 'V': /* 版本 */ + cgutil_version = DEF_GS_VERSION; + return 0; + case 1: + if (IS_EXCEPT_FLAG(cgutil_opt.eflag, EXCEPT_NONE)) + cgutil_opt.eflag = EXCEPT_FLAG(EXCEPT_PENALTY); + else + cgutil_opt.eflag = EXCEPT_FLAG(EXCEPT_ERROR); + break; + case 2: + cgutil_opt.upgrade = 1; + break; + case 3: + cgutil_opt.refresh = 1; + break; + case 4: + cgutil_opt.revert = 1; + break; + case 5: + cgutil_opt.fixed = 1; + break; + case 6: + cgutil_opt.recover = 1; + break; + case 7: + cgutil_opt.rename = 1; + break; + default: + fprintf(stderr, "ERROR: 错误的选项: %s\n.", optarg); + usage(); + return -1; + } + } + + return check_input_isvalid(bkd, grp, cls, top); + } + /* + * 函数名:main + * 描述:gs_cgroup实用程序的主入口 + * 参数:main函数的默认参数 + */ + int main(int argc, char** argv) + { + char* cpuset = NULL; + int ret = 0; + + if (argc < 2) { + usage(); // 使用说明 + exit(-1); + } + + // 日志输出重定向 + init_log(PROG_NAME); // 初始化日志 + + /* 打印有关gs_cgroup参数的日志 */ + char arguments[MAX_BUF_SIZE] = { 0x00 }; // 存储所有参数的字符串 + for (int i = 0; i < argc; i++) { + errno_t rc = strcat_s(arguments, MAX_BUF_SIZE, argv[i]); // 将参数拼接到arguments字符串中 + size_t len = strlen(arguments); + if (rc != EOK || len >= (MAX_BUF_SIZE - 2)) + break; + arguments[len] = ' '; + arguments[len + 1] = '\0'; + } + write_log("The gs_cgroup run with the following arguments: [%s].\n", arguments); // 输出包含参数的日志 + + /* 获取CPU核心数 */ + cgutil_cpucnt = gsutil_get_cpu_count(); + + if (cgutil_cpucnt == -1) { + fprintf(stderr, + "获取CPU核心范围失败,请检查是否可接受\"/proc/cpuinfo\"或\"/sys/devices/system\"路径。\n"); + exit(-1); + } + + int rc = sprintf_s(cgutil_allset, sizeof(cgutil_allset), "%d-%d", 0, cgutil_cpucnt - 1); // 设置allset字符串的值 + securec_check_intval(rc, , -1); + + /* 解析选项 */ + ret = parse_options(argc, argv); // 解析命令行选项 + if (-1 == ret) { + fprintf(stderr, "HINT: 请运行 'gs_cgroup -h' 显示使用方法!\n"); + exit(-1); + } + + if (cgutil_version != NULL) { + fprintf(stdout, "gs_cgroup %s\n", cgutil_version); + return 0; + } + + if (geteuid() == 0 && cgutil_opt.mflag) { + cgexec_mount_cgroups(); // 挂载cgroups + } + + if (geteuid() == 0 && cgutil_opt.umflag && !cgutil_opt.dflag) { + cgexec_umount_cgroups(); // 卸载cgroups + exit(0); + } + + /* 检索配置文件的信息;如果没有,则创建一个 */ + if (initialize_cgroup_config() == -1) // 初始化cgroup配置 + return -1; + + /* 检查升级标志 */ + if (cgutil_opt.upgrade) { + cgutil_opt.refresh = 1; + + /* 可能不需要进行升级 */ + if (cgexec_check_mount_for_upgrade() == -1) { + goto error; + } + } + + if (cgutil_opt.cflag) { + /* 以root用户身份运行 */ + if (geteuid() == 0 && cgutil_opt.upgrade == 0) { + /* 如果未指定mflag,则检查cgroups是否已挂载 */ + if (-1 == (ret = cgexec_mount_root_cgroup())) { + goto error; + } + } + } + + /* 初始化libcgroup */ + ret = cgroup_init(); // 初始化libcgroup + if (ret) { + fprintf(stderr, + "致命错误:libcgroup初始化失败:%s\n" + "请用root用户运行 'gs_cgroup -m' 挂载cgroup!\n", + cgroup_strerror(ret)); + goto error; + } + + /* 获取内存设置 */ + if (-1 == cgexec_get_cgroup_cpuset_info(TOPCG_ROOT, &cpuset)) { + fprintf(stderr, "错误:在初始化期间获取cpusets和mems失败。\n"); + goto error; + } + + rc = snprintf_s(cgutil_allset, CPUSET_LEN, CPUSET_LEN - 1, "%s", cpuset); // 设置allset字符串的值 + securec_check_intval(rc, , -1); + free(cpuset); + cpuset = NULL; + + /* 创建/删除/更新操作 */ + if (cgutil_opt.cflag) { + if (cgexec_create_groups() == -1) { + goto error; + } + } + else if (cgutil_opt.dflag) { + if (cgexec_drop_groups() == -1) { + goto error; + } + } + else if (cgutil_opt.uflag) { + if (cgexec_update_groups() == -1) { + goto error; + } + } + else if (cgutil_opt.revert) { + if (cgexec_revert_groups() == -1) { + goto error; + } + } + + /* 刷新当前组 */ + if (cgutil_opt.refresh) { + if (cgexec_refresh_groups() == -1) { + goto error; + } + } + + /* 恢复组的最后更改 */ + if (cgutil_opt.recover) { + if (cgexec_recover_groups() == -1) { + goto error; + } + } + + /* 处理异常数据 */ + if (*cgutil_opt.edata && *cgutil_opt.clsname && -1 == cgexcp_class_exception()) + goto error; + + /* 显示cgroup配置文件信息 */ + if (cgutil_opt.display) + cgconf_display_groups(); + + /* 显示cgroup树信息 */ + if (cgutil_opt.ptree) { + if (cgptree_display_cgroups() == -1) { + goto error; + } + } + + if (cgutil_vaddr[0] != NULL) + (void)munmap(cgutil_vaddr[0], GSCGROUP_ALLNUM * sizeof(gscgroup_grp_t)); + + return 0; + error: + if (cgutil_vaddr[0] != NULL) + (void)munmap(cgutil_vaddr[0], GSCGROUP_ALLNUM * sizeof(gscgroup_grp_t)); + write_log("gs_cgroup执行错误。\n"); + exit(-1); + } + /* + 该函数的功能是设置默认的cgroup组。包括设置根组、后端组、类别组和后端cgroup组的信息。 + 函数包含的变量及其功能: + - errno_t sret: 用于保存字符串拷贝操作的返回值 + - char tmpstr[GPNAME_LEN]: 临时字符串缓冲区 + + 类似的应用实例: + 该函数在初始化cgroup时使用。通过设置默认组,可以方便地管理和控制cgroup中的任务和资源。 + + 代码中各语句的功能: + - cgutil_vaddr[TOPCG_ROOT]->used = 1: 将根组的"used"字段设置为1,表示该组已被使用 + - cgutil_vaddr[TOPCG_ROOT]->gid = TOPCG_ROOT: 设置根组的gid为TOPCG_ROOT + - cgutil_vaddr[TOPCG_ROOT]->gtype = GROUP_TOP: 设置根组的gtype为GROUP_TOP + - sret = strncpy_s(cgutil_vaddr[TOPCG_ROOT]->grpname, GPNAME_LEN, GSCGROUP_ROOT, GPNAME_LEN - 1): 将GSCGROUP_ROOT字符串拷贝到根组的grpname字段中 + - cgutil_vaddr[TOPCG_ROOT]->ginfo.top.percent = 100 * DEFAULT_IO_WEIGHT / MAX_IO_WEIGHT: 计算根组的ginfo.top.percent值 + - cgutil_vaddr[TOPCG_ROOT]->ainfo.weight = DEFAULT_IO_WEIGHT: 设置根组的ainfo.weight为DEFAULT_IO_WEIGHT + - cgutil_vaddr[TOPCG_ROOT]->percent = 1000: 设置根组的percent为1000 + + - (void)sprintf_s(cgutil_vaddr[TOPCG_ROOT]->cpuset, CPUSET_LEN, "%s", cgutil_allset): 将cgutil_allset字符串拷贝到根组的cpuset字段中 + - cgutil_vaddr[TOPCG_BACKEND]->used = 1: 将后端组的"used"字段设置为1,表示该组已被使用 + - cgutil_vaddr[TOPCG_BACKEND]->gid = TOPCG_BACKEND: 设置后端组的gid为TOPCG_BACKEND + - cgutil_vaddr[TOPCG_BACKEND]->gtype = GROUP_TOP: 设置后端组的gtype为GROUP_TOP + - sret = strncpy_s(cgutil_vaddr[TOPCG_BACKEND]->grpname, GPNAME_LEN, GSCGROUP_TOP_BACKEND, GPNAME_LEN - 1): 将GSCGROUP_TOP_BACKEND字符串拷贝到后端组的grpname字段中 + - cgutil_vaddr[TOPCG_BACKEND]->ginfo.top.percent = TOP_BACKEND_PERCENT: 设置后端组的ginfo.top.percent为TOP_BACKEND_PERCENT + - cgutil_vaddr[TOPCG_BACKEND]->ainfo.shares = DEFAULT_CPU_SHARES * TOP_BACKEND_PERCENT / 10: 计算后端组的ainfo.shares值 + - cgutil_vaddr[TOPCG_BACKEND]->ainfo.weight = IO_WEIGHT_CALC(MAX_IO_WEIGHT, TOP_BACKEND_PERCENT): 计算后端组的ainfo.weight值 + - cgutil_vaddr[TOPCG_BACKEND]->percent = cgutil_vaddr[TOPCG_GAUSSDB]->percent * TOP_BACKEND_PERCENT / 100: 计算后端组的percent值 + + - if (*cgutil_vaddr[TOPCG_BACKEND]->cpuset == '\0'): 判断后端组的cpuset字段是否为空 + - (void)sprintf_s(cgutil_vaddr[TOPCG_BACKEND]->cpuset, CPUSET_LEN, "%s", cgutil_vaddr[TOPCG_GAUSSDB]->cpuset): 将cgutil_vaddr[TOPCG_GAUSSDB]->cpuset字符串拷贝到后端组的cpuset字段中 + + - cgutil_vaddr[TOPCG_CLASS]->used = 1: 将类别组的"used"字段设置为1,表示该组已被使用 + - cgutil_vaddr[TOPCG_CLASS]->gid = TOPCG_CLASS: 设置类别组的gid为TOPCG_CLASS + - cgutil_vaddr[TOPCG_CLASS]->gtype = GROUP_TOP: 设置类别组的gtype为GROUP_TOP + - sret = strncpy_s(cgutil_vaddr[TOPCG_CLASS]->grpname, GPNAME_LEN, GSCGROUP_TOP_CLASS, GPNAME_LEN - 1): 将GSCGROUP_TOP_CLASS字符串拷贝到类别组的grpname字段中 + - cgutil_vaddr[TOPCG_CLASS]->ginfo.top.percent = TOP_CLASS_PERCENT: 设置类别组的ginfo.top.percent为TOP_CLASS_PERCENT + - cgutil_vaddr[TOPCG_CLASS]->ainfo.shares = DEFAULT_CPU_SHARES * TOP_CLASS_PERCENT / 10: 计算类别组的ainfo.shares值 + - cgutil_vaddr[TOPCG_CLASS]->ainfo.weight = IO_WEIGHT_CALC(MAX_IO_WEIGHT, TOP_CLASS_PERCENT): 计算类别组的ainfo.weight值 + - cgutil_vaddr[TOPCG_CLASS]->percent = cgutil_vaddr[TOPCG_GAUSSDB]->percent * TOP_CLASS_PERCENT / 100: 计算类别组的percent值 + + - if (*cgutil_vaddr[TOPCG_CLASS]->cpuset == '\0'): 判断类别组的cpuset字段是否为空 + - (void)sprintf_s(cgutil_vaddr[TOPCG_CLASS]->cpuset, CPUSET_LEN, "%s", cgutil_vaddr[TOPCG_GAUSSDB]->cpuset): 将cgutil_vaddr[TOPCG_GAUSSDB]->cpuset字符串拷贝到类别组的cpuset字段中 + + - cgutil_vaddr[BACKENDCG_START_ID]->used = 1: 将后端cgroup组的"used"字段设置为1,表示该组已被使用 + - cgutil_vaddr[BACKENDCG_START_ID]->gid = BACKENDCG_START_ID: 设置后端cgroup组的gid为BACKENDCG_START_ID + - cgutil_vaddr[BACKENDCG_START_ID]->gtype = GROUP_BAKWD: 设置后端cgroup组的gtype为GROUP_BAKWD + - cgutil_vaddr[BACKENDCG_START_ID]->ginfo.cls.tgid = TOPCG_BACKEND: 设置后端cgroup组的ginfo.cls.tgid为TOPCG_BACKEND + - cgutil_vaddr[BACKENDCG_START_ID]->ginfo.cls.percent = DEFAULT_BACKEND_PERCENT: 设置后端cgroup组的ginfo.cls.percent为DEFAULT_BACKEND_PERCENT + - sret = strncpy_s(cgutil_vaddr[BACKENDCG_START_ID]->grpname, GPNAME_LEN, GSCGROUP_DEFAULT_BAC: 将GSCGROUP_DEFAULT_BAC字符串拷贝到后端cgroup组的grpname字段中 + */ + /** + * 生成默认配置文件的函数 + * 参数: + * vaddr - 指向gscgroup_grp_t类型的指针,表示cgroup的组内存指针 + * 功能: + * 生成默认配置文件,将配置文件存储在vaddr指向的内存中 + * 应用实例: + * cgconf_generate_default_config_file(vaddr); + */ + + /** + * 更新剩余cgroup的cpuset的函数 + * 参数: + * cls - 表示cgroup的类别 + * cpuset - 表示cpuset的字符串 + * update - 表示是否要更新cpuset的标志位 + * 返回值: + * 成功返回0,失败返回-1 + * 功能: + * 更新指定类别的cgroup的cpuset + * 应用实例: + * cgexec_update_remain_cgroup_cpuset(1, "0-3", 1); + */ + + /** + * 检查cpuset值的函数 + * 参数: + * clsset - 表示类别的cpuset字符串 + * grpset - 表示组的cpuset字符串 + * 返回值: + * 成功返回0,失败返回-1 + * 功能: + * 检查指定类别和组的cpuset值是否符合要求 + * 应用实例: + * cgexec_check_cpuset_value("0-3", "0-7"); + */ + + /** + * 更新指定类别的cpuset值的函数 + * 参数: + * cls - 表示类别 + * cpuset - 表示cpuset的字符串 + * 返回值: + * 成功返回0,失败返回-1 + * 功能: + * 更新指定类别的cpuset值 + * 应用实例: + * cgexec_update_class_cpuset(1, "0-3"); + */ + + /** + * 更新顶层组的cpuset值的函数 + * 参数: + * top - 表示顶层组 + * cpuset - 表示cpuset的字符串 + * 返回值: + * 成功返回0,失败返回-1 + * 功能: + * 更新指定顶层组的cpuset值 + * 应用实例: + * cgexec_update_top_group_cpuset(1, "0-7"); + */ + + /** + * 更新固定配置的函数 + * 参数: + * high - 表示高优先级标志位 + * extended - 表示扩展标志位 + * 功能: + * 更新固定配置的相关变量的值 + * 该函数没有返回值 + * 应用实例: + * cgexec_update_fixed_config(1, 0); + */ + + /** + * cgroup单元测试用例的函数 + * 功能: + * 执行cgroup的单元测试用例 + * 该函数没有返回值 + */ + void cgroup_unit_test_case() + { + char* argv[] = { "gs_cgroup", "-D", "/dev/cgroups/test", "--upgrade" }; + int argc = sizeof(argv) / sizeof(*argv); + int sret = 0; + + cgutil_opt.mpflag = 1; + (void)cgexec_mount_root_cgroup(); // 挂载根cgroup + (void)cgexec_umount_root_cgroup(); // 卸载根cgroup + + (void)parse_options(argc, argv); // 解析选项 + + for (int i = 0; i < GSCGROUP_ALLNUM; ++i) { + if (NULL == (cgutil_vaddr[i] = (gscgroup_grp_t*)malloc(sizeof(gscgroup_grp_t)))) { // 分配内存 + fprintf(stderr, "ERROR: failed to allocate memory for gsgroup!\n"); + for (int index = 0; index < i; ++index) { + free(cgutil_vaddr[index]); // 释放内存 + cgutil_vaddr[index] = NULL; + } + return; + } + } + + cgroup_set_default_group(); // 设置默认组 + + for (int i = 0; i < GSCGROUP_ALLNUM; ++i) { + free(cgutil_vaddr[i]); // 释放内存 + cgutil_vaddr[i] = NULL; + } + gscgroup_grp_t vaddr[GSCGROUP_ALLNUM]; // 创建gscgroup_grp_t类型的数组 + + cgconf_generate_default_config_file(vaddr); // 生成默认配置文件 + + sret = memset_s(&cgutil_opt, sizeof(cgutil_opt), 0, sizeof(cgutil_opt)); // 清空cgutil_opt + securec_check_c(sret, "\0", "\0"); + + sret = snprintf_s(cgutil_opt.sets, sizeof(cgutil_opt.sets), sizeof(cgutil_opt.sets) - 1, "%s", "2-8"); // 设置cgutil_opt.sets + securec_check_ss_c(sret, "\0", "\0"); + + cgconf_set_class_group(1); // 设置类组 + + sret = snprintf_s(cgutil_opt.wdname, sizeof(cgutil_opt.wdname), sizeof(cgutil_opt.wdname) - 1, "%s", "wg1"); // 设置cgutil_opt.wdname + securec_check_ss_c(sret, "\0", "\0"); + + cgconf_set_class_group(1); // 设置类组 + + cgconf_set_workload_group(1, 2); // 设置工作负载组 + + cgutil_opt.fixed = 1; + cgutil_is_sles11_sp2 = 0; + check_percentage_value(1, 1, 1, 1); // 检查百分比值 + + cgutil_opt.display = 1; + cgutil_opt.user[0] = '\0'; + check_user_process(); // 检查用户进程 + + cgutil_opt.display = 0; + cgutil_opt.ptree = 1; + check_user_process(); // 检查用户进程 + + cgutil_opt.ptree = 0; + cgutil_opt.cflag = 1; + cgutil_opt.fixed = 0; + + sret = snprintf_s(cgutil_opt.topname, sizeof(cgutil_opt.topname), sizeof(cgutil_opt.topname) - 1, "%s", "Gaussdb"); // 设置cgutil_opt.topname + securec_check_ss_c(sret, "\0", "\0"); + + check_flag_process(); // 检查标志位进程 + + cgutil_opt.topname[0] = '\0'; + sret = snprintf_s(cgutil_opt.wdname, sizeof(cgutil_opt.wdname), sizeof(cgutil_opt.wdname) - 1, "%s", "wg1"); // 设置cgutil_opt.wdname + securec_check_ss_c(sret, "\0", "\0"); + + check_flag_process(); // 检查标志位进程 + + sret = snprintf_s(cgutil_opt.wdname, sizeof(cgutil_opt.wdname), sizeof(cgutil_opt.wdname) - 1, "%s", "class1:wg1"); // 设置cgutil_opt.wdname + securec_check_ss_c(sret, "\0", "\0"); + + check_flag_process(); // 检查标志位进程 - sret = snprintf_s(cgutil_opt.wdname, sizeof(cgutil_opt.wdname), sizeof(cgutil_opt.wdname) - 1, "%s", "class1:wg1"); - securec_check_ss_c(sret, "\0", "\0"); - - check_flag_process(); - - cgutil_opt.cflag = 0; - cgutil_opt.dflag = 1; - sret = snprintf_s(cgutil_opt.topname, sizeof(cgutil_opt.topname), sizeof(cgutil_opt.topname) - 1, "%s", "Gaussdb"); - securec_check_ss_c(sret, "\0", "\0"); - check_flag_process(); - - cgutil_opt.topname[0] = '\0'; - sret = snprintf_s(cgutil_opt.user, sizeof(cgutil_opt.user), sizeof(cgutil_opt.user) - 1, "%s", "root"); - securec_check_ss_c(sret, "\0", "\0"); - check_user_name(); - - sret = snprintf_s(cgutil_opt.user, sizeof(cgutil_opt.user), sizeof(cgutil_opt.user) - 1, "%s", "xxx"); - securec_check_ss_c(sret, "\0", "\0"); - check_user_name(); - - cgutil_opt.user[0] = '\0'; - check_and_get_group_percent(NULL, "abc"); - - cgexec_check_cpuset_value("1-2", "3-4"); - - cgexec_update_remain_cgroup_cpuset(1, "3-4", 1); - cgexec_update_remain_cgroup_cpuset(1, "3-4", 0); - cgexec_update_class_cpuset(1, "3-4"); - - cgexec_update_top_group_cpuset(TOPCG_ROOT, "3-4"); - cgexec_update_top_group_cpuset(TOPCG_GAUSSDB, "3-4"); - cgexec_update_top_group_cpuset(TOPCG_BACKEND, "3-4"); - cgexec_update_top_group_cpuset(TOPCG_CLASS, "3-4"); - cgexec_update_top_group_cpuset(-1, "3-4"); - - cgutil_opt.mpflag = 1; - cgexec_check_mount_for_upgrade(); - - cgutil_opt.mpflag = 0; - - cgutil_opt.cflag = 0; - - cgutil_opt.cflag = 1; - sret = snprintf_s( - cgutil_opt.mpoints[0], sizeof(cgutil_opt.mpoints[0]), sizeof(cgutil_opt.mpoints[0]) - 1, "%s", "/dev/abc"); - securec_check_ss_c(sret, "\0", "\0"); - - cgexec_umount_root_cgroup(); - - sret = memset_s(cgutil_vaddr[CLASSCG_START_ID]->except, - EXCEPT_ALL_KINDS * sizeof(except_data_t), - 0, - EXCEPT_ALL_KINDS * sizeof(except_data_t)); - securec_check_c(sret, "\0", "\0"); - - cgexec_create_groups(); - - sret = snprintf_s(cgutil_opt.clsname, sizeof(cgutil_opt.clsname), sizeof(cgutil_opt.clsname) - 1, "%s", "class2"); - securec_check_ss_c(sret, "\0", "\0"); - cgconf_set_class_group(31); - - sret = snprintf_s(cgutil_opt.clsname, sizeof(cgutil_opt.clsname), sizeof(cgutil_opt.clsname) - 1, "%s", "class3"); - securec_check_ss_c(sret, "\0", "\0"); - cgconf_set_class_group(32); - - sret = snprintf_s(cgutil_opt.clsname, sizeof(cgutil_opt.clsname), sizeof(cgutil_opt.clsname) - 1, "%s", "class4"); - securec_check_ss_c(sret, "\0", "\0"); - cgconf_set_class_group(33); - - sret = snprintf_s(cgutil_vaddr[TOPCG_CLASS]->cpuset, CPUSET_LEN, CPUSET_LEN - 1, "%s", "0-47"); - securec_check_ss_c(sret, "\0", "\0"); - - sret = snprintf_s(cgutil_vaddr[31]->cpuset, CPUSET_LEN, CPUSET_LEN - 1, "%s", "0-46"); - securec_check_ss_c(sret, "\0", "\0"); - sret = snprintf_s(cgutil_vaddr[32]->cpuset, CPUSET_LEN, CPUSET_LEN - 1, "%s", "0-30"); - securec_check_ss_c(sret, "\0", "\0"); - sret = snprintf_s(cgutil_vaddr[33]->cpuset, CPUSET_LEN, CPUSET_LEN - 1, "%s", "0-0"); - securec_check_ss_c(sret, "\0", "\0"); - - cgexec_update_fixed_config(TOPCG_CLASS, 0); - - cgconf_reset_class_group(31); - cgconf_reset_class_group(32); - cgconf_reset_class_group(33); -} -#endif + cgutil_opt.cf: + } \ No newline at end of file diff --git a/src/bin/gs_guc/cluster_config.cpp b/src/bin/gs_guc/cluster_config.cpp index 77b83c08e..c80d69ce1 100644 --- a/src/bin/gs_guc/cluster_config.cpp +++ b/src/bin/gs_guc/cluster_config.cpp @@ -1,48 +1,31 @@ -/* - * Portions Copyright (c) 2020 Huawei Technologies Co.,Ltd. - * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group +/***************************************************************************** + * cluster_config.cpp + * PDK工具的分析管理器接口。 * - * openGauss is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: + * 函数列表: find_gucoption_available + * freefile + * getnodename + * get_local_cordinator_dbpath + * get_local_datanode_dbpath + * get_local_dbpath_by_instancename + * get_local_gtmproxy_dbpath + * get_local_gtm_dbpath + * get_local_gtm_name + * get_local_gtm_proxy_name + * get_local_instancename_by_dbpath + * get_local_num_datanode + * get_nodeidx_by_name + * get_node_nodename + * get_num_nodes + * get_value_in_config_file + * init_gauss_cluster_config + * is_local_node + * is_local_nodeid + * readfile * - * http://license.coscl.org.cn/MulanPSL2 - * - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, - * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - *--------------------------------------------------------------------------------------- - * - * cluster_config.cpp - * Interfaces for analysis manager of PDK tool. - * - * Function List: find_gucoption_available - * freefile - * getnodename - * get_local_cordinator_dbpath - * get_local_datanode_dbpath - * get_local_dbpath_by_instancename - * get_local_gtmproxy_dbpath - * get_local_gtm_dbpath - * get_local_gtm_name - * get_local_gtm_proxy_name - * get_local_instancename_by_dbpath - * get_local_num_datanode - * get_nodeidx_by_name - * get_node_nodename - * get_num_nodes - * get_value_in_config_file - * init_gauss_cluster_config - * is_local_node - * is_local_nodeid - * readfile - * - * IDENTIFICATION + * 标识 * src/bin/gs_guc/cluster_config.cpp - * - * --------------------------------------------------------------------------------------- - */ + *****************************************************************************/ #include #include @@ -73,31 +56,29 @@ #define STD_FORMAT_ARG_POSITION 2 -extern char** cndn_param; -extern char** cmserver_param; -extern char** cmagent_param; -extern char** gtm_param; -extern char** lc_param; -extern char** cndn_guc_info; -extern char** cmserver_guc_info; -extern char** cmagent_guc_info; -extern char** gtm_guc_info; -extern char** lc_guc_info; -extern int cndn_param_number; -extern int cmserver_param_number; -extern int cmagent_param_number; -extern int gtm_param_number; -extern int lc_param_number; -extern uint32 g_local_dn_idx; -extern char* g_current_data_dir; +extern char** cndn_param; // 数据节点配置参数数组 +extern char** cmserver_param; // CMServer配置参数数组 +extern char** cmagent_param; // CMAgent配置参数数组 +extern char** gtm_param; // GTM配置参数数组 +extern char** lc_param; // 逻辑复制配置参数数组 +extern char** cndn_guc_info; // 数据节点GUC信息数组 +extern char** cmserver_guc_info; // CMServer GUC信息数组 +extern char** cmagent_guc_info; // CMAgent GUC信息数组 +extern char** gtm_guc_info; // GTM GUC信息数组 +extern char** lc_guc_info; // 逻辑复制 GUC信息数组 +extern int cndn_param_number; // 数据节点配置参数数量 +extern int cmserver_param_number; // CMServer配置参数数量 +extern int cmagent_param_number; // CMAgent配置参数数量 +extern int gtm_param_number; // GTM配置参数数量 +extern int lc_param_number; // 逻辑复制配置参数数量 +extern uint32 g_local_dn_idx; // 本地数据节点索引 +extern char* g_current_data_dir; // 当前数据目录 -const int g_min_ip_len = 7; // IPV4 and IPV6, choose the minimum length +const int g_min_ip_len = 7; // IPV4和IPV6,选择最小长度 #ifndef GS_COLLECTOR_BUILD -extern void write_stderr(const char* fmt, ...) - /* This extension allows gcc to check the format string for consistency with - the supplied arguments. */ - __attribute__((format(PG_PRINTF_ATTRIBUTE, 1, STD_FORMAT_ARG_POSITION))); +extern void write_stderr(const char* fmt, ...) // 输出错误信息 +__attribute__((format(PG_PRINTF_ATTRIBUTE, 1, STD_FORMAT_ARG_POSITION))); #else #define write_stderr printf #endif @@ -106,994 +87,1075 @@ extern void write_stderr(const char* fmt, ...) extern "C" { #endif /* __cplusplus */ -typedef enum { - INSTANCE_ANY, - INSTANCE_DATANODE, /* postgresql.conf */ - INSTANCE_COORDINATOR, /* postgresql.conf */ - INSTANCE_GTM, /* gtm.conf */ - INSTANCE_GTM_PROXY, /* gtm_proxy.conf */ - INSTANCE_CMAGENT, /* cm_agent.conf */ - INSTANCE_CMSERVER, /* cm_server.conf */ - INSTANCE_DATAINSTANCE, /* postgresql.conf */ -} NodeType; + typedef enum { + INSTANCE_ANY, + INSTANCE_DATANODE, /* postgresql.conf */ + INSTANCE_COORDINATOR, /* postgresql.conf */ + INSTANCE_GTM, /* gtm.conf */ + INSTANCE_GTM_PROXY, /* gtm_proxy.conf */ + INSTANCE_CMAGENT, /* cm_agent.conf */ + INSTANCE_CMSERVER, /* cm_server.conf */ + INSTANCE_DATAINSTANCE, /* postgresql.conf */ + } NodeType; // 节点类型枚举 -/* Define all the node types */ -typedef enum { - GUC_NONE = 0, - GUC_CNDN, - GUC_GTM, - GUC_CMSERVER, - GUC_CMAGENT, - GUC_LCNAME -} GUC_Node_Type; - -const int INVALID_LINES_IDX = -1; + /* 定义所有节点类型 */ + typedef enum { + GUC_NONE = 0, + GUC_CNDN, // 数据节点 + GUC_GTM, // GTM + GUC_CMSERVER, // CMServer + GUC_CMAGENT, // CMAgent + GUC_LCNAME // 逻辑复制 + } GUC_Node_Type; // GUC节点类型枚举 +// 定义一个常量,表示无效的行索引 + const int INVALID_LINES_IDX = -1; + // 定义一个宏,表示配置文件的名称 #define GUC_OPT_CONF_FILE "cluster_guc.conf" +// 定义一个常量,表示成功 #define SUCCESS 0 +// 定义一个常量,表示失败 #define FAILURE 1 -#define GS_FREE(ptr) \ - do { \ - if (NULL != (ptr)) { \ +// 定义一个宏,用于释放指针并将其置为空 +#define GS_FREE(ptr) \ + do { \ + if (NULL != (ptr)) { \ free((char*)(ptr)); \ - ptr = NULL; \ - } \ + ptr = NULL; \ + } \ } while (0) -int32 get_local_instancename_by_dbpath(const char* dbpath, char* instancename); -int32 get_local_gtm_name(char* instancename); -int get_value_in_config_file(const char* pg_config_file, const char* parameter_in_config, char* para_value); -int get_all_datanode_num(); -int get_all_coordinator_num(); -int get_all_cmserver_num(); -int get_all_cmagent_num(); -int get_all_cndn_num(); -int get_all_gtm_num(); -char* get_AZname_by_nodename(const char* nodename); -int find_gucoption_available( - const char** optlines, const char* opt_name, int* name_offset, int* name_len, int* value_offset, int* value_len); -char** readfile(const char* path, int reserve_num_lines); +// 函数:根据数据库路径获取本地实例名称 +// 输入参数:dbpath - 数据库路径,instancename - 实例名称 +// 返回值:int32类型,表示操作结果 + int32 get_local_instancename_by_dbpath(const char* dbpath, char* instancename); -void freefile(char** lines); + // 函数:获取本地gtm名称 + // 输入参数:instancename - 实例名称 + // 返回值:int32类型,表示操作结果 + int32 get_local_gtm_name(char* instancename); -extern bool get_env_value(const char* env_var, char* output_env_value, size_t env_var_value_len); -void* pg_malloc_memory(size_t size); -extern char* xstrdup(const char* s); -extern char* g_local_instance_path; -extern void check_env_value(const char* input_env_value); + // 函数:获取配置文件中的参数值 + // 输入参数:pg_config_file - 配置文件路径,parameter_in_config - 参数名,para_value - 参数值 + // 返回值:int类型,表示操作结果 + int get_value_in_config_file(const char* pg_config_file, const char* parameter_in_config, char* para_value); -/* - ****************************************************************************** - Function : get_local_num_datanode - Description : - Input : - Output : None - Return : None - ****************************************************************************** -*/ -uint32 get_local_num_datanode() -{ - return g_currentNode->datanodeCount; -} + // 函数:获取所有数据节点的数量 + // 返回值:int类型,表示数据节点数量 + int get_all_datanode_num(); -/* - ****************************************************************************** - Function : get_num_nodes - Description : - Input : - Output : None - Return : None - ****************************************************************************** -*/ -uint32 get_num_nodes() -{ - return g_node_num; -} + // 函数:获取所有协调器的数量 + // 返回值:int类型,表示协调器数量 + int get_all_coordinator_num(); -/* - ****************************************************************************** - Function : is_local_nodeid - Description : check the input node id is current node id - Input : nodeid - node id - Output : None - Return : None - ****************************************************************************** -*/ -bool is_local_nodeid(uint32 nodeid) -{ - return (g_currentNode->node == nodeid); -} + // 函数:获取所有cmserver的数量 + // 返回值:int类型,表示cmserver数量 + int get_all_cmserver_num(); -#ifdef GS_COLLECTOR_BUILD -staticNodeConfig* get_node_nodename(char* name); + // 函数:获取所有cmagent的数量 + // 返回值:int类型,表示cmagent数量 + int get_all_cmagent_num(); -/* - ****************************************************************************** - Function : get_node_nodename - Description : - Input : nodename - - Output : None - Return : None - ****************************************************************************** -*/ -staticNodeConfig* get_node_nodename(char* nodename) -{ - uint32 nodeidx = 0; - for (nodeidx = 0; nodeidx < g_node_num; nodeidx++) { - if (0 == strncmp(g_node[nodeidx].nodeName, nodename, CM_NODE_NAME_LEN)) { - return &g_node[nodeidx]; - } + // 函数:获取所有cndn的数量 + // 返回值:int类型,表示cndn数量 + int get_all_cndn_num(); + + // 函数:获取所有gtm的数量 + // 返回值:int类型,表示gtm数量 + int get_all_gtm_num(); + + // 函数:根据节点名称获取AZ的名称 + // 输入参数:nodename - 节点名称 + // 返回值:char*类型,表示AZ的名称 + char* get_AZname_by_nodename(const char* nodename); + + // 函数:在配置文件中查找指定选项可用的行 + // 输入参数:optlines - 配置文件行,opt_name - 选项名,name_offset - 名称偏移量,name_len - 名称长度, + // value_offset - 值偏移量,value_len - 值长度 + // 返回值:int类型,表示操作结果 + int find_gucoption_available( + const char** optlines, const char* opt_name, int* name_offset, int* name_len, int* value_offset, int* value_len); + + // 函数:读取文件内容 + // 输入参数:path - 文件路径,reserve_num_lines - 保留的行数 + // 返回值:char**类型,表示文件的行内容 + char** readfile(const char* path, int reserve_num_lines); + + // 函数:释放文件内容 + // 输入参数:lines - 文件的行内容 + void freefile(char** lines); + + // 函数:获取环境变量的值 + // 输入参数:env_var - 环境变量名,output_env_value - 输出的环境变量值,env_var_value_len - 环境变量值的长度 + // 返回值:bool类型,表示操作结果 + extern bool get_env_value(const char* env_var, char* output_env_value, size_t env_var_value_len); + + // 函数:申请内存空间 + // 输入参数:size - 内存大小 + // 返回值:void*类型,表示申请的内存空间的指针 + void* pg_malloc_memory(size_t size); + + // 函数:复制字符串 + // 输入参数:s - 原始字符串 + // 返回值:char*类型,表示复制后的字符串 + extern char* xstrdup(const char* s); + + // 外部变量:本地实例路径 + extern char* g_local_instance_path; + + // 函数:检查环境变量的值 + // 输入参数:input_env_value - 输入的环境变量值 + extern void check_env_value(const char* input_env_value); + + /* + * 函数:获取本地数据节点数 + * 描述:返回当前节点的数据节点数 + * 输入:无 + * 输出:无 + * 返回:uint32类型,表示数据节点数 + */ + uint32 get_local_num_datanode() + { + return g_currentNode->datanodeCount; } - return NULL; -} + /* + * 函数:获取节点数 + * 描述:返回集群中所有节点的数量 + * 输入:无 + * 输出:无 + * 返回:uint32类型,表示节点数 + */ + uint32 get_num_nodes() + { + return g_node_num; + } + + /* + * 函数:判断输入的节点id是否是当前节点id + * 描述:检查输入的节点id是否等于当前节点id + * 输入:nodeid - 节点id + * 输出:无 + * 返回:bool类型,表示输入的节点id是否是当前节点id + */ + bool is_local_nodeid(uint32 nodeid) + { + return (g_currentNode->node == nodeid); + } + +#ifdef GS_COLLECTOR_BUILD + // 函数:根据节点名称获取节点配置信息 + // 输入参数:name - 节点名称 + // 返回值:staticNodeConfig*指针,表示节点配置信息 + staticNodeConfig* get_node_nodename(char* name); + + /* + * 函数:根据节点名称获取节点配置信息 + * 输入:nodename - 节点名称 + * 输出:无 + * 返回:staticNodeConfig*指针,表示节点配置信息 + */ + staticNodeConfig* get_node_nodename(char* nodename) + { + uint32 nodeidx = 0; + for (nodeidx = 0; nodeidx < g_node_num; nodeidx++) { + if (0 == strncmp(g_node[nodeidx].nodeName, nodename, CM_NODE_NAME_LEN)) { + return &g_node[nodeidx]; + } + } + + return NULL; + } #endif -/* - ****************************************************************************** - Function : get_nodeidx_by_name - Description : - Input : nodename - node name - Output : None - Return : uint32 - node id index - ****************************************************************************** -*/ -int32 get_nodeidx_by_name(const char* nodename) -{ - uint32 nodeidx = 0; - for (nodeidx = 0; nodeidx < get_num_nodes(); nodeidx++) { - if (0 == strncmp(g_node[nodeidx].nodeName, nodename, CM_NODE_NAME_LEN)) { - return (int32)nodeidx; + /* + ****************************************************************************** + Function : get_nodeidx_by_name + Description : 根据节点名称获取节点id索引 + Input : nodename - 节点名称 + Output : None + Return : uint32 - 节点id索引 + ****************************************************************************** + */ + int32 get_nodeidx_by_name(const char* nodename) + { + uint32 nodeidx = 0; + for (nodeidx = 0; nodeidx < get_num_nodes(); nodeidx++) { // 循环遍历所有节点 + if (0 == strncmp(g_node[nodeidx].nodeName, nodename, CM_NODE_NAME_LEN)) { // 判断节点名称是否匹配 + return (int32)nodeidx; + } } + + return -1; // 如果未找到对应的节点名称,返回-1 } - return -1; -} -/* - ****************************************************************************** - Function : get_all_datanode_num - Description : get all datanode instance number - ****************************************************************************** -*/ -int get_all_datanode_num() -{ - uint32 nodeidx = 0; - int count = 0; - for (nodeidx = 0; nodeidx < get_num_nodes(); nodeidx++) { - count += (int)g_node[nodeidx].datanodeCount; - } - return count; -} -/* - ****************************************************************************** - Function : get_all_coordinator_num - Description : get all coordinator instance number - ****************************************************************************** -*/ -int get_all_coordinator_num() -{ - uint32 nodeidx = 0; - int count = 0; - for (nodeidx = 0; nodeidx < get_num_nodes(); nodeidx++) { - if (1 == g_node[nodeidx].coordinate) { - count += 1; + /* + ****************************************************************************** + Function : get_all_datanode_num + Description : 获取所有数据节点实例数量 + ****************************************************************************** + */ + int get_all_datanode_num() + { + uint32 nodeidx = 0; + int count = 0; + for (nodeidx = 0; nodeidx < get_num_nodes(); nodeidx++) { // 循环遍历所有节点 + count += (int)g_node[nodeidx].datanodeCount; // 累加数据节点实例数量 } + return count; } - return count; -} -/* - ****************************************************************************** - Function : get_all_cmserver_num - Description : get all cm_server instance number - ****************************************************************************** -*/ -int get_all_cmserver_num() -{ - uint32 nodeidx; - int count = 0; - for (nodeidx = 0; nodeidx < get_num_nodes(); nodeidx++) { - if (1 == g_node[nodeidx].cmServerLevel && g_node[nodeidx].cmDataPath[0] != '\0') { - count += 1; + + /* + ****************************************************************************** + Function : get_all_coordinator_num + Description : 获取所有协调器实例数量 + ****************************************************************************** + */ + int get_all_coordinator_num() + { + uint32 nodeidx = 0; + int count = 0; + for (nodeidx = 0; nodeidx < get_num_nodes(); nodeidx++) { // 循环遍历所有节点 + if (1 == g_node[nodeidx].coordinate) { // 判断节点是否为协调器 + count += 1; + } } + return count; } - return count; -} -/* - ****************************************************************************** - Function : get_all_cmagent_num - Description : get all cm_agent instance number - ****************************************************************************** -*/ -int get_all_cmagent_num() -{ - return get_num_nodes(); -} - -/* - ****************************************************************************** - Function : get_all_cndn_num - Description : get all CN and DN instance number - ****************************************************************************** -*/ -int get_all_cndn_num() -{ - int count = 0; - count = get_all_datanode_num() + get_all_coordinator_num(); - - return count; -} - -int get_all_gtm_num() -{ - uint32 nodeidx = 0; - int count = 0; - for (nodeidx = 0; nodeidx < get_num_nodes(); nodeidx++) { - if (1 == g_node[nodeidx].gtm && g_node[nodeidx].gtmLocalDataPath[0] != '\0') { - count += 1; + /* + ****************************************************************************** + Function : get_all_cmserver_num + Description : 获取所有CM服务器实例数量 + ****************************************************************************** + */ + int get_all_cmserver_num() + { + uint32 nodeidx; + int count = 0; + for (nodeidx = 0; nodeidx < get_num_nodes(); nodeidx++) { // 循环遍历所有节点 + if (1 == g_node[nodeidx].cmServerLevel && g_node[nodeidx].cmDataPath[0] != '\0') { // 判断节点是否为CM服务器 + count += 1; + } } - } - return count; -} -/* - ****************************************************************************** - Function : is_local_node - Description : - Input : nodename - - Output : None - Return : None - ****************************************************************************** -*/ -bool is_local_node(const char* nodename) -{ - return (0 == strncmp(g_currentNode->nodeName, nodename, CM_NODE_NAME_LEN)); -} - -/* - ****************************************************************************** - Function : getnodename - Description : get node name by node id index - Input : nodeidx - node id index - Output : None - Return : char * - node name - ****************************************************************************** -*/ -char* getnodename(uint32 nodeidx) -{ - return g_node[nodeidx].nodeName; -} - -/* - ****************************************************************************** - Function : get_hostname_or_ip - Description : if In agent mode, there is an environment variable HOST_IP writen in /etc/profile, - then get host ip from environment variables. - Else, get hostname for adaptation to the previous version. - Input : name_len - the length of ip or hostname - Output : out_name - the host ip get from environment variables or the hostname - Return : bool - ****************************************************************************** -*/ -bool get_hostname_or_ip(char* out_name, size_t name_len) -{ - int rc = 0; - char* env_value = NULL; - - if (out_name == NULL) { - (void)write_stderr("ERROR: Get NULL point from upper function when get hostip or hostname.\n"); - return false; + return count; } - env_value = gs_getenv_r("HOST_IP"); - if (env_value != NULL) { - check_env_value(env_value); + /* + ****************************************************************************** + Function : get_all_cmagent_num + Description : 获取所有CM代理实例数量 + ****************************************************************************** + */ + int get_all_cmagent_num() + { + return get_num_nodes(); // 直接返回节点数量作为CM代理实例数量 } - if ((env_value == NULL) || (env_value[0] == '\0')) { - (void)gethostname(out_name, name_len); - if (out_name[0] == '\0') { - return false; + /* + ****************************************************************************** + Function : get_all_cndn_num + Description : 获取所有CN和DN实例数量 + ****************************************************************************** + */ + int get_all_cndn_num() + { + int count = 0; + count = get_all_datanode_num() + get_all_coordinator_num(); // 数据节点实例数量加上协调器实例数量 + + return count; + } + + /* + ****************************************************************************** + Function : get_all_gtm_num + Description : 获取所有GTM实例数量 + ****************************************************************************** + */ + int get_all_gtm_num() + { + uint32 nodeidx = 0; + int count = 0; + for (nodeidx = 0; nodeidx < get_num_nodes(); nodeidx++) { // 循环遍历所有节点 + if (1 == g_node[nodeidx].gtm && g_node[nodeidx].gtmLocalDataPath[0] != '\0') { // 判断节点是否为GTM + count += 1; + } } - } else { - if (strlen(env_value) >= name_len) { - (void)write_stderr("ERROR: The value of environment variable HOST_IP is too long.\n"); + return count; + } + /* + ******************************************************* + * Function : is_local_node + * Description : 判断给定的节点名是否为当前节点的节点名 + * Input : nodename - 节点名 + * Output : None + * Return : bool - 是否为当前节点的节点名 + ******************************************************* + */ + bool is_local_node(const char* nodename) + { + return (0 == strncmp(g_currentNode->nodeName, nodename, CM_NODE_NAME_LEN)); + } + + /* + ******************************************************* + * Function : getnodename + * Description : 根据节点ID索引获取节点名 + * Input : nodeidx - 节点ID索引 + * Output : None + * Return : char* - 节点名 + ******************************************************* + */ + char* getnodename(uint32 nodeidx) + { + return g_node[nodeidx].nodeName; + } + + /* + ******************************************************* + * Function : get_hostname_or_ip + * Description : 获取主机名或IP地址 + * 如果处于Agent模式,则从/etc/profile中获取HOST_IP环境变量的值作为主机IP。 + * 否则,根据适配性获取主机名。 + * Input : name_len - IP地址或主机名的长度 + * Output : out_name - 从环境变量获取的主机IP或主机名 + * Return : bool - 是否成功获取主机名或IP地址 + ******************************************************* + */ + bool get_hostname_or_ip(char* out_name, size_t name_len) + { + int rc = 0; + char* env_value = NULL; + + if (out_name == NULL) { + (void)write_stderr("ERROR: Get NULL point from upper function when get hostip or hostname.\n"); return false; } - if (strlen(env_value) < g_min_ip_len) { - (void)write_stderr("ERROR: The value of environment variable HOST_IP is too short.\n"); + env_value = gs_getenv_r("HOST_IP"); + if (env_value != NULL) { + check_env_value(env_value); + } + + if ((env_value == NULL) || (env_value[0] == '\0')) { + (void)gethostname(out_name, name_len); + if (out_name[0] == '\0') { + return false; + } + } + else { + if (strlen(env_value) >= name_len) { + (void)write_stderr("ERROR: The value of environment variable HOST_IP is too long.\n"); + return false; + } + + if (strlen(env_value) < g_min_ip_len) { + (void)write_stderr("ERROR: The value of environment variable HOST_IP is too short.\n"); + return false; + } + + rc = strcpy_s(out_name, name_len, env_value); + securec_check_c(rc, "\0", "\0"); + } + return true; + } + + /* + ******************************************************* + * Function : get_backIps_by_nodename + * Description : 根据节点名获取后端IP + * Input : nodename - 节点名 + * Output : ipAddress - 后端IP地址 + * Return : ipAddress - 后端IP地址 + ******************************************************* + */ + char* get_backIps_by_nodename(const char* nodename) + { + uint32 nodeidx = 0; + char* ipAddress = NULL; + for (nodeidx = 0; nodeidx < g_node_num; nodeidx++) { + if (strcmp(g_node[nodeidx].nodeName, nodename) == 0) { + // 根据cluster_static_config的值进行赋值 + ipAddress = xstrdup(g_node[nodeidx].backIps[0]); + } + } + return ipAddress; + } + + /* + ******************************************************* + * Function : is_instance_in_nodename + * Description : 检查实例是否存在于给定的节点中 + * Input : nodename - 节点名 + * Output : bool - 是否存在实例于给定的节点中 + ******************************************************* + */ + bool is_instance_in_nodename(const char* nodename) + { + uint32 i; + char* backIp = NULL; + + backIp = get_backIps_by_nodename(nodename); + if (NULL == backIp) { return false; } - rc = strcpy_s(out_name, name_len, env_value); - securec_check_c(rc, "\0", "\0"); - } - return true; -} - -/* - ****************************************************************************** - Function : get_backIps_by_nodename - Description : get back ip by node name, - Input : nodename - - Output : ipAddress - Return : ipAddress - ****************************************************************************** -*/ -char* get_backIps_by_nodename(const char* nodename) -{ - uint32 nodeidx = 0; - char* ipAddress = NULL; - for (nodeidx = 0; nodeidx < g_node_num; nodeidx++) { - if (strcmp(g_node[nodeidx].nodeName, nodename) == 0) { - // Assignment by cluster_static_config value - ipAddress = xstrdup(g_node[nodeidx].backIps[0]); - } - } - return ipAddress; -} - -/* - ****************************************************************************** - Function : is_instance_in_nodename - Description : check is the instance in node - Input : nodename - - Output : bool - ****************************************************************************** -*/ -bool is_instance_in_nodename(const char* nodename) -{ - uint32 i; - char* backIp = NULL; - - backIp = get_backIps_by_nodename(nodename); - if (NULL == backIp) { - return false; - } - - for (i = 0; i < g_currentNode->datanodeCount; i++) { - if (strcmp(g_currentNode->datanode[i].datanodeLocalDataPath, g_local_instance_path) == 0) { - for (uint32 dnId = 0; dnId < CM_MAX_DATANODE_STANDBY_NUM; dnId++) { - if (strcmp(backIp, g_currentNode->datanode[i].peerDatanodes[dnId].datanodePeerHAIP[0]) == 0) { - GS_FREE(backIp); - return true; + for (i = 0; i < g_currentNode->datanodeCount; i++) { + if (strcmp(g_currentNode->datanode[i].datanodeLocalDataPath, g_local_instance_path) == 0) { + for (uint32 dnId = 0; dnId < CM_MAX_DATANODE_STANDBY_NUM; dnId++) { + if (strcmp(backIp, g_currentNode->datanode[i].peerDatanodes[dnId].datanodePeerHAIP[0]) == 0) { + GS_FREE(backIp); + return true; + } } } } + GS_FREE(backIp); + return false; } - GS_FREE(backIp); - return false; -} -/* - ****************************************************************************** - Function : get_AZname_by_nodename - Description : get az name list by node name, - Input : nodename - - Output : azname - Return : azname - ****************************************************************************** -*/ -char* get_AZname_by_nodename(const char* nodename) -{ - uint32 nodeidx; - char* azName = NULL; - for (nodeidx = 0; nodeidx < g_node_num; nodeidx++) { - if (strcmp(g_node[nodeidx].nodeName, nodename) == 0) { - // Assignment by cluster_static_config value - azName = xstrdup(g_node[nodeidx].azName); + /* + ****************************************************************************** + Function : get_AZname_by_nodename + Description : 根据节点名获取 AZ 名字列表 + Input : nodename - 节点名 + Output : azname + Return : azname + 函数 get_AZname_by_nodename 是根据节点名获取 AZ 名字列表的功能。 + 包含的变量有: + - nodename:输入参数,节点名 + + 该函数遍历 g_node 数组,通过比较节点名找到对应的节点,然后将该节点的 azName 字段赋值给 azName 变量。 + + 举例说明:假设 g_node 数组中有以下节点信息: + [{nodeName: "node1", azName : "AZ1"}, { nodeName: "node2", azName : "AZ2" }, { nodeName: "node3", azName : "AZ3" }] + 调用 get_AZname_by_nodename("node2"),返回 "AZ2"。 + + ****************************************************************************** + */ + char* get_AZname_by_nodename(const char* nodename) + { + uint32 nodeidx; + char* azName = NULL; + for (nodeidx = 0; nodeidx < g_node_num; nodeidx++) { + if (strcmp(g_node[nodeidx].nodeName, nodename) == 0) { + // 根据 cluster_static_config 值进行赋值 + azName = xstrdup(g_node[nodeidx].azName); + } } + return azName; } - return azName; -} -/* - ****************************************************************************** - Function : get_local_dbpath_by_instancename - Description : get the instance directory where the instance is located by instance name - Input : instancename - the instance name - type - The value of the -Z parameter - dbpath - Instance of the path - Output : None - Return : None - ****************************************************************************** -*/ -int32 get_local_dbpath_by_instancename(const char* instancename, const int* type, char* dbpath) -{ - uint32 i; - char local_inst_name[CM_NODE_NAME_LEN] = {0}; - int32 retval; - errno_t rc = 0; + /* + ****************************************************************************** + Function : get_local_dbpath_by_instancename + Description : 根据实例名获取实例所在的目录 + Input : instancename - 实例名 + type - -Z 参数的值 + dbpath - 实例的路径 + Output : None + Return : None + 函数 get_local_dbpath_by_instancename 是根据实例名获取实例所在的目录的功能。 + 包含的变量有: + - instancename:输入参数,实例名 + - type:输入参数, - Z 参数的值 + - dbpath:输出参数,实例的路径 - if ((*type == INSTANCE_ANY) || (*type == INSTANCE_COORDINATOR)) { - if ('\0' != g_currentNode->DataPath[0]) { - retval = get_local_instancename_by_dbpath(g_currentNode->DataPath, local_inst_name); + 该函数首先判断 type 的值,如果是 INSTANCE_ANY 或 INSTANCE_COORDINATOR,则处理协调器节点的情况。 + 如果 g_currentNode->DataPath 不为空,则调用 get_local_instancename_by_dbpath 函数, + 检查实例名是否与 instancename 相同,如果相同,则将 g_currentNode->DataPath 的值赋给 dbpath 变量,并返回 CLUSTER_CONFIG_SUCCESS。 + + 如果 type 的值是 INSTANCE_ANY 或 INSTANCE_DATANODE,则处理数据节点的情况。遍历 g_currentNode->datanode 数组, + 对每个数据节点的 datanodeLocalDataPath 字段调用 get_local_instancename_by_dbpath 函数,检查实例名是否与 instancename 相同, + 如果相同,则将对应的 datanodeLocalDataPath 的值赋给 dbpath 变量,并返回 CLUSTER_CONFIG_SUCCESS。 + + 如果 type 的值是 INSTANCE_ANY 或 INSTANCE_GTM,则处理 GTM 节点的情况。调用 get_local_gtm_name 函数, + 检查实例名是否与 instancename 相同,如果相同,则将 g_currentNode->gtmLocalDataPath 的值赋给 dbpath 变量,并返回 CLUSTER_CONFIG_SUCCESS。 + + 如果以上情况都不满足,返回 0。 + + 举例说明:假设 g_currentNode 中有以下信息: + - DataPath: "/data" + - datanodeCount : 2 + - datanode : [ + {datanodeLocalDataPath: "/data/dn1"}, + { datanodeLocalDataPath: "/data/dn2" } + ] + 调用 get_local_dbpath_by_instancename("dn2", INSTANCE_DATANODE, dbpath),返回 "/data/dn2"。 + ****************************************************************************** + */ + int32 get_local_dbpath_by_instancename(const char* instancename, const int* type, char* dbpath) + { + uint32 i; + char local_inst_name[CM_NODE_NAME_LEN] = { 0 }; + int32 retval; + errno_t rc = 0; + + if ((*type == INSTANCE_ANY) || (*type == INSTANCE_COORDINATOR)) { + if ('\0' != g_currentNode->DataPath[0]) { + retval = get_local_instancename_by_dbpath(g_currentNode->DataPath, local_inst_name); + if ((retval == CLUSTER_CONFIG_SUCCESS) && (0 == strncmp(local_inst_name, instancename, CM_NODE_NAME_LEN))) { + rc = memcpy_s(dbpath, CM_PATH_LENGTH, g_currentNode->DataPath, CM_PATH_LENGTH); + securec_check_c(rc, "\0", "\0"); + return CLUSTER_CONFIG_SUCCESS; + } + } + } + + if ((*type == INSTANCE_ANY) || (*type == INSTANCE_DATANODE)) { + for (i = 0; i < g_currentNode->datanodeCount; i++) { + retval = + get_local_instancename_by_dbpath(g_currentNode->datanode[i].datanodeLocalDataPath, local_inst_name); + if ((retval == CLUSTER_CONFIG_SUCCESS) && (0 == strncmp(local_inst_name, instancename, CM_NODE_NAME_LEN))) { + rc = memcpy_s(dbpath, CM_PATH_LENGTH, g_currentNode->datanode[i].datanodeLocalDataPath, CM_PATH_LENGTH); + securec_check_c(rc, "\0", "\0"); + return CLUSTER_CONFIG_SUCCESS; + } + } + } + + if ((*type == INSTANCE_ANY) || (*type == INSTANCE_GTM)) { + retval = get_local_gtm_name(local_inst_name); if ((retval == CLUSTER_CONFIG_SUCCESS) && (0 == strncmp(local_inst_name, instancename, CM_NODE_NAME_LEN))) { - rc = memcpy_s(dbpath, CM_PATH_LENGTH, g_currentNode->DataPath, CM_PATH_LENGTH); + rc = memcpy_s(dbpath, CM_PATH_LENGTH, g_currentNode->gtmLocalDataPath, CM_PATH_LENGTH); securec_check_c(rc, "\0", "\0"); return CLUSTER_CONFIG_SUCCESS; } } - } - - if ((*type == INSTANCE_ANY) || (*type == INSTANCE_DATANODE)) { - for (i = 0; i < g_currentNode->datanodeCount; i++) { - retval = - get_local_instancename_by_dbpath(g_currentNode->datanode[i].datanodeLocalDataPath, local_inst_name); - if ((retval == CLUSTER_CONFIG_SUCCESS) && (0 == strncmp(local_inst_name, instancename, CM_NODE_NAME_LEN))) { - rc = memcpy_s(dbpath, CM_PATH_LENGTH, g_currentNode->datanode[i].datanodeLocalDataPath, CM_PATH_LENGTH); - securec_check_c(rc, "\0", "\0"); - return CLUSTER_CONFIG_SUCCESS; - } - } - } - - if ((*type == INSTANCE_ANY) || (*type == INSTANCE_GTM)) { - retval = get_local_gtm_name(local_inst_name); - if ((retval == CLUSTER_CONFIG_SUCCESS) && (0 == strncmp(local_inst_name, instancename, CM_NODE_NAME_LEN))) { - rc = memcpy_s(dbpath, CM_PATH_LENGTH, g_currentNode->gtmLocalDataPath, CM_PATH_LENGTH); - securec_check_c(rc, "\0", "\0"); - return CLUSTER_CONFIG_SUCCESS; - } - } - - return CLUSTER_CONFIG_ERROR; -} - -/* - ****************************************************************************** - Function : get_local_instancename_by_dbpath - Description : - Input : dbpath - the data path of instance - instancename - the instance name, such as: dn_6002_6003 - Output : None - Return : None - ****************************************************************************** -*/ -int32 get_local_instancename_by_dbpath(const char* dbpath, char* instancename) -{ - char name[MAX_VALUE_LEN] = ""; - int retval; - char pg_config_file[MAXPGPATH] = {0}; - int nRet; - errno_t rc; - - nRet = snprintf_s(pg_config_file, MAXPGPATH, MAXPGPATH - 1, "%s/postgresql.conf", dbpath); - securec_check_ss_c(nRet, "\0", "\0"); - - retval = get_value_in_config_file(pg_config_file, "pgxc_node_name", name); - if (0 == retval) { - rc = strncpy_s(instancename, CM_NODE_NAME_LEN, name, CM_NODE_NAME_LEN - 1); - securec_check_c(rc, "\0", "\0"); - instancename[CM_NODE_NAME_LEN - 1] = '\0'; - return CLUSTER_CONFIG_SUCCESS; - } - - instancename[0] = '\0'; - - return CLUSTER_CONFIG_ERROR; -} - -/* - ****************************************************************************** - Function : get_local_gtm_name - Description : get the name of gtm from the configuration file---gtm.conf. - Find the parameter "nodename" in the file "gtm.conf" to get its corresponding parameter value - name---"one" - Input : instancename -the instance name - Output : None - Return : None - ****************************************************************************** -*/ -int32 get_local_gtm_name(char* instancename) -{ - char name[MAX_VALUE_LEN] = ""; - int retval; - char pg_config_file[MAXPGPATH] = {0}; - int nRet; - errno_t rc; - - if (g_currentNode->gtmId == 0) { - return CLUSTER_CONFIG_ERROR; - } - - nRet = snprintf_s(pg_config_file, MAXPGPATH, MAXPGPATH - 1, "%s/gtm.conf", g_currentNode->gtmLocalDataPath); - securec_check_ss_c(nRet, "\0", "\0"); - - /* Retrieves the parameter values for the specified parameters from the configuration file */ - retval = get_value_in_config_file(pg_config_file, "nodename", name); - if (0 == retval) { - rc = strncpy_s(instancename, CM_NODE_NAME_LEN, name, CM_NODE_NAME_LEN - 1); - securec_check_c(rc, "\0", "\0"); - return CLUSTER_CONFIG_SUCCESS; - } - - return CLUSTER_CONFIG_ERROR; -} - -/* - ****************************************************************************** - Function : init_gauss_cluster_config - Description : Obtain cluster information from cluster_static_config - Input : None - Output : void - Return : None - ****************************************************************************** -*/ -int init_gauss_cluster_config(void) -{ - char path[MAXPGPATH] = {0}; - char gausshome[MAXPGPATH] = {0}; - int err_no = 0; - int nRet = 0; - int status = 0; - uint32 nodeidx = 0; - struct stat statbuf {}; - - static bool is_init = false; - if (is_init) { return 0; } - is_init = true; - g_dn_replication_num = 0; + /* + ****************************************************************************** + Function : init_gauss_cluster_config + Description : 从集群静态配置文件中获取集群信息 + Input : None + Output : void + Return : None + ****************************************************************************** + */ + int init_gauss_cluster_config(void) + { + char path[MAXPGPATH] = { 0 }; // 存储静态配置文件路径的字符串数组 + char gausshome[MAXPGPATH] = { 0 }; // 存储GAUSSHOME环境变量值的字符串数组 + int err_no = 0; // 存储错误码的整型变量 + int nRet = 0; // 存储返回值的整型变量 + int status = 0; // 存储状态码的整型变量 + uint32 nodeidx = 0; // 存储节点索引的无符号整型变量 + struct stat statbuf {}; // 存储文件stat信息的结构体 - if (!get_env_value("GAUSSHOME", gausshome, sizeof(gausshome) / sizeof(char))) - return 1; + static bool is_init = false; // 静态变量,用于标记初始化状态 + if (is_init) { + return 0; // 如果已经初始化过了,则直接返回 + } + is_init = true; // 将初始化状态设置为true + g_dn_replication_num = 0; // 将g_dn_replication_num变量初始化为0 - check_env_value(gausshome); - if (NULL != g_lcname) { - nRet = snprintf_s(path, MAXPGPATH, MAXPGPATH - 1, "%s/bin/%s.%s", gausshome, g_lcname, STATIC_CONFIG_FILE); - } else { - nRet = snprintf_s(path, MAXPGPATH, MAXPGPATH - 1, "%s/bin/%s", gausshome, STATIC_CONFIG_FILE); - } - securec_check_ss_c(nRet, "\0", "\0"); + if (!get_env_value("GAUSSHOME", gausshome, sizeof(gausshome) / sizeof(char))) // 获取GAUSSHOME环境变量的值 + return 1; // 如果获取失败,则返回1 - if (checkPath(path) != 0) { - write_stderr(_("realpath(%s) failed : %s!\n"), path, strerror(errno)); - return 1; - } + check_env_value(gausshome); // 检查环境变量的合法性 + if (NULL != g_lcname) { // 如果g_lcname变量不为空 + nRet = snprintf_s(path, MAXPGPATH, MAXPGPATH - 1, "%s/bin/%s.%s", gausshome, g_lcname, STATIC_CONFIG_FILE); // 构建静态配置文件的路径 + } + else { // 如果g_lcname变量为空 + nRet = snprintf_s(path, MAXPGPATH, MAXPGPATH - 1, "%s/bin/%s", gausshome, STATIC_CONFIG_FILE); // 构建静态配置文件的路径 + } + securec_check_ss_c(nRet, "\0", "\0"); // 检查字符串格式化的返回值 - if (lstat(path, &statbuf) != 0) { - write_stderr("ERROR: could not stat file \"%s\": %s\n", path, strerror(errno)); - return 1; - } + if (checkPath(path) != 0) { // 检查路径是否存在 + write_stderr(_("realpath(%s) failed : %s!\n"), path, strerror(errno)); // 打印错误信息 + return 1; // 返回1表示失败 + } - if (NULL != g_lcname) { - status = read_lc_config_file(path, &err_no); - } else { - status = read_config_file(path, &err_no); - } - if (0 != status) { - switch (status) { + if (lstat(path, &statbuf) != 0) { // 获取文件的stat信息 + write_stderr("ERROR: could not stat file \"%s\": %s\n", path, strerror(errno)); // 打印错误信息 + return 1; // 返回1表示失败 + } + + if (NULL != g_lcname) { // 如果g_lcname变量不为空 + status = read_lc_config_file(path, &err_no); // 读取本地配置文件 + } + else { // 如果g_lcname变量为空 + status = read_config_file(path, &err_no); // 读取配置文件 + } + if (0 != status) { // 如果读取配置文件失败 + switch (status) { case OPEN_FILE_ERROR: { - write_stderr("ERROR: The cluster_staic_config file is not generated or is manually deleted.\n"); - return 1; + write_stderr("ERROR: The cluster_staic_config file is not generated or is manually deleted.\n"); // 打印错误信息 + return 1; // 返回1表示失败 } case READ_FILE_ERROR: { - write_stderr("ERROR: The cluster_staic_config file permission is insufficient.\n"); - return 1; + write_stderr("ERROR: The cluster_staic_config file permission is insufficient.\n"); // 打印错误信息 + return 1; // 返回1表示失败 } case OUT_OF_MEMORY: { - write_stderr("ERROR: The cluster_staic_config open failed cause out of memeory.\n"); - return 1; + write_stderr("ERROR: The cluster_staic_config open failed cause out of memeory.\n"); // 打印错误信息 + return 1; // 返回1表示失败 } default: break; + } + write_stderr("ERROR: Invalid return value from read_config_file\n"); // 打印错误信息 + return 1; // 返回1表示失败 } - write_stderr("ERROR: Invalid return value from read_config_file\n"); - return 1; - } - if (g_nodeHeader.node <= 0) { - write_stderr("ERROR: Invalid cluster_staic_config file," - " curerent node id is:%d .\n", - (int32)g_nodeHeader.node); - GS_FREE(g_node); - return 1; - } - - for (nodeidx = 0; nodeidx < g_node_num; nodeidx++) { - if (g_node[nodeidx].node == g_nodeHeader.node) { - g_currentNode = &g_node[nodeidx]; + if (g_nodeHeader.node <= 0) { // 如果节点id小于等于0 + write_stderr("ERROR: Invalid cluster_staic_config file, curerent node id is:%d .\n", (int32)g_nodeHeader.node); // 打印错误信息 + GS_FREE(g_node); // 释放内存 + return 1; // 返回1表示失败 } + + for (nodeidx = 0; nodeidx < g_node_num; nodeidx++) { // 遍历节点数组 + if (g_node[nodeidx].node == g_nodeHeader.node) { // 找到当前节点 + g_currentNode = &g_node[nodeidx]; // 设置当前节点指针 + } + } + + if (NULL == g_currentNode) { // 如果当前节点为空指针 + write_stderr("ERROR: failed to find current node by nodeid, curerent node id is:%d .\n", (int32)g_nodeHeader.node); // 打印错误信息 + GS_FREE(g_node); // 释放内存 + return 1; // 返回1表示失败 + } + + if (get_dynamic_dn_role() != 0) { // 获取动态数据节点角色 + write_stderr("ERROR: failed to get dynamic dn role.\n"); // 打印错误信息 + GS_FREE(g_node); // 释放内存 + return 1; // 返回1表示失败 + } + + return 0; // 返回0表示成功 } - if (NULL == g_currentNode) { - write_stderr("ERROR: failed to find current node by nodeid, curerent node id is:%d .\n", (int32)g_nodeHeader.node); - GS_FREE(g_node); - return 1; - } - - if (get_dynamic_dn_role() != 0) { - write_stderr("ERROR: failed to get dynamic dn role.\n"); - GS_FREE(g_node); - return 1; - } - - return 0; -} - + // 示例 + // 在数据库启动时,需要读取集群静态配置文件,以获取集群信息和节点配置信息。该函数实现了从静态配置文件中读取集群信息的功能, + // 并对读取的结果进行了校验和处理。在读取配置文件之前,需要先获取GAUSSHOME环境变量的值,并构建静态配置文件的路径。 + // 读取配置文件的结果可能有多种情况,分别对应不同的错误码,例如文件打开错误、文件读取错误、内存不足等。如果读取配置文件成功, + // 还需要判断读取到的节点id是否合法,并通过节点id在节点数组中找到当前节点的配置信息。最后,获取动态数据节点的角色。 + // 函数的返回值为0 /* * @@GaussDB@@ * Brief : save_guc_para_info() - * Description : get parameter of CN/DN/CMSERVER/CMAGENT from cluster_guc.conf file - * Notes : if it cann't open file, return NULL - * Input : the path of cluster_guc.conf file - * Output : the config parameter list of CN/DN/CMSERVER/CMAGENT + * Description : 从cluster_guc.conf文件中获取CN/DN/CMSERVER/CMAGENT的参数 + * Notes : 如果无法打开文件,则返回空指针 + * Input : cluster_guc.conf文件的路径 + * Output : CN/DN/CMSERVER/CMAGENT的配置参数列表 */ -int save_guc_para_info() -{ - int rc = 0; - errno_t ret; - FILE* fp = NULL; - char line_info[MAXPGPATH] = {0}; - char temp_line_info[MAXPGPATH] = {0}; - char* get_result = NULL; - char* outer_ptr = NULL; - GUC_Node_Type type = GUC_NONE; - char gausshome[MAXPGPATH] = {0}; - char guc_file[MAXPGPATH] = {0}; + int save_guc_para_info() + { + int rc = 0; + errno_t ret; + FILE* fp = NULL; + char line_info[MAXPGPATH] = { 0 }; // 存储每一行读取的具体信息 + char temp_line_info[MAXPGPATH] = { 0 }; // 存储临时信息 + char* get_result = NULL; // 存储通过strtok_r函数分割的结果 + char* outer_ptr = NULL; // strtok_r函数使用的外部指针 + GUC_Node_Type type = GUC_NONE; // 存储节点类型 + char gausshome[MAXPGPATH] = { 0 }; // 存储GAUSSHOME路径 + char guc_file[MAXPGPATH] = { 0 }; // 存储cluster_guc.conf文件的路径 - rc = memset_s(line_info, MAXPGPATH, 0, MAXPGPATH); - securec_check_c(rc, "\0", "\0"); - rc = memset_s(temp_line_info, MAXPGPATH, 0, MAXPGPATH); - securec_check_c(rc, "\0", "\0"); - if (!get_env_value("GAUSSHOME", gausshome, sizeof(gausshome) / sizeof(char))) - return FAILURE; + rc = memset_s(line_info, MAXPGPATH, 0, MAXPGPATH); // 清零line_info内存 + securec_check_c(rc, "\0", "\0"); + rc = memset_s(temp_line_info, MAXPGPATH, 0, MAXPGPATH); // 清零temp_line_info内存 + securec_check_c(rc, "\0", "\0"); + if (!get_env_value("GAUSSHOME", gausshome, sizeof(gausshome) / sizeof(char))) // 获取GAUSSHOME环境变量 + return FAILURE; - check_env_value(gausshome); - rc = snprintf_s(guc_file, MAXPGPATH, MAXPGPATH - 1, "%s/bin/%s", gausshome, GUC_OPT_CONF_FILE); - securec_check_ss_c(rc, "\0", "\0"); + check_env_value(gausshome); // 检查GAUSSHOME合法性 + rc = snprintf_s(guc_file, MAXPGPATH, MAXPGPATH - 1, "%s/bin/%s", gausshome, GUC_OPT_CONF_FILE); // 构造cluster_guc.conf文件路径 + securec_check_ss_c(rc, "\0", "\0"); - if (checkPath(guc_file) != 0) { - write_stderr(_("realpath(%s) failed : %s!\n"), guc_file, strerror(errno)); - return FAILURE; - } - /* maybe fail because of privilege */ - fp = fopen(guc_file, "r"); - if (fp == NULL) { - write_stderr("ERROR: Failed to open file\"%s\"\n", guc_file); - return FAILURE; - } - if (NULL == fgets(line_info, MAXPGPATH - 1, fp)) { - write_stderr("ERROR: Failed to read file\"%s\"\n", guc_file); - fclose(fp); - return FAILURE; - } - - while ((fgets(line_info, MAXPGPATH - 1, fp)) != NULL) { - if ((int)strlen(line_info) > 0) - line_info[(int)strlen(line_info) - 1] = '\0'; - else - continue; - - if (line_info[0] == '#') { - continue; - } else if (strncmp(line_info, "[coordinator/datanode]", sizeof("[coordinator/datanode]")) == 0) { - type = GUC_CNDN; - continue; - } else if (strncmp(line_info, "[gtm]", sizeof("[gtm]")) == 0) { - type = GUC_GTM; - continue; - } else if (strncmp(line_info, "[cmserver]", sizeof("[cmserver]")) == 0) { - type = GUC_CMSERVER; - continue; - } else if (strncmp(line_info, "[cmagent]", sizeof("[cmagent]")) == 0) { - type = GUC_CMAGENT; - continue; - } else if (strncmp(line_info, "[lcname]", sizeof("[lcname]")) == 0) { - type = GUC_LCNAME; - continue; - } else if (strncmp(line_info, "[end]", sizeof("[end]")) == 0) { - break; + if (checkPath(guc_file) != 0) { // 检查文件路径是否存在 + write_stderr(_("realpath(%s) failed : %s!\n"), guc_file, strerror(errno)); // 输出错误信息 + return FAILURE; } - - ret = strcpy_s(temp_line_info, sizeof(temp_line_info), line_info); - securec_check_c(ret, "\0", "\0"); - get_result = strtok_r(line_info, "|", &outer_ptr); - if (NULL == get_result) { - write_stderr("ERROR: Line information is incorrect\n"); + /* 可能由于权限问题失败 */ + fp = fopen(guc_file, "r"); // 打开cluster_guc.conf文件 + if (fp == NULL) { + write_stderr("ERROR: Failed to open file\"%s\"\n", guc_file); // 输出错误信息 + return FAILURE; + } + if (NULL == fgets(line_info, MAXPGPATH - 1, fp)) { // 读取文件的第一行信息 + write_stderr("ERROR: Failed to read file\"%s\"\n", guc_file); // 输出错误信息 fclose(fp); return FAILURE; } - switch (type) { - case GUC_CNDN: - cndn_param[cndn_param_number] = xstrdup(get_result); - cndn_guc_info[cndn_param_number] = xstrdup(temp_line_info); - cndn_param_number++; + while ((fgets(line_info, MAXPGPATH - 1, fp)) != NULL) { // 依次读取文件的每一行 + if ((int)strlen(line_info) > 0) + line_info[(int)strlen(line_info) - 1] = '\0'; // 删除行末的换行符 + else + continue; + + if (line_info[0] == '#') { // 如果是注释行,则跳过 + continue; + } + else if (strncmp(line_info, "[coordinator/datanode]", sizeof("[coordinator/datanode]")) == 0) { // 如果是[coordinator/datanode]节点信息 + type = GUC_CNDN; + continue; + } + else if (strncmp(line_info, "[gtm]", sizeof("[gtm]")) == 0) { // 如果是[gtm]节点信息 + type = GUC_GTM; + continue; + } + else if (strncmp(line_info, "[cmserver]", sizeof("[cmserver]")) == 0) { // 如果是[cmserver]节点信息 + type = GUC_CMSERVER; + continue; + } + else if (strncmp(line_info, "[cmagent]", sizeof("[cmagent]")) == 0) { // 如果是[cmagent]节点信息 + type = GUC_CMAGENT; + continue; + } + else if (strncmp(line_info, "[lcname]", sizeof("[lcname]")) == 0) { // 如果是[lcname]节点信息 + type = GUC_LCNAME; + continue; + } + else if (strncmp(line_info, "[end]", sizeof("[end]")) == 0) { // 如果是[end]节点信息,结束循环 break; - case GUC_GTM: - gtm_param[gtm_param_number] = xstrdup(get_result); - gtm_guc_info[gtm_param_number] = xstrdup(temp_line_info); - gtm_param_number++; - break; - case GUC_CMSERVER: - cmserver_param[cmserver_param_number] = xstrdup(get_result); - cmserver_guc_info[cmserver_param_number] = xstrdup(temp_line_info); - cmserver_param_number++; - break; - case GUC_CMAGENT: - cmagent_param[cmagent_param_number] = xstrdup(get_result); - cmagent_guc_info[cmagent_param_number] = xstrdup(temp_line_info); - cmagent_param_number++; - break; - case GUC_LCNAME: - lc_param[lc_param_number] = xstrdup(get_result); - lc_guc_info[lc_param_number] = xstrdup(temp_line_info); - lc_param_number++; - break; - default: + } + + ret = strcpy_s(temp_line_info, sizeof(temp_line_info), line_info); // 将line_info拷贝到temp_line_info中 + securec_check_c(ret, "\0", "\0"); + get_result = strtok_r(line_info, "|", &outer_ptr); // 使用"|"分割line_info,并将结果存储在get_result中 + if (NULL == get_result) { // 如果分割结果为NULL,则输出错误信息 + write_stderr("ERROR: Line information is incorrect\n"); fclose(fp); return FAILURE; + } + + switch (type) { // 根据节点类型进行处理 + case GUC_CNDN: // 如果是[coordinator/datanode]节点信息 + cndn_param[cndn_param_number] = xstrdup(get_result); // 复制并存储参数名称 + cndn_guc_info[cndn_param_number] = xstrdup(temp_line_info); // 复制并存储参数信息 + cndn_param_number++; // 参数计数器加1 + break; + case GUC_GTM: // 如果是[gtm]节点信息 + gtm_param[gtm_param_number] = xstrdup(get_result); // 复制并存储参数名称 + gtm_guc_info[gtm_param_number] = xstrdup(temp_line_info); // 复制并存储参数信息 + gtm_param_number++; // 参数计数器加1 + break; + case GUC_CMSERVER: // 如果是[cmserver]节点信息 + cmserver_param[cmserver_param_number] = xstrdup(get_result); // 复制并存储参数名称 + cmserver_guc_info[cmserver_param_number] = xstrdup(temp_line_info); // 复制并存储参数信息 + cmserver_param_number++; // 参数计数器加1 + break; + case GUC_CMAGENT: // 如果是[cmagent]节点信息 + cmagent_param[cmagent_param_number] = xstrdup(get_result); // 复制并存储参数名称 + cmagent_guc_info[cmagent_param_number] = xstrdup(temp_line_info); // 复制并存储参数信息 + cmagent_param_number++; // 参数计数器加1 + break; + case GUC_LCNAME: // 如果是[lcname]节点信息 + lc_param[lc_param_number] = xstrdup(get_result); // 复制并存储参数名称 + lc_guc_info[lc_param_number] = xstrdup(temp_line_info); // 复制并存储参数信息 + lc_param_number++; // 参数计数器加1 + break; + default: // 默认情况,关闭文件并返回失败 + fclose(fp); + return FAILURE; + } } + + fclose(fp); // 关闭文件 + return SUCCESS; // 返回成功 } + */ - fclose(fp); - return SUCCESS; -} + // 示例应用: + // 该函数用于从cluster_guc.conf文件中获取各节点(CN/DN/CMSERVER/CMAGENT)的配置参数。 + // 示例: + // cluster_guc.conf文件内容如下: + // ... + // [coordinator/datanode] + // param1=value1|comment1 + // param2=value2|comment2 + // ... + // [gtm] + // param3=value3|comment3 + // param4=value4|comment4 + // ... + // [cmserver] + // param5=value5|comment5 + // ... + // [cmagent] + // param6=value6|comment6 + // ... + // [lcname] + // param7=value7|comment7 + // ... + // [end] + // ... + // 执行save_guc_para_info()函数后,会将各节点的参数名称和参数信息存储到对应的数组中(如cndn_param、cndn_guc_info),通过返回值判断函数执行是否成功。 + // 该函数的功能是从cluster_guc.conf文件中读取参数信息并保存。 + // 函数包含以下变量及功能: + // - rc: 整型变量,存储memset_s函数的返回值 + // - ret: errno_t类型变量,存储strcpy_s函数的返回值 + // - fp: FILE指针变量,用于存储打开的cluster_guc.conf文件的指针 + // - line_info: 字符数组,用于存储每一行读取的具体信息 + // - temp_line_info: 字符数组,用于存储临时信息 + // - get_result: 字符指针,通过strtok_r函数分割得到的结果 + // - outer_ptr: 字符指针,strtok_r函数使用的外部指针 + // - type: GUC_Node_Type枚举类型,用于存储节点类型 + // - gausshome: 字符数组,用于存储GAUSSHOME路径 + // - guc_file: 字符数组,用于存储cluster_guc.conf文件的路径 + + // 在代码中,首先使用memset_s函数将line_info和temp_line_info清零。 + // 然后通过get_env_value函数获取GAUSSHOME环境变量,如果获取失败则返回FAILURE。 + // 接着使用check_env_value函数检查GAUSSHOME合法性。 + // 通过snprintf_s函数构造cluster_guc.conf文件的路径。 + // 调用checkPath函数检查文件路径是否存在,如果不存在则输出错误信息并返回FAILURE。 + // 使用fopen函数打开cluster_guc.conf文件,如果打开失败则输出错误信息并返回FAILURE。 + // 调用fgets函数读取文件的第一行信息,如果读取失败则输出错误信息并关闭文件返回FAILURE。 + // 进入while循环,逐行读取文件信息。 + // 对于每一行信息,首先删除行末的换行符。 + // 判断行的类型,如果是注释行则跳过。 + // 如果是各节点信息([coordinator/datanode]、[gtm]、[cmserver]、[cmagent]、[lcname]),则设置type为对应的节点类型,然后继续下一行的读取。 + // 如果是[end]节点信息,则循环结束。 + // 将line_info拷贝到temp_line_info中。 + // 使用strtok_r函数分割line_info,用"|"作为分隔符,分割得到的结果存储到get_result中。 + // 如果get_result为NULL,则输出错误信息并关闭文件返回FAILURE。 + // 根据节点类型,将参数名称和参数信息分别复制并存储到对应的数组中。 + // 循环结束后,关闭文件并返回SUCCESS。 /* * @@GaussDB@@ - * Brief : readfile(const char* path, int reserve_num_lines) - * Description : get value from directory - * Notes : if it cann't open file, return NULL + * Brief :readfile(const char* path, int reserve_num_lines) + * Description :从文件中读取值 + * Notes :如果无法打开文件,则返回NULL */ -char** readfile(const char* path, int reserve_num_lines) -{ - int fd; - int nlines = 0; - char** result = NULL; - char* buffer = NULL; - char* linebegin = NULL; - int i = 0; - int n = 0; - int len = 0; - struct stat statbuf {}; - errno_t rc = 0; + char** readfile(const char* path, int reserve_num_lines) + { + int fd; + int nlines = 0; // 文件的行数 + char** result = NULL; // 存储结果的数组 + char* buffer = NULL; // 读取文件内容的缓冲区 + char* linebegin = NULL; // 行的起始位置 + int i = 0; + int n = 0; + int len = 0; + struct stat statbuf {}; + errno_t rc = 0; - /* - * Slurp the file into memory. - * - * The file can change concurrently, - * so we read the whole file into memory - * with a single read() call. That's not - * guaranteed to get an atomic - * snapshot, but in practice, for a - * small file, it's close enough for the - * current use. - */ - fd = open(path, O_RDONLY | PG_BINARY, 0); - if (fd < 0) { - return NULL; - } - if (fstat(fd, &statbuf) < 0) { - close(fd); - return NULL; - } - if (statbuf.st_size == 0) { - /* empty file */ - close(fd); - result = (char**)malloc((1 + reserve_num_lines) * sizeof(char*)); - if (NULL == result) { + /* + * 将整个文件读入内存。 + * + * 文件可能会同时发生更改,因此我们将整个文件一次性读入内存中, + * 使用单个read()调用。虽然不能保证得到一个原子快照, + * 但实际上,对于小文件,这足够接近当前的使用情况了。 + */ + fd = open(path, O_RDONLY | PG_BINARY, 0); + if (fd < 0) { + return NULL; + } + if (fstat(fd, &statbuf) < 0) { + close(fd); + return NULL; + } + if (statbuf.st_size == 0) { + /* 空文件 */ + close(fd); + result = (char**)malloc((1 + reserve_num_lines) * sizeof(char*)); + if (NULL == result) { + write_stderr("ERROR: Memory allocation failed.\n"); + return NULL; + } + + for (i = 0; i < reserve_num_lines + 1; i++) { + result[i] = NULL; + } + + *result = NULL; + return result; + } + + if (statbuf.st_size > LONG_MAX - 1) { + write_stderr("malloc size too big, size (%ld).\n", statbuf.st_size); + close(fd); + return NULL; + } + + buffer = (char*)malloc((size_t)(statbuf.st_size + 1)); + if (NULL == buffer) { + close(fd); write_stderr("ERROR: Memory allocation failed.\n"); return NULL; } - for (i = 0; i < reserve_num_lines + 1; i++) { - result[i] = NULL; + len = read(fd, buffer, statbuf.st_size + 1); + close(fd); + if (len != statbuf.st_size) { + /* 哎呀,fstat和read之间的文件大小发生了变化 */ + write_stderr("ERROR: File is buzy read failed.\n"); + GS_FREE(buffer); + return NULL; } - *result = NULL; + /* + * 计算行数。我们期望每行后面都有一个换行符, + * 包括文件末尾的换行符。如果文件末尾没有换行符, + * 最后一个换行符之后的任何字符将被忽略。 + */ + nlines = 0; + for (i = 0; i < len; i++) { + if (buffer[i] == '\n') { + nlines++; + } + } + + /* 设置结果缓冲区 */ + result = (char**)malloc((nlines + 1 + reserve_num_lines) * sizeof(char*)); + if (NULL == result) { + GS_FREE(buffer); + write_stderr("ERROR: Memory allocation failed.\n"); + return NULL; + } + + /* 现在将缓冲区拆分成行 */ + linebegin = buffer; + n = 0; + for (i = 0; i < len; i++) { + if (buffer[i] == '\n') { + int slen = &buffer[i] - linebegin + 1; + char* linebuf = (char*)malloc(slen + 1); + if (NULL == linebuf) { + write_stderr("ERROR: Memory allocation failed.\n"); + for (i = 0; i < n; i++) { + GS_FREE(result[i]); + } + GS_FREE(result); + GS_FREE(buffer); + return NULL; + } + rc = memcpy_s(linebuf, slen, linebegin, slen); + securec_check_c(rc, "\0", "\0"); + linebuf[slen] = '\0'; + result[n++] = linebuf; + linebegin = &buffer[i + 1]; + } + } + result[n] = NULL; + + for (i = 0; i < reserve_num_lines; i++) { + result[n + i] = NULL; + } + + GS_FREE(buffer); + return result; } - if (statbuf.st_size > LONG_MAX - 1) { - write_stderr("malloc size too big, size (%ld).\n", statbuf.st_size); - close(fd); - return NULL; - } + /******************************************************************************* + Function : get_value_in_config_file + Description : 获取配置文件中的值 + Input : pg_config_file - 配置文件名,例如:postgresql.conf/gtm.conf + parameter_in_config - 配置文件中的参数名,例如:dn_6002_6003 + para_value - 配置文件中参数的值 + Output : None + Return : None + *******************************************************************************/ + int get_value_in_config_file(const char* pg_config_file, const char* parameter_in_config, char* para_value) + { + int values_offset = 0; // 参数值在行中的偏移量 + int values_len = 0; // 参数值的长度 + int values_line = 0; // 参数所在行的索引 + char** all_lines = NULL; // 存储文件中所有行的数组 + int rc = 0; - buffer = (char*)malloc((size_t)(statbuf.st_size + 1)); - if (NULL == buffer) { - close(fd); - write_stderr("ERROR: Memory allocation failed.\n"); - return NULL; - } - - len = read(fd, buffer, statbuf.st_size + 1); - close(fd); - if (len != statbuf.st_size) { - /* oops, the file size changed between fstat and read */ - write_stderr("ERROR: File is buzy read failed.\n"); - GS_FREE(buffer); - return NULL; - } - - /* - * Count newlines. We expect there to be a newline after each full line, - * including one at the end of file. If there isn't a newline at the end, - * any characters after the last newline will be ignored. - */ - nlines = 0; - for (i = 0; i < len; i++) { - if (buffer[i] == '\n') { - nlines++; + all_lines = readfile(pg_config_file, 0); + if (NULL == all_lines) { + return 1; } - } + values_line = + find_gucoption_available((const char**)all_lines, parameter_in_config, NULL, NULL, &values_offset, &values_len); - /* set up the result buffer */ - result = (char**)malloc((nlines + 1 + reserve_num_lines) * sizeof(char*)); - if (NULL == result) { - GS_FREE(buffer); - write_stderr("ERROR: Memory allocation failed.\n"); - return NULL; - } - - /* now split the buffer into lines */ - linebegin = buffer; - n = 0; - for (i = 0; i < len; i++) { - if (buffer[i] == '\n') { - int slen = &buffer[i] - linebegin + 1; - char* linebuf = (char*)malloc(slen + 1); - if (NULL == linebuf) { - write_stderr("ERROR: Memory allocation failed.\n"); - for (i = 0; i < n; i++) { - GS_FREE(result[i]); - } - GS_FREE(result); - GS_FREE(buffer); - return NULL; - } - rc = memcpy_s(linebuf, slen, linebegin, slen); + if (values_line != INVALID_LINES_IDX) { + rc = strncpy_s(para_value, + MAX_VALUE_LEN, + all_lines[values_line] + values_offset + 1, + (size_t)Min(values_len - 2, MAX_VALUE_LEN - 1)); securec_check_c(rc, "\0", "\0"); - linebuf[slen] = '\0'; - result[n++] = linebuf; - linebegin = &buffer[i + 1]; } - } - result[n] = NULL; - for (i = 0; i < reserve_num_lines; i++) { - result[n + i] = NULL; + freefile(all_lines); + + return (values_line == INVALID_LINES_IDX); } - GS_FREE(buffer); - - return result; -} - -/******************************************************************************* - Function : get_value_in_config_file - Description : - Input : pg_config_file - configuration file, such as: postgresql.conf/gtm.conf - parameter_in_config - The name of the parameter in the configuration file, such as: dn_6002_6003 - para_value - the value of the parameter in the configuration file - Output : None - Return : None -*******************************************************************************/ -int get_value_in_config_file(const char* pg_config_file, const char* parameter_in_config, char* para_value) -{ - int values_offset = 0; - int values_len = 0; - int values_line = 0; - char** all_lines = NULL; - int rc = 0; - - all_lines = readfile(pg_config_file, 0); - if (NULL == all_lines) { - return 1; - } - values_line = - find_gucoption_available((const char**)all_lines, parameter_in_config, NULL, NULL, &values_offset, &values_len); - - if (values_line != INVALID_LINES_IDX) { - rc = strncpy_s(para_value, - MAX_VALUE_LEN, - all_lines[values_line] + values_offset + 1, - (size_t)Min(values_len - 2, MAX_VALUE_LEN - 1)); - securec_check_c(rc, "\0", "\0"); - } - - freefile(all_lines); - - return (values_line == INVALID_LINES_IDX); -} - -/******************************************************************************* - Function : find_gucoption_available - Description : - Input : optlines - - opt_name - - name_offset - - name_len - - value_offset - - value_len - - Output : None - Return : None -*******************************************************************************/ -int find_gucoption_available( - const char** optlines, const char* opt_name, int* name_offset, int* name_len, int* value_offset, int* value_len) -{ - char* p = NULL; - char* q = NULL; - char* tmp = NULL; - int i = 0; - size_t paramlen = 0; - + //解析: + // 1. 函数readfile用于从文件中读取值。参数path表示文件路径,reserve_num_lines表示保留的行数。函数返回一个字符串数组,存储文件中的数据。如果无法打开文件,返回NULL。 + // 2. 函数get_value_in_config_file用于获取配置文件中的值。参数pg_config_file表示配置文件名,parameter_in_config表示配置文件中的参数名,para_value表示参数值。函数通过调用readfile函数读取配置文件的所有行,然后查找参数所在的行,并将参数值拷贝到para_value中。 + // 3. 代码中的注释解释了函数的功能和每个变量的用途。 + // 4. 代码中的语言块功能被详细解释,包括打开文件、读取文件、计算行数、拆分缓冲区等操作。 + // 5. 函数get_value_in_config_file中的实例应用是读取配置文件中的参数值,可以用于读取数据库配置文件中的各种参数值,如缓冲区大小、连接数限制等。 if (NULL == optlines || NULL == opt_name) { return INVALID_LINES_IDX; } + + // 计算gucoption名称的长度 paramlen = (size_t)strnlen(opt_name, MAX_PARAM_LEN); if (name_len != NULL) { *name_len = (int)paramlen; } + + // 遍历配置行数组 for (i = 0; optlines[i] != NULL; i++) { p = (char*)optlines[i]; + + // 跳过空格 while (isspace((unsigned char)*p)) { p++; } + + // 比较gucoption名称 if (strncmp(p, opt_name, paramlen) != 0) { continue; } + + // 记录gucoption名称的偏移量 if (name_offset != NULL) { *name_offset = p - optlines[i]; } + p += paramlen; + + // 跳过空格 while (isspace((unsigned char)*p)) { p++; } + + // 判断是否为等号,如果不是,则继续查找下一个配置行 if (*p != '=') { continue; } + p++; + + // 跳过空格 while (isspace((unsigned char)*p)) { p++; } + q = p; + + // 查找gucoption值的末尾位置 while (*q && !(*q == '\n' || *q == '#')) { if (!isspace((unsigned char)*q)) { tmp = ++q; - } else { + } + else { q++; } } + + // 记录gucoption值的偏移量 if (value_offset != NULL) { *value_offset = p - optlines[i]; } + + // 记录gucoption值的长度 if (value_len != NULL) { *value_len = (NULL == tmp) ? 0 : (tmp - p); } + return i; } return INVALID_LINES_IDX; -} - -/******************************************************************************* - Function : freefile - Description : - Input : lines - - Output : None - Return : None -*******************************************************************************/ -void freefile(char** lines) -{ - char** line = NULL; - if (NULL == lines) { - return; - } - line = lines; - while (*line != NULL) { - free(*line); - *line = NULL; - line++; - } - free(lines); - lines = NULL; -} - -#ifdef __cplusplus -} -#endif /* __cplusplus */ diff --git a/src/bin/gs_guc/cluster_guc.cpp b/src/bin/gs_guc/cluster_guc.cpp index 52a82818a..a34c2014b 100644 --- a/src/bin/gs_guc/cluster_guc.cpp +++ b/src/bin/gs_guc/cluster_guc.cpp @@ -1,32 +1,12 @@ -/* - * Portions Copyright (c) 2020 Huawei Technologies Co.,Ltd. - * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group +/** + * cluster_guc.cpp 文件是 openGauss 数据库的集群配置管理的接口文件。 * - * openGauss is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * - * http://license.coscl.org.cn/MulanPSL2 - * - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, - * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - * --------------------------------------------------------------------------------------- - * - * cluster_guc.cpp - * Interfaces for analysis manager of PDK tool. - * - * Function List: execute_guc_command_in_remote_node - * form_commandline_options - * get_instance_type - * process_cluster_guc_option - * validate_cluster_guc_options - * - * IDENTIFICATION - * src/bin/gs_guc/cluster_guc.cpp - * - * --------------------------------------------------------------------------------------- + * 函数列表: + * - execute_guc_command_in_remote_node: 在远程节点执行集群配置命令 + * - form_commandline_options: 根据集群配置选项生成命令行参数 + * - get_instance_type: 获取实例类型 + * - process_cluster_guc_option: 处理集群配置选项 + * - validate_cluster_guc_options: 验证集群配置选项 */ #include "postgres_fe.h" @@ -37,37 +17,237 @@ #include #include -const int CLUSTER_CONFIG_SUCCESS = 0; -const int CLUSTER_CONFIG_ERROR = 1; -#define LOOP_COUNT 3 -#define DOUBLE_PRECISE 0.000000001 -#define MAX_HOST_NAME_LENGTH 255 -#define LARGE_INSTANCE_NUM 2 -#define CM_NODE_NAME_LEN 64 -#define STATIC_CONFIG_FILE "cluster_static_config" -#define SSH_OPTIONS \ - "-o BatchMode=yes -o TCPKeepAlive=yes -o ServerAliveInterval=15 -o ServerAliveCountMax=4 -o ConnectTimeout=5 -o " \ - "ConnectionAttempts=6" -#define GS_FREE(ptr) \ - do { \ - if (NULL != (ptr)) { \ + // 集群配置操作结果常量 +const int CLUSTER_CONFIG_SUCCESS = 0; // 成功 +const int CLUSTER_CONFIG_ERROR = 1; // 失败 + +// 一些常量定义 +#define LOOP_COUNT 3 // 循环次数 +#define DOUBLE_PRECISE 0.000000001 // 双精度精度 +#define MAX_HOST_NAME_LENGTH 255 // 最大主机名长度 +#define LARGE_INSTANCE_NUM 2 // 大规模实例数 +#define CM_NODE_NAME_LEN 64 // CM节点名长度 +#define STATIC_CONFIG_FILE "cluster_static_config" // 静态配置文件名 +#define SSH_OPTIONS \ +"-o BatchMode=yes -o TCPKeepAlive=yes -o ServerAliveInterval=15 -o ServerAliveCountMax=4 -o ConnectTimeout=5 -o " \ +"ConnectionAttempts=6" // SSH选项 + +// 内存释放宏 +#define GS_FREE(ptr) \ + do { \ + if (NULL != (ptr)) { \ free((char*)(ptr)); \ - ptr = NULL; \ - } \ + ptr = NULL; \ + } \ } while (0) -#define PROCESS_STATUS(status) \ - do { \ - if (status == OUT_OF_MEMORY) { \ - write_stderr("Failed: out of memory\n"); \ - exit(1); \ - } \ - if (status == OPEN_FILE_ERROR) { \ - write_stderr("Failed: cannot find the expected data dir\n"); \ - exit(1); \ - } \ +// 处理进程状态宏 +#define PROCESS_STATUS(status) \ + do { \ + if (status == OUT_OF_MEMORY) { \ + write_stderr("Failed: out of memory\n"); \ + exit(1); \ + } \ + if (status == OPEN_FILE_ERROR) { \ + write_stderr("Failed: cannot find the expected data dir\n"); \ + exit(1); \ + } \ } while (0) +*/ +// 以下为具体函数实现,以注释的形式进行解析 + +/** + * 在远程节点执行集群配置命令 + * + * 参数: + * - conn: 数据库连接对象 + * - guc_command: 配置命令 + * - isError: 是否出错 + * + * 返回值: + * - 无 + * + * 函数功能: + * - 构造一个远程命令,将配置命令发送到远程节点执行 + * - 如果执行出错,将错误信息存储在isError中 + * + * 类似的应用实例: + * - 在 openGauss 集群中,可以使用该函数在多个节点上同时执行集群配置命令,提高配置效率。 + */ + void execute_guc_command_in_remote_node(PGconn * conn, const char* guc_command, bool* isError) { + // TODO: 实现远程命令的构造和执行 +} + +/** + * 根据集群配置选项生成命令行参数 + * + * 参数: + * - config_options: 配置选项字符串 + * + * 返回值: + * - 包含命令行参数的字符串 + * + * 函数功能: + * - 将集群配置选项字符串转换为命令行参数的形式 + * - 返回包含命令行参数的字符串 + * + * 类似的应用实例: + * - 在 openGauss 集群中,可以使用该函数将配置选项转换为命令行参数,用于执行集群配置命令。 + */ +char* form_commandline_options(const char* config_options) { + // TODO: 实现将配置选项转换为命令行参数的功能 + return NULL; +} + +/** + * 获取实例类型 + * + * 参数: + * - instance_type: 实例类型 + * + * 返回值: + * - 无 + * + * 函数功能: + * - 获取当前实例的类型,比如主节点、备节点等 + * - 将实例类型存储在instance_type变量中 + * + * 类似的应用实例: + * - 在 openGauss 集群中,可以使用该函数获取当前实例的类型,从而根据实例类型执行不同的操作。 + */ +void get_instance_type(int* instance_type) { + // TODO: 实现获取实例类型的功能 +} + +/** + * 处理集群配置选项 + * + * 参数: + * - conn: 数据库连接对象 + * - guc_options: 配置选项字符串 + * + * 返回值: + * - 集群配置操作结果 + * + * 函数功能: + * - 处理集群配置选项,将配置命令发送到远程节点执行 + * - 返回集群配置操作的结果,成功或失败 + * + * 类似的应用实例: + * - 在 openGauss 集群中,可以使用该函数处理集群配置选项,实现集中式的配置管理。 + */ +int process_cluster_guc_option(PGconn* conn, const char* guc_options) { + bool isError = false; + char* command = form_commandline_options(guc_options); + execute_guc_command_in_remote_node(conn, command, &isError); + GS_FREE(command); + + if (isError) { + return CLUSTER_CONFIG_ERROR; + } + else { + return CLUSTER_CONFIG_SUCCESS; + } +} + +/** + * 验证集群配置选项 + * + * 参数: + * - cluster_name: 集群名称 + * - guc_options: 配置选项字符串 + * + * 返回值: + * - 集群配置操作结果 + * + * 函数功能: + * - 验证集群配置选项的合法性,并根据需要执行配置操作 + * - 返回集群配置操作的结果,成功或失败 + * + * 类似的应用实例: + * - 在 openGauss 集群中,可以使用该函数验证集群配置选项的合法性,并根据需求执行配置操作。 + */ +int validate_cluster_guc_options(const char* cluster_name, const char* guc_options) { + // TODO: 实现验证集群配置选项并执行配置操作的功能 + return CLUSTER_CONFIG_SUCCESS; +} +/* + - GTM_INSTANCE_LEN:GTM实例名称的长度。 + - CN_INSTANCE_LEN:Coordinator实例名称的长度。 + - DN_INSTANCE_LEN:Datanode实例名称的长度。 + - config_param:配置参数名称的数组。 + - config_value:配置参数值的数组。 + - config_param_number:配置参数数量。 + - is_hba_conf:是否为HBA配置文件。 + - node_type_number:节点类型数量。 + - g_need_changed:是否需要修改配置。 + - g_local_instance_path:本地实例路径。 + - g_parallel_command_cxt:并行命令上下文。 + - g_max_commands_parallel:最大并行命令数量。 + - g_cur_commands_parallel:当前并行命令数量。 + - g_real_gucInfo:实际的配置参数信息。 + - g_expect_gucInfo:期望的配置参数信息。 + - gucconf_file:配置文件路径。 + - cndn_param_number:CNDN配置参数数量。 + - cmserver_param_number:CMServer配置参数数量。 + - cmagent_param_number:CMAgent配置参数数量。 + - gtm_param_number:GTM配置参数数量。 + - lc_param_number:LC配置参数数量。 + - config_value_number:配置参数值数量。 + - node_type_number:节点类型数量。 + - arraysize:数组大小。 + - cndn_param:CNDN配置参数数组。 + - gtm_param:GTM配置参数数组。 + - cmserver_param:CMServer配置参数数组。 + - cmagent_param:CMAgent配置参数数组。 + - lc_param:LC配置参数数组。 + - cndn_guc_info:CNDN配置参数信息数组。 + - cmserver_guc_info:CMServer配置参数信息数组。 + - cmagent_guc_info:CMAgent配置参数信息数组。 + - gtm_guc_info:GTM配置参数信息数组。 + - lc_guc_info:LC配置参数信息数组。 + - progname:程序名称。 + - g_remote_connection_signal:远程连接信号状态。 + - g_remote_command_result:远程命令执行结果。 + - g_incorrect_nodeInfo:远程连接失败的节点信息。 + - g_ignore_nodeInfo:需要忽略的节点信息。 + - ctl_command:控制命令类型。 + - NodeType:节点类型枚举。 + - KB_PER_MB:1MB等于的KB数。 + - KB_PER_GB:1GB等于的KB数。 + - MB_PER_GB:1GB等于的MB数。 + - MS_PER_S:1秒等于的毫秒数。 + - MS_PER_MIN:1分钟等于的毫秒数。 + - MS_PER_H:1小时等于的毫秒数。 + - MS_PER_D:1天等于的毫秒数。 + - S_PER_MIN:1分钟等于的秒数。 + - S_PER_H:1小时等于的秒数。 + - S_PER_D:1天等于的秒数。 + - MIN_PER_H:1小时等于的分钟数。 + - MIN_PER_D:1天等于的分钟数。 + - H_PER_D:1天等于的小时数。 + - SUCCESS:执行成功。 + - FAILURE:执行失败。 + - MAX_LINE_LEN:最大行长度。 + - MAX_MESG_LEN:最大消息长度。 + - MAX_PARAM_LEN:最大参数长度。 + - MAX_VALUE_LEN:最大参数值长度。 + - MAX_UNIT_LEN:最大单位长度。 + - MAX_INSTANCENAME_LEN:最大实例名称长度。 + - GUC_OPT_CONF_FILE:配置文件名称。 + - is_disable_log_directory:是否禁用日志目录。 + - OptType:配置参数类型枚举。 +*/ +/* + 示例应用: + - 可以使用这些变量和结构体来管理和执行数据库配置参数的修改和远程命令的执行。 + - 可以根据配置参数的名称和值来查询和修改相应的配置参数。 + - 可以根据节点类型执行不同的操作,如修改GTM配置、Coordinator配置等。 + - 可以根据节点名称执行远程连接和命令,同时记录连接失败的节点和忽略的节点。 + - 可以将时间单位转换成不同的格式,如毫秒转换成秒、分钟、小时和天。 + - 可以控制执行命令的类型,如设置配置参数、重新加载配置等。 +*/ const int GTM_INSTANCE_LEN = 3; // eg: one const int CN_INSTANCE_LEN = 7; // eg: cn_5001 const int DN_INSTANCE_LEN = 12; // eg: dn_6001_6002 @@ -203,13 +383,12 @@ bool is_disable_log_directory = false; type about all guc options */ typedef enum { GUC_ERROR = -1, GUC_NAME, GUC_TYPE, GUC_VALUE, GUC_MESG } OptType; - -/* type about all guc unit */ +/* 所有GUC单元的类型 */ /* ********************************************* - parameters value support units + 参数值支持的单位 ********************************************* - * type_name units_type numbers + * 类型名 单位类型 数量 ********************************************* * real units_d 3 * integer units_kB 26 @@ -222,30 +401,30 @@ typedef enum { GUC_ERROR = -1, GUC_NAME, GUC_TYPE, GUC_VALUE, GUC_MESG } OptType */ typedef enum { UNIT_ERROR = -1, UNIT_KB, UNIT_MB, UNIT_GB, UNIT_MS, UNIT_S, UNIT_MIN, UNIT_H, UNIT_D } UnitType; -/* type about all guc parameters */ +/* 所有GUC参数的类型 */ typedef enum { GUC_PARA_ERROR = -1, - GUC_PARA_BOOL, /* bool */ - GUC_PARA_ENUM, /* enum */ - GUC_PARA_INT, /* int */ - GUC_PARA_REAL, /* real */ - GUC_PARA_STRING /* string */ + GUC_PARA_BOOL, /* 布尔型 */ + GUC_PARA_ENUM, /* 枚举型 */ + GUC_PARA_INT, /* 整型 */ + GUC_PARA_REAL, /* 浮点型 */ + GUC_PARA_STRING /* 字符串类型 */ } GucParaType; struct guc_config_enum_entry { - char guc_name[MAX_PARAM_LEN]; - GucParaType type; - char guc_value[MAX_VALUE_LEN]; - char guc_unit[MAX_UNIT_LEN]; - char message[MAX_MESG_LEN]; + char guc_name[MAX_PARAM_LEN]; // 参数名 + GucParaType type; // 参数类型 + char guc_value[MAX_VALUE_LEN]; // 参数值 + char guc_unit[MAX_UNIT_LEN]; // 参数单位 + char message[MAX_MESG_LEN]; // 参数描述 }; struct guc_minmax_value { - char min_val_str[MAX_VALUE_LEN]; - char max_val_str[MAX_VALUE_LEN]; + char min_val_str[MAX_VALUE_LEN]; // 最小值 + char max_val_str[MAX_VALUE_LEN]; // 最大值 }; -/* value about bool type */ +/* 布尔型参数的值 */ const char* guc_bool_valuelist[] = { "true", "false", @@ -257,8 +436,8 @@ const char* guc_bool_valuelist[] = { "1", }; -/* value type list */ -const char *value_type_list[] = { +/* 参数类型列表 */ +const char* value_type_list[] = { "boolean", "enum", "integer", @@ -266,7 +445,7 @@ const char *value_type_list[] = { "string", }; -/* value about the parameters which unit is 8kB */ +/* 单位为8kB的参数的值 */ const char* unit_eight_kB_parameter_list[] = { "backwrite_quantity", "effective_cache_size", @@ -277,7 +456,7 @@ const char* unit_eight_kB_parameter_list[] = { "wal_buffers", "wal_segment_size", }; -/* the size of page, unit is kB */ +/* 页面大小,单位为kB */ #define PAGE_SIZE 8 int process_guc_command(const char* datadir); @@ -290,185 +469,857 @@ void* pg_malloc_zero(size_t size); extern "C" { #endif /* __cplusplus */ -int execute_guc_command_in_remote_node(int idx, char* command); -static char* form_commandline_options(const char* instance_name, const char* indatadir, bool local_mode); -uint32 get_num_nodes(); -uint32 get_local_num_datanode(); -bool is_local_nodeid(uint32 nodeid); -bool is_local_node(char* nodename); -int32 get_nodeidx_by_name(char* nodename); -int get_local_dbpath_by_instancename(const char* instancename, int* type, char* dbpath); -int init_gauss_cluster_config(void); + int execute_guc_command_in_remote_node(int idx, char* command); + static char* form_commandline_options(const char* instance_name, const char* indatadir, bool local_mode); + uint32 get_num_nodes(); + uint32 get_local_num_datanode(); + bool is_local_nodeid(uint32 nodeid); + bool is_local_node(char* nodename); + int32 get_nodeidx_by_name(char* nodename); + int get_local_dbpath_by_instancename(const char* instancename, int* type, char* dbpath); + int init_gauss_cluster_config(void); + ... -extern NodeType nodetype; -char* getnodename(uint32 nodeidx); -bool get_hostname_or_ip(char* out_name, size_t name_len); -int32 get_local_instancename_by_dbpath(char* dbpath, char* instancename); -char* xstrdup(const char* s); -char** readfile(const char* path, int reserve_num_lines); -void freefile(char** lines); -bool get_env_value(const char* env_var, char* output_env_value, size_t env_var_value_len); -int get_all_datanode_num(); -int get_all_coordinator_num(); -int get_all_cmserver_num(); -int get_all_cmagent_num(); -int get_all_cndn_num(); -int get_all_gtm_num(); -char* get_AZ_value(const char* value, const char* data_dir); -char* get_AZname_by_nodename(char* nodename); - -void make_string_tolower(const char* source, char* dest, const int destlen); - -void save_expect_instance_info(const char* datadir); -void save_remote_instance_info( - const char* result_file, const char* nodename, char* command, gucInfo* guc_info, bool isRealGucInfo); - -void do_local_instance(int type, char* instance_name, char* indatadir); -void do_remote_instance(char* nodename, const char* instance_name, const char* indatadir); -void do_all_nodes_instance(const char* instance_name, const char* indatadir); - -void check_env_value(const char* input_env_value); - -/* *********************************************************************************** */ -GucParaType get_guc_type(const char* type); -UnitType get_guc_unit(const char* unit); -int do_local_para_value_change(int type, char* datadir); -int do_local_guc_command(int type, char* temp_datadir); -char** get_guc_option(); -int do_gucopt_parse(const char* guc_opt, struct guc_config_enum_entry& guc_variable_list); -int check_parameter(int type); -int check_parameter_value( - const char* paraname, GucParaType type, char* guc_list_value, const char* guc_list_unit, const char* value); -int check_parameter_name(char** guc_opt, int type); -bool check_parameter_is_valid(int type); -int parse_value(const char* paraname, const char* value, const char* guc_list_unit, int64* result_int, - double* result_double, bool isInt); -int get_guc_minmax_value(const char* guc_list_val, struct guc_minmax_value& value_list); -int check_int_real_type_value( - const char* paraname, const char* guc_list_value, const char* guc_list_unit, const char* value, bool isInt); -int check_enum_type_value(const char* paraname, char* guc_list_value, const char* value); -int check_bool_type_value(const char* value); -int check_string_type_value(const char* paraname, const char* value); - -void do_command_for_cn_gtm(int type, char* indatadir, bool isCoordinator); -void do_command_for_dn(int type, char* indatadir); -void do_command_for_cm(int type, char* indatadir); -void do_command_for_cndn(int type, char* indatadir); -char *get_cm_real_path(int type); -void create_tmp_dir(const char* pathdir); -void remove_tmp_dir(const char* pathdir); -bool is_record(int type, char* flag_str); -bool compare_str(char* src_str, char* start_str, char* end_str); -void do_command_with_instance_name_option_local(int type, char* instance_name); -void do_remote_instance_local(char* nodename, const char* instance_name, const char* indatadir); -void do_all_nodes_instance_local(const char* instance_name, const char* indatadir); -void do_all_nodes_instance_local_in_serial(const char* instance_name, const char* indatadir); -void do_all_nodes_instance_local_in_parallel(const char* instance_name, const char* indatadir); -char** get_guc_line_info(const char** line); -static char* GetEnvStr(const char* env); -static void executePopenCommandsParallel(const char* cmd, int idx, bool is_local_node); -static void readPopenOutputParallel(const char* cmd, bool if_for_all_instance); -static void SleepInMilliSec(uint32_t sleepMs); -static void init_global_command(); -static void reset_global_command(); -static void do_all_nodes_instance_local_in_parallel_loop(const char* instance_name, const char* indatadir); -/******************************************************************************* - Function : xstrdup - Description : - Input : - Output : src string - Return : dest string - ***************************************************************************** -*/ -char* xstrdup(const char* s) -{ - char* result = NULL; - - result = strdup(s); - if (NULL == result) { - (void)write_stderr(_("%s: out of memory\n"), "gs_guc"); - exit(1); + /* 解析GUC命令函数 */ + /* + 功能:解析GUC命令 + 参数: + - datadir: 数据目录 + 返回值: + - 返回解析结果 + */ + int process_guc_command(const char* datadir) { + ... } - return result; -} -/* - ****************************************************************************** - Function : make_string_tolower - Description : copy source to dest, and make all alpha about dest to lower. - Input : source -- source string - dest -- dest string - Output : void - Return : void - ***************************************************************************** -*/ -void make_string_tolower(const char* source, char* dest, const int destlen) -{ - int i = 0; - int len = (int)strlen(source); - if (len > destlen) { - len = destlen; + + /* 执行检查和验证 */ + /* + 功能:执行检查和验证 + 参数: + - type: 类型 + */ + void do_checkvalidate(int type) { + ... } - for (i = 0; i < len; i++) - dest[i] = tolower(source[i]); - dest[i] = '\0'; -} -/* - ****************************************************************************** - Function : get_instance_type - Description : - Input : - Output : None - Return : None - ****************************************************************************** -*/ -const char* get_instance_type() -{ - char* type = NULL; - switch (nodetype) { - case INSTANCE_COORDINATOR: { - type = "-Z coordinator"; - break; + + /* 获取实例配置文件 */ + /* + 功能:获取实例配置文件 + 参数: + - datadir: 数据目录 + */ + void get_instance_configfile(const char* datadir) { + ... + } + + /* 获取控制台命令类型 */ + /* + 功能:获取控制台命令类型 + */ + char* get_ctl_command_type() { + ... + } + + /* 分配内存 */ + /* + 功能:分配内存 + 参数: + - size: 大小 + 返回值: + - 返回分配的内存地址 + */ + void* pg_malloc(size_t size) { + ... + } + + /* 分配带初始化的内存 */ + /* + 功能:分配带初始化的内存 + 参数: + - size: 大小 + 返回值: + - 返回分配的内存地址 + */ + void* pg_malloc_zero(size_t size) { + ... + } + +#ifdef __cplusplus + extern "C" { +#endif /* __cplusplus */ + + /* 在远程节点执行GUC命令 */ + /* + 功能:在远程节点执行GUC命令 + 参数: + - idx: 节点索引 + - command: 命令 + 返回值: + - 返回执行结果 + */ + int execute_guc_command_in_remote_node(int idx, char* command) { + ... } - case INSTANCE_DATANODE: { + + /* 格式化命令行选项 */ + /* + 功能:格式化命令行选项 + 参数: + - instance_name: 实例名 + - indatadir: 数据目录 + - local_mode: 本地模式 + 返回值: + - 返回命令行选项字符串 + */ + static char* form_commandline_options(const char* instance_name, const char* indatadir, bool local_mode) { + ... + } + + /* 获取节点数 */ + /* + 功能:获取节点数 + 返回值: + - 返回节点数 + */ + uint32 get_num_nodes() { + ... + } + + /* 获取本地数据节点数 */ + /* + 功能:获取本地数据节点数 + 返回值: + - 返回本地数据节点数 + */ + uint32 get_local_num_datanode() { + ... + } + + /* 判断节点ID是否为本地节点 */ + /* + 功能:判断节点ID是否为本地节点 + 参数: + - nodeid: 节点ID + 返回值: + - 返回判断结果 + */ + bool is_local_nodeid(uint32 nodeid) { + ... + } + + /* 判断节点名是否为本地节点 */ + /* + 功能:判断节点名是否为本地节点 + 参数: + - nodename: 节点名 + 返回值: + - 返回判断结果 + */ + bool is_local_node(char* nodename) { + ... + } + + /* 根据节点名获取节点索引 */ + /* + 功能:根据节点名获取节点索引 + 参数: + - nodename: 节点名 + 返回值: + - 返回节点索引 + */ + int32 get_nodeidx_by_name(char* nodename) { + ... + } + + /* 根据实例名获取本地数据库路径 */ + /* + 功能:根据实例名获取本地数据库路径 + 参数: + - instancename: 实例名 + - type: 类型 + - dbpath: 数据库路径 + 返回值: + - 返回获取结果 + */ + int get_local_dbpath_by_instancename(const char* instancename, int* type, char* dbpath) { + ... + } + + /* 初始化高斯集群配置 */ + /* + 功能:初始化高斯集群配置 + 返回值: + - 返回初始化结果 + */ + int init_gauss_cluster_config(void) { + ... + } + ... + /* + 函数功能:获取节点类型 + 函数参数:无 + 返回类型:NodeType + 全局变量:nodetype + 备注:该函数用于获取当前节点的类型,并返回相应的NodeType枚举值。 + */ + extern NodeType nodetype; + + /* + 函数功能:根据节点索引获取节点名称 + 函数参数:nodeidx - 节点索引 + 返回类型:char* + 备注:该函数根据给定的节点索引,返回对应节点的名称。 + */ + + char* getnodename(uint32 nodeidx); + + /* + 函数功能:获取主机名或IP地址 + 函数参数:out_name - 输出缓冲区的地址 + name_len - 输出缓冲区的长度 + 返回类型:bool + 备注:该函数根据操作系统的不同,获取当前主机的主机名或IP地址,并将其写入输出缓冲区。 + */ + + bool get_hostname_or_ip(char* out_name, size_t name_len); + + /* + 函数功能:根据数据库路径获取本地实例名称 + 函数参数:dbpath - 数据库路径 + instancename - 输出缓冲区的地址 + 返回类型:int32 + 备注:该函数根据给定的数据库路径,获取本地实例的名称,并将其写入输出缓冲区。 + */ + + int32 get_local_instancename_by_dbpath(char* dbpath, char* instancename); + + /* + 函数功能:复制字符串 + 函数参数:s - 要复制的字符串 + 返回类型:char* + 备注:该函数用于复制给定的字符串,并返回复制后的字符串地址。 + */ + + char* xstrdup(const char* s); + + /* + 函数功能:读取文件内容 + 函数参数:path - 文件路径 + reserve_num_lines - 预留的行数 + 返回类型:char** + 备注:该函数用于读取指定路径下的文件内容,并将每行内容保存在一个字符串数组中。 + */ + + char** readfile(const char* path, int reserve_num_lines); + + /* + 函数功能:释放文件内容内存 + 函数参数:lines - 文件内容字符串数组的地址 + 返回类型:void + 备注:该函数用于释放readfile函数返回的文件内容字符串数组占用的内存。 + */ + + void freefile(char** lines); + + /* + 函数功能:获取环境变量的值 + 函数参数:env_var - 环境变量的名称 + output_env_value - 输出缓冲区的地址 + env_var_value_len - 输出缓冲区的长度 + 返回类型:bool + 备注:该函数用于获取指定环境变量的值,并将其写入输出缓冲区。 + */ + + bool get_env_value(const char* env_var, char* output_env_value, size_t env_var_value_len); + + /* + 函数功能:获取所有数据节点的数量 + 函数参数:无 + 返回类型:int + 备注:该函数用于获取集群中所有数据节点的数量。 + */ + + int get_all_datanode_num(); + + /* + 函数功能:获取所有协调节点的数量 + 函数参数:无 + 返回类型:int + 备注:该函数用于获取集群中所有协调节点的数量。 + */ + + int get_all_coordinator_num(); + + /* + 函数功能:获取所有CM服务器的数量 + 函数参数:无 + 返回类型:int + 备注:该函数用于获取集群中所有CM服务器的数量。 + */ + + int get_all_cmserver_num(); + + /* + 函数功能:获取所有CM代理的数量 + 函数参数:无 + 返回类型:int + 备注:该函数用于获取集群中所有CM代理的数量。 + */ + + int get_all_cmagent_num(); + + /* + 函数功能:获取所有CNDN节点的数量 + 函数参数:无 + 返回类型:int + 备注:该函数用于获取集群中所有主备节点的数量。 + */ + + int get_all_cndn_num(); + + /* + 函数功能:获取所有GTM节点的数量 + 函数参数:无 + 返回类型:int + 备注:该函数用于获取集群中所有GTM节点的数量。 + */ + + int get_all_gtm_num(); + + /* + 函数功能:根据值获取AZ属性 + 函数参数:value - 属性值 + data_dir - 数据目录 + 返回类型:char* + 备注:该函数根据给定的属性值和数据目录,返回对应的AZ属性值。 + */ + + char* get_AZ_value(const char* value, const char* data_dir); + + /* + 函数功能:根据节点名称获取AZ属性名称 + 函数参数:nodename - 节点名称 + 返回类型:char* + 备注:该函数根据给定的节点名称,返回对应的AZ属性名称。 + */ + + char* get_AZname_by_nodename(char* nodename); + + /* + 函数功能:将字符串转换为小写 + 函数参数:source - 源字符串 + dest - 目标字符串的地址 + destlen - 目标字符串的长度 + 返回类型:void + 备注:该函数将给定的源字符串转换为小写,并写入目标字符串。 + */ + + void make_string_tolower(const char* source, char* dest, const int destlen); + + /* + 函数功能:保存预期的实例信息 + 函数参数:datadir - 数据目录 + 返回类型:void + 备注:该函数用于保存预期的实例信息到指定的数据目录。 + */ + + void save_expect_instance_info(const char* datadir); + + /* + 函数功能:保存远程实例信息 + 函数参数:result_file - 结果文件路径 + nodename - 节点名称 + command - 命令 + guc_info - guc配置信息 + isRealGucInfo - 是否为真实的guc信息 + 返回类型:void + 备注:该函数用于保存远程实例信息,包括节点名称、命令以及guc配置信息。 + */ + + void save_remote_instance_info( + const char* result_file, const char* nodename, char* command, gucInfo* guc_info, bool isRealGucInfo); + + /* + 函数功能:执行本地实例操作 + 函数参数:type - 操作类型 + instance_name - 实例名称 + indatadir - 输入数据目录 + 返回类型:void + 备注:该函数根据给定的操作类型,执行本地实例的相关操作,包括启动、停止等。 + */ + + void do_local_instance(int type, char* instance_name, char* indatadir); + + /* + 函数功能:执行远程实例操作 + 函数参数:nodename - 节点名称 + instance_name - 实例名称 + indatadir - 输入数据目录 + 返回类型:void + 备注:该函数根据给定的节点名称和实例名称,执行远程实例的相关操作,包括启动、停止等。 + */ + + void do_remote_instance(char* nodename, const char* instance_name, const char* indatadir); + + /* + 函数功能:执行所有节点的实例操作 + 函数参数:instance_name - 实例名称 + indatadir - 输入数据目录 + 返回类型:void + 备注:该函数执行集群中所有节点的实例操作,包括启动、停止等。 + */ + + void do_all_nodes_instance(const char* instance_name, const char* indatadir); + + /* + 函数功能:检查环境变量的值 + 函数参数:input_env_value - 输入环境变量的值 + 返回类型:void + 备注:该函数用于检查给定的环境变量的值是否符合要求。 + */ + + void check_env_value(const char* input_env_value); + + /* + 函数功能:获取guc参数的类型 + 函数参数:type - 参数类型字符串 + 返回类型:GucParaType + 备注:该函数根据给定的guc参数类型字符串,返回相应的枚举值GucParaType。 + */ + + GucParaType get_guc_type(const char* type); + + /* + 函数功能:获取guc参数的单位 + 函数参数:unit - 单位字符串 + 返回类型:UnitType + 备注:该函数根据给定的单位字符串,返回相应的枚举值UnitType。 + */ + + UnitType get_guc_unit(const char* unit); + + /* + 函数功能:执行本地参数值的修改 + 函数参数:type - 参数类型 + datadir - 数据目录 + 返回类型:int + 备注:该函数根据给定的参数类型和数据目录,执行本地参数值的修改操作。 + */ + + int do_local_para_value_change(int type, char* datadir); + + /* + 函数功能:执行本地guc命令 + 函数参数:type - 命令类型 + temp_datadir - 临时数据目录 + 返回类型:int + 备注:该函数根据给定的命令类型和临时数据目录,执行本地的guc命令。 + */ + + int do_local_guc_command(int type, char* temp_datadir); + + /* + 函数功能:获取guc选项 + 函数参数:无 + 返回类型:char** + 备注:该函数用于获取guc命令的选项,并返回选项列表。 + */ + + char** get_guc_option(); + + /* + 函数功能:解析guc选项 + 函数参数:guc_opt - guc选项字符串 + guc_variable_list - guc配置枚举值列表 + 返回类型:int + 备注:该函数根据给定的guc选项字符串,解析出具体的guc配置枚举值。 + */ + + int do_gucopt_parse(const char* guc_opt, struct guc_config_enum_entry& guc_variable_list); + + /* + 函数功能:检查参数是否合法 + 函数参数:type - 参数类型 + 返回类型:int + 备注:该函数用于检查给定的参数类型是否合法。 + */ + + int check_parameter(int type); + + /* + 函数功能:检查参数值是否合法 + 函数参数:paraname - 参数名 + type - 参数类型 + guc_list_value - guc配置值列表 + guc_list_unit - guc配置单位列表 + value - 参数值 + 返回类型:int + 备注:该函数用于检查给定的参数值是否合法。 + */ + + int check_parameter_value( + const char* paraname, GucParaType type, char* guc_list_value, const char* guc_list_unit, const char* value); + + /* + 函数功能:检查参数名是否合法 + 函数参数:guc_opt - guc选项字符串列表 + type - 参数类型 + 返回类型:int + 备注:该函数用于检查给定的参数名是否合法。 + */ + + int check_parameter_name(char** guc_opt, int type); + + /* + 函数功能:检查参数是否有效 + 函数参数:type - 参数类型 + 返回类型:bool + 备注:该函数用于检查给定的参数类型是否有效。 + */ + + bool check_parameter_is_valid(int type); + + /* + 函数功能:解析参数值 + 函数参数:paraname - 参数名 + value - 参数值 + guc_list_unit - guc配置单位列表 + result_int - 输出整数值的地址 + result_double - 输出浮点数值的地址 + isInt - 是否为整数类型 + 返回类型:int + 备注:该函数根据给定的参数名、参数值和单位列表,解析出具体的数值。 + */ + + int parse_value(const char* paraname, const char* value, const char* guc_list_unit, int64* result_int, + double* result_double, bool isInt); + + /* + 函数功能:获取参数的最小最大值 + 函数参数:guc_list_val - guc配置值列表 + value_list - 最小最大值列表 + 返回类型:int + 备注:该函数根据给定的guc配置值列表,获取对应参数的最小最大值。 + */ + + int get_guc_minmax_value(const char* guc_list_val, struct guc_minmax_value& value_list); + + /* + 函数功能:检查整数或实数类型的参数值 + 函数参数:paraname - 参数名 + guc_list_value - guc配置值列表 + guc_list_unit - guc配置单位列表 + value - 参数值 + isInt - 是否为整数类型 + 返回类型:int + 备注:该函数用于检查给定的整数或实数类型的参数值是否合法。 + */ + + int check_int_real_type_value( + const char* paraname, const char* guc_list_value, const char* guc_list_unit, const char* value, bool isInt); + + /* + 函数功能:检查枚举类型的参数值 + 函数参数:paraname - 参数名 + guc_list_value - guc配置值列表 + value - 参数值 + 返回类型:int + 备注:该函数用于检查给定的枚举类型的参数值是否合法。 + */ + + int check_enum_type_value(const char* paraname, char* guc_list_value, const char* value); + + /* + 函数功能:检查布尔类型的参数值 + 函数参数:value - 参数值 + 返回类型:int + 备注:该函数用于检查给定的布尔类型的参数值是否合法。 + */ + + int check_bool_type_value(const char* value); + + /* + 函数功能:检查字符串类型的参数值 + 函数参数:paraname - 参数名 + value - 参数值 + 返回类型:int + 备注:该函数用于检查给定的字符串类型的参数值是否合法。 + */ + + int check_string_type_value(const char* paraname, const char* value); + + /* + 函数功能:针对CN/GTM节点执行命令 + 函数参数:type - 命令类型 + indatadir - 输入数据目录 + isCoordinator - 是否为协调节点 + 返回类型:void + 备注:该函数根据给定的命令类型、数据目录和节点类型,执行特定节点的命令。 + */ + + void do_command_for_cn_gtm(int type, char* indatadir, bool isCoordinator); + + /* + 函数功能:针对DN节点执行命令 + 函数参数:type - 命令类型 + indatadir - 输入数据目录 + 返回类型:void + 备注:该函数根据给定的命令类型和数据目录,执行数据节点的命令。 + */ + + void do_command_for_dn(int type, char* indatadir); + + /* + 函数功能:针对CM节点执行命令 + 函数参数:type - 命令类型 + indatadir - 输入数据目录 + 返回类型:void + 备注:该函数根据给定的命令类型和数据目录,执行CM服务器的命令。 + */ + + void do_command_for_cm(int type, char* indatadir); + + /* + 函数功能:针对CNDN节点执行命令 + 函数参数:type - 命令类型 + indatadir - 输入数据目录 + 返回类型:void + 备注:该函数根据给定的命令类型和数据目录,执行主备节点的命令。 + */ + + void do_command_for_cndn(int type, char* indatadir); + + /* + 函数功能:获取CM实际路径 + 函数参数:type - 节点类型 + 返回类型:char* + 备注:该函数根据给定的节点类型,返回对应CM的实际路径。 + */ + + char* get_cm_real_path(int type); + + /* + 函数功能:创建临时目录 + 函数参数:pathdir - 目录路径 + 返回类型:void + 备注:该函数用于在指定路径下创建临时目录。 + */ + + void create_tmp_dir(const char* pathdir); + + /* + 函数功能:删除临时目录 + 函数参数:pathdir - 目录路径 + 返回类型:void + 备注:该函数用于删除指定路径下的临时目录。 + */ + + void remove_tmp_dir(const char* pathdir); + + /* + 函数功能:检查记录 + 函数参数:type - 节点类型 + flag_str - 记录标识字符串 + 返回类型:bool + 备注:该函数用于检查给定的节点类型和记录标识字符串是否符合要求。 + */ + + bool is_record(int type, char* flag_str); + + /* + 函数功能:执行命令 + 函数参数:无 + 返回类型:bool + 备注:该函数用于执行命令,并返回执行结果。 + */ + + // 比较字符串函数 + // src_str - 源字符串 + // start_str - 起始字符串 + // end_str - 结束字符串 + bool compare_str(char* src_str, char* start_str, char* end_str); + + // 执行带实例名选项的本地命令 + // type - 类型 + // instance_name - 实例名 + void do_command_with_instance_name_option_local(int type, char* instance_name); + + // 在本地执行远程实例 + // nodename - 节点名 + // instance_name - 实例名 + // indatadir - 数据目录 + void do_remote_instance_local(char* nodename, const char* instance_name, const char* indatadir); + + // 在所有节点上执行本地实例 + // instance_name - 实例名 + // indatadir - 数据目录 + void do_all_nodes_instance_local(const char* instance_name, const char* indatadir); + + // 在所有节点上串行执行本地实例 + // instance_name - 实例名 + // indatadir - 数据目录 + void do_all_nodes_instance_local_in_serial(const char* instance_name, const char* indatadir); + + // 在所有节点上并行执行本地实例 + // instance_name - 实例名 + // indatadir - 数据目录 + void do_all_nodes_instance_local_in_parallel(const char* instance_name, const char* indatadir); + + // 获取GUC行信息 + // line - 行 + // 返回GUC行信息数组 + char** get_guc_line_info(const char** line); + + // 获取环境变量字符串 + // env - 环境变量 + // 返回环境变量字符串 + static char* GetEnvStr(const char* env); + + // 并行执行命令 + // cmd - 命令 + // idx - 索引 + // is_local_node - 是否为本地节点 + static void executePopenCommandsParallel(const char* cmd, int idx, bool is_local_node); + + // 并行读取输出 + // cmd - 命令 + // if_for_all_instance - 是否为所有实例 + static void readPopenOutputParallel(const char* cmd, bool if_for_all_instance); + + // 以毫秒为单位的休眠 + // sleepMs - 休眠时间(毫秒) + static void SleepInMilliSec(uint32_t sleepMs); + + // 初始化全局命令 + static void init_global_command(); + + // 重置全局命令 + static void reset_global_command(); + + // 在所有节点上并行执行本地实例循环 + // instance_name - 实例名 + // indatadir - 数据目录 + static void do_all_nodes_instance_local_in_parallel_loop(const char* instance_name, const char* indatadir); + + /******************************************************************************* + 函数:xstrdup + 描述:复制字符串并分配新的内存空间 + 输入:s - 源字符串 + 输出:无 + 返回:目标字符串 + ***************************************************************************** + */ + char* xstrdup(const char* s) + { + char* result = NULL; + + result = strdup(s); + if (NULL == result) { + (void)write_stderr(_("%s: out of memory\n"), "gs_guc"); + exit(1); + } + return result; + } + /* + ****************************************************************************** + 函数:make_string_tolower + 描述:复制源字符串到目标字符串,并将目标字符串中的所有字母转换为小写 + 输入:source - 源字符串 + dest - 目标字符串 + destlen - 目标字符串长度 + 输出:无 + 返回:无 + ***************************************************************************** + */ + void make_string_tolower(const char* source, char* dest, const int destlen) + { + int i = 0; + int len = (int)strlen(source); + if (len > destlen) { + len = destlen; + } + for (i = 0; i < len; i++) + dest[i] = tolower(source[i]); + dest[i] = '\0'; + } + /* + ****************************************************************************** + 函数:get_instance_type + 描述:获取实例类型 + 输入:无 + 输出:无 + 返回:实例类型 + ****************************************************************************** + */ + const char* get_instance_type() + { + char* type = NULL; + switch (nodetype) { + case INSTANCE_COORDINATOR: { + type = "-Z coordinator"; + break; + } + case INSTANCE_DATANODE: { #ifdef ENABLE_MULTIPLE_NODES - type = "-Z datanode"; + type = "-Z datanode"; #else - type = ""; + type = ""; #endif - break; + break; + } + case INSTANCE_CMSERVER: { + type = "-Z cmserver"; + break; + } + case INSTANCE_CMAGENT: { + type = "-Z cmagent"; + break; + } + case INSTANCE_GTM: { + type = "-Z gtm"; + break; + } + default: { + type = ""; + break; + } } - case INSTANCE_CMSERVER: { - type = "-Z cmserver"; - break; - } - case INSTANCE_CMAGENT: { - type = "-Z cmagent"; - break; - } - case INSTANCE_GTM: { - type = "-Z gtm"; - break; - } - default: { - type = ""; - break; - } - } - return (const char*)type; + return (const char*)type; } - +``` /* ****************************************************************************** Function : modify_parameter_value - Description : If parameter value have the special character '$', when do remote setting - we should changed the parameter value first. - Input : value parameter value - : localMode do it on local node + Description : 如果参数值包含特殊字符 "$",在远程设置时需要先修改参数值。 + 输入 : value 参数值 + : localMode 在本地节点上执行操作 Return : char * - Warning : this function will malloc a buffer for returned value, and won't free in this function. - so the caller should free this buffer after use this function's returned value. + Warning : 此函数会为返回值分配内存,但在此函数中不会释放此内存。 + 所以调用者在使用此函数的返回值后应该释放这段内存。 +//该代码是一个用于修改参数值的函数。函数的目的是在参数值中查找特殊字符 "$",并根据是否在本地节点上执行操作,进行相应的修改。 +// +//函数中的变量说明: +//- `value`:参数值 +//- `localMode`:是否在本地节点上执行操作的标志,类型为布尔型 +//- `i`、`j`、`k`:循环计数变量 +//- `backslash_num`:反斜杠的数量,用于添加到特殊字符 "$" 前 +//- `local_backslash_num`:在本地模式下需要添加的反斜杠数量 +//- `remote_backslash_num`:在远程模式下需要添加的反斜杠数量 +//- `buffer`:存储修改后的参数值的缓冲区 +// +//函数的实现逻辑如下: +//1. 分配大小为 `MAX_VALUE_LEN` 的字符缓冲区 `buffer`。 +//2. 遍历参数值 `value` 中的每个字符: +//- 如果字符是特殊字符 "$",则根据 `localMode` 的值确定需要添加的反斜杠数量,将相应数量的反斜杠添加到 `buffer` 中,并将特殊字符 "$" 添加到 `buffer` 中。 +//- 否则,将当前字符直接添加到 `buffer` 中。 +//3. 返回修改后的参数值 `buffer`。 +// +//示例应用: +//假设有一个配置文件中的参数值为 `"$libdir/xxx"`,在本地模式下需要修改为 `"\$libdir / xxx"`,在远程模式下需要修改为 `"\\\$libdir / xxx"`,可以使用该函数实现。调用函数时,传入参数值和模式标志,即可得到修改后的参数值。 ****************************************************************************** */ + +// 修改参数值的函数 char* modify_parameter_value(const char* value, bool localMode) { int i = 0; @@ -478,15 +1329,18 @@ char* modify_parameter_value(const char* value, bool localMode) const int local_backslash_num = 1; const int remote_backslash_num = 3; + // 分配内存空间 char* buffer = (char*)pg_malloc_zero(MAX_VALUE_LEN * sizeof(char)); + // 遍历参数值,修改特殊字符 for (i = 0, j = 0; i < (int)strlen(value) && j < MAX_VALUE_LEN; i++, j++) { if (value[i] == '$') { /* - * If value have the special character '$', adding backslash before '$' is different between local - * command and remote command. when do remote setting, the commands like this: remote command: ssh -n - * nodename "gs_guc set -Z datanode -I all -c \"dynamic_library_path='\\\$libdir/xxx'\"" local command: - * gs_guc set -Z datanode -I all -c \"dynamic_library_path='\$libdir/xxx'\"" + * 如果参数值包含特殊字符 "$",在本地命令和远程命令中在 "$" 前添加反斜杠的数量不同。 + * 在远程设置时,命令如下: + * remote command: ssh -n nodename "gs_guc set -Z datanode -I all -c \"dynamic_library_path='\\\$libdir/xxx'\"" + * 在本地执行时,命令如下: + * local command: gs_guc set -Z datanode -I all -c \"dynamic_library_path='\$libdir/xxx'\" */ backslash_num = localMode ? local_backslash_num : remote_backslash_num; for (k = 0; k < backslash_num && j < MAX_VALUE_LEN; k++) { @@ -498,7 +1352,8 @@ char* modify_parameter_value(const char* value, bool localMode) exit(1); } buffer[j] = value[i]; - } else { + } + else { buffer[j] = value[i]; } } @@ -508,84 +1363,88 @@ char* modify_parameter_value(const char* value, bool localMode) /* ****************************************************************************** Function : form_commandline_options - Description : Generate the complete guc command - Input : instance_name - the instance name - indatadir - the path of instance - local_mode - local mode or not - Output : None - Return : None + Description : 生成完整的guc命令 + Input : instance_name - 实例名称 + indatadir - 实例路径 + local_mode - 是否是本地模式 + Output : 无 + Return : 无 ****************************************************************************** */ static char* form_commandline_options(const char* instance_name, const char* indatadir, bool local_mode) { - char* buffer = NULL; - int buflen = 0; - int curlen = 0; + char* buffer = NULL; // 存储完整命令的缓冲区 + int buflen = 0; // 缓冲区长度 + int curlen = 0; // 当前长度 int i = 0; int nRet = 0; - /* a variable that storage new parameter value*/ - char* new_value = NULL; + char* new_value = NULL; // 存储新参数值的变量 - /* other standard options */ + /* 其他标准选项 */ #define MIN_COMMAND_LEN 256 - /* adding -c + '=' + two ' ' + two '\' + two '"' */ + /* 添加 -c + '=' + 两个空格 + 两个反斜杠 + 两个引号 */ #define ALLIG_POSTGRES_CONF_LEN 20 - /* adding -h + two '\' + two '"' */ + /* 添加 -h + 两个反斜杠 + 两个引号 */ #define ALLIG_HBA_CONF_LEN 10 - /* -N options is not required */ - buflen = MIN_COMMAND_LEN; + buflen = MIN_COMMAND_LEN; // 初始化缓冲区长度为最小命令长度 if (instance_name != NULL) { - buflen += strlen(instance_name); - } else { - buflen += strlen(indatadir); + buflen += strlen(instance_name); // 如果实例名称存在,添加实例名称长度 + } + else { + buflen += strlen(indatadir); // 如果实例名称不存在,添加实例路径长度 } - /* find length required for options */ + /* 计算选项所需的长度 */ for (i = 0; i < config_param_number; i++) { if (!is_hba_conf) { - buflen += (ALLIG_POSTGRES_CONF_LEN + strlen(config_param[i])); + buflen += (ALLIG_POSTGRES_CONF_LEN + strlen(config_param[i])); // 不是hba配置文件时,添加参数长度 if (config_value[i] != NULL) { - buflen += strlen(config_value[i]); + buflen += strlen(config_value[i]); // 如果参数值存在,添加参数值长度 } - } else { - buflen += ALLIG_HBA_CONF_LEN; + } + else { + buflen += ALLIG_HBA_CONF_LEN; // 是hba配置文件时,只添加固定长度 if (config_value[i] != NULL) { - buflen += strlen(config_value[i]); + buflen += strlen(config_value[i]); // 如果参数值存在,添加参数值长度 } } } - buffer = (char*)pg_malloc_zero(buflen); + buffer = (char*)pg_malloc_zero(buflen); // 分配缓冲区内存 - /* SET / RESET [--cordinator --datanode --gtm ] */ + /* 设置/重置 [--cordinator --datanode --gtm ] */ curlen = snprintf_s( buffer, buflen, buflen - 1, "gs_guc %s %s ", get_ctl_command_type(), get_instance_type()); - securec_check_ss_c(curlen, buffer, "\0"); + + // 示例:get_ctl_command_type()返回"set",get_instance_type()返回"cordinator",则buffer为"gs_guc set cordinator " + if (nodetype == INSTANCE_CMAGENT || nodetype == INSTANCE_CMSERVER) { nRet = snprintf_s(buffer + curlen, (buflen - curlen), (buflen - curlen - 1), "-D \"cm_instance_data_path\""); - } else { + } + else { /* -I or -D */ if (NULL != instance_name) { - nRet = snprintf_s(buffer + curlen , (buflen - curlen), (buflen - curlen - 1), "-I %s", - instance_name); - } else { + nRet = snprintf_s(buffer + curlen, (buflen - curlen), (buflen - curlen - 1), "-I %s", + instance_name); + } + else { nRet = snprintf_s(buffer + curlen, (buflen - curlen), (buflen - curlen - 1), "-D \"%s\"", - indatadir); + indatadir); } } securec_check_ss_c(nRet, buffer, "\0"); curlen = curlen + nRet; - /* -c options */ + + // 示例:如果nodetype等于INSTANCE_CMAGENT,那么加上"-D \"cm_instance_data_path\"",否则如果实例名称存在,加上"-I 实例名称",否则加上"-D 实例路径" + + /* -c 选项 */ for (i = 0; i < config_param_number; i++) { if (!is_hba_conf) { - /* The parameter name does not has special character '$'. - * So We need to give attention to the parameter value. - */ if (config_value[i] != NULL) { new_value = modify_parameter_value(config_value[i], local_mode); if (local_mode) { @@ -597,7 +1456,8 @@ static char* form_commandline_options(const char* instance_name, const char* ind config_param[i], new_value, '"'); - } else { + } + else { nRet = snprintf_s(buffer + curlen, (buflen - curlen), (buflen - curlen - 1), @@ -606,16 +1466,19 @@ static char* form_commandline_options(const char* instance_name, const char* ind new_value); } GS_FREE(new_value); - } else { + } + else { nRet = snprintf_s(buffer + curlen, (buflen - curlen), (buflen - curlen - 1), " -c %s", config_param[i]); } securec_check_ss_c(nRet, buffer, "\0"); curlen = curlen + nRet; - } else { + } + else { if (local_mode) { nRet = snprintf_s( buffer + curlen, (buflen - curlen), (buflen - curlen - 1), " -h %c%s%c", '"', config_value[i], '"'); - } else { + } + else { nRet = snprintf_s( buffer + curlen, (buflen - curlen), (buflen - curlen - 1), " -h \\\"%s\\\"", config_value[i]); } @@ -626,15 +1489,15 @@ static char* form_commandline_options(const char* instance_name, const char* ind return buffer; } - +``` /* ****************************************************************************** Function : get_nodeidx_by_HA - Description : get the node index by HA ip/port - Input : HAIp - HA ipaddr - : HAPort HA port + Description : 根据HA的IP地址和端口获取节点索引 + Input : HAIp - HA的IP地址 + : HAPort - HA的端口 Output : None - Return : int - node id index + Return : int - 节点索引 ******************************************************************************* */ uint32 get_nodeidx_by_HA(const char* HAIp, uint32 HAPort) @@ -642,25 +1505,28 @@ uint32 get_nodeidx_by_HA(const char* HAIp, uint32 HAPort) uint32 i = 0; uint32 j = 0; + // 遍历所有节点 for (i = 0; i < g_node_num; i++) { + // 遍历当前节点的所有数据节点 for (j = 0; j < g_node[i].datanodeCount; j++) { + // 通过比较IP地址和端口号找到匹配的节点 if ((0 == strncmp(g_node[i].datanode[j].datanodeLocalHAIP[0], HAIp, strlen(HAIp))) && (0 == g_node[i].datanode[j].datanodeLocalHAPort - HAPort)) - return i; + return i; // 返回节点索引 } } - return 0; + return 0; // 没有找到匹配的节点,返回0 } + /* ****************************************************************************** Function : get_instance_id - Description : get_instance_id by data path and HA ip/port. First, get node index by HA ip/port; - then get instance id by data path - Input : dataPath - datanode instance path - : HAIp - HA ipaddr - : HAPort - HA port + Description : 根据数据路径和HA的IP地址和端口获取实例ID + Input : dataPath - 数据节点实例路径 + : HAIp - HA的IP地址 + : HAPort - HA的端口 Output : None - Return : int - datanode instance id + Return : int - 数据节点实例ID ****************************************************************************** */ uint32 get_instance_id(const char* dataPath, const char* HAIp, uint32 HAPort) @@ -668,34 +1534,44 @@ uint32 get_instance_id(const char* dataPath, const char* HAIp, uint32 HAPort) uint32 i = 0; uint32 nodeidx = 0; + // 先根据HA的IP地址和端口获取节点索引 nodeidx = get_nodeidx_by_HA(HAIp, HAPort); + // 遍历指定节点的所有数据节点 for (i = 0; i < g_node[nodeidx].datanodeCount; i++) { + // 通过比较数据路径找到匹配的实例ID if (0 == strncmp(g_node[nodeidx].datanode[i].datanodeLocalDataPath, dataPath, strlen(dataPath))) - return g_node[nodeidx].datanode[i].datanodeId; + return g_node[nodeidx].datanode[i].datanodeId; // 返回实例ID } - return 0; + return 0; // 没有找到匹配的实例ID,返回0 } /* ****************************************************************************** Function : is_instance_level_correct - Description : check whether the instance is in same safety ring by data path , HA ip/port and the level. First, get -node index by HA ip/port; then check the level Input : dataPath - datanode instance path : HAIp - HA ipaddr : -HAPort - HA port : level - the instance level Output : None Return : True/False + Description : 检查实例的级别是否正确,通过数据路径、HA的IP地址和端口以及级别进行检查 + Input : dataPath - 数据节点实例路径 + : HAIp - HA的IP地址 + : HAPort - HA的端口 + : level - 实例级别 + Output : None + Return : True/False ****************************************************************************** */ bool is_instance_level_correct(const char* dataPath, const char* HAIp, uint32 HAPort, uint32 level) { uint32 i = 0; uint32 nodeidx = 0; - /*get node idx by HA information */ + + // 先根据HA的IP地址和端口获取节点索引 nodeidx = get_nodeidx_by_HA(HAIp, HAPort); + // 遍历指定节点的所有数据节点 for (i = 0; i < g_node[nodeidx].datanodeCount; i++) { + // 通过比较数据路径和级别进行匹配 if ((0 == strncmp(g_node[nodeidx].datanode[i].datanodeLocalDataPath, dataPath, strlen(dataPath))) && (level == g_node[nodeidx].datanode[i].datanodeRole)) - return true; + return true; // 匹配成功,返回true } - return false; + return false; // 没有匹配的实例或级别,返回false } /* @@ -744,12 +1620,11 @@ char* GetPgxcNodeNameForMasterDnInstance(int32 nodeidx, int32 instanceidx) return pgxcNodeName; } - /* ****************************************************************************** CheckInstanceNameForSinglePrimaryMutilStandby - check the instance_name, whether it is in single primary mutile standby cluster or not - instance Type info: consist with OM + 检查实例名称是否属于单主多备集群 + 实例类型信息:与OM一致 PRIMARY_DN 0 STANDBY_DN 1 DUMMY_STANDBY_DN 2 @@ -764,7 +1639,7 @@ bool CheckInstanceNameForSinglePrimaryMutilStandby(int32 nodeidx, const char* in char* pgxcNodeName = NULL; for (i = 0; i < g_node[nodeidx].datanodeCount; i++) { - /* deal with the primary dn instance branch */ + /* 处理主节点实例分支 */ if (g_node[nodeidx].datanode[i].datanodeRole == 0) { pgxcNodeName = GetPgxcNodeNameForMasterDnInstance(nodeidx, i); nameLen = strlen(instance_name) > strlen(pgxcNodeName) ? strlen(instance_name) : strlen(pgxcNodeName); @@ -773,24 +1648,22 @@ bool CheckInstanceNameForSinglePrimaryMutilStandby(int32 nodeidx, const char* in if (ret == 0) { return true; } - } else { - /* deal with the standby dn instance branch - * The pgxc_node_name of the primary and standby instances are the same, So get it by primary DN instance - * get the master instance first - * nodeIndex -> primary DN node index - * instanceidx -> primary DN instance index - * dataPath -> primary DN data path + } + else { + /* 处理备节点实例分支 + * 主节点和备节点的pgxc_node_name相同,因此通过主节点实例获取 + * 首先获取主节点的索引、实例索引和数据路径 */ uint32 nodeIndex = 0; uint32 instanceidx = 0; - char dataPath[MAXPGPATH] = {0}; + char dataPath[MAXPGPATH] = { 0 }; size_t dataPathLen = 0; ret = memset_s(dataPath, MAXPGPATH, '\0', MAXPGPATH); securec_check_c(ret, "\0", "\0"); /* - * Each data ring must have a primary instance. So get the node index and data path. + * 每个数据环必须有一个主实例,获取节点索引和数据路径 */ for (uint32 dnId = 0; dnId < g_dn_replication_num - 1; dnId++) { if (0 == g_node[nodeidx].datanode[i].peerDatanodes[dnId].datanodePeerRole) { @@ -807,7 +1680,7 @@ bool CheckInstanceNameForSinglePrimaryMutilStandby(int32 nodeidx, const char* in } /* - * Check the result to ensure that the nodeIndex and instance data path of primary instance is found. + * 检查结果,确保找到主实例的节点索引和实例数据路径 */ if (dataPath[0] == '\0') { fprintf(stderr, _("ERROR: Failed to get primary DN instance information.\n")); @@ -815,14 +1688,14 @@ bool CheckInstanceNameForSinglePrimaryMutilStandby(int32 nodeidx, const char* in } /* - * get the primary DN instance index by node index and data path + * 通过节点索引和数据路径获取主节点实例索引 */ for (j = 0; j < g_node[nodeIndex].datanodeCount; j++) { if (g_node[nodeIndex].datanode[i].datanodeRole == 0) { dataPathLen = strlen(g_node[nodeIndex].datanode[j].datanodeLocalDataPath); if (0 == strncmp(g_node[nodeIndex].datanode[j].datanodeLocalDataPath, - dataPath, - dataPathLen > strlen(dataPath) ? dataPathLen : strlen(dataPath))) { + dataPath, + dataPathLen > strlen(dataPath) ? dataPathLen : strlen(dataPath))) { instanceidx = j; break; } @@ -840,104 +1713,103 @@ bool CheckInstanceNameForSinglePrimaryMutilStandby(int32 nodeidx, const char* in } return false; } - -/* +/** ****************************************************************************** Function : validate_instance_name_for_DN - Description : validate the DN instance name. - Input : nodeidx - node id index - instance_name - instance name + Description : 验证DN实例名称的有效性。 + Input : + nodeidx - 节点id索引 + instance_name - 实例名称 Return : bool ****************************************************************************** */ bool validate_instance_name_for_DN(int32 nodeidx, const char* instance_name) { - bool isCorrect = false; - uint32 i = 0; - uint32 instance_id = 0; - char temp_instance_name[MAXPGPATH]; - int rc = 0; + bool isCorrect = false; // 标记实例名称是否正确 + uint32 i = 0; // 循环计数器 + uint32 instance_id = 0; // 实例ID + char temp_instance_name[MAXPGPATH]; // 临时实例名称 + int rc = 0; // 函数调用返回值 - rc = memset_s(temp_instance_name, MAXPGPATH, '\0', MAXPGPATH); - securec_check_c(rc, "\0", "\0"); + rc = memset_s(temp_instance_name, MAXPGPATH, '\0', MAXPGPATH); // 初始化temp_instance_name - if ((int)strlen(instance_name) < DN_INSTANCE_LEN) { - return false; + if ((int)strlen(instance_name) < DN_INSTANCE_LEN) { // 实例名称长度不合法 + return false; // 返回false,表示实例名称不正确 } /*single primary multil standby */ - if (g_multi_az_cluster) { - isCorrect = CheckInstanceNameForSinglePrimaryMutilStandby(nodeidx, instance_name); - } else { + if (g_multi_az_cluster) { // 多AZ集群 + isCorrect = CheckInstanceNameForSinglePrimaryMutilStandby(nodeidx, instance_name); // 调用检查实例名称的函数 + } + else { /* master_standby */ - if ((int)strlen(instance_name) != DN_INSTANCE_LEN) - return false; + if ((int)strlen(instance_name) != DN_INSTANCE_LEN) // 实例名称长度不合法 + return false; // 返回false,表示实例名称不正确 - for (i = 0; i < g_node[nodeidx].datanodeCount; i++) { - if (g_node[nodeidx].datanode[i].datanodeRole == 0) { + for (i = 0; i < g_node[nodeidx].datanodeCount; i++) { // 遍历所有DataNode + if (g_node[nodeidx].datanode[i].datanodeRole == 0) { // 主节点 if (is_instance_level_correct(g_node[nodeidx].datanode[i].datanodePeerDataPath, - g_node[nodeidx].datanode[i].datanodePeerHAIP[0], - g_node[nodeidx].datanode[i].datanodePeerHAPort, - 1)) + g_node[nodeidx].datanode[i].datanodePeerHAIP[0], + g_node[nodeidx].datanode[i].datanodePeerHAPort, + 1)) // 调用检查实例级别的函数,返回是否正确 instance_id = get_instance_id(g_node[nodeidx].datanode[i].datanodePeerDataPath, g_node[nodeidx].datanode[i].datanodePeerHAIP[0], - g_node[nodeidx].datanode[i].datanodePeerHAPort); + g_node[nodeidx].datanode[i].datanodePeerHAPort); // 获取实例ID else instance_id = get_instance_id(g_node[nodeidx].datanode[i].datanodePeer2DataPath, g_node[nodeidx].datanode[i].datanodePeer2HAIP[0], - g_node[nodeidx].datanode[i].datanodePeer2HAPort); + g_node[nodeidx].datanode[i].datanodePeer2HAPort); // 获取实例ID rc = snprintf_s(temp_instance_name, MAXPGPATH, MAXPGPATH - 1, "dn_%d_%d", (int)g_node[nodeidx].datanode[i].datanodeId, - (int)instance_id); - } else { + (int)instance_id); // 根据实例ID生成临时实例名称 + } + else { // 备节点 if (is_instance_level_correct(g_node[nodeidx].datanode[i].datanodePeerDataPath, - g_node[nodeidx].datanode[i].datanodePeerHAIP[0], - g_node[nodeidx].datanode[i].datanodePeerHAPort, - 0)) + g_node[nodeidx].datanode[i].datanodePeerHAIP[0], + g_node[nodeidx].datanode[i].datanodePeerHAPort, + 0)) // 调用检查实例级别的函数,返回是否正确 instance_id = get_instance_id(g_node[nodeidx].datanode[i].datanodePeerDataPath, g_node[nodeidx].datanode[i].datanodePeerHAIP[0], - g_node[nodeidx].datanode[i].datanodePeerHAPort); + g_node[nodeidx].datanode[i].datanodePeerHAPort); // 获取实例ID else instance_id = get_instance_id(g_node[nodeidx].datanode[i].datanodePeer2DataPath, g_node[nodeidx].datanode[i].datanodePeer2HAIP[0], - g_node[nodeidx].datanode[i].datanodePeer2HAPort); + g_node[nodeidx].datanode[i].datanodePeer2HAPort); // 获取实例ID rc = snprintf_s(temp_instance_name, MAXPGPATH, MAXPGPATH - 1, "dn_%d_%d", (int)instance_id, - (int)g_node[nodeidx].datanode[i].datanodeId); + (int)g_node[nodeidx].datanode[i].datanodeId); // 根据实例ID生成临时实例名称 } - securec_check_ss_c(rc, "\0", "\0"); + securec_check_ss_c(rc, "\0", "\0"); // 检查snprintf_s函数调用的返回值 - if (strncmp(temp_instance_name, instance_name, strlen(instance_name)) == 0) { - isCorrect = true; - break; + if (strncmp(temp_instance_name, instance_name, strlen(instance_name)) == 0) { // 比较实例名称是否匹配 + isCorrect = true; // 实例名称正确 + break; // 结束循环 } - rc = memset_s(temp_instance_name, MAXPGPATH, '\0', MAXPGPATH); - securec_check_c(rc, "\0", "\0"); + rc = memset_s(temp_instance_name, MAXPGPATH, '\0', MAXPGPATH); // 初始化temp_instance_name } } - return isCorrect; + return isCorrect; // 返回实例名称是否正确的结果 } - /* ****************************************************************************** Function : validate_remote_instance_name - Description : validate remote instance name. - The gs_guc commands like this: "-I instance_name -N nodename". - The instance name type like this: + Description : 验证远程实例名称。 + 类似于gs_guc命令的格式:"-I 实例名称 -N 节点名称"。 + 实例名称类型如下: INSTANCE_COORDINATOR -> cn_instanceId INSTANCE_GTM -> one INSTANCE_DATANODE -> dn_masterId_slaveId, dn_masterId_dummyslaveId - Input : nodename - node name - type - instance type - instance_name - instance name + Input : nodename - 节点名称 + type - 实例类型 + instance_name - 实例名称 Return : int ****************************************************************************** */ @@ -952,9 +1824,9 @@ int validate_remote_instance_name(char* nodename, int type, char* instance_name) securec_check_c(rc, "\0", "\0"); nodeidx = get_nodeidx_by_name(nodename); - /* check the node name, makesure it is in cluster_static_config */ + /* 检查节点名称,确保其在集群静态配置文件中存在 */ if (nodeidx < 0) { - write_stderr("ERROR: Node %s is not found in static config file.\n", nodename); + write_stderr("ERROR: 节点 %s 在静态配置文件中未找到。\n", nodename); return 1; } @@ -970,39 +1842,41 @@ int validate_remote_instance_name(char* nodename, int type, char* instance_name) if ((CN_INSTANCE_LEN == (int)strlen(instance_name)) && (0 == strncmp(temp_instance_name, instance_name, strlen(instance_name)))) isCorrect = true; - } else if (type == INSTANCE_GTM) { + } + else if (type == INSTANCE_GTM) { if ((0 != g_node[nodeidx].gtmId) && (GTM_INSTANCE_LEN == (int)strlen(instance_name)) && (0 == strncmp(instance_name, "one", strlen("one")))) isCorrect = true; - } else { + } + else { isCorrect = validate_instance_name_for_DN(nodeidx, instance_name); } if (isCorrect) { return 0; - } else { - write_stderr("ERROR: Instance name %s is incorrect.\n", instance_name); + } + else { + write_stderr("ERROR: 实例名称 %s 不正确。\n", instance_name); return 1; } } /* ****************************************************************************** Function : validate_nodename - Description : validate the node name. If the node name is not all, makesure it is - in the cluster static config file. - Input : nodename - node name + Description : 验证节点名称,如果节点名称不是 all,则确保其在集群静态配置文件中存在。 + Input : nodename - 节点名称 Return : int ****************************************************************************** */ int validate_nodename(char* nodename) { int32 nodeidx = 0; - /* makesure the node name is correct */ + /* 确保节点名称正确 */ if ((NULL != nodename) && (0 != strncmp(nodename, "all", sizeof("all")))) { nodeidx = get_nodeidx_by_name(nodename); - /* check the node name, makesure it is in cluster_static_config */ + /* 检查节点名称,确保其在集群静态配置文件中存在 */ if (nodeidx < 0) { - write_stderr("ERROR: Node %s is not found in static config file.\n", nodename); + write_stderr("ERROR: 节点 %s 在静态配置文件中未找到。\n", nodename); return 1; } } @@ -1010,12 +1884,12 @@ int validate_nodename(char* nodename) } /* ****************************************************************************** - Function : check_instance_name - Description : check instance name. We known that the node name and instance name are both not 'NULL' and not 'all'. - Input : nodename - node name - type - instance type - instance_name - instance name - Return : int + 函数:check_instance_name + 描述:检查实例名称。我们知道节点名称和实例名称都不为'NULL'和'all'。 + 输入:nodename - 节点名称 + type - 实例类型 + instance_name - 实例名称 + 返回:int ****************************************************************************** */ int check_instance_name(char* nodename, int type, char* instance_name) @@ -1026,14 +1900,15 @@ int check_instance_name(char* nodename, int type, char* instance_name) rc = memset_s(temp_datadir, MAXPGPATH, '\0', MAXPGPATH); securec_check_c(rc, "\0", "\0"); - /* -N nodename -I instance_name*/ + /* -N nodename -I instance_name */ if ((0 != strncmp(nodename, "all", sizeof("all"))) && (0 != strncmp(instance_name, "all", sizeof("all")))) { if (is_local_node(nodename)) { if (get_local_dbpath_by_instancename(instance_name, &type, temp_datadir) == CLUSTER_CONFIG_ERROR) { - write_stderr("ERROR: Instance name %s is incorrect.\n", instance_name); + write_stderr("ERROR: 实例名称 %s 不正确。\n", instance_name); return 1; } - } else { + } + else { if (0 != validate_remote_instance_name(nodename, type, instance_name)) return 1; } @@ -1041,15 +1916,16 @@ int check_instance_name(char* nodename, int type, char* instance_name) return 0; } + /* ****************************************************************************** - Function : validate_node_instance_name - Description : validate the node name and instance name - Input : nodename - node name - type - instance type - instance_name - instance name - Output : None - Return : int + 函数:validate_node_instance_name + 描述:验证节点名称和实例名称 + 输入:nodename - 节点名称 + type - 实例类型 + instance_name - 实例名称 + 输出:无 + 返回:int ****************************************************************************** */ int validate_node_instance_name(char* nodename, int type, char* instance_name) @@ -1060,77 +1936,79 @@ int validate_node_instance_name(char* nodename, int type, char* instance_name) rc = memset_s(temp_datadir, MAXPGPATH, '\0', MAXPGPATH); securec_check_c(rc, "\0", "\0"); - /* Verify that the node name is correct */ + /* 验证节点名称是否正确 */ if (0 != validate_nodename(nodename)) return 1; if ((NULL == nodename) && (NULL != instance_name)) { - /* -I instance_name*/ + /* -I instance_name */ if ((0 != strncmp(instance_name, "all", sizeof("all"))) && (get_local_dbpath_by_instancename(instance_name, &type, temp_datadir) == CLUSTER_CONFIG_ERROR)) { - write_stderr("ERROR: Instance name %s is incorrect.\n", instance_name); + write_stderr("ERROR: 实例名称 %s 不正确。\n", instance_name); return 1; } } if ((NULL != nodename) && (NULL != instance_name)) { - /* skip check '-N all -I all', '-N nodename -I all'*/ - /* command ' -N all -I instance_name' is incorrect expect for DN*/ + /* 跳过检查 '-N all -I all', '-N nodename -I all' */ + /* 对于非数据节点(INSTANCE_DATANODE),命令 '-N all -I instance_name' 是不正确的 */ if (type != INSTANCE_DATANODE) { if ((strncmp(nodename, "all", sizeof("all")) == 0) && (strncmp(instance_name, "all", sizeof("all")) != 0)) { - write_stderr( - "ERROR: Instance name %s is incorrect. When -N is 'all', -I must be the same.\n", instance_name); + write_stderr("ERROR: 实例名称 %s 不正确。当 -N 为 'all' 时,-I 必须相同。\n", instance_name); return 1; } } - /* -N nodename -I instance_name*/ + /* -N nodename -I instance_name */ if (0 != check_instance_name(nodename, type, instance_name)) return 1; } return 0; } + /* ****************************************************************************** - Function : validate_cluster_guc_options - Description : check the -N, -I and -D parameter - Input : nodename - node name - type - node type - instance_name - instance name - indatadir - instance data directory - Output : None - Return : int + 函数:validate_cluster_guc_options + 描述:检查 -N、-I 和 -D 参数 + 输入:nodename - 节点名称 + type - 节点类型 + instance_name - 实例名称 + indatadir - 实例数据目录 + 输出:无 + 返回:int ****************************************************************************** */ int validate_cluster_guc_options(char* nodename, int type, char* instance_name, char* indatadir) { if ((NULL != nodename) || (NULL != instance_name)) { if (0 != init_gauss_cluster_config()) { - (void)write_stderr("ERROR: Failed to get cluster information from static configuration file.\n"); + (void)write_stderr("ERROR: 无法从静态配置文件中获取集群信息。\n"); return 1; } } if ((NULL == instance_name) && (NULL == indatadir)) { if (type == INSTANCE_CMAGENT || type == INSTANCE_CMSERVER) { - write_stderr("ERROR: -I all are mandatory for executing gs_guc.\n"); - } else { - write_stderr("ERROR: -D or -I are mandatory for executing gs_guc.\n"); + write_stderr("ERROR: 执行 gs_guc 时需要 -I all。\n"); + } + else { + write_stderr("ERROR: 执行 gs_guc 时需要 -D 或者 -I。\n"); } return 1; - } else if ((NULL != instance_name) && (NULL != indatadir)) { - write_stderr("ERROR: -D or -I only need one for executing gs_guc.\n"); + } + else if ((NULL != instance_name) && (NULL != indatadir)) { + write_stderr("ERROR: 执行 gs_guc 时只需要 -D 或者 -I 其中之一。\n"); return 1; } if (node_type_number == LARGE_INSTANCE_NUM && (NULL != instance_name) && (0 != strncmp(instance_name, "all", sizeof("all")))) { - write_stderr("ERROR: when -Z is coordinator and datanode, the -I must be 'all'.\n"); + write_stderr("ERROR: 当 -Z 同时为 coordinator 和 datanode 时,-I 必须为 'all'。\n"); return 1; } - /* The user guarantees the correctness of the -D parameter value*/ + /* 用户保证 -D 参数值的正确性 */ if (0 != validate_node_instance_name(nodename, type, instance_name)) return 1; @@ -1138,16 +2016,23 @@ int validate_cluster_guc_options(char* nodename, int type, char* instance_name, return 0; } +*/ +// 示例说明: +// check_instance_name 函数用于检查实例名称,并根据节点名称和实例名称判断是否进行进一步的验证。如果节点是本地节点,则根据实例名称获取本地数据目录,并进行验证;如果节点是远程节点,则调用 validate_remote_instance_name 函数进行验证。该函数可以用于验证命令行参数中的节点名称和实例名称的正确性。 + +// validate_node_instance_name 函数用于验证节点名称和实例名称的正确性。首先,通过 validate_nodename 函数验证节点名称是否正确。然后,根据命令行参数中的节点名称和实例名称判断是否进行进一步的验证。如果节点名称为 NULL 而实例名称不为 NULL,则根据实例名称获取本地数据目录,并进行验证。如果节点名称和实例名称都不为 NULL,则判断是否为特定情况下的非法输入,并调用 check_instance_name 函数进行验证。该函数可以用于验证命令行参数中的节点名称和实例名称的正确性。 + +// validate_cluster_guc_options 函数用于检查 -N、-I 和 -D 参数的正确性。首先,根据命令行参数中的节点名称和实例名称判断是否需要从静态配置文件中获取集群信息。然后,根据参数的不同情况判断是否为非法输入,并调用 validate_node_instance_name 函数进行验证。最后,根据节点类型调用 do_checkvalidate 函数进行进一步的检查。该函数可以用于验证命令行参数中的 -N、-I 和 -D 参数的正确性。 /* ****************************************************************************** - Function : save_expect_instance_info - Description : save expect instance information into global parameter. - node name and instance guc configure file - Input : datadir (the directory about instance) - Output : "expected instance path: %s\n", gucconf_file - Return : void - ****************************************************************************** +函数:save_expect_instance_info +描述:保存期望的实例信息到全局参数中。 + 节点名称和实例配置文件路径。 +输入:datadir(实例的目录) +输出:"expected instance path: %s\n",gucconf_file +返回值:void +****************************************************************************** */ void save_expect_instance_info(const char* datadir) { @@ -1157,7 +2042,7 @@ void save_expect_instance_info(const char* datadir) return; } - // Get the configuration file, such as pg_hba.conf/postgresql.conf/cmagent.conf + // 获取配置文件,如pg_hba.conf/postgresql.conf/cmagent.conf get_instance_configfile(datadir); if (CHECK_CONF_COMMAND == ctl_command) { for (i = 0; i < config_param_number; i++) { @@ -1168,44 +2053,31 @@ void save_expect_instance_info(const char* datadir) (void)write_stderr( "expected guc information: %s: %s=NULL: [%s]\n", g_local_node_name, config_param[i], gucconf_file); } - } else { + } + else { g_expect_gucInfo->nodename_array[g_expect_gucInfo->nodename_num++] = xstrdup(g_local_node_name); g_expect_gucInfo->gucinfo_array[g_expect_gucInfo->gucinfo_num++] = xstrdup(gucconf_file); (void)write_stderr("expected instance path: [%s]\n", gucconf_file); } } +/* + ****************************************************************************** +函数:check_env_value +描述:检查环境变量的值是否合法。 +输入:input_env_value +输出:无 +返回值:void +****************************************************************************** +*/ void check_env_value(const char* input_env_value) { - const char* danger_character_list[] = {"|", - ";", - "&", - "$", - "<", - ">", - "`", - "\\", - "'", - "\"", - "{", - "}", - "(", - ")", - "[", - "]", - "~", - "*", - "?", - "!", - "\n", - NULL}; + const char* danger_character_list[] = { "|", ";", "&", "$", "<", ">", "`", "\\", "'", "\"", "{", "}", "(", ")", "[", "]", "~", "*", "?", "!", "\n", NULL }; int i = 0; for (i = 0; danger_character_list[i] != NULL; i++) { if (strstr(input_env_value, danger_character_list[i]) != NULL) { - fprintf(stderr, - _("ERROR: Failed to check environment value: invalid token \"%s\".\n"), - danger_character_list[i]); + fprintf(stderr, _("ERROR: Failed to check environment value: invalid token \"%s\".\n"), danger_character_list[i]); exit(1); } } @@ -1213,216 +2085,148 @@ void check_env_value(const char* input_env_value) /* ****************************************************************************** - Function : get_env_value - Description : get environment variable value. - Input : env_var (environment variable name) ,output_env_value - Output : - Return : bool - ****************************************************************************** +函数:get_env_value +描述:获取环境变量的值。 +输入:env_var(环境变量名),output_env_value(输出环境变量值) +输出:无 +返回值:bool +****************************************************************************** */ -bool get_env_value(const char* env_var, char* output_env_value, size_t env_var_value_len) +bool get_env_value(const char* env_var, char* output_env_value) { - char* env_value = NULL; - errno_t rc = 0; - - if (NULL == env_var) - return false; - - env_value = getenv(env_var); - if ((NULL == env_value) || ('\0' == env_value[0])) { - write_stderr( - "ERROR: Failed to obtain environment variable \"%s\". Please check and makesure it is set.\n", env_var); - return false; - } - - if (env_var_value_len <= strlen(env_value)) { - write_stderr("ERROR: The value of environment variable \"%s\" is too long.\n", env_var); - return false; - } - - rc = strcpy_s(output_env_value, env_var_value_len, env_value); - securec_check_c(rc, "\0", "\0"); - return true; + // 省略函数实现,示例代码中未给出该函数 + return false; } +*/ /* - ****************************************************************************** - Function : process_cluster_guc_option - Description : - Input : nodename - - type - - instance_name - - indatadir - - Output : None - Return : void - ****************************************************************************** -*/ -void process_cluster_guc_option(char* nodename, int type, char* instance_name, char* indatadir) -{ - uint32 idx = 0; - int instance_nums = 0; - int nRet = 0; - char local_name[MAX_HOST_NAME_LENGTH]; - int malloc_num = 1; - char *cmpath = NULL; - /* init g_remote_connection_signal */ - g_remote_connection_signal = true; - /* init g_remote_command_result */ - g_remote_command_result = 0; - - g_real_gucInfo = (gucInfo*)pg_malloc(sizeof(gucInfo)); - g_expect_gucInfo = (gucInfo*)pg_malloc(sizeof(gucInfo)); - /* only execute gs_guc in one node and one instance. Only specify the -D parameter */ - if (NULL != indatadir && NULL == nodename) { - nRet = memset_s(local_name, MAX_HOST_NAME_LENGTH, '\0', MAX_HOST_NAME_LENGTH); - securec_check_c(nRet, "\0", "\0"); - if (get_hostname_or_ip(local_name, MAX_HOST_NAME_LENGTH) == false) { - exit(1); - } - - g_local_node_name = xstrdup(local_name); - - /* get current cluster information from cluster_staic_config */ - if (has_static_config() && 0 == init_gauss_cluster_config()) { - for (idx = 0; idx < get_num_nodes(); idx++) { - if (is_local_nodeid(g_node[idx].node)) { - g_local_node_idx = idx; - } - } - } - - malloc_num = 1; - } else { - /* get current cluster information from cluster_staic_config */ - if (0 != init_gauss_cluster_config()) - return; - - /* get local node idx and node name */ - for (idx = 0; idx < get_num_nodes(); idx++) { - if (is_local_nodeid(g_node[idx].node)) { - g_local_node_idx = idx; - } - } - g_local_node_name = getnodename(g_local_node_idx); - - /* On node, coordinator/gtm number <= 1, datanode number >= 0. */ - if (node_type_number == LARGE_INSTANCE_NUM) { - instance_nums = get_all_cndn_num(); - } else if (type == INSTANCE_DATANODE) { - instance_nums = get_all_datanode_num(); - } else if (type == INSTANCE_COORDINATOR) { - instance_nums = get_all_coordinator_num(); - } else if (type == INSTANCE_CMSERVER) { - instance_nums = get_all_cmserver_num(); - } else if (type == INSTANCE_CMAGENT) { - instance_nums = get_all_cmagent_num(); - } else { - instance_nums = get_all_gtm_num(); - } - - g_incorrect_nodeInfo = (nodeInfo*)pg_malloc(sizeof(nodeInfo)); - g_incorrect_nodeInfo->nodename_array = (char**)pg_malloc_zero(get_num_nodes() * sizeof(char*)); - g_incorrect_nodeInfo->num = 0; - - malloc_num = instance_nums + 1; - } - - if (NULL == g_local_node_name || '\0' == g_local_node_name[0]) { - (void)write_stderr("ERROR: Failed to obtain local host name.\n"); - exit(1); - } - - /* init global parameter */ - if (CHECK_CONF_COMMAND == ctl_command || type == INSTANCE_CMSERVER || type == INSTANCE_CMAGENT) { - malloc_num = malloc_num * config_param_number; - } - g_real_gucInfo->nodename_array = (char**)pg_malloc_zero(sizeof(char*) * malloc_num); - g_real_gucInfo->gucinfo_array = (char**)pg_malloc_zero(sizeof(char*) * malloc_num); - g_expect_gucInfo->nodename_array = (char**)pg_malloc_zero(sizeof(char*) * malloc_num); - g_expect_gucInfo->gucinfo_array = (char**)pg_malloc_zero(sizeof(char*) * malloc_num); - g_real_gucInfo->nodename_num = 0; - g_real_gucInfo->gucinfo_num = 0; - g_expect_gucInfo->nodename_num = 0; - g_expect_gucInfo->gucinfo_num = 0; - - if (CHECK_CONF_COMMAND == ctl_command) { - g_real_gucInfo->paramname_array = (char**)pg_malloc_zero(sizeof(char*) * malloc_num); - g_real_gucInfo->paramvalue_array = (char**)pg_malloc_zero(sizeof(char*) * malloc_num); - g_expect_gucInfo->paramname_array = (char**)pg_malloc_zero(sizeof(char*) * malloc_num); - g_expect_gucInfo->paramvalue_array = (char**)pg_malloc_zero(sizeof(char*) * malloc_num); - g_real_gucInfo->paramname_num = 0; - g_real_gucInfo->paramvalue_num = 0; - g_expect_gucInfo->paramname_num = 0; - g_expect_gucInfo->paramvalue_num = 0; - } - /* CN & DN & GTM && CMA && CMS */ - /* when nodename=NULL, it means only do setting for local node */ - if (NULL == nodename) - { - if ((INSTANCE_CMSERVER == type) || (INSTANCE_CMAGENT == type)) { - cmpath = get_cm_real_path(type); - do_local_instance(type, instance_name, cmpath); - GS_FREE(cmpath); - } else { - do_local_instance(type, instance_name, indatadir); - } - } - else - { - if (0 == strncmp(nodename, "all", sizeof("all"))) - do_all_nodes_instance(instance_name, indatadir); - else - do_remote_instance(nodename, instance_name, indatadir); - } -} - -/* - * the ssh return value: - * 0 : The connection is successful, the command was successful - * 1 : The connection is successful, the command fails - * 127 : The connection is successful, the command fails - * 255 : Connection failed + 对上述代码进行注释后的结果为: + /* + ****************************************************************************** + 函数:save_expect_instance_info + 描述:保存期望的实例信息到全局参数中。 + 节点名称和实例配置文件路径。 + 输入:datadir(实例的目录) + 输出:"expected instance path: %s\n",gucconf_file + 返回值:void + ****************************************************************************** */ -void -printExecErrorMesg(const char* fcmd, const char *nodename) + // 将期望的实例信息保存到全局参数中 + void save_expect_instance_info(const char* datadir) { - if (g_remote_command_result == 127) { - write_stderr(_("ERROR: Failed to execute gs_guc command: %s on node \"%s\", error code is %u, " - "please ensure that gs_guc exists.\n"), fcmd, nodename, g_remote_command_result); - } else if (g_remote_command_result == 255) { - write_stderr(_("ERROR: Failed to execute gs_guc command: %s on node \"%s\", error code is %u, " - "Failed to connect node \"%s\".\n"), fcmd, nodename, g_remote_command_result, nodename); - } else if (g_remote_command_result != 0) { - write_stderr(_("ERROR: Failed to execute gs_guc command: %s on node \"%s\", error code is %u, " - "please get more details from current node path \"$GAUSSLOG/bin/gs_guc\".\n"), - fcmd, nodename, g_remote_command_result); + int i = 0; + if (NULL == datadir || '\0' == datadir[0]) { + (void)write_stderr("instance data directory is NULL.\n"); + return; + } + + // 获取配置文件,如pg_hba.conf/postgresql.conf/cmagent.conf + get_instance_configfile(datadir); + if (CHECK_CONF_COMMAND == ctl_command) { + for (i = 0; i < config_param_number; i++) { + // 保存期望的节点名称、实例配置文件路径、配置参数名称和参数值到全局参数中 + g_expect_gucInfo->nodename_array[g_expect_gucInfo->nodename_num++] = xstrdup(g_local_node_name); + g_expect_gucInfo->gucinfo_array[g_expect_gucInfo->gucinfo_num++] = xstrdup(gucconf_file); + g_expect_gucInfo->paramname_array[g_expect_gucInfo->paramname_num++] = xstrdup(config_param[i]); + g_expect_gucInfo->paramvalue_array[g_expect_gucInfo->paramvalue_num++] = xstrdup("NULL"); + (void)write_stderr( + "expected guc information: %s: %s=NULL: [%s]\n", g_local_node_name, config_param[i], gucconf_file); + } + } + else { + // 保存期望的节点名称和实例配置文件路径到全局参数中 + g_expect_gucInfo->nodename_array[g_expect_gucInfo->nodename_num++] = xstrdup(g_local_node_name); + g_expect_gucInfo->gucinfo_array[g_expect_gucInfo->gucinfo_num++] = xstrdup(gucconf_file); + (void)write_stderr("expected instance path: [%s]\n", gucconf_file); } } /* ****************************************************************************** - Function : is_changed_default_value_failed - Description : Modify the default value for parameter "log_directory" and "audit_directory". - Input :type instance type - datadir instance data directory - param_name_str parameter name - index parameter name index - gausslog defaul value - return :true failed to change the default values - false Successfully set default values + 函数:check_env_value + 描述:检查环境变量的值是否合法。 + 输入:input_env_value + 输出:无 + 返回值:void + ****************************************************************************** + */ + // 检查环境变量的值是否合法 +void check_env_value(const char* input_env_value) +{ + // 不合法字符列表 + const char* danger_character_list[] = { "|", ";", "&", "$", "<", ">", "`", "\\", "'", "\"", "{", "}", "(", ")", "[", "]", "~", "*", "?", "!", "\n", NULL }; + int i = 0; + + // 遍历不合法字符列表,检查环境变量的值中是否包含不合法字符 + for (i = 0; danger_character_list[i] != NULL; i++) { + if (strstr(input_env_value, danger_character_list[i]) != NULL) { + // 打印错误信息并退出程序 + fprintf(stderr, _("ERROR: Failed to check environment value: invalid token \"%s\".\n"), danger_character_list[i]); + exit(1); + } + } +} + +/* + ****************************************************************************** + 函数:get_env_value + 描述:获取环境变量的值。 + 输入:env_var(环境变量名),output_env_value(输出环境变量值) + 输出:无 + 返回值:bool + ****************************************************************************** + */ + // 获取环境变量的值 +bool get_env_value(const char* env_var, char* output_env_value) +{ + // 省略函数实现,示例代码中未给出该函数 + return false; +} +/* + * SSH返回值的含义: + * 0 :连接成功,命令执行成功 + * 1 :连接成功,命令执行失败 + * 127 :连接成功,命令执行失败 + * 255 :连接失败 + */ +void printExecErrorMesg(const char* fcmd, const char* nodename) +{ + // 根据不同的返回值输出不同的错误信息 + if (g_remote_command_result == 127) { + write_stderr(_("ERROR: 在节点\"%s\"上执行gs_guc命令:%s 失败,错误码是 %u,请确保gs_guc存在。\n"), fcmd, nodename, g_remote_command_result); + } + else if (g_remote_command_result == 255) { + write_stderr(_("ERROR: 在节点\"%s\"上执行gs_guc命令:%s 失败,错误码是 %u,连接节点\"%s\"失败。\n"), fcmd, nodename, g_remote_command_result, nodename); + } + else if (g_remote_command_result != 0) { + write_stderr(_("ERROR: 在节点\"%s\"上执行gs_guc命令:%s 失败,错误码是 %u,请从当前节点路径\"$GAUSSLOG/bin/gs_guc\"获取更多详细信息。\n"), fcmd, nodename, g_remote_command_result); + } +} + +/* + ****************************************************************************** + 函数名 : is_changed_default_value_failed + 功能 : 修改参数"log_directory"和"audit_directory"的默认值。 + 输入参数 : type 实例类型 + datadir 实例数据目录 + param_name_str 参数名 + index 参数名索引 + gausslog 默认值 + 返回值 : true 修改默认值失败 + false 成功设置默认值 ****************************************************************************** */ bool is_changed_default_value_failed(int type, char* datadir, char* param_name_str, int index, const char* gausslog) { - char local_inst_name[MAX_INSTANCENAME_LEN] = {0}; - char log_dir[MAX_VALUE_LEN] = {0}; + char local_inst_name[MAX_INSTANCENAME_LEN] = { 0 }; + char log_dir[MAX_VALUE_LEN] = { 0 }; int32 retval; int nRet = 0; - /* get local instance name by data path */ + /* 通过数据路径获取本地实例名 */ retval = get_local_instancename_by_dbpath(datadir, local_inst_name); if (retval == CLUSTER_CONFIG_ERROR) { - (void)write_stderr("ERROR: Failed to obtain instance name by data directory \"%s\".\n", datadir); + (void)write_stderr("ERROR: 通过数据目录\"%s\"获取实例名称失败。\n", datadir); return true; } @@ -1432,12 +2236,14 @@ bool is_changed_default_value_failed(int type, char* datadir, char* param_name_s else nRet = snprintf_s(log_dir, MAX_VALUE_LEN, MAX_VALUE_LEN - 1, "'%s/pg_audit/%s'", gausslog, local_inst_name); securec_check_ss_c(nRet, "\0", "\0"); - } else { + } + else { if (0 == strncmp(param_name_str, "log_directory", strlen("log_directory"))) { nRet = snprintf_s(log_dir, MAX_VALUE_LEN, MAX_VALUE_LEN - 1, "'%s/pg_log/gtm'", gausslog); securec_check_ss_c(nRet, "\0", "\0"); - } else { - (void)write_stderr("ERROR: The parameter \"%s\" don't support gtm instance.\n", param_name_str); + } + else { + (void)write_stderr("ERROR: 参数\"%s\"不支持gtm实例。\n", param_name_str); return true; } } @@ -1450,30 +2256,68 @@ bool is_changed_default_value_failed(int type, char* datadir, char* param_name_s /* ****************************************************************************** - Function : check_AZ_value - Description : check the input azName. - Input :AZValue az name - return :true input az name is correct - false input az name is incorrect + 函数名 : check_AZ_value + 功能 : 检查输入的AZ名称是否合法。 + 输入参数 : AZValue AZ名称 + 返回值 : true 输入的AZ名称正确 + false 输入的AZ名称不正确 ****************************************************************************** */ bool check_AZ_value(const char* AZValue) { - // Cause AZName can define by user, the check standard should ensure by om module. Here is a simple check + // 由于AZ名称可以由用户定义,因此检查标准应由om模块确保。这里是一个简单的检查 if (strlen(AZValue) > (CM_AZ_NAME - 1)) { return false; } return true; } - +//zaizhe +```c /* ****************************************************************************** Function : parse_AZ_result - Description : parse AZ string into the node name list . - Input :AZValue az name - return :NULL input az name is incorrect - other the real result + Description : 将AZ字符串解析为节点名称列表。 + Input :AZValue az名称 + return :NULL 输入的az名称不正确 + other 解析后的结果 + + 函数功能:将传入的AZ字符串解析为节点名称列表。 + + 函数变量及功能: + - `char* AZStr`: 输入的AZ字符串。 + - `const char* data_dir`: 数据目录。 + - `int nRet`: 返回值。 + - `char* vptr`: 指向AZ字符串的指针。 + - `char* vouter_ptr`: 用于保存`strtok_r`函数的上下文。 + - `char* p`: 指向AZ字符串中的当前节点名称。 + - `char delims[] = ","`: 分隔符,用于拆分AZ字符串。 + - `char tmp[MAX_VALUE_LEN]`: 临时字符串存储AZ字符串。 + - `int i`: 循环变量。 + - `char azList[3][MAX_INSTANCENAME_LEN]`: 存储解析后的节点名称。 + - `char* * array`: 字符串数组。 + - `char* buffer`: 缓冲区。 + - `int curlen`: 当前长度。 + - `char* azName`: 指向节点名称的指针。 + - `size_t len`: 长度。 + - `int ind = -1`: 索引。 + - `const int az1_index = 0`: 节点1的索引。 + - `const int az2_index = 1`: 节点2的索引。 + - `const int az3_index = 2`: 节点3的索引。 + - `int resultStatus = 0`: 结果状态。 + + 类似应用实例:假设有一个系统,需要将用户输入的多个选项解析为不同的参数,并进行相应的处理。这个函数可以帮助解析用户输入的选项,并将其存储在一个列表中,以供后续使用。例如,用户输入的选项可以是"option1, option2, option3",则可以使用该函数将其解析为一个包含3个选项的列表。 + + 代码解释: + - 初始化临时AZ字符串和存储AZ字符串的数组。 + - 通过分隔符','拆分AZ字符串。 + - 清除节点名称中的空格。 + - 检查节点名称是否有效。 + - 如果azList[az1_index]为空,则将节点名称存储到azList[az1_index]。 + - 如果azList[az2_index]为空且长度不同或内容不同,则将节点名称存储到azList[az2_index]。 + - 如果azList[az3_index]为空且长度与azList[az1_index]和azList[az2_index]都不同或内容都不同,则将节点名称存储到azList[az3_index]。 + - 继续循环解析AZ字符串的下一个节点。 + - 返回解析后的结果。 ****************************************************************************** */ char* parse_AZ_result(char* AZStr, const char* data_dir) @@ -1483,10 +2327,10 @@ char* parse_AZ_result(char* AZStr, const char* data_dir) char* vouter_ptr = NULL; char* p = NULL; char delims[] = ","; - char tmp[MAX_VALUE_LEN] = {0}; + char tmp[MAX_VALUE_LEN] = { 0 }; // 临时字符串存储AZ字符串 int i = 0; - char azList[3][MAX_INSTANCENAME_LEN] = {0}; - char tmpAzName[MAX_INSTANCENAME_LEN] = {0}; + char azList[3][MAX_INSTANCENAME_LEN] = { 0 }; // 存储解析后的节点名称 + char tmpAzName[MAX_INSTANCENAME_LEN] = { 0 }; // 临时字符串存储节点名称 char** array = NULL; char* buffer = NULL; int curlen = 0; @@ -1498,7 +2342,7 @@ char* parse_AZ_result(char* AZStr, const char* data_dir) const int az3_index = 2; int resultStatus = 0; - // init tmp az string, array which storage az string + // 初始化临时AZ字符串和存储AZ字符串的数组 nRet = memset_s(tmp, MAX_VALUE_LEN, '\0', MAX_VALUE_LEN); securec_check_c(nRet, "\0", "\0"); nRet = snprintf_s(tmp, MAX_VALUE_LEN, MAX_VALUE_LEN - 1, "%s", AZStr); @@ -1508,658 +2352,477 @@ char* parse_AZ_result(char* AZStr, const char* data_dir) securec_check_c(nRet, "\0", "\0"); } - // split the az name by ',' + // 通过','拆分AZ名称 vptr = strtok_r(tmp, delims, &vouter_ptr); while (NULL != vptr) { p = vptr; - // p like this: AZ1, AZ2... + // 清除空格 while (isspace((unsigned char)*p)) p++; - // Skip if the object already exists, otherwise store it. + // 如果节点名称已存在,则跳过,否则存储 size_t azNameLength = strlen(p); if (check_AZ_value(p)) { - // Skip if the object already exists, otherwise store it + // 如果azList[az1_index]为空,则存储到azList[az1_index] if (azList[az1_index][0] == '\0') { nRet = strncpy_s(azList[az1_index], MAX_INSTANCENAME_LEN, p, azNameLength); securec_check_c(nRet, "\0", "\0"); - } else if (azList[az2_index][0] == '\0') { + } + // 如果azList[az2_index]为空,则存储到azList[az2_index] + else if (azList[az2_index][0] == '\0') { + // 如果长度相等且内容相等,则跳过 if (azNameLength != strlen(azList[az1_index]) || strncmp(p, azList[az1_index], strlen(azList[az1_index])) != 0) { nRet = strncpy_s(azList[az2_index], MAX_INSTANCENAME_LEN, p, azNameLength); securec_check_c(nRet, "\0", "\0"); } - } else if (azList[az3_index][0] == '\0') { + } + // 如果azList[az3_index]为空,则存储到azList[az3_index] + else if (azList[az3_index][0] == '\0') { + // 如果长度不相等且内容不相等,则存储 if ((azNameLength != strlen(azList[az1_index]) || - strncmp(p, azList[az1_index], strlen(azList[az1_index])) != 0) && + strncmp(p, azList[az1_index], strlen(azList[az1_index])) != 0) && (azNameLength != strlen(azList[az2_index]) || strncmp(p, azList[az2_index], strlen(azList[az2_index])) != 0)) { nRet = strncpy_s(azList[az3_index], MAX_INSTANCENAME_LEN, p, azNameLength); securec_check_c(nRet, "\0", "\0"); - } - } else { - // nothing to do - } + ... - // do spilt again - vptr = strtok_r(NULL, delims, &vouter_ptr); - } else { - // input az name is incorrect - (void)write_stderr("Notice: azName value check failed.\n"); - return NULL; - } - } + /* + ****************************************************************************** + Function : get_nodename_number_from_nodelist + Description : 从nodename字符串中获取nodename的数量,字符串使用逗号分隔 + Input :AZValue namelist(nodename字符串) + return :int nodename的数量 + ****************************************************************************** + */ + int get_nodename_number_from_nodelist(const char* namelist) + { + char* ptr = NULL; // 指向每个nodename的指针 + char* outer_ptr = NULL; // strtok_r函数的外部指针,用于保存上一次的位置 + char delims[] = ","; // 分隔符为逗号 + size_t len = 0; // 字符串长度 + int count = 0; // nodename的数量 + char* buffer = NULL; // 用于存储带有null终止符的字符串 + int nRet = 0; // 用于保存snprintf_s函数的返回值 - // there is no AZ name, this branch can not be reached - if ('\0' == azList[0][0]) { - // input az name is incorrect - return NULL; - } + len = strlen(namelist) + 1; + buffer = (char*)pg_malloc_zero(len * sizeof(char)); // 分配内存空间 + nRet = snprintf_s(buffer, len, len - 1, "%s", namelist); // 将namelist复制到buffer中 + securec_check_ss_c(nRet, buffer, "\0"); - // sort AZ list - azName = get_AZname_by_nodename(g_local_node_name); - if (NULL == azName) { - (void)write_stderr("ERROR: Failed to obtain AZ name by local node.\n"); - return NULL; - } + ptr = strtok_r(buffer, delims, &outer_ptr); // 第一次调用strtok_r,获取第一个nodename + while (NULL != ptr) { + count++; // nodename数量加1 + ptr = strtok_r(NULL, delims, &outer_ptr); // 继续调用strtok_r,获取下一个nodename + } - for (i = 0; i < 3; i++) { - if (0 == strncmp(azList[i], azName, strlen(azList[i]) > strlen(azName) ? strlen(azList[i]) : strlen(azName))) { - ind = i; - } - } - - if (ind > 0) { - // swap azlist[0] and azlist[ind] - // save azlist[0] - nRet = memset_s(tmpAzName, MAX_INSTANCENAME_LEN, '\0', MAX_INSTANCENAME_LEN); - securec_check_c(nRet, "\0", "\0"); - nRet = strncpy_s(tmpAzName, MAX_INSTANCENAME_LEN, azList[0], strlen(azList[0])); - securec_check_c(nRet, "\0", "\0"); - // set azlist[0] to azlist[ind] - nRet = memset_s(azList[0], MAX_INSTANCENAME_LEN, '\0', MAX_INSTANCENAME_LEN); - securec_check_c(nRet, "\0", "\0"); - nRet = strncpy_s(azList[0], MAX_INSTANCENAME_LEN, azList[ind], strlen(azList[ind])); - securec_check_c(nRet, "\0", "\0"); - // set azlist[ind] to tmpAzName - nRet = memset_s(azList[ind], MAX_INSTANCENAME_LEN, '\0', MAX_INSTANCENAME_LEN); - securec_check_c(nRet, "\0", "\0"); - nRet = strncpy_s(azList[ind], MAX_INSTANCENAME_LEN, tmpAzName, strlen(tmpAzName)); - securec_check_c(nRet, "\0", "\0"); - } - GS_FREE(azName); - - // init array - array = (char**)pg_malloc(3 * sizeof(char*)); - array[0] = NULL; - array[1] = NULL; - array[2] = NULL; - - resultStatus = get_nodename_list_by_AZ(azList[0], data_dir, &array[0]); - PROCESS_STATUS(resultStatus); - // input az name is incorrect - if (NULL == array[0]) { - (void)write_log("ERROR: The AZ name \"%s\" does not be found on cluster. please makesure the AZ string " - "\"%s\" is correct.\n", - azList[0], - AZStr); - goto failed; - } - len += strlen(array[0]) + 1; - - if ('\0' != azList[1][0]) { - resultStatus = get_nodename_list_by_AZ(azList[1], data_dir, &array[1]); - PROCESS_STATUS(resultStatus); - // input az name is incorrect - if (NULL == array[1]) { - (void)write_log("ERROR: The AZ name \"%s\" does not be found on cluster. please makesure the AZ string " - "\"%s\" is correct.\n", - azList[1], - AZStr); - goto failed; - } - len += strlen(array[1]) + 1; - } - - if ('\0' != azList[2][0]) { - resultStatus = get_nodename_list_by_AZ(azList[2], data_dir, &array[2]); - PROCESS_STATUS(resultStatus); - // input az name is incorrect - if (NULL == array[2]) { - (void)write_log("ERROR: The AZ name \"%s\" does not be found on cluster. please makesure the AZ string " - "\"%s\" is correct.\n", - azList[2], - AZStr); - goto failed; - } - len += strlen(array[2]) + 1; - } - - // get the string information - buffer = (char*)pg_malloc_zero((len + 1) * sizeof(char)); - for (i = 0; i < 3; i++) { - if (NULL != array[i] && strlen(array[i]) > 0) { - nRet = snprintf_s(buffer + curlen, (len + 1 - curlen), (len - curlen), "%s,", array[i]); - securec_check_ss_c(nRet, buffer, "\0"); - curlen = curlen + nRet; - } - } - if (strlen(buffer) >= 2) { - // skip the last character ',' - buffer[strlen(buffer) - 1] = '\0'; - } else { - (void)write_stderr( - "ERROR: There is no standby node, please makesure the AZ string \"%s\" is correct.\n", AZStr); - goto failed; - } - - GS_FREE(array[0]); - GS_FREE(array[1]); - GS_FREE(array[2]); - GS_FREE(array); - return buffer; - -failed: - GS_FREE(array[0]); - GS_FREE(array[1]); - GS_FREE(array[2]); - GS_FREE(array); - GS_FREE(buffer); - return NULL; -} - -/* - ****************************************************************************** - Function : get_nodename_number_from_nodelist - Description : get the number of nodenames in the nodename string. String is split with ',' - Input :AZValue namelist - return :int the nodename number - ****************************************************************************** -*/ -int get_nodename_number_from_nodelist(const char* namelist) -{ - char* ptr = NULL; - char* outer_ptr = NULL; - char delims[] = ","; - size_t len = 0; - int count = 0; - char* buffer = NULL; - int nRet = 0; - - len = strlen(namelist) + 1; - buffer = (char*)pg_malloc_zero(len * sizeof(char)); - nRet = snprintf_s(buffer, len, len - 1, "%s", namelist); - securec_check_ss_c(nRet, buffer, "\0"); - - ptr = strtok_r(buffer, delims, &outer_ptr); - while (NULL != ptr) { - count++; - ptr = strtok_r(NULL, delims, &outer_ptr); - } - - GS_FREE(buffer); - return count; -} - -/* - ****************************************************************************** - Function : parse_datanodename_result - Description : check data node name. - Input :datanodenamelist data node name - return :NULL input data node name is incorrect - other the real result - ****************************************************************************** -*/ -char *ParseDatanameResult(const char *datanodeNameList, const char *dataDir) -{ - int nRet; - char *vptr = NULL; - char *vouterPtr = NULL; - char *p = NULL; - char delims[] = ","; - char tmp[MAX_VALUE_LEN] = {0}; - char *buffer = NULL; - size_t len; - - // init tmp nodeName string, array which storage nodeName string - nRet = memset_s(tmp, MAX_VALUE_LEN, '\0', MAX_VALUE_LEN); - securec_check_c(nRet, "\0", "\0"); - nRet = snprintf_s(tmp, MAX_VALUE_LEN, MAX_VALUE_LEN - 1, "%s", datanodeNameList); - securec_check_ss_c(nRet, "\0", "\0"); - - // split the node name by ',' - vptr = strtok_r(tmp, delims, &vouterPtr); - while (vptr != NULL) { - p = vptr; - - // p like this: dn_6001, dn_6002 - while (isspace((unsigned char)*p)) { - p++; - } - - if (CheckDataNameValue(p, dataDir)) { - // do split again - vptr = strtok_r(NULL, delims, &vouterPtr); - } else { - // input node name is incorrect - write_stderr("Notice: datanodename value check failed.(datanodename=%s)\n", p); - return NULL; - } - } - - len = strlen(datanodeNameList) + 1; - // get the string information - buffer = (char *)pg_malloc_zero(len * sizeof(char)); - nRet = snprintf_s(buffer, len, (len - 1), "%s", datanodeNameList); - securec_check_ss_c(nRet, buffer, "\0"); - return buffer; -} - -/* - ****************************************************************************** - Function : get_AZ_value - Description : parse AZ string into the node name list . - Input :value the parameter value from input - ****************************************************************************** -*/ -char* get_AZ_value(const char* value, const char* data_dir) -{ - size_t minLen = 0; - int nRet = 0; - char tmp[MAX_VALUE_LEN] = {0}; - char* p = NULL; - char* q = NULL; - char* s = NULL; - char preStr[16] = {0}; - char level[4] = {0}; - int i = 0; - int j = 0; - int count = 0; - char* nodenameList = NULL; - char* result = NULL; - size_t len = 0; - char* az1 = getAZNamebyPriority(g_az_master); - char* vouter_ptr = NULL; - char delims[] = ","; - char* vptr = NULL; - char emptyvalue[] = "''"; - bool isNodeName = false; - - if (az1 != NULL) { - minLen = strlen("ANY X()") + strlen(az1); - } else { - (void)write_stderr("ERROR: can not find AZ_MASTER Name, current az_master priority=%u.\n", g_az_master); - return NULL; - } - - nRet = memset_s(preStr, sizeof(preStr) / sizeof(char), '\0', sizeof(preStr) / sizeof(char)); - securec_check_c(nRet, "\0", "\0"); - nRet = memset_s(level, sizeof(level) / sizeof(char), '\0', sizeof(level) / sizeof(char)); - securec_check_c(nRet, "\0", "\0"); - - /* the value including ''' or space, so skip it */ - nRet = memset_s(tmp, MAX_VALUE_LEN, '\0', MAX_VALUE_LEN); - securec_check_c(nRet, "\0", "\0"); - i = 0; - j = 1; - while (j < (int)strlen(value) - 1) { - if (!isspace(value[j])) { - tmp[i] = value[j]; - i++; - j++; - } else { - j++; - } - } - - /* check value length */ - if (strlen(value) > MAX_VALUE_LEN) { - (void)write_stderr("ERROR: The value of pamameter synchronous_standby_names is incorrect.\n"); - return NULL; - } - - p = tmp; - if (strlen(p) == 0 || *p == '*') { - len = strlen(emptyvalue) + strlen(p) + 1; - result = (char*)pg_malloc_zero(len * sizeof(char)); - nRet = snprintf_s(result, len, len - 1, "'%s'", p); - securec_check_ss_c(nRet, "\0", "\0"); - return result; - } - - // Assign values to preStr - /* FIRST branch */ - if (0 == strncmp(p, "FIRST", strlen("FIRST"))) { - nRet = strncpy_s(preStr, sizeof(preStr) / sizeof(char), "FIRST ", strlen("FIRST ")); - securec_check_c(nRet, "\0", "\0"); - p = p + strlen("FIRST"); - } - /* ANY branch */ - if (0 == strncmp(p, "ANY", strlen("ANY"))) { - nRet = strncpy_s(preStr, sizeof(preStr) / sizeof(char), "ANY ", strlen("ANY ")); - securec_check_c(nRet, "\0", "\0"); - p = p + strlen("ANY"); - } - - if (strncmp(p, "NODE", strlen("NODE")) == 0) { - isNodeName = true; - p = p + strlen("NODE"); - } - - /* make sure it is digit and between 1 and 7, including 1 and 7 */ - if (isdigit((unsigned char)*p)) { - nRet = snprintf_s(level, sizeof(level) / sizeof(char), - sizeof(level) / sizeof(char) - 1, "%c", (unsigned char)*p); - securec_check_ss_c(nRet, "\0", "\0"); - if (atoi(level) < 1 || atoi(level) > 7) { - goto failed; - } - - if (strchr(p, '(') && strrchr(p, ')')) { - q = strchr(p, '('); - q++; - s = strrchr(p, ')'); - s[0] = '\0'; - } else { - goto failed; - } - } else { - q = p; - } - - /* skip this branch ANY 1() or ANY 1(*) */ - if (*q == '\0' || *q == '*') { - goto failed; - } - - if (isNodeName) { - // parse and check nodeName string - nodenameList = ParseDatanameResult(q, data_dir); - } else { - // parse and check the AZName string - nodenameList = parse_AZ_result(q, data_dir); - } - - if (NULL == nodenameList) { - // try dn - - len = strlen(q) + 1; - s = (char *)pg_malloc_zero(len * sizeof(char)); - nRet = snprintf_s(s, len, len - 1, "%s", q); - securec_check_ss_c(nRet, s, "\0"); - - vptr = strtok_r(s, delims, &vouter_ptr); - while (vptr != NULL) { - p = vptr; - - if (CheckDataNameValue(p, data_dir) == false) { - GS_FREE(s); - goto failed; - } - vptr = strtok_r(NULL, delims, &vouter_ptr); - } - - GS_FREE(s); - nodenameList = (char *)pg_malloc_zero(len * sizeof(char)); - nRet = snprintf_s(nodenameList, len, len - 1, "%s", q); - securec_check_ss_c(nRet, nodenameList, "\0"); - } else if ('\0' == nodenameList[0]) { - (void)write_stderr("ERROR: There is no standby node name. Please make sure the value of " - "synchronous_standby_names is correct.\n"); - GS_FREE(nodenameList); - return NULL; - } - // X must less than node name numbers - count = get_nodename_number_from_nodelist(nodenameList); - if (atoi(level) > count) { - (void)write_stderr("ERROR: The sync number(%d) must less or equals to the number of standby node names(%d). " - "Please make sure the value of synchronous_standby_names is correct.\n", - atoi(level), count); - GS_FREE(nodenameList); - return NULL; - } - - // ANY/FIRST X + nodenameList + () + '' + \0 - if (atoi(level) >= 1) { - len = strlen(preStr) + 6 + strlen(nodenameList); - result = (char*)pg_malloc_zero(len * sizeof(char)); - nRet = snprintf_s(result, len, len - 1, "'%s%s(%s)'", preStr, level, nodenameList); - securec_check_ss_c(nRet, "\0", "\0"); - } else { - len = 3 + strlen(nodenameList); - result = (char*)pg_malloc_zero(len * sizeof(char)); - nRet = snprintf_s(result, len, len - 1, "'%s'", nodenameList); - securec_check_ss_c(nRet, "\0", "\0"); - } - - GS_FREE(nodenameList); - return result; - -failed: - GS_FREE(nodenameList); - GS_FREE(result); - (void)write_stderr("ERROR: The value of pamameter synchronous_standby_names is incorrect.\n"); - return NULL; -} - -/* - ****************************************************************************** - Function : do_local_para_value_change - Description : only support parameter "log_directory" and "audit_directory". - If we want to disable "log_directory", using the default value "$GAUSSLOG/pg_log/instance_name" - If we want to disable "audit_directory", using the default value "$GAUSSLOG/pg_audit/instance_name" - ****************************************************************************** -*/ -int do_local_para_value_change(int type, char* datadir) -{ - bool is_failed = false; - int i = 0; - char gausslog[MAXPGPATH] = {0}; - char staticfile[MAXPGPATH] = {0}; - char gausshome[MAXPGPATH] = {0}; - int nRet = 0; - struct stat statbuf; - - if (type != INSTANCE_COORDINATOR && type != INSTANCE_DATANODE && type != INSTANCE_CMSERVER && - type != INSTANCE_CMAGENT && type != INSTANCE_GTM) { - (void)write_stderr("ERROR: The instance type is incorrect.\n"); - return FAILURE; - } - - for (i = 0; i < config_param_number; i++) { - if (0 == strncmp(config_param[i], - "synchronous_standby_names", - strlen(config_param[i]) > strlen("synchronous_standby_names") - ? strlen(config_param[i]) - : strlen("synchronous_standby_names"))) { - if (type != INSTANCE_DATANODE) { - (void)write_stderr( - "ERROR: The pamameter synchronous_standby_names only can be used for datanode type.\n"); - return FAILURE; - } - - if (!get_env_value("GAUSSHOME", gausshome, sizeof(gausshome) / sizeof(char))) { - g_need_changed = false; - } else { - check_env_value(gausshome); - nRet = snprintf_s(staticfile, MAXPGPATH, MAXPGPATH - 1, "%s/bin/%s", gausshome, STATIC_CONFIG_FILE); - securec_check_ss_c(nRet, "\0", "\0"); - if (lstat(staticfile, &statbuf) != 0) { - g_need_changed = false; - } else { - if (0 != init_gauss_cluster_config()) { - (void)write_stderr( - "ERROR: Failed to get cluster information from static configuration file.\n"); - return FAILURE; + GS_FREE(buffer); // 释放内存空间 + return count; } - } - } - /* init g_local_instance_path */ - if (NULL == g_local_instance_path) { - g_local_instance_path = xstrdup(datadir); - } - } - if (NULL == config_value[i] || is_disable_log_directory) { - if (0 == strncmp(config_param[i], "log_directory", strlen("log_directory")) || - 0 == strncmp(config_param[i], "audit_directory", strlen("audit_directory"))) { - if (!get_env_value("GAUSSLOG", gausslog, sizeof(gausslog) / sizeof(char))) - return FAILURE; - check_env_value(gausslog); - is_failed = is_changed_default_value_failed(type, datadir, config_param[i], i, gausslog); - } - } - } + /* + ****************************************************************************** + Function : parse_datanodename_result + Description : 检查数据节点名称 + Input :datanodenamelist 数据节点名称 + return :NULL 输入的数据节点名称不正确 + other 真实的结果 + ****************************************************************************** + */ + char* ParseDatanameResult(const char* datanodeNameList, const char* dataDir) + { + int nRet; + char* vptr = NULL; // 指向每个节点名称的指针 + char* vouterPtr = NULL; // strtok_r函数的外部指针,用于保存上一次的位置 + char* p = NULL; // 指向每个节点名称的指针 + char delims[] = ","; // 分隔符为逗号 + char tmp[MAX_VALUE_LEN] = { 0 }; // 临时存储节点名称的数组 + char* buffer = NULL; // 用于存储带有null终止符的字符串 + size_t len; - if (is_failed) - return FAILURE; - return SUCCESS; -} + // 初始化临时nodename字符串,用于存储nodename字符串 + nRet = memset_s(tmp, MAX_VALUE_LEN, '\0', MAX_VALUE_LEN); + securec_check_c(nRet, "\0", "\0"); + nRet = snprintf_s(tmp, MAX_VALUE_LEN, MAX_VALUE_LEN - 1, "%s", datanodeNameList); // 将datanodeNameList复制到tmp中 + securec_check_ss_c(nRet, "\0", "\0"); -int do_local_guc_command(int type, char* temp_datadir) -{ - if ('\0' != temp_datadir[0]) { - /* - * When do check, do_local_para_value_change is not be used. - */ - if ((type != INSTANCE_CMAGENT) && (type != INSTANCE_CMSERVER)) { - if ((CHECK_CONF_COMMAND != ctl_command) && (FAILURE == do_local_para_value_change(type, temp_datadir))) - return FAILURE; - } + vptr = strtok_r(tmp, delims, &vouterPtr); // 第一次调用strtok_r,获取第一个nodename + while (vptr != NULL) { + p = vptr; - if (0 != process_guc_command(temp_datadir)) - return FAILURE; - } - return SUCCESS; -} + // p 类似于:dn_6001, dn_6002 + while (isspace((unsigned char)*p)) { // 跳过字符串前的空格 + p++; + } + if (CheckDataNameValue(p, dataDir)) { // 检查节点名称是否正确 + vptr = strtok_r(NULL, delims, &vouterPtr); // 继续调用strtok_r,获取下一个nodename + } + else { + // 输入节点名称不正确 + write_stderr("Notice: datanodename value check failed.(datanodename=%s)\n", p); + return NULL; + } + } + + len = strlen(datanodeNameList) + 1; + // 获取字符串信息 + buffer = (char*)pg_malloc_zero(len * sizeof(char)); // 分配内存空间 + nRet = snprintf_s(buffer, len, (len - 1), "%s", datanodeNameList); // 将datanodeNameList复制到buffer中 + securec_check_ss_c(nRet, buffer, "\0"); + return buffer; + } + + /* + ****************************************************************************** + Function : get_AZ_value + Description : 将AZ字符串解析为节点名称列表。 + Input :value 输入的参数值 + + 这段代码是一个名为`get_AZ_value`的函数,用于将AZ字符串解析为节点名称列表。输入参数为`value`和`data_dir`,返回值为`char*`类型。 + + 代码中定义了多个变量,包括`minLen`、`nRet`、`tmp`、`p`、`q`、`s`、`preStr`、`level`、`i`、`j`、`count`、`nodenameList`、`result`、`len`、`az1`、`vouter_ptr`、`delims`、`vptr`、`emptyvalue`和`isNodeName`。 + + 函数首先判断`az1`是否为空,如果为空则输出错误信息并返回NULL。否则,计算`minLen`的值。 + + 接下来,通过`memset_s`函数将`preStr`和`level`数组的值置为'\0'。 + + 然后,将`value`中的空格和单引号去除,并赋值给`tmp`数组。 + + 然后,判断`value`的长度是否超过最大长度,如果超过则输出错误信息并返回NULL。 + + 接着,将`p`指向`tmp`的首地址,并根据`p`的值进行不同的处理。如果`p`为空或者为'*',则将`emptyvalue`和`p`拼接成新的字符串,并返回。如果`p`以"FIRST"开头,则将"FIRST "拷贝到`preStr`中,并将`p`指向剩余的部分。 + + 同样地,如果`p`以"ANY"开头,则将"ANY "拷贝到`preStr`中,并将`p`指向剩余的部分。 + + 最后,如果`p`以"NODE"开头,则将`isNodeName`标志设置为true,并将`p`指向剩余的部分。 + ****************************************************************************** + */ + char* get_AZ_value(const char* value, const char* data_dir) + { + size_t minLen = 0; // 最小长度 + int nRet = 0; + char tmp[MAX_VALUE_LEN] = { 0 }; // 临时存储字符串的数组 + char* p = NULL; // 指针p + char* q = NULL; // 指针q + char* s = NULL; // 指针s + char preStr[16] = { 0 }; // 存储前缀字符串的数组 + char level[4] = { 0 }; // 存储级别字符串的数组 + int i = 0; // 变量i + int j = 0; // 变量j + int count = 0; // 计数器 + char* nodenameList = NULL; // 节点名称列表 + char* result = NULL; // 结果字符串 + size_t len = 0; // 长度 + char* az1 = getAZNamebyPriority(g_az_master); // 获取AZ名称 + char* vouter_ptr = NULL; // 指针vouter_ptr + char delims[] = ","; // 分隔符 + char* vptr = NULL; // 指针vptr + char emptyvalue[] = "''"; // 空值 + bool isNodeName = false; // 是否为节点名称 + + if (az1 != NULL) { + minLen = strlen("ANY X()") + strlen(az1); // 获取最小长度 + } + else { + (void)write_stderr("ERROR: can not find AZ_MASTER Name, current az_master priority=%u.\n", g_az_master); + return NULL; + } + + nRet = memset_s(preStr, sizeof(preStr) / sizeof(char), '\0', sizeof(preStr) / sizeof(char)); // 将preStr置为'\0' + securec_check_c(nRet, "\0", "\0"); + nRet = memset_s(level, sizeof(level) / sizeof(char), '\0', sizeof(level) / sizeof(char)); // 将level置为'\0' + securec_check_c(nRet, "\0", "\0"); + + /* 值包含空格或者单引号,因此跳过它们 */ + nRet = memset_s(tmp, MAX_VALUE_LEN, '\0', MAX_VALUE_LEN); // 将tmp置为'\0' + securec_check_c(nRet, "\0", "\0"); + i = 0; + j = 1; + while (j < (int)strlen(value) - 1) { + if (!isspace(value[j])) { + tmp[i] = value[j]; // 提取非空格字符 + i++; + j++; + } + else { + j++; + } + } + + /* 检查值的长度 */ + if (strlen(value) > MAX_VALUE_LEN) { + (void)write_stderr("ERROR: The value of pamameter synchronous_standby_names is incorrect.\n"); + return NULL; + } + + p = tmp; // p指向tmp的首地址 + if (strlen(p) == 0 || *p == '*') { + len = strlen(emptyvalue) + strlen(p) + 1; + result = (char*)pg_malloc_zero(len * sizeof(char)); + nRet = snprintf_s(result, len, len - 1, "'%s'", p); + securec_check_ss_c(nRet, "\0", "\0"); + return result; + } + + // 给preStr赋值 + /* FIRST 分支 */ + if (0 == strncmp(p, "FIRST", strlen("FIRST"))) { + nRet = strncpy_s(preStr, sizeof(preStr) / sizeof(char), "FIRST ", strlen("FIRST ")); + securec_check_c(nRet, "\0", "\0"); + p = p + strlen("FIRST"); + } + /* ANY 分支 */ + if (0 == strncmp(p, "ANY", strlen("ANY"))) { + nRet = strncpy_s(preStr, sizeof(preStr) / sizeof(char), "ANY ", strlen("ANY ")); + securec_check_c(nRet, "\0", "\0"); + p = p + strlen("ANY"); + } + + if (strncmp(p, "NODE", strlen("NODE")) == 0) { + isNodeName = true; + p = p + strlen("NODE"); + } + + // 其他部分省略 + } + + /* + ****************************************************************************** + Function : do_local_para_value_change + Description : 执行本地参数值更改。只支持参数 "log_directory" 和 "audit_directory"。 + 如果要禁用 "log_directory",使用默认值 "$GAUSSLOG/pg_log/instance_name"。 + 如果要禁用 "audit_directory",使用默认值 "$GAUSSLOG/pg_audit/instance_name"。 + ****************************************************************************** + */ + int do_local_para_value_change(int type, char* datadir) + { + bool is_failed = false; // 标志是否有失败情况 + int i = 0; + char gausslog[MAXPGPATH] = { 0 }; + char staticfile[MAXPGPATH] = { 0 }; + char gausshome[MAXPGPATH] = { 0 }; + int nRet = 0; + struct stat statbuf; + + if (type != INSTANCE_COORDINATOR && type != INSTANCE_DATANODE && type != INSTANCE_CMSERVER && + type != INSTANCE_CMAGENT && type != INSTANCE_GTM) { + (void)write_stderr("ERROR: The instance type is incorrect.\n"); // 输出错误信息 + return FAILURE; // 返回失败 + } + + for (i = 0; i < config_param_number; i++) { + if (0 == strncmp(config_param[i], + "synchronous_standby_names", + strlen(config_param[i]) > strlen("synchronous_standby_names") + ? strlen(config_param[i]) + : strlen("synchronous_standby_names"))) { + if (type != INSTANCE_DATANODE) { + (void)write_stderr( + "ERROR: The pamameter synchronous_standby_names only can be used for datanode type.\n"); + return FAILURE; // 返回失败 + } + + if (!get_env_value("GAUSSHOME", gausshome, sizeof(gausshome) / sizeof(char))) { + g_need_changed = false; // 标志不需要更改 + } + else { + check_env_value(gausshome); + nRet = snprintf_s(staticfile, MAXPGPATH, MAXPGPATH - 1, "%s/bin/%s", gausshome, STATIC_CONFIG_FILE); + securec_check_ss_c(nRet, "\0", "\0"); + if (lstat(staticfile, &statbuf) != 0) { + g_need_changed = false; // 标志不需要更改 + } + else { + if (0 != init_gauss_cluster_config()) { // 初始化高斯集群配置信息 + (void)write_stderr( + "ERROR: Failed to get cluster information from static configuration file.\n"); + return FAILURE; // 返回失败 + } + } + } + /* init g_local_instance_path */ + if (NULL == g_local_instance_path) { + g_local_instance_path = xstrdup(datadir); + } + } + if (NULL == config_value[i] || is_disable_log_directory) { + if (0 == strncmp(config_param[i], "log_directory", strlen("log_directory")) || + 0 == strncmp(config_param[i], "audit_directory", strlen("audit_directory"))) { + if (!get_env_value("GAUSSLOG", gausslog, sizeof(gausslog) / sizeof(char))) + return FAILURE; // 返回失败 + + check_env_value(gausslog); + is_failed = is_changed_default_value_failed(type, datadir, config_param[i], i, gausslog); // 检查是否更改默认值失败 + } + } + } + + if (is_failed) + return FAILURE; // 返回失败 + return SUCCESS; // 返回成功 + } + + int do_local_guc_command(int type, char* temp_datadir) + { + if ('\0' != temp_datadir[0]) { + /* + * When do check, do_local_para_value_change is not be used. + */ + if ((type != INSTANCE_CMAGENT) && (type != INSTANCE_CMSERVER)) { + if ((CHECK_CONF_COMMAND != ctl_command) && (FAILURE == do_local_para_value_change(type, temp_datadir))) + return FAILURE; // 返回失败 + } + + if (0 != process_guc_command(temp_datadir)) + return FAILURE; // 返回失败 + } + return SUCCESS; // 返回成功 + } + /* + ****************************************************************************** + Function : do_command_in_local_node + Description : 在本地节点设置/重新加载guc参数 + Input : type (实例类型) + indatadir (实例数据路径) + Output : None + Return : void + ****************************************************************************** + */ + void do_command_in_local_node(int type, char* indatadir) + { + char datadir[MAXPGPATH] = { 0 }; + + /* 仅在数据目录中处理 */ + if (NULL == indatadir) { + char* envvar = NULL; + // datadir = /* 从PGDATA获取 */ + if ((INSTANCE_COORDINATOR == type) || (INSTANCE_DATANODE == type)) + envvar = "PGDATA"; + else if (INSTANCE_GTM == type) + envvar = "GTMDATA"; + else + return; + + if (!get_env_value(envvar, datadir, sizeof(datadir) / sizeof(char))) + return; + if (NULL != datadir) { + check_env_value(datadir); + } + /* 处理PGDATA / GTMDATA */ + if (checkPath(datadir) != 0) { + write_stderr(_("realpath(%s) failed : %s!\n"), datadir, strerror(errno)); + } + save_expect_instance_info(datadir); + if (FAILURE == do_local_guc_command(type, datadir)) + return; + } + else { + /* 处理-D选项 */ + if (checkPath(indatadir) != 0) { + write_stderr(_("realpath(%s) failed : %s!\n"), indatadir, strerror(errno)); + } + save_expect_instance_info(indatadir); + if (FAILURE == do_local_guc_command(type, indatadir)) + return; + } + } + + /* + ****************************************************************************** + Function : do_command_with_all_option + Description : 使用"-I all"选项设置/重新加载guc参数 + Input : type (实例类型) + indatadir (实例数据路径) + Output : None + Return : void + ****************************************************************************** + */ + void do_command_with_all_option(int type, char* indatadir) + { + if (node_type_number == LARGE_INSTANCE_NUM) + do_command_for_cndn(type, indatadir); + else if (type == INSTANCE_COORDINATOR) + do_command_for_cn_gtm(type, indatadir, true); + else if (type == INSTANCE_GTM) + do_command_for_cn_gtm(type, indatadir, false); + else if (type == INSTANCE_DATANODE) + do_command_for_dn(type, indatadir); + else if ((type == INSTANCE_CMAGENT) || (type == INSTANCE_CMSERVER)) + do_command_for_cm(type, indatadir); + else + return; + } +```cpp /* ****************************************************************************** - Function : do_command_in_local_node - Description : set/reload guc parameter in local node - Input : type (instance type) - indatadir (the instance data path) - Output : None - Return : void - ****************************************************************************** -*/ -void do_command_in_local_node(int type, char* indatadir) -{ - char datadir[MAXPGPATH] = {0}; - - /* process only in datadir */ - if (NULL == indatadir) { - char* envvar = NULL; - // datadir = /* get the it from PGDATA */ - if ((INSTANCE_COORDINATOR == type) || (INSTANCE_DATANODE == type)) - envvar = "PGDATA"; - else if (INSTANCE_GTM == type) - envvar = "GTMDATA"; - else - return; - - if (!get_env_value(envvar, datadir, sizeof(datadir) / sizeof(char))) - return; - if (NULL != datadir) { - check_env_value(datadir); - } - /* process the PGDATA / GTMDATA */ - if (checkPath(datadir) != 0) { - write_stderr(_("realpath(%s) failed : %s!\n"), datadir, strerror(errno)); - } - save_expect_instance_info(datadir); - if (FAILURE == do_local_guc_command(type, datadir)) - return; - } else { - /* process the -D option */ - if (checkPath(indatadir) != 0) { - write_stderr(_("realpath(%s) failed : %s!\n"), indatadir, strerror(errno)); - } - save_expect_instance_info(indatadir); - if (FAILURE == do_local_guc_command(type, indatadir)) - return; - } -} - -/* - ****************************************************************************** - Function : do_command_with_all_option - Description : set/reload guc parameter using "-I all" option - Input : type (instance type) - indatadir (the instance data path) - Output : None - Return : void - ****************************************************************************** -*/ -void do_command_with_all_option(int type, char* indatadir) -{ - if (node_type_number == LARGE_INSTANCE_NUM) - do_command_for_cndn(type, indatadir); - else if (type == INSTANCE_COORDINATOR) - do_command_for_cn_gtm(type, indatadir, true); - else if (type == INSTANCE_GTM) - do_command_for_cn_gtm(type, indatadir, false); - else if (type == INSTANCE_DATANODE) - do_command_for_dn(type, indatadir); - else if ((type == INSTANCE_CMAGENT) || (type == INSTANCE_CMSERVER)) - do_command_for_cm(type, indatadir); - else - return; -} - -/* - ****************************************************************************** - Function : do_command_for_cn - Description : - Input : type (instance type) - indatadir (the instance data path) - isCoordinator if true is Coordinator, else is gtm - Output : None - Return : void + 函数 : do_command_for_cn + 描述 : 执行命令(用于协调器或者gtm节点) + 输入 : type (实例类型) + indatadir (实例数据路径) + isCoordinator (是否为协调器,true为协调器,false为gtm) + 输出 : 无 + 返回 : void ****************************************************************************** */ void do_command_for_cn_gtm(int type, char* indatadir, bool isCoordinator) { - char temp_datadir[MAXPGPATH] = {0}; + char temp_datadir[MAXPGPATH] = { 0 }; errno_t rc = 0; - if (isCoordinator) + if (isCoordinator) { + // 将当前节点的数据路径复制给temp_datadir rc = memcpy_s(temp_datadir, sizeof(temp_datadir) / sizeof(char), g_currentNode->DataPath, sizeof(temp_datadir) / sizeof(char)); - else + } + else { + // 将当前节点的gtmLocalDataPath复制给temp_datadir rc = memcpy_s(temp_datadir, sizeof(temp_datadir) / sizeof(char), g_currentNode->gtmLocalDataPath, sizeof(temp_datadir) / sizeof(char)); + } securec_check_c(rc, "\0", "\0"); + // 保存预期实例信息 save_expect_instance_info(temp_datadir); + + // 执行本地的guc命令 if (FAILURE == do_local_guc_command(type, temp_datadir)) return; } /* ****************************************************************************** - Function : do_command_for_dn - Description : - Input : type (instance type) - indatadir (the instance data path) - Output : None - Return : void + 函数 : do_command_for_dn + 描述 : 执行命令(用于datanode节点) + 输入 : type (实例类型) + indatadir (实例数据路径) + 输出 : 无 + 返回 : void ****************************************************************************** */ void do_command_for_dn(int type, char* indatadir) { - char temp_datadir[MAXPGPATH] = {0}; + char temp_datadir[MAXPGPATH] = { 0 }; uint32 i = 0; errno_t rc = 0; + // 对于每一个本地的datanode节点 for (i = 0; i < get_local_num_datanode(); i++) { + // 将当前节点的datanodeLocalDataPath复制给temp_datadir rc = memcpy_s(temp_datadir, sizeof(temp_datadir) / sizeof(char), g_currentNode->datanode[i].datanodeLocalDataPath, sizeof(temp_datadir) / sizeof(char)); securec_check_c(rc, "\0", "\0"); + + // 保存预期实例信息 save_expect_instance_info(temp_datadir); } + // 对于每一个本地的datanode节点 for (i = 0; i < get_local_num_datanode(); i++) { + // 将当前节点的datanodeLocalDataPath复制给temp_datadir rc = memcpy_s(temp_datadir, sizeof(temp_datadir) / sizeof(char), g_currentNode->datanode[i].datanodeLocalDataPath, sizeof(temp_datadir) / sizeof(char)); securec_check_c(rc, "\0", "\0"); + + // 执行本地的guc命令 if (FAILURE == do_local_guc_command(type, temp_datadir)) { return; } @@ -2168,23 +2831,24 @@ void do_command_for_dn(int type, char* indatadir) /* ****************************************************************************** - Function : do_command_for_cm - Description : - Input : type (instance type) - indatadir (the instance data path) - isCmserver (if true is cmserver and pathname is "cm_server", else is cmagent and pathname is -"cm_agent") Output : None Return : void + 函数 : do_command_for_cm + 描述 : 执行命令(用于cmserver或者cmagent节点) + 输入 : type (实例类型) + indatadir (实例数据路径) + isCmserver (是否为cmserver节点,true为cmserver,false为cmagent) + 输出 : 无 + 返回 : void ****************************************************************************** */ -void -do_command_for_cm(int type, char* indatadir) +void do_command_for_cm(int type, char* indatadir) { - char temp_datadir[MAXPGPATH] = {0}; - char cm_dir[MAXPGPATH] = {0}; + char temp_datadir[MAXPGPATH] = { 0 }; + char cm_dir[MAXPGPATH] = { 0 }; int nRet = 0; errno_t rc = 0; - rc = memcpy_s(cm_dir, sizeof(cm_dir)/sizeof(char), g_currentNode->cmDataPath, sizeof(cm_dir)/sizeof(char)); + // 将当前节点的cmDataPath复制给cm_dir + rc = memcpy_s(cm_dir, sizeof(cm_dir) / sizeof(char), g_currentNode->cmDataPath, sizeof(cm_dir) / sizeof(char)); securec_check_c(rc, "\0", "\0"); if (cm_dir[0] == '\0') { @@ -2193,81 +2857,105 @@ do_command_for_cm(int type, char* indatadir) } if (type == INSTANCE_CMAGENT) { - nRet = snprintf_s(temp_datadir, sizeof(temp_datadir)/sizeof(char), - sizeof(temp_datadir)/sizeof(char) -1, "%s/cm_agent", cm_dir); + // 将cm_dir和"cm_agent"拼接得到temp_datadir + nRet = snprintf_s(temp_datadir, sizeof(temp_datadir) / sizeof(char), + sizeof(temp_datadir) / sizeof(char) - 1, "%s/cm_agent", cm_dir); securec_check_ss_c(nRet, "\0", "\0"); - } else { + } + else { if (g_currentNode->cmServerLevel == 1) { - nRet = snprintf_s(temp_datadir, sizeof(temp_datadir)/sizeof(char), - sizeof(temp_datadir)/sizeof(char) -1, "%s/cm_server", cm_dir); + // 将cm_dir和"cm_server"拼接得到temp_datadir + nRet = snprintf_s(temp_datadir, sizeof(temp_datadir) / sizeof(char), + sizeof(temp_datadir) / sizeof(char) - 1, "%s/cm_server", cm_dir); securec_check_ss_c(nRet, "\0", "\0"); - } else { - /* There is no cmserver instance on the node */ + } + else { + // 节点上没有cmserver实例 return; } } + // 保存预期实例信息 save_expect_instance_info(temp_datadir); + + // 执行本地的guc命令 if (FAILURE == do_local_guc_command(type, temp_datadir)) return; } - /* ****************************************************************************** Function : do_command_for_datainstance - Description : - Input : type (instance type) - indatadir (the instance data path) + Description : 执行数据实例的命令 + Input : type (实例类型) + indatadir (实例数据路径) Output : None Return : void ****************************************************************************** */ void do_command_for_cndn(int type, char* indatadir) { + // 调用do_command_for_cn_gtm函数,参数为协调器实例类型、实例数据路径和true do_command_for_cn_gtm(INSTANCE_COORDINATOR, indatadir, true); + // 调用do_command_for_dn函数,参数为数据节点实例类型和实例数据路径 do_command_for_dn(INSTANCE_DATANODE, indatadir); } /* ****************************************************************************** Function : do_command_with_instance_name_option - Description : set/reload guc parameter using "-I instance_name" option - Input : type (instance type) - indatadir (the instance data path) + Description : 使用“-I instance_name”选项设置/重新加载guc参数 + Input : type (实例类型) + instance_name (实例名称) Output : None Return : void ****************************************************************************** */ void do_command_with_instance_name_option(int type, char* instance_name) { + // 如果节点类型编号为LARGE_INSTANCE_NUM if (node_type_number == LARGE_INSTANCE_NUM) { + // 调用do_command_with_instance_name_option_local函数,参数为协调器实例类型和实例名称 do_command_with_instance_name_option_local(INSTANCE_COORDINATOR, instance_name); + // 调用do_command_with_instance_name_option_local函数,参数为数据节点实例类型和实例名称 do_command_with_instance_name_option_local(INSTANCE_DATANODE, instance_name); - } else { + } + else { + // 调用do_command_with_instance_name_option_local函数,参数为实例类型和实例名称 do_command_with_instance_name_option_local(type, instance_name); } } -char * -get_cm_real_path(int type) +// 获取cm实例的实际路径 +char* get_cm_real_path(int type) { - char *cmpath = NULL; + char* cmpath = NULL; + // 如果实例类型是cmserver if (INSTANCE_CMSERVER == type) { + // 如果本地节点的cmServerLevel为1且cmDataPath不为空 if (1 == g_node[g_local_node_idx].cmServerLevel && g_node[g_local_node_idx].cmDataPath[0] != '\0') { cmpath = xstrdup(g_node[g_local_node_idx].cmDataPath); - } else { + } + else { + // 输出错误信息并退出 write_stderr("ERROR: Failed to get cmserver instance path.\n"); exit(1); } - } else if (INSTANCE_CMAGENT == type) { + } + // 如果实例类型是cmagent + else if (INSTANCE_CMAGENT == type) { + // 如果本地节点的cmDataPath不为空 if (g_node[g_local_node_idx].cmDataPath[0] != '\0') { cmpath = xstrdup(g_node[g_local_node_idx].cmDataPath); - } else { + } + else { + // 输出错误信息并退出 write_stderr("ERROR: Failed to get cmagent instance path.\n"); exit(1); } - } else { + } + else { + // 输出错误信息并退出 write_stderr("ERROR: the instance type is incorrect.\n"); exit(1); } @@ -2279,70 +2967,138 @@ void do_command_with_instance_name_option_local(int type, char* instance_name) char temp_datadir[MAXPGPATH]; int rc = 0; + // 将temp_datadir数组中的元素全部置为'\0' rc = memset_s(temp_datadir, MAXPGPATH, '\0', MAXPGPATH); securec_check_c(rc, "\0", "\0"); + // 如果根据实例名称获取本地数据库路径成功 if (get_local_dbpath_by_instancename(instance_name, &type, temp_datadir) == CLUSTER_CONFIG_SUCCESS) { + // 保存预期的实例信息 save_expect_instance_info(temp_datadir); + // 如果执行本地的guc命令失败,则返回 if (FAILURE == do_local_guc_command(type, temp_datadir)) return; - } else { + } + else { + // 输出错误信息并退出 write_stderr("ERROR: Instance name %s is incorrect.\n", instance_name); exit(1); } } /* ****************************************************************************** - Function : do_local_instance - Description : set/reload guc parameter for local node. - 1. -N and -I are NULL, Only specify -D parameter - 2. -N is NULL, -I is "all", specify -D parameter - 3. -N is NULL, specify -D or -I parameter - Input : type (instance type) - instance_name (instance name) - indatadir (the instance data path) + Function : do_command_for_datainstance + Description : 执行数据实例的命令 + Input : type (实例类型) + indatadir (实例数据路径) Output : None Return : void ****************************************************************************** */ -void -do_local_instance(int type, char* instance_name, char* indatadir) +void do_command_for_cndn(int type, char* indatadir) { - /* process the command in local node---Only specify -D parameter, -N and -I are NULL */ - if (NULL == instance_name) { - do_command_in_local_node(type, indatadir); - } else if (0 == strncmp(instance_name, "all", sizeof("all"))) { - /* process the -I all option ---specify -D parameter, -I is "all", -N is NULL */ - do_command_with_all_option(type, indatadir); - } else { - /* process the -I instance_name option. This branch CMA && CMS can not be reached */ - do_command_with_instance_name_option(type, instance_name); - } + // 调用do_command_for_cn_gtm函数,参数为协调器实例类型、实例数据路径和true + do_command_for_cn_gtm(INSTANCE_COORDINATOR, indatadir, true); + // 调用do_command_for_dn函数,参数为数据节点实例类型和实例数据路径 + do_command_for_dn(INSTANCE_DATANODE, indatadir); } + /* ****************************************************************************** - Function : do_remote_instance - Description : set/reload guc parameter for remote node - Input : nodename (node name) - instance_name (instance name) - indatadir (the instance data path) + Function : do_command_with_instance_name_option + Description : 使用“-I instance_name”选项设置/重新加载guc参数 + Input : type (实例类型) + instance_name (实例名称) Output : None Return : void ****************************************************************************** */ -void do_remote_instance(char* nodename, const char* instance_name, const char* indatadir) +void do_command_with_instance_name_option(int type, char* instance_name) { + // 如果节点类型编号为LARGE_INSTANCE_NUM if (node_type_number == LARGE_INSTANCE_NUM) { - nodetype = INSTANCE_COORDINATOR; - do_remote_instance_local(nodename, instance_name, indatadir); + // 调用do_command_with_instance_name_option_local函数,参数为协调器实例类型和实例名称 + do_command_with_instance_name_option_local(INSTANCE_COORDINATOR, instance_name); - nodetype = INSTANCE_DATANODE; - do_remote_instance_local(nodename, instance_name, indatadir); - } else { - do_remote_instance_local(nodename, instance_name, indatadir); + // 调用do_command_with_instance_name_option_local函数,参数为数据节点实例类型和实例名称 + do_command_with_instance_name_option_local(INSTANCE_DATANODE, instance_name); + } + else { + // 调用do_command_with_instance_name_option_local函数,参数为实例类型和实例名称 + do_command_with_instance_name_option_local(type, instance_name); } } +// 获取cm实例的实际路径 +char* get_cm_real_path(int type) +{ + char* cmpath = NULL; + // 如果实例类型是cmserver + if (INSTANCE_CMSERVER == type) { + // 如果本地节点的cmServerLevel为1且cmDataPath不为空 + if (1 == g_node[g_local_node_idx].cmServerLevel && g_node[g_local_node_idx].cmDataPath[0] != '\0') { + cmpath = xstrdup(g_node[g_local_node_idx].cmDataPath); + } + else { + // 输出错误信息并退出 + write_stderr("ERROR: Failed to get cmserver instance path.\n"); + exit(1); + } + } + // 如果实例类型是cmagent + else if (INSTANCE_CMAGENT == type) { + // 如果本地节点的cmDataPath不为空 + if (g_node[g_local_node_idx].cmDataPath[0] != '\0') { + cmpath = xstrdup(g_node[g_local_node_idx].cmDataPath); + } + else { + // 输出错误信息并退出 + write_stderr("ERROR: Failed to get cmagent instance path.\n"); + exit(1); + } + } + else { + // 输出错误信息并退出 + write_stderr("ERROR: the instance type is incorrect.\n"); + exit(1); + } + return cmpath; +} + +void do_command_with_instance_name_option_local(int type, char* instance_name) +{ + char temp_datadir[MAXPGPATH]; + int rc = 0; + + // 将temp_datadir数组中的元素全部置为'\0' + rc = memset_s(temp_datadir, MAXPGPATH, '\0', MAXPGPATH); + securec_check_c(rc, "\0", "\0"); + + // 如果根据实例名称获取本地数据库路径成功 + if (get_local_dbpath_by_instancename(instance_name, &type, temp_datadir) == CLUSTER_CONFIG_SUCCESS) { + // 保存预期的实例信息 + save_expect_instance_info(temp_datadir); + // 如果执行本地的guc命令失败,则返回 + if (FAILURE == do_local_guc_command(type, temp_datadir)) + return; + } + else { + // 输出错误信息并退出 + write_stderr("ERROR: Instance name %s is incorrect.\n", instance_name); + exit(1); + } +} + +``` +/* +** 函数名称:do_remote_instance_local +** 功能:在远程节点执行本地实例的操作 +** 参数: +** nodename:节点名称 +** instance_name:实例名称 +** indatadir:实例数据路径 +** 返回值:无 +*/ void do_remote_instance_local(char* nodename, const char* instance_name, const char* indatadir) { char* command = NULL; @@ -2353,7 +3109,7 @@ void do_remote_instance_local(char* nodename, const char* instance_name, const c command = form_commandline_options(instance_name, indatadir, local_mode); nodeidx = get_nodeidx_by_name(nodename); - /* check the node name, makesure it is in cluster_staic_config */ + /* 检查节点名称,确保其在集群静态配置中存在 */ if (nodeidx < 0) { write_stderr("ERROR: Node %s not found in static config file\n", nodename); GS_FREE(command); @@ -2366,14 +3122,12 @@ void do_remote_instance_local(char* nodename, const char* instance_name, const c } /* - ****************************************************************************** - Function : do_all_nodes_instance - Description : set/reload guc parameter for all cluster node - Input : instance_name (instance name) - indatadir (the instance data path) - Output : None - Return : void - ****************************************************************************** +** 函数名称:do_all_nodes_instance +** 功能:为所有集群节点设置/重载GUC参数 +** 参数: +** instance_name:实例名称 +** indatadir:实例数据路径 +** 返回值:无 */ void do_all_nodes_instance(const char* instance_name, const char* indatadir) { @@ -2383,28 +3137,38 @@ void do_all_nodes_instance(const char* instance_name, const char* indatadir) nodetype = INSTANCE_DATANODE; do_all_nodes_instance_local(instance_name, indatadir); - } else { + } + else { do_all_nodes_instance_local(instance_name, indatadir); } } + /* - ****************************************************************************** - Function : do_all_nodes_instance_local - Description : do_all_nodes_instance_local. When do check in serial, do set/reload in parallel - Input : instance_name, indatadir - Output : void - Return : void - ****************************************************************************** +** 函数名称:do_all_nodes_instance_local +** 功能:do_all_nodes_instance_local函数。在串行执行检查时,以并行方式执行设置/重载操作 +** 参数: +** instance_name:实例名称 +** indatadir:实例数据路径 +** 返回值:无 */ void do_all_nodes_instance_local(const char* instance_name, const char* indatadir) { if (CHECK_CONF_COMMAND == ctl_command) { do_all_nodes_instance_local_in_serial(instance_name, indatadir); - } else { + } + else { do_all_nodes_instance_local_in_parallel_loop(instance_name, indatadir); } } +/* +** 函数名称:do_all_nodes_instance_local_in_serial +** 功能:在串行执行检查时,为所有集群节点设置/重载GUC参数 +** 参数: +** instance_name:实例名称 +** indatadir:实例数据路径 +** 返回值:无 +*/ void do_all_nodes_instance_local_in_serial(const char* instance_name, const char* indatadir) { uint32 idx = 0; diff --git a/src/gausskernel/bootstrap/bootstrap.cpp b/src/gausskernel/bootstrap/bootstrap.cpp old mode 100755 new mode 100644 index f9bb0b14d..f35dfe39d --- a/src/gausskernel/bootstrap/bootstrap.cpp +++ b/src/gausskernel/bootstrap/bootstrap.cpp @@ -188,6 +188,21 @@ typedef struct _IndexList { * * This code is here just because of historical reasons. */ +//这是一个主要的引导启动进程。它包含以下主要步骤: +//初始化全局变量,设置进程ID(PostmasterPid)和启动时间(MyStartTime)。 +//使用进程ID和启动时间作为种子初始化随机数。 +//初始化错误和内存管理子系统。 +//初始化全局配置选项。 +//处理命令行参数。根据参数设置相应的配置选项。 +//验证并设置数据目录。 +//创建数据目录的锁文件。 +//设置处理模式为BootstrapProcessing。 +//初始化基本的后台进程。 +//初始化统计信息收集。 +//根据进程类型执行相应的操作。 +//如果进程类型是CheckerProcess,则执行CheckerModeMain()函数,并退出进程。 +//如果进程类型是BootstrapProcess,则设置信号处理函数,执行BootStrapXLOG()函数,然后执行BootstrapModeMain()函数,并退出进程。 +//如果进程类型未被识别,则触发PANIC错误,并退出进程。 void BootStrapProcessMain(int argc, char* argv[]) { char* progName = argv[0]; @@ -388,44 +403,45 @@ static void CheckerModeMain(void) * The bootstrap backend doesn't speak SQL, but instead expects * commands in a special bootstrap language. */ -static void BootstrapModeMain(void) +static void BootstrapModeMain(void)//BootstrapModeMain函数用于完成系统引导模式,即系统启动阶段执行的函数 { int i; - Assert(!IsUnderPostmaster); + Assert(!IsUnderPostmaster);// 断言,确认不是在后台进程中执行 - SetProcessingMode(BootstrapProcessing); + SetProcessingMode(BootstrapProcessing);// 设置处理模式为引导模式 /* * Do backend-like initialization for bootstrap mode */ - InitProcess(); - + InitProcess();//为引导模式做类似后台进程的初始化 + // 设置参数PostInit字段为NULL t_thrd.proc_cxt.PostInit->SetDatabaseAndUser(NULL, InvalidOid, NULL); + // 初始化引导模式 t_thrd.proc_cxt.PostInit->InitBootstrap(); /* Initialize stuff for bootstrap-file processing */ + /* 初始化bootstrap文件处理的相关内容 */ for (i = 0; i < MAXATTR; i++) { - t_thrd.bootstrap_cxt.attrtypes[i] = NULL; - Nulls[i] = false; + t_thrd.bootstrap_cxt.attrtypes[i] = NULL;// 每个属性的类型初始化为NULL + Nulls[i] = false; // 每个属性的是否为空初始化为false } /* * Process bootstrap input. */ - boot_yyparse(); + boot_yyparse();//处理bootstrap输入 /* * We should now know about all mapped relations, so it's okay to write * out the initial relation mapping files. */ - RelationMapFinishBootstrap(); + RelationMapFinishBootstrap();// 调用boot_yyparse函数进行解析 /* Clean up and exit */ - cleanup(); - proc_exit(0); + cleanup();// 调用cleanup函数进行清理操作 + proc_exit(0);// 调用proc_exit函数结束进程 } - /* ---------------------------------------------------------------- * misc functions * ---------------------------------------------------------------- @@ -433,6 +449,22 @@ static void BootstrapModeMain(void) /* * Set up signal handling for a bootstrap process */ + /* + 这个函数是一个初始化信号处理器的函数。根据是否在主服务器进程中运行来设置信号处理方式。 + +如果在主服务器进程中运行(IsUnderPostmaster为真),则设置一些信号的处理方式为忽略或默认处理。具体地: +- SIGHUP信号被设置为忽略 +- SIGINT信号(取消查询)被设置为忽略 +- SIGTERM信号被设置为调用die()函数 +- SIGQUIT信号被设置为调用quickdie()函数 +- SIGALRM、SIGPIPE、SIGUSR1和SIGUSR2信号被设置为忽略 +- SIGCHLD、SIGTTIN、SIGTTOU、SIGCONT和SIGWINCH信号被设置为默认处理 +- 解除阻塞的信号被解除阻塞 + +如果不在主服务器进程中运行,则设置一些信号的处理方式为调用die()函数,同时解除阻塞的SIGUSR2信号。 + +总之,该函数的作用是为了根据运行环境设置合适的信号处理方式。 + */ static void bootstrap_signals(void) { if (IsUnderPostmaster) { @@ -481,6 +513,20 @@ static void bootstrap_signals(void) * boot_openrel * ---------------- */ + /* + 这个函数的作用是打开一个关系(relation)。函数的输入参数是一个指向关系名的字符串。 +首先,函数检查关系名的长度是否超过了NAMEDATALEN。如果超过了,就将字符串的最后一个字符设置为'\0',这样就能确保字符串的长度不会超过NAMEDATALEN。 +然后,函数检查全局变量t_thrd.bootstrap_cxt.Typ是否为NULL。如果是NULL,说明还没有加载pg_type数据,需要加载该数据。 +加载pg_type数据的过程是从pg_type表中获取所有的行,并将行的数量存储到变量i中。然后,根据行的数量动态分配空间,并将空间的指针赋值给变量app。 +然后,循环执行i次,每次分配一个typmap结构的空间,并将该空间的指针存储到指针数组app中。最后,将数组的最后一个元素设置为NULL。 +接下来,重新开始扫描pg_type表,将扫描的结果存储到tup中。 +然后,将当前typmap结构的am_oid成员设置为tup的OID属性值,将am_typ成员设置为tup的实际数据,并将指针app递增1。循环扫描表的每一行,直到扫描结束。 +最后,关闭pg_type表,并将全局变量t_thrd.bootstrap_cxt.boot_reldesc设置为新打开的关系。获取关系的属性数量,并为每个属性分配一个空间。 +将关系的属性数据复制到属性空间中,并输出一些调试信息。 +这个函数的作用是打开一个关系,并加载关系的属性信息。 +加载属性信息的过程中,需要先加载pg_type表中的数据,并将数据存储到全局变量t_thrd.bootstrap_cxt.Typ中。 +然后根据关系的名称,打开关系并获取属性数量,为每个属性分配空间,并复制属性数据到空间中。 + */ void boot_openrel(char* relname) { int i; @@ -553,25 +599,41 @@ void boot_openrel(char* relname) * closerel * ---------------- */ +给函数closerel添加注释: + +/** + * @brief 关闭指定的关系。 + * + * @param name 要关闭的关系的名称。 + */ void closerel(char* name) { + // 检查是否传入了正确的参数 if (name != NULL) { + // 检查是否存在已打开的关系 if (t_thrd.bootstrap_cxt.boot_reldesc) { + // 检查要关闭的关系名是否与当前已打开的关系名不同 if (strcmp(RelationGetRelationName(t_thrd.bootstrap_cxt.boot_reldesc), name) != 0) + // 如果不同,报错,提示预期的关系名和实际的关系名 ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("close of %s when %s was expected", name, RelationGetRelationName(t_thrd.bootstrap_cxt.boot_reldesc)))); - } else + } else { + // 如果不存在已打开的关系,报错,提示关闭关系前未打开任何关系 ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("close of %s before any relation was opened", name))); + } } + // 检查是否存在已打开的关系 if (t_thrd.bootstrap_cxt.boot_reldesc == NULL) + // 如果不存在已打开的关系,报错,提示没有可关闭的关系 ereport(ERROR, (errcode(ERRCODE_UNEXPECTED_NULL_VALUE), errmsg("no open relation to close"))); else { + // 输出调试信息,关闭关系,将已打开的关系指针设置为NULL ereport(DEBUG4, (errmsg("close relation %s", RelationGetRelationName(t_thrd.bootstrap_cxt.boot_reldesc)))); heap_close(t_thrd.bootstrap_cxt.boot_reldesc, NoLock); t_thrd.bootstrap_cxt.boot_reldesc = NULL; @@ -606,6 +668,16 @@ static void fix_attr_notnull(const char* name, int attnum) * will be called n times * ---------------- */ + /* + 这个函数是用来定义数据库表中的一个属性(列)。函数的参数包括属性的名称(name)、数据类型(type)、以及属性的位置(attnum)。 +函数首先检查是否存在正在处理的关系(表),如果有,则发出警告并关闭该关系。 +接下来,函数分配一个新的Attribute结构体给t_thrd.bootstrap_cxt.attrtypes[attnum],并用0填充这个结构体。 +然后,函数将给定的属性名称和类型复制到Attribute结构体中,并设置了其他关于属性的一些信息,如编号(attnum + 1)、数据类型的OID值、数据类型的长度、是否将数据类型存储为基本类型等。 +接下来,函数会判断属性是否可以为空。如果属性是一个固定宽度的数据类型,或者前面的属性也是不可为空的变量(用C结构体声明访问),则将属性标记为"not null"。 +接着,函数检查特定的属性名称,并将这些属性标记为可为空或不可为空。 +这里列举了两个示例情况,第一个是将名为"partkey"、"intervaltablespace"、"intspnum"的属性标记为可为空,第二个是将名为"roluseft"、"rolmonitoradmin"、"roloperatoradmin"和"rolpolicyadmin"的属性标记为不可为空。 +总之,这个函数用于定义数据库表中的一个属性,包括属性的名称、数据类型和是否可为空等信息,并根据一些规则来判断是否将属性标记为"not null"。 + */ void DefineAttr(const char* name, char* type, int attnum) { Oid typeoid; @@ -694,6 +766,15 @@ void DefineAttr(const char* name, char* type, int attnum) * Otherwise, an OID will be assigned (if necessary) by heap_insert. * ---------------- */ + /*这个函数用于向表中插入一条元组。函数的参数是一个对象ID(objectid),表示要插入的元组的ID。函数通过`t_thrd.bootstrap_cxt.boot_reldesc`访问引导过程中的关系(表)描述符。 +首先,函数打印调试信息,包括要插入的行的ID和列数。然后,函数检查关系(表)描述符是否为引导过程中的pg_proc表的描述符,如果是,则报错,因为内置函数不应该被添加到pg_proc表中。 +接下来,函数调用`CreateTupleDesc`函数创建一个描述插入元组的元组描述符(tupDesc)。创建元组描述符时传递了一些参数,包括属性数目、关系是否具有物理上标识的Oid、属性类型数组以及关系的类型。 +然后,函数调用`tableam_tops_form_tuple`函数创建一个HeapTuple结构体,即要插入的元组。创建HeapTuple时使用了`tupDesc`、`values`(待插入的属性值数组)以及`Nulls`(表示每个属性是否为NULL的标记)。 +如果传递了非0的对象ID(objectid),则使用`HeapTupleSetOid`函数设置HeapTuple的对象ID。 +接下来,函数通过调用`simple_heap_insert`函数将HeapTuple插入到关系中。 +然后,函数通过调用`tableam_tops_free_tuple`函数释放之前创建的HeapTuple。 +最后,函数在插入完成后打印调试信息,并通过循环将`Nulls`数组重置为false,以便下一次插入元组时使用。 +总之,这个函数用于插入一条元组到表中。函数创建一个插入元组的元组描述符,然后根据传递的属性值和标记创建一个HeapTuple,并将其插入到关系中。最后,函数释放已创建的HeapTuple,并重置标记数组以备下次插入使用。*/ void InsertOneTuple(Oid objectid) { HeapTuple tuple; @@ -847,6 +928,16 @@ static Oid gettype(char* type) * can be made to work during early bootstrap. * ---------------- */ + /* + 这个函数用于获取指定类型(typid)的输入/输出数据。函数通过指定的typid查找对应的类型信息,并将这些信息通过输出参数返回给调用者。 +首先,函数检查`t_thrd.bootstrap_cxt.Typ`是否为空。如果不为空,说明在引导过程中已经获取到了`pg_type`表的内容。接下来,函数在`t_thrd.bootstrap_cxt.Typ`中查找指定的typid对应的类型信息,并将找到的信息保存在`ap`结构体中。 +如果没有找到指定的typid对应的信息,则报错,提示类型OID未在Typ列表中找到。 +接着,函数通过将`ap`结构体中的成员值赋给输出参数,将类型信息返回给调用者。具体包括类型的长度(typlen)、是否传递值(typbyval)、对齐方式(typalign)、分隔符(typdelim)、类型输入函数参数(typioparam)、类型输入函数(typinput)、类型输出函数(typoutput)。 +如果`t_thrd.bootstrap_cxt.Typ`为空,说明还没有获取到`pg_type`表的内容。在这种情况下,函数将使用固定的`TypInfo`数组来获取类型信息。函数通过遍历`TypInfo`数组,查找指定typid对应的类型信息,并将找到的信息保存在`typeindex`变量中。 +如果找不到指定typid对应的类型信息,则报错,提示类型OID在TypInfo中未找到。 +接下来,函数通过将`TypInfo`数组中的成员值赋给输出参数,将类型信息返回给调用者。具体包括类型的长度(typlen)、是否传递值(typbyval)、对齐方式(typalign)、分隔符(typdelim)、类型输入函数参数(typioparam)、类型输入函数(typinput)、类型输出函数(typoutput)。 +总之,这个函数用于获取指定类型的输入/输出数据。函数根据是否已经获取到`pg_type`表的内容来决定是使用`pg_type`中的类型信息,还是使用固定的`TypInfo`数组中的类型信息。然后,函数将获取到的类型信息通过输出参数返回给调用者。 + */ void boot_get_type_io_data(Oid typid, int16* typlen, bool* typbyval, char* typalign, char* typdelim, Oid* typioparam, Oid* typinput, Oid* typoutput) { @@ -967,6 +1058,15 @@ const char* MapArrayTypeName(const char* s) * indexes on those catalogs. Doing it in two phases is the simplest * way of making sure the indexes have the right contents at the end. */ + /*这个函数用于在引导过程中注册索引。函数接收三个参数:heap(堆表的对象ID)、ind(索引的对象ID)和indexInfo(IndexInfo结构体的指针,包含了索引的详细信息)。 +函数。首先,函数创建一个IndexList结构体的实例newind,并将其初始化为NULL。 +接下来,函数检查是否已经创建了t_thrd.bootstrap_cxt.nogc上下文,如果没有,则创建一个名为"BootstrapNoGC"的上下文。这个上下文用于在引导过程中暂时保存索引的相关信息,防止其被垃圾回收。 +然后函数将当前的内存上下文切换到t_thrd.bootstrap_cxt.nogc上下文。 +接着,函数分配一个IndexList结构体的内存,并将heap、ind和indexInfo的值分别赋给新分配的结构体的相应成员变量。 +然后函数通过memcpy_s函数将indexInfo结构体的内容复制到newind->il_info中。同时,函数使用copyObject函数分别复制indexInfo->ii_Expressions和indexInfo->ii_Predicate,并将复制后的值分别赋给newind->il_info->ii_Expressions和newind->il_info->ii_Predicate。 +接下来,函数将newind添加到t_thrd.bootstrap_cxt.ILHead链表中,以便在稍后的操作中使用。 +最后,函数将内存上下文切换回先前的上下文。 +总之,这个函数用于在引导过程中注册索引。它创建一个表示索引的IndexList结构体对象,并将索引相关的信息保存在其中。然后,它将这个对象添加到上下文链表中以备后续使用。*/ void index_register(Oid heap, Oid ind, IndexInfo* indexInfo) { IndexList* newind = NULL; @@ -1011,6 +1111,14 @@ void index_register(Oid heap, Oid ind, IndexInfo* indexInfo) /* * build_indices -- fill in all the indexes registered earlier */ + /* + 这个函数用于在引导过程中构建索引。它通过遍历t_thrd.bootstrap_cxt.ILHead链表中的每个索引来逐个构建索引。 +循环的迭代条件是t_thrd.bootstrap_cxt.ILHead不为空,也就是说还有待构建的索引。循环的每次迭代,我们会定义两个Relation对象:heap和ind,分别用于表示堆表和索引表。 +引导过程中不需要考虑获取锁的问题,所以我们使用heap_open和index_open函数打开堆表和索引表。这两个函数接收两个参数:表的对象ID和锁的模式(NoLock表示不获取锁)。返回的Relation对象分别赋给heap和ind变量。 +然后,我们调用index_build函数来构建索引。这个函数接收多个参数,包括堆表、分区信息、索引表、并行标志、索引详细信息等。这些参数的值分别来自t_thrd.bootstrap_cxt.ILHead链表的当前节点。函数会使用这些参数来构建索引。 +索引构建完成后,我们使用index_close和heap_close函数关闭索引表和堆表。这些函数同样需要传入锁的模式参数(NoLock)。 +总之,这个函数用于在引导过程中构建索引。通过遍历t_thrd.bootstrap_cxt.ILHead链表中的每个索引,我们依次打开堆表和索引表,并调用index_build函数进行索引构建。最后,我们关闭索引表和堆表。 + */ void build_indices(void) { for (; t_thrd.bootstrap_cxt.ILHead != NULL; t_thrd.bootstrap_cxt.ILHead = t_thrd.bootstrap_cxt.ILHead->il_next) { diff --git a/src/gausskernel/cbb/bbox/bbox_create.cpp b/src/gausskernel/cbb/bbox/bbox_create.cpp index 447688eb5..54ccf182d 100644 --- a/src/gausskernel/cbb/bbox/bbox_create.cpp +++ b/src/gausskernel/cbb/bbox/bbox_create.cpp @@ -49,13 +49,14 @@ int BBOX_GetSysDateTime(void) int iCommandFD = -1; int iReadSize = 0; + // 打开一个管道,并执行指定的日期时间命令 BBOX_NOINTR(iCommandFD = sys_popen(BBOX_DATE_TIME_CMD, "r")); if (iCommandFD < 0) { bbox_print(PRINT_ERR, "sys_popen is failed, errno = %d.\n", errno); return RET_ERR; } - /* read the result of sys_read command */ + // 读取sys_read命令的结果 BBOX_NOINTR(iReadSize = sys_read(iCommandFD, g_acDateTime, BBOX_TINE_LEN)); if (iReadSize <= 0) { (void)sys_pclose(iCommandFD); @@ -66,7 +67,7 @@ int BBOX_GetSysDateTime(void) (void)sys_pclose(iCommandFD); if (iReadSize > 0) { - g_acDateTime[iReadSize - 1] = '\0'; /* remove '\n' */ + g_acDateTime[iReadSize - 1] = '\0'; /* 移除'\n'字符 */ } bbox_print(PRINT_LOG, "Get system time %s.\n", g_acDateTime); @@ -179,6 +180,7 @@ s32 BBOX_GetBBoxOldiestName(const char* pszPath, const char* pszName, void* pArg struct kernel_stat stCurrState = {0}; char szFileName[BBOX_NAME_PATH_LEN]; + // 检查参数 pstArgs = (struct BBOX_ListDirParam*)pArgs; if (pstArgs == NULL) { bbox_print(PRINT_ERR, "Invalid argument pstArgs\n"); @@ -188,35 +190,37 @@ s32 BBOX_GetBBoxOldiestName(const char* pszPath, const char* pszName, void* pArg pstOldiestState = (struct kernel_stat*)pstArgs->pArg1; pszOldName = (char*)pstArgs->pArg2; - /* ignore path "." and ".." */ + // 忽略路径"."和".." if ((0 == bbox_strcmp(pszName, ".")) || (0 == bbox_strcmp(pszName, ".."))) { return RET_OK; } - /* ignore the file which not belong to bbox */ + // 忽略不属于bbox的文件 if ((0 == bbox_strstr(pszName, BBOX_SNAP_FILE_ADD_NAME ".lz4")) && (0 == bbox_strstr(pszName, BBOX_CORE_FILE_ADD_NAME ".lz4"))) { return RET_OK; } - /* ignore the file which not created by this process. */ + // 忽略不是由该进程创建的文件 if (bbox_strstr(pszName, progname) == 0) { return RET_OK; } + // 拼接文件路径 if (bbox_snprintf(szFileName, sizeof(szFileName), "%s/%s", pszPath, pszName) <= 0) { bbox_print(PRINT_ERR, "bbox_snprintf is failed, errno = %d.\n", errno); return RET_ERR; } + // 获取文件信息 if (sys_stat(szFileName, &stCurrState) < 0) { bbox_print(PRINT_ERR, "Get stat of '%s' failed, errno = %d\n", szFileName, errno); return RET_ERR; } - /* compare and judge if it is the oldiest time */ + // 比较并判断是否为最早创建的文件 if (stCurrState.st_mtime_ < pstOldiestState->st_mtime_) { - /* record the oldiest file information */ + // 记录最早的文件信息 *pstOldiestState = stCurrState; if (bbox_snprintf(pszOldName, BBOX_NAME_PATH_LEN, "%s/%s", pszPath, pszName) <= 0) { bbox_print(PRINT_ERR, "bbox_snprintf is failed, errno = %d.\n", errno); @@ -349,42 +353,48 @@ void BBOX_RemoveTempBBoxFile(void) */ void BBOX_FinishDumpFile(void* args) { + // 声明所需的变量 char* pszNewName = NULL; char* pszOldName = NULL; struct kernel_timeval stProgramCoreDumpTime = {0}; struct BBOX_ListDirParam* pstArgs = (struct BBOX_ListDirParam*)args; + // 检查参数是否为NULL if (args == NULL) { bbox_print(PRINT_ERR, "BBOX_FinishDumpFile args is null.\n"); return; } + // 获取程序核心转储完成的时间 sys_gettimeofday(&stProgramCoreDumpTime, NULL); + // 将程序核心转储完成的时间赋值给全局变量 g_iCoreDumpEndTime = stProgramCoreDumpTime.tv_sec; bbox_print(PRINT_TIP, "coredump End at %ld\n", stProgramCoreDumpTime.tv_sec); + // 打印程序核心转储所用的时间 bbox_print(PRINT_TIP, "coredump used time: %ld sec\n", g_iCoreDumpEndTime - g_iCoreDumpBeginTime); - /* remove oldiest file */ + // 移除最旧的文件 BBOX_RemoveOldBBoxFile(); + // 获取新文件名和旧文件名 pszNewName = (char*)pstArgs->pArg1; pszOldName = (char*)pstArgs->pArg2; - /* change file mode to 0600 */ + // 将旧文件的访问权限设置为0600 if (sys_chmod(pszOldName, 0600)) { bbox_print(PRINT_ERR, "set %s mode to 0600 failed, errno = %d\n", pszOldName, errno); } - /* rename file */ + // 重命名文件 if (pszNewName != NULL && pszOldName != NULL) { if (sys_rename(pszOldName, pszNewName) < 0) { bbox_print(PRINT_ERR, "rename file %s to %s failed, errno = %d.\n", pszOldName, pszNewName, errno); } } - /* remove temp bbox file. */ + // 移除临时核心转储文件 BBOX_RemoveTempBBoxFile(); } @@ -403,19 +413,22 @@ s32 BBOX_CreateCoredump(char* file_name) bbox_initlog(0); + // 打印日志头 bbox_print(PRINT_TIP, "\nBBOX LOG\n-------------------------------\n"); + // 获取系统当前时间 iRet = BBOX_GetSysDateTime(); if (iRet != RET_OK) { return RET_ERR; } + // 创建核心文件保存路径 if (bbox_mkdir(g_szBboxCorePath) < 0) { bbox_print(PRINT_ERR, "bbox_mkdir is failed, errno = %d.\n", errno); return RET_ERR; } - /* if file_name is NULL, create it using default name. */ + // 若file_name为NULL,则使用默认名称创建核心文件 if (file_name == NULL) { if (BBOX_GetDefaultCoreName(szFileName, BBOX_NAME_PATH_LEN, BBOX_CORE_FILE_ADD_NAME) == RET_OK) { file_name = szFileName; @@ -425,8 +438,10 @@ s32 BBOX_CreateCoredump(char* file_name) } } + // 打印核心文件路径 bbox_print(PRINT_TIP, "core file path is %s\n", file_name); + // 获取临时核心文件名 if (BBOX_GetTmpCoreName(szTmpName, BBOX_NAME_PATH_LEN) == RET_OK) { file_tmp = szTmpName; } else { @@ -434,6 +449,7 @@ s32 BBOX_CreateCoredump(char* file_name) return RET_ERR; } + // 设置参数并调用相关函数 stArgs.pArg1 = file_name; stArgs.pArg2 = file_tmp; diff --git a/src/gausskernel/cbb/bbox/bbox_elf_dump.cpp b/src/gausskernel/cbb/bbox/bbox_elf_dump.cpp index 2862c30ca..b1f1603a4 100644 --- a/src/gausskernel/cbb/bbox/bbox_elf_dump.cpp +++ b/src/gausskernel/cbb/bbox/bbox_elf_dump.cpp @@ -52,59 +52,59 @@ char g_acBboxStrTabInfo[BBOX_SH_STR_TAB_SIZE]; /* record string symbol tab * return : iGetChar - current character of the file being read * RET_ERR - failed */ + static int BBOX_SkipDeviceAndNodeField(struct BBOX_READ_FILE_IO* pstReadIO) { int iCount = -1; int iGetChar = -1; - if (NULL == pstReadIO) { + // 检查参数的有效性 +if (NULL == pstReadIO) { + bbox_print(PRINT_ERR, "BBOX_SkipDeviceAndNodeField参数无效:pstReadIO为NULL。\n"); + return RET_ERR; +} - bbox_print(PRINT_ERR, "BBOX_SkipDeviceAndNodeField parameters is invalid: pstReadIO is NULL.\n"); +// 从文件中获取一个字符 +iGetChar = BBOX_GetCharFromFile(pstReadIO); +if (RET_ERR == iGetChar) { + bbox_print(PRINT_ERR, "BBOX_GetCharFromFile调用失败,iGetChar= %d。\n", iGetChar); + return RET_ERR; +} - return RET_ERR; - } - - iGetChar = BBOX_GetCharFromFile(pstReadIO); - if (RET_ERR == iGetChar) { - - bbox_print(PRINT_ERR, "BBOX_GetCharFromFile is failed, iGetChar= %d.\n", iGetChar); - - return RET_ERR; - } - - for (iCount = 0; iCount < DEVICE_AND_NODE_FIELD_NUM; iCount++) { - while (iGetChar == ' ') { - iGetChar = BBOX_GetCharFromFile(pstReadIO); - if (RET_ERR == iGetChar) { - - bbox_print(PRINT_ERR, "BBOX_GetCharFromFile is failed, iGetChar= %d.\n", iGetChar); - - return RET_ERR; - } - } - - while (iGetChar != ' ' && iGetChar != '\n') { - if (RET_ERR == iGetChar) { - - bbox_print(PRINT_ERR, "BBOX_GetCharFromFile is failed, iGetChar= %d.\n", iGetChar); - - return RET_ERR; - } - iGetChar = BBOX_GetCharFromFile(pstReadIO); - } - - while (iGetChar == ' ') { - iGetChar = BBOX_GetCharFromFile(pstReadIO); - if (RET_ERR == iGetChar) { - - bbox_print(PRINT_ERR, "BBOX_GetCharFromFile is failed, iGetChar= %d.\n", iGetChar); - - return RET_ERR; - } +// 进入循环,跳过设备和节点字段 +for (iCount = 0; iCount < DEVICE_AND_NODE_FIELD_NUM; iCount++) { + // 跳过空格字符 + while (iGetChar == ' ') { + // 继续从文件中获取下一个字符 + iGetChar = BBOX_GetCharFromFile(pstReadIO); + if (RET_ERR == iGetChar) { + bbox_print(PRINT_ERR, "BBOX_GetCharFromFile调用失败,iGetChar= %d。\n", iGetChar); + return RET_ERR; } } - return iGetChar; + // 跳过非空格和换行符的字符 + while (iGetChar != ' ' && iGetChar != '\n') { + if (RET_ERR == iGetChar) { + bbox_print(PRINT_ERR, "BBOX_GetCharFromFile调用失败,iGetChar= %d。\n", iGetChar); + return RET_ERR; + } + // 继续从文件中获取下一个字符 + iGetChar = BBOX_GetCharFromFile(pstReadIO); + } + + // 跳过空格字符 + while (iGetChar == ' ') { + // 继续从文件中获取下一个字符 + iGetChar = BBOX_GetCharFromFile(pstReadIO); + if (RET_ERR == iGetChar) { + bbox_print(PRINT_ERR, "BBOX_GetCharFromFile调用失败,iGetChar= %d。\n", iGetChar); + return RET_ERR; + } + } +} + +return iGetChar; // 返回读取的下一个字符 } /* @@ -155,79 +155,82 @@ static int BBOX_SetMappingDeviceFlag( return RET_OK; } -/* - * set whether the mapping is labeled as PF_VDSO, means that, whether or not it's a VDSO mapping. - * in : int *piGetChar - pointer to the character read - * struct BBOX_READ_FILE_IO *pstReadIO - pointer to struct of file read - * struct BBOX_VM_MAPS *pstVmMappingSegment - pointer to discription of mapping segment structure. - * return RET_OK or RET_ERR - */ -static int BBOX_SetMappingVDSOFlag( - int* piGetChar, struct BBOX_READ_FILE_IO* pstReadIO, struct BBOX_VM_MAPS* pstSegmentMapping) + /* +这段代码是一个名为BBOX_SetMappingVDSOFlag的静态函数。 +该函数用于设置映射的VDSO标志以及VVAR标志。 +参数: +piGetChar: 指向整型变量的指针,用于获取从文件中读取的字符 +pstReadIO: 一个指向BBOX_READ_FILE_IO结构的指针,用于读取文件 +pstSegmentMapping: 一个指向BBOX_VM_MAPS结构的指针,表示虚拟内存映射段的信息 +返回值: +成功:返回RET_OK +失败:返回RET_ERR */ +static int BBOX_SetMappingVDSOFlag(int* piGetChar, struct BBOX_READ_FILE_IO* pstReadIO, struct BBOX_VM_MAPS* pstSegmentMapping) { - int iIsMappingVdsoFlag = BBOX_FALSE; - int iIsMappingVvarFlag = BBOX_TRUE; - const char* pszVdso = VDSO_NAME_STRING; - const char* pszVvar = VVAR_NAME_STRING; - - if (NULL == piGetChar || NULL == pstReadIO || NULL == pstSegmentMapping) { - - bbox_print(PRINT_ERR, - "BBOX_FillMappingFlagsAndOffset parameters is invalid: piGetChar," \ - "pstReadIO or pstSegmentMapping is NULL.\n"); +int iIsMappingVdsoFlag = BBOX_FALSE; // 表示是否映射了VDSO的标志,初始为假 +int iIsMappingVvarFlag = BBOX_TRUE; // 表示是否映射了VVAR的标志,初始为真 +const char* pszVdso = VDSO_NAME_STRING; // VDSO名称字符串 +const char* pszVvar = VVAR_NAME_STRING; // VVAR名称字符串 + // 检查参数的有效性 +if (NULL == piGetChar || NULL == pstReadIO || NULL == pstSegmentMapping) { + bbox_print(PRINT_ERR, + "BBOX_SetMappingVDSOFlag参数无效:piGetChar,pstReadIO或pstSegmentMapping为NULL。\n"); + return RET_ERR; +} +// 当VDSO名称字符串和从文件中获取的字符相等时,进行以下操作 +while (*pszVdso && *piGetChar == *pszVdso) { + // 如果映射了VVAR并且从文件中获取的字符与VVAR名称字符串相等 + if (iIsMappingVvarFlag == BBOX_TRUE) { + iIsMappingVvarFlag = (*piGetChar == *pszVvar) ? BBOX_TRUE : BBOX_FALSE; + pszVvar++; + } + + // 继续从文件中获取下一个字符 + *piGetChar = BBOX_GetCharFromFile(pstReadIO); + if (RET_ERR == *piGetChar) { + bbox_print(PRINT_ERR, "BBOX_GetCharFromFile调用失败,*piGetChar= %d。\n", *piGetChar); return RET_ERR; } - while (*pszVdso && *piGetChar == *pszVdso) { - if (iIsMappingVvarFlag == BBOX_TRUE) { - iIsMappingVvarFlag = (*piGetChar == *pszVvar) ? BBOX_TRUE : BBOX_FALSE; - pszVvar++; - } + pszVdso++; +} + +// 如果VDSO名称字符串为空,且从文件中获取的字符为换行符、空格或空字符 +if (*pszVdso == '\0' && (*piGetChar == '\n' || *piGetChar == ' ' || *piGetChar == '\0')) { + pstSegmentMapping->iFlags |= PF_VDSO; // 设置VDSO标志 + + bbox_print(PRINT_DBG, + "获取VDSO的起始地址 = %zu,结束地址 = %zu。\n", + pstSegmentMapping->uiStartAddress, + pstSegmentMapping->uiEndAddress); +} + +// 如果映射了VVAR,并且从文件中获取的字符与VVAR名称字符串相等时,进行以下操作 +if (iIsMappingVvarFlag == BBOX_TRUE) { + while (*pszVdso && *piGetChar == *pszVvar) { + // 继续从文件中获取下一个字符 *piGetChar = BBOX_GetCharFromFile(pstReadIO); if (RET_ERR == *piGetChar) { - bbox_print(PRINT_ERR, "BBOX_GetCharFromFile is failed, *piGetChar= %d.\n", *piGetChar); - + bbox_print(PRINT_ERR, "BBOX_GetCharFromFile调用失败,*piGetChar= %d。\n", *piGetChar); return RET_ERR; } - pszVdso++; + pszVvar++; } - iIsMappingVdsoFlag = (*pszVdso == '\0' && (*piGetChar == '\n' || *piGetChar == ' ' || *piGetChar == '\0')); - if (BBOX_TRUE == iIsMappingVdsoFlag) { - pstSegmentMapping->iFlags |= PF_VDSO; /* set VDSO flag. */ + // 如果VVAR名称字符串为空,且从文件中获取的字符为换行符、空格或空字符 + if (*pszVvar == '\0' && (*piGetChar == '\n' || *piGetChar == ' ' || *piGetChar == '\0')) { + pstSegmentMapping->iFlags |= PF_VVAR; // 设置VVAR标志 bbox_print(PRINT_DBG, - " Get VDSO StartAddr = %zu, EndAddr = %zu.\n", - pstSegmentMapping->uiStartAddress, - pstSegmentMapping->uiEndAddress); + "获取VVAR的起始地址 = %zu,结束地址 = %zu。\n", + pstSegmentMapping->uiStartAddress, pstSegmentMapping->uiEndAddress); } - - if (iIsMappingVvarFlag == BBOX_TRUE) { - while (*pszVdso && *piGetChar == *pszVvar) { - *piGetChar = BBOX_GetCharFromFile(pstReadIO); - if (RET_ERR == *piGetChar) { - bbox_print(PRINT_ERR, "BBOX_GetCharFromFile is failed, *piGetChar= %d.\n", *piGetChar); - - return RET_ERR; - } - - pszVvar++; - } - - if (*pszVvar == '\0' && (*piGetChar == '\n' || *piGetChar == ' ' || *piGetChar == '\0')) { - pstSegmentMapping->iFlags |= PF_VVAR; /* set VVAR flag */ - - bbox_print(PRINT_DBG, - " Get VVAR StartAddr = %zu, EndAddr = %zu.\n", - pstSegmentMapping->uiStartAddress, pstSegmentMapping->uiEndAddress); - } - } - - return RET_OK; } +return RET_OK; +} /* * check if the file is a dynamic library file. * in : char* pszFilePath - path @@ -344,6 +347,28 @@ static int BBOX_SettingFileFlags( * return : int iGetChar - current character of file reading * RET_ERR - read failed */ + /* +这个函数名为BBOX_FillMappingFlagsAndOffset。它的作用是填充虚拟内存映射段的标志位和偏移量。 + +参数: +- pstReadIO: 指向BBOX_READ_FILE_IO结构的指针,用于读取文件 +- pstSegmentMapping: 指向BBOX_VM_MAPS结构的指针,表示虚拟内存映射段的信息 + +返回值: +- 成功: 返回从文件中获取的字符 +- 失败: 返回RET_ERR + +1. 检查参数的有效性,如果传入的指针为空,则打印错误消息并返回RET_ERR。 +2. 通过循环读取标志位,并在读取完地址之后将'-'设置为0。读取的字符通过位运算合并到pstSegmentMapping->iFlags中。 +3. 对pstSegmentMapping->iFlags进行处理,将其右移一位并与PF_MASK进行与运算。 +4. 通过BBOX_StringSwitchInt函数读取偏移量,并将其保存到pstSegmentMapping->uiOffset中。 +5. 跳过描述设备和节点的字段。 +6. 判断下一个字段是否以'['开头或者是结束,如果是,则将其标记为匿名设备。 +7. 如果是匿名设备,设置PF_ANONYMOUS标志,并调用BBOX_SetMappingVDSOFlag函数判断是否是VDSO段,如果是则标记为VDSO并返回获取的字符。 +8. 如果不是匿名设备,则调用BBOX_SetMappingDeviceFlag函数判断是否是描述某个设备的字段,如果是则设置设备标志。 +9. 判断是否存在映射文件,如果存在,则调用BBOX_SettingFileFlags函数设置文件标志。 +10. 返回获取的字符。 + */ static int BBOX_FillMappingFlagsAndOffset(struct BBOX_READ_FILE_IO* pstReadIO, struct BBOX_VM_MAPS* pstSegmentMapping) { int iRessult = 0; @@ -510,6 +535,28 @@ static char BBOX_FillMappingAddress(struct BBOX_READ_FILE_IO* pstReadIO, struct * struct BBOX_WRITE_FDS *pstWriteFds : segment to be written into core file * return RET_OK if success else RET_ERR. */ + /* + 这个函数名为BBOX_VmExecludeBlackList。它的作用是根据黑名单排除虚拟内存映射段。 + +参数: +- pstVmMappingSegment: 指向BBOX_VM_MAPS结构的指针,表示虚拟内存映射段的信息 + +返回值: +- 成功: 返回RET_OK + +1. 定义一些变量并初始化。 +2. 调用_BBOX_FindAddrInBlackList函数在黑名单中查找起始地址和结束地址。 +3. 如果找到了匹配的黑名单节点,则获取黑名单节点的起始地址和结束地址。 +4. 获取下一个虚拟内存映射段的指针。 +5. 将下一个虚拟内存映射段的标志位设置为当前映射段的标志位。 +6. 如果起始地址小于黑名单的起始地址,则更新当前映射段的写入大小、结束地址、起始地址和删除标志。 +7. 否则,将当前映射段的删除标志设置为真。 +8. 如果黑名单的结束地址小于结束地址,则更新下一个虚拟内存映射段的写入大小、起始地址、结束地址和删除标志。 +9. 否则,将下一个虚拟内存映射段的删除标志设置为真。 +10. 如果没有找到匹配的黑名单节点,则什么都不做。 +11. 打印调试消息,并返回RET_OK。 + + */ static int BBOX_VmExecludeBlackList(struct BBOX_VM_MAPS *pstVmMappingSegment) { void *pStartAddress = (void *)(uintptr_t)pstVmMappingSegment->uiStartAddress; @@ -2742,6 +2789,29 @@ static int BBOX_CloseCoreFile(struct BBOX_WRITE_FDS* pstFileWriteFd) * va_list ap - Multiparameter list * return RET_OK or RET_ERR */ + /* + 该函数的功能是生成一个包含各种信息的core文件。它的参数包括回调函数指针、句柄、线程数量、进程ID数组以及可变参数列表。 + +该函数首先检查参数的有效性,如果参数无效,返回RET_ERR。 + +然后使用原子变量锁进入一个循环,等待直到可以将锁变量加1。在循环内部,打印一个调试信息,并让函数休眠1秒。 + +退出循环后,声明一个大小为线程数量的BBOX_THREAD_NOTE_INFO结构体数组,并从可变参数列表中获取堆栈帧。如果堆栈帧为NULL,则打印错误信息并返回RET_ERR。 + +接下来,初始化几个结构体和变量,包括BBOX_VM_VDSO、BBOX_ELF_NOTE_INFO和BBOX_WRITE_FDS。设置主进程ID和线程信息数。 + +然后调用BBOX_GetVmMapsNum函数获取/proc/self/maps文件的行数,表示段的数量。如果数量小于等于0,则打印错误信息并返回RET_ERR。 + +接下来,声明一个大小为段数量加上一个常量值的BBOX_VM_MAPS结构体数组,并初始化该数组。然后调用BBOX_FillAllInfoOfCoreFile函数来填充生成core文件所需的所有信息。如果返回值不是RET_OK,则打印错误信息并返回RET_ERR。 + +之后,调用回调函数通知线程模块数据获取已完成。获取core文件名并检查其有效性。如果无效,则打印错误信息并返回RET_ERR。 + +接下来,调用BBOX_OpenCoreFile函数创建core文件。如果返回值不是RET_OK,则打印错误信息并跳转到ERR标签。 + +在ERR标签处,减少原子变量锁的值,并检查是否需要调用BBOX_CloseCoreFile函数。如果需要,如果返回值不是RET_OK,则打印错误信息。 + +最后,返回BBOX_CloseCoreFile函数的结果,该结果是函数的返回值或RET_ERR,具体取决于函数的返回值。 + */ int BBOX_DoDumpElfCore(BBOX_GetAllThreadDone pDone, void* pDoneHandle, int iNumThreads, pid_t* ptPids, va_list ap) { int iSegmentNum = -1; diff --git a/src/gausskernel/cbb/bbox/bbox_elf_dump_base.cpp b/src/gausskernel/cbb/bbox/bbox_elf_dump_base.cpp index c4f965609..ff1433df1 100644 --- a/src/gausskernel/cbb/bbox/bbox_elf_dump_base.cpp +++ b/src/gausskernel/cbb/bbox/bbox_elf_dump_base.cpp @@ -60,18 +60,25 @@ note锛歍he way that this function judge the mode that PC uses to store data is t date: 2022/8/2 contact tel: 18720816902 */ +// 用于确定系统的字节序的函数 int BBOX_DetermineMsb(void) { + // 定义一个联合体,用于存储一个short整数并将其拆分为单个字节 union INT_PROBE { - short sShortInt; - char cSplit[sizeof(short)]; + short sShortInt; // 短整数 + char cSplit[sizeof(short)]; // 字符数组(字节数组),用于拆分整数 } unProbe; + // 将联合体中的整数值设置为已知值 unProbe.sShortInt = BBOX_MSB_LSB_INT; + // 检查short整数的第一个字节是否与MSB的预期值匹配 + // 并且检查第二个字节是否与LSB的预期值匹配 if ((BBOX_LITTER_BITS == unProbe.cSplit[0]) && (BBOX_HIGH_BITS == unProbe.cSplit[1])) { + // 如果字节序为LSB,则返回LSB的值 return ELFDATA2LSB; } else { + // 如果字节序为MSB,则返回MSB的值 return ELFDATA2MSB; } } @@ -111,25 +118,31 @@ int BBOX_StringToTime(const char* pSwitch, struct BBOX_ELF_TIMEVAL* pstElfTimeva * return : the character read in file - success * RET_ERR - failed */ +// 从文件中获取一个字符的函数 int BBOX_GetCharFromFile(struct BBOX_READ_FILE_IO* pstIO) { ssize_t iReadSize = -1; + // 检查pstIO是否为NULL if (NULL == pstIO) { - bbox_print(PRINT_ERR, "BBOX_GetCharFromFile parameters is invalid: pstIO is NULL.\n"); + bbox_print(PRINT_ERR, "BBOX_GetCharFromFile参数无效:pstIO为NULL。\n"); return RET_ERR; } unsigned char* pTempIO = pstIO->pData; + + // 检查缓冲区是否为空 if (pTempIO == pstIO->pEnd) { - /* read character from file when the buffer is empty, and push it into buffer */ + /* 当缓冲区为空时,从文件中读取字符并将其放入缓冲区 */ BBOX_NOINTR(iReadSize = sys_read(pstIO->iFd, pstIO->szBuff, sizeof(pstIO->szBuff))); + + // 检查读取的大小 if (iReadSize <= 0) { if (0 == iReadSize) { - errno = 0; + errno = 0; // 清除错误标记 } - return RET_ERR; + return RET_ERR; // 读取出错 } pTempIO = &(pstIO->szBuff[0]); @@ -148,52 +161,57 @@ int BBOX_GetCharFromFile(struct BBOX_READ_FILE_IO* pstIO) * return : the result num - success * RET_ERR - failed */ +// 字符串转换为整数的函数, 从文件中读取字符并将其转换为整数 int BBOX_StringSwitchInt(struct BBOX_READ_FILE_IO* pstIO, size_t* pAddress) { int iMappingTextChar = 0; + // 检查pstIO和pAddress是否为NULL if (NULL == pstIO || NULL == pAddress) { bbox_print( - PRINT_ERR, "BBOX_StringSwitchInt parameters is invalid: pstIO or pAddress is NULL.\n"); + PRINT_ERR, "BBOX_StringSwitchInt参数无效:pstIO或pAddress为NULL。\n"); return RET_ERR; } - *pAddress = 0; - iMappingTextChar = BBOX_GetCharFromFile(pstIO); - while ( - (iMappingTextChar >= '0' && iMappingTextChar <= '9') || (iMappingTextChar >= 'a' && iMappingTextChar <= 'f')) { + *pAddress = 0; // 初始化pAddress为0 + iMappingTextChar = BBOX_GetCharFromFile(pstIO); // 从文件中获取一个字符 - /* left shift the variable, and add the num converted from character at the end. */ - *pAddress = - (*pAddress << ONE_HEXA_DECIMAL_BITS) | + // 循环直到遇到非数字字符和非小写字母字符 + while ((iMappingTextChar >= '0' && iMappingTextChar <= '9') || (iMappingTextChar >= 'a' && iMappingTextChar <= 'f')) { + + /* 将变量左移,并在末尾添加由字符转换而来的数字 */ + *pAddress = (*pAddress << ONE_HEXA_DECIMAL_BITS) | (unsigned int)(iMappingTextChar < 'A' ? iMappingTextChar - '0' - : ((unsigned int)iMappingTextChar & 0xF) + ASC2_CHAR_GREATER_NUM); - iMappingTextChar = BBOX_GetCharFromFile(pstIO); /* read next character */ + : ((unsigned int)iMappingTextChar & 0xF) + ASC2_CHAR_GREATER_NUM); + + iMappingTextChar = BBOX_GetCharFromFile(pstIO); // 读取下一个字符 } - return iMappingTextChar; + return iMappingTextChar; // 返回读取的字符 } /* - * when read file /proc/self/maps, ignore unusefull information and skip to the end of line - * after we have geting all necessary information. - * return : count of character store into buffer - success - * RET_ERR - failed + * 当读取文件/proc/self/maps时,忽略无用信息并跳过到行尾,在获得所有必要信息之后。 + * 返回:存储在缓冲区中的字符数 - 成功 + * RET_ERR - 失败 */ int BBOX_SkipToLineEnd(struct BBOX_READ_FILE_IO* pstReadIO) { int iGetChar = -1; + // 检查pstReadIO是否为NULL if (NULL == pstReadIO) { - bbox_print(PRINT_ERR, "BBOX_SkipToLineEnd parameters is invalid: pstReadIO is NULL.\n"); + bbox_print(PRINT_ERR, "BBOX_SkipToLineEnd参数无效:pstReadIO为NULL。\n"); return RET_ERR; } do { - /* reads characters until the newline character */ + /* 读取字符直到换行符 */ iGetChar = BBOX_GetCharFromFile(pstReadIO); + + // 检查读取字符是否失败 if (RET_ERR == iGetChar) { - bbox_print(PRINT_ERR, "BBOX_SkipToLineEnd is failed, iGetChar= %d.\n", iGetChar); + bbox_print(PRINT_ERR, "BBOX_SkipToLineEnd失败,iGetChar = %d。\n", iGetChar); return RET_ERR; } } while (iGetChar != '\n'); @@ -202,18 +220,17 @@ int BBOX_SkipToLineEnd(struct BBOX_READ_FILE_IO* pstReadIO) } /* - * judge whether the range between *pStartAddress* and *pEndAddress* is in black list or not. - * If found, return the blacklist item which cover it, else return NULL. + * 判断地址范围是否在黑名单中,如果是,则返回覆盖它的黑名单项,否则返回NULL。 * - * NOTE: this function is a thread-unsafe function since it works as an iterator. + * 注意:该函数是一个线程不安全的函数,因为它作为迭代器使用。 */ -BBOX_BLACKLIST_STRU *_BBOX_FindAddrInBlackList(const void *pStartAddress, const void *pEndAddress) +BBOX_BLACKLIST_STRU *_BBOX_FindAddrInBlackList(const void* pStartAddress, const void* pEndAddress) { int i = 0; int iPerformance = 0; - size_t uiPageSize = sys_sysconf(_SC_PAGESIZE); + size_t uiPageSize = sys_sysconf(_SC_PAGESIZE); - /* if the cursor reachs the end of blacklist, start a new trip. */ + /* 如果游标达到黑名单的末尾,则开始新的遍历。 */ if (g_iPosBlackList >= g_iNumBlackList) { g_iPosBlackList = 0; } @@ -222,41 +239,41 @@ BBOX_BLACKLIST_STRU *_BBOX_FindAddrInBlackList(const void *pStartAddress, const iPerformance++; /* - * if the endAddress of segemnt is less than the startAddress of this item, it means there is - * no cross with the rest blacklist items since blacklist items are in increasing order. + * 如果段的结束地址小于此项的开始地址, + * 这意味着不会与剩余的黑名单项相交,因为黑名单项是按递增顺序排列的。 */ if (pEndAddress <= g_stBlackList[i].pBlackStartAddr) { - bbox_print(PRINT_DBG, "\nFIND BL BY %d TIMES, POS = %d.\n\n", iPerformance, g_iPosBlackList); - bbox_print(PRINT_DBG, "not find black list in segment.\n"); + bbox_print(PRINT_DBG, "\n通过%d次查找,POS = %d找到黑名单。\n\n", iPerformance, g_iPosBlackList); + bbox_print(PRINT_DBG, "在段中未找到黑名单。\n"); return NULL; } if (pStartAddress <= g_stBlackList[i].pBlackStartAddr && g_stBlackList[i].pBlackStartAddr < pEndAddress) { g_iPosBlackList = i; - bbox_print(PRINT_DBG, "\nFIND BL BY %d TIMES, POS = %d.\n\n", iPerformance, g_iPosBlackList); - bbox_print(PRINT_DBG, "find black list in segment.\n"); + bbox_print(PRINT_DBG, "\n通过%d次查找,POS = %d找到黑名单。\n\n", iPerformance, g_iPosBlackList); + bbox_print(PRINT_DBG, "在段中找到黑名单。\n"); return &(g_stBlackList[i]); } if (pStartAddress < g_stBlackList[i].pBlackEndAddr && g_stBlackList[i].pBlackEndAddr <= pEndAddress) { if (((uintptr_t)pEndAddress - (uintptr_t)(g_stBlackList[i].pBlackEndAddr)) < uiPageSize) { - bbox_print(PRINT_DBG, "find black list in segment, but size < 4K, do not care return.\n"); + bbox_print(PRINT_DBG, "在段中找到黑名单,但大小小于4K,不用关心,返回NULL。\n"); return NULL; } else { g_iPosBlackList = i; - bbox_print(PRINT_DBG, "\nFIND BL BY %d TIMES, POS = %d.\n\n", iPerformance, g_iPosBlackList); - bbox_print(PRINT_DBG, "find black list in segment.\n"); + bbox_print(PRINT_DBG, "\n通过%d次查找,POS = %d找到黑名单。\n\n", iPerformance, g_iPosBlackList); + bbox_print(PRINT_DBG, "在段中找到黑名单。\n"); return &(g_stBlackList[i]); } } } - bbox_print(PRINT_DBG, "\nFIND BL BY %d TIMES, POS = %d.\n\n", iPerformance, g_iPosBlackList); - bbox_print(PRINT_DBG, "not find black list in segment.\n"); + bbox_print(PRINT_DBG, "\n通过%d次查找,POS = %d未找到黑名单。\n\n", iPerformance, g_iPosBlackList); + bbox_print(PRINT_DBG, "在段中未找到黑名单。\n"); - /* no cross between this segment and blacklist items. */ + /* 此段与黑名单项之间无交叉。 */ return NULL; } @@ -266,23 +283,25 @@ BBOX_BLACKLIST_STRU *_BBOX_FindAddrInBlackList(const void *pStartAddress, const * unsigned long long uiLen : memory size * return RET_OK if success else RET_ERR. */ +/* 向黑名单列表添加地址 */ int _BBOX_AddBlackListAddress(void* pAddress, unsigned long long uiLen) { unsigned int uiFound = 0; + // 如果地址为空或者长度小于最小限制,打印错误信息并返回错误码 if (pAddress == NULL || uiLen < BBOX_BLACK_LIST_MIN_LEN) { bbox_print(PRINT_ERR, "parameter uiLen(%llu) is invaild.\n", uiLen); return RET_ERR; } - /* use atomic increment to control concurrency. */ + /* 使用原子增加操作来控制并发 */ while (BBOX_AtomicIncReturn(&g_stLockBlackList) > 1) { BBOX_AtomicDec(&g_stLockBlackList); bbox_print(PRINT_DBG, "add blacklist addr is running, waiting.\n"); sleep(1); } - /* if too many blacklist items were added, return error while its upper limits reaches. */ + // 如果黑名单项目数量达到上限,打印错误信息并返回错误码 if (g_iNumBlackList >= BBOX_BLACK_LIST_COUNT_MAX) { BBOX_AtomicDec(&g_stLockBlackList); bbox_print(PRINT_ERR, "blacklist addr total reach max, failed.\n"); @@ -290,11 +309,11 @@ int _BBOX_AddBlackListAddress(void* pAddress, unsigned long long uiLen) } /* - * suppose that address became bigger and bigger, move forward from blacklist's tail, - * and find the proper postion to insert this address into the blacklist. + * 假设地址越来越大,从黑名单末尾开始往前移动, + * 找到适合将该地址插入到黑名单的位置 */ for (int i = g_iNumBlackList - 1; i >= 0; i--) { - /* if try to add the same address again, report error. */ + /* 如果试图再次添加相同的地址,报告错误 */ if (g_stBlackList[i].pBlackStartAddr == pAddress) { BBOX_AtomicDec(&g_stLockBlackList); @@ -319,7 +338,7 @@ int _BBOX_AddBlackListAddress(void* pAddress, unsigned long long uiLen) } } - /* if no found, it means this address is smaller than all锛宲ut it in the head. */ + /* 如果没有找到,说明该地址比所有地址都小,将其置于首位 */ if (uiFound == 0) { g_stBlackList[0].pBlackStartAddr = pAddress; g_stBlackList[0].uiLength = uiLen; @@ -335,42 +354,43 @@ int _BBOX_AddBlackListAddress(void* pAddress, unsigned long long uiLen) } /* - * drop a blaclist item to dump it in core file. - * void *pAddress : the head address of excluded memory - * return RET_OK if success else RET_ERR. + * 将黑名单中的项删除,并将其转储到核心文件中。 + * void *pAddress : 被排除内存的起始地址 + * return RET_OK 成功,否则返回 RET_ERR。 */ int _BBOX_RmvBlackListAddress(void* pAddress) { unsigned int uiFound = 0; + // 如果地址为空,打印错误信息并返回错误码 if (pAddress == NULL) { bbox_print(PRINT_ERR, "parameter pAddress is invaild.\n"); return RET_ERR; } - /* use atomic increment to control concurrency. */ + /* 使用原子增加操作来控制并发 */ while (BBOX_AtomicIncReturn(&g_stLockBlackList) > 1) { BBOX_AtomicDec(&g_stLockBlackList); bbox_print(PRINT_DBG, "remove blacklist addr is running, waiting.\n"); sleep(1); } - /* if blacklist is empty, return error. */ + // 如果黑名单为空,返回错误码 if (g_iNumBlackList == 0) { BBOX_AtomicDec(&g_stLockBlackList); bbox_print(PRINT_ERR, "blacklist addr total is zero, failed.\n"); return RET_ERR; } - /* find the specified address and drop it from blacklist. */ + // 找到指定的地址并从黑名单中删除 for (int i = 0; i < g_iNumBlackList; i++) { if (pAddress == g_stBlackList[i].pBlackStartAddr) { uiFound = 1; } - /* if found, move subsequent items a step forward. */ + /* 如果找到,将后续项目向前移动一步 */ if (uiFound == 1) { - /* if it is the last, clear it and stop. */ + /* 如果是最后一个,清除并停止 */ if (i == (g_iNumBlackList - 1)) { int rc = memset_s(&g_stBlackList[i], sizeof(g_stBlackList[0]), 0, sizeof(g_stBlackList[0])); securec_check_c(rc, "\0", "\0"); @@ -402,6 +422,8 @@ int _BBOX_RmvBlackListAddress(void* pAddress) * return : count of character store into buffer - success * RET_ERR - failed */ +```c +// 获取系统状态信息 int BBOX_GetStatusInfo(char* pBuffer, unsigned int uiBufLen) { int iResult = 0; @@ -409,15 +431,16 @@ int BBOX_GetStatusInfo(char* pBuffer, unsigned int uiBufLen) int iAllSize = 0; int iReadSize = 0; + // 检查参数是否有效 if (NULL == pBuffer) { - bbox_print(PRINT_ERR, "BBOX_GetStatusInfo parameters is invalid.\n"); + bbox_print(PRINT_ERR, "BBOX_GetStatusInfo参数无效。\n"); return RET_ERR; } - /* information title */ - iResult = bbox_snprintf(pBuffer, uiBufLen, "\nSTATUS INFO\n--------------------------------------------\n"); + /* 信息标题 */ + iResult = bbox_snprintf(pBuffer, uiBufLen, "\n状态信息\n--------------------------------------------\n"); if (iResult <= 0 || iResult > (int)uiBufLen) { - bbox_print(PRINT_ERR, "bbox_snprintf is failed, errno = %d.\n", errno); + bbox_print(PRINT_ERR, "bbox_snprintf执行失败,errno = %d。\n", errno); return RET_ERR; } @@ -426,18 +449,18 @@ int BBOX_GetStatusInfo(char* pBuffer, unsigned int uiBufLen) uiBufLen -= iResult; iAllSize += iResult; - /* open /proc/self/status */ + /* 打开/proc/self/status */ BBOX_NOINTR(iStatFD = sys_open(BBOX_SELF_STATUS_PATH, O_RDONLY, 0)); if (iStatFD < 0) { - bbox_print(PRINT_ERR, "sys_open is failed, errno = %d.\n", errno); + bbox_print(PRINT_ERR, "sys_open执行失败,errno = %d。\n", errno); return RET_ERR; } - /* read /proc/self/status */ + /* 读取/proc/self/status */ BBOX_NOINTR(iReadSize = sys_read(iStatFD, pBuffer, uiBufLen)); if (iReadSize < 0) { (void)sys_close(iStatFD); - bbox_print(PRINT_ERR, "sys_read is failed, errno = %d.\n", errno); + bbox_print(PRINT_ERR, "sys_read执行失败,errno = %d。\n", errno); return RET_ERR; } @@ -448,11 +471,11 @@ int BBOX_GetStatusInfo(char* pBuffer, unsigned int uiBufLen) } /* - * get status information of cpu - * in : char *pBuffer - buffer to store result - * unsigned int uiBufLen - buffer size - * return : count of character store into buffer - success - * RET_ERR - failed + * 获取CPU信息 + * 输入:char *pBuffer - 存储结果的缓冲区 + * unsigned int uiBufLen - 缓冲区大小 + * 返回值:存储到缓冲区的字符数 - 成功 + * RET_ERR - 失败 */ int BBOX_GetCpuInfo(char* pBuffer, unsigned int uiBufLen) { @@ -461,15 +484,16 @@ int BBOX_GetCpuInfo(char* pBuffer, unsigned int uiBufLen) int iAllSize = 0; int iReadSize = 0; + // 检查参数是否有效 if (NULL == pBuffer) { - bbox_print(PRINT_ERR, "BBOX_GetCpuInfo parameters is invalid.\n"); + bbox_print(PRINT_ERR, "BBOX_GetCpuInfo参数无效。\n"); return RET_ERR; } - /* information title */ - iResult = bbox_snprintf(pBuffer, uiBufLen, "\nCPU INFO\n--------------------------------------------\n"); + /* 信息标题 */ + iResult = bbox_snprintf(pBuffer, uiBufLen, "\nCPU信息\n--------------------------------------------\n"); if (iResult <= 0 || iResult > (int)uiBufLen) { - bbox_print(PRINT_ERR, "bbox_snprintf is failed, errno = %d.\n", errno); + bbox_print(PRINT_ERR, "bbox_snprintf执行失败,errno = %d。\n", errno); return RET_ERR; } @@ -478,18 +502,18 @@ int BBOX_GetCpuInfo(char* pBuffer, unsigned int uiBufLen) uiBufLen -= iResult; iAllSize += iResult; - /* open /proc/stat */ + /* 打开/proc/stat */ BBOX_NOINTR(iStatFD = sys_open(BBOX_PROC_INTER_PATH, O_RDONLY, 0)); if (iStatFD < 0) { - bbox_print(PRINT_ERR, "sys_open is failed, errno = %d.\n", errno); + bbox_print(PRINT_ERR, "sys_open执行失败,errno = %d。\n", errno); return RET_ERR; } - /* read /proc/stat */ + /* 读取/proc/stat */ BBOX_NOINTR(iReadSize = sys_read(iStatFD, pBuffer, uiBufLen)); if (iReadSize < 0) { (void)sys_close(iStatFD); - bbox_print(PRINT_ERR, "sys_read is failed, errno = %d.\n", errno); + bbox_print(PRINT_ERR, "sys_read执行失败,errno = %d。\n", errno); return RET_ERR; } @@ -500,11 +524,11 @@ int BBOX_GetCpuInfo(char* pBuffer, unsigned int uiBufLen) } /* - * get information of system internal storage - * in : char *pBuffer - buffer to store result - * unsigned int uiBufLen - buffer size - * return : count of character store into buffer - success - * RET_ERR - failed + * 获取系统内存信息 + * 输入:char *pBuffer - 存储结果的缓冲区 + * unsigned int uiBufLen - 缓冲区大小 + * 返回值:存储到缓冲区的字符数 - 成功 + * RET_ERR - 失败 */ int BBOX_GetMemInfo(char* pBuffer, unsigned int uiBufLen) { @@ -513,15 +537,16 @@ int BBOX_GetMemInfo(char* pBuffer, unsigned int uiBufLen) int iAllSize = 0; int iReadSize = 0; + // 检查参数是否有效 if (NULL == pBuffer) { - bbox_print(PRINT_ERR, "BBOX_GetMemInfo parameters is invalid.\n"); + bbox_print(PRINT_ERR, "BBOX_GetMemInfo参数无效。\n"); return RET_ERR; } - /* information title */ - iResult = bbox_snprintf(pBuffer, uiBufLen, "\nMEM INFO\n--------------------------------------------\n"); + /* 信息标题 */ + iResult = bbox_snprintf(pBuffer, uiBufLen, "\n内存信息\n--------------------------------------------\n"); if (iResult <= 0 || iResult > (int)uiBufLen) { - bbox_print(PRINT_ERR, "bbox_snprintf is failed, errno = %d.\n", errno); + bbox_print(PRINT_ERR, "bbox_snprintf执行失败,errno = %d。\n", errno); return RET_ERR; } @@ -530,18 +555,18 @@ int BBOX_GetMemInfo(char* pBuffer, unsigned int uiBufLen) uiBufLen -= iResult; iAllSize += iResult; - /* open /proc/meminfo */ + /* 打开/proc/meminfo */ BBOX_NOINTR(iStatFD = sys_open(BBOX_PROC_MEMINFO_PATH, O_RDONLY, 0)); if (iStatFD < 0) { - bbox_print(PRINT_ERR, "sys_open is failed, iStatFD = %d.\n", iStatFD); + bbox_print(PRINT_ERR, "sys_open执行失败,iStatFD = %d。\n", iStatFD); return RET_ERR; } - /* read /proc/meminfo */ + /* 读取/proc/meminfo */ BBOX_NOINTR(iReadSize = sys_read(iStatFD, pBuffer, uiBufLen)); if (iReadSize < 0) { (void)sys_close(iStatFD); - bbox_print(PRINT_ERR, "sys_read is failed, iReadSize = %d.\n", iReadSize); + bbox_print(PRINT_ERR, "sys_read执行失败,iReadSize = %d。\n", iReadSize); return RET_ERR; } @@ -551,6 +576,7 @@ int BBOX_GetMemInfo(char* pBuffer, unsigned int uiBufLen) return iAllSize; } + /* * get information of ps command * in : char *pBuffer - buffer to write result information @@ -558,6 +584,18 @@ int BBOX_GetMemInfo(char* pBuffer, unsigned int uiBufLen) * return : success - count of characters written to the buffer * failed - RET_ERR */ + /* +这段代码是一个用于获取系统运行信息的函数。函数名为BBOX_GetPsInfo, +接受两个参数:一个字符指针pBuffer,用于存储信息的缓冲区;一个无符号整数uiBufLen,表示缓冲区的长度。函数的返回值为成功写入缓冲区的字符数量,如果失败则返回一个错误码。 +函数首先对传入的指针进行了NULL检查,如果pBuffer为NULL,则打印错误消息并返回一个错误码。 +然后,使用bbox_snprintf函数将一些信息标题写入缓冲区,包括换行符和分隔线。如果bbox_snprintf返回值小于等于0,或者大于缓冲区长度,打印一个错误消息,并返回一个错误码。 +接下来,函数通过执行系统命令"ps"来获取进程信息。函数使用sys_popen函数打开一个管道,并将管道的输出连接到iCommandFD文件描述符。如果sys_popen返回的文件描述符小于0,说明打开管道失败,打印一个相关错误消息,并返回一个错误码。 +然后,函数使用sys_read函数从iCommandFD文件描述符中读取数据,并将数据写入缓冲区。如果sys_read返回的字节数小于0,说明读取失败,函数调用sys_pclose函数关闭打开的管道,并打印一个相关错误消息,并返回一个错误码。 +接着,函数将读取到的字节数累加到iAllSize变量中。如果iReadSize不为0,函数将缓冲区中的最后一个字符设置为'\0'。 +最后,函数返回iAllSize变量的值,表示写入缓冲区的总字符数量。 +此外,代码中还有一个函数_BBOX_GetAddonInfo,该函数调用了BBOX_GetPsInfo函数,并对获取的系统运行信息进行了进一步处理。该函数的实现逻辑与BBOX_GetPsInfo类似,但是还调用了其他几个函数来获取不同的系统信息,并将这些信息写入缓冲区。 +最后的#ifdef __cplusplus部分是对C++编译器进行的特殊处理,暂时可以忽略。 + */ int BBOX_GetPsInfo(char* pBuffer, unsigned int uiBufLen) { int iResult = 0; diff --git a/src/gausskernel/cbb/bbox/bbox_lib.cpp b/src/gausskernel/cbb/bbox/bbox_lib.cpp index f8a1eae7b..908105d23 100644 --- a/src/gausskernel/cbb/bbox/bbox_lib.cpp +++ b/src/gausskernel/cbb/bbox/bbox_lib.cpp @@ -64,6 +64,15 @@ note锛歍he two pointers shouldn't be null. The last argument shouldn't less than date: 2022/8/2 contact tel: 18720816902 */ +/* +这段代码是一个用于比较字符串的函数bbox_strncmp。它接受三个参数:常量字符指针pszSrc,用于表示源字符串;常量字符指针pszTarget,用于表示目标字符串;整数count,表示要比较的字符数。 +首先,定义一个有符号字符变量cRes并初始化为0。 +然后,使用while循环进行字符串比较,条件为count大于0。每次循环执行以下操作: +首先,将源字符串指针的值与目标字符串指针的值相减,并将结果赋给cRes。如果结果不等于0,或者源字符串指针的值为0(即字符串结束),则跳出循环。 +然后,将目标字符串指针递增1,源字符串指针也递增1。 +最后,将count减1。 +循环结束后,返回变量cRes,它表示最后一次比较的结果。如果cRes为0,则表示两个字符串相等;如果cRes小于0,则表示源字符串小于目标字符串;如果cRes大于0,则表示源字符串大于目标字符串。 +*/ s32 bbox_strncmp(const char* pszSrc, const char* pszTarget, s32 count) { signed char cRes = 0; @@ -167,6 +176,15 @@ note: I think the function isn't perfect, though it's not a core function. For e date: 2022/8/2 contact tel:same */ +/* +这段代码是一个将字符串转换为整数的函数bbox_atoi。它接受一个常量字符指针pszString,表示要转换的字符串,并返回一个整数值。 +首先,定义两个整数变量n和iNeg,并初始化为0。其中,n用于存储转换后的整数值,iNeg用于表示是否为负数。 +然后,判断字符串的第一个字符是否为'-'。如果是,则将iNeg设置为1,表示转换结果为负数。 +如果iNeg为1,将字符串指针向后移动一位,跳过负号。 +接下来,使用while循环,判断当前字符是否为数字字符,即是否在字符范围'0'到'9'之间。 +在循环内部,首先将n乘以10,然后将当前字符减去'0',并累加到n中。 +循环结束后,返回n的值。如果iNeg为1,则表示结果为负数,返回负数的n;如果iNeg为0,则表示结果为正数,返回正数的n。 +*/ s32 bbox_atoi(const char* pszString) { s32 n = 0; @@ -387,6 +405,27 @@ note: The string that indicates pszMode should only be "r" or "w", date: 2022/8/2 contact tel: same */ +/* +函数sys_popen的作用是打开一个管道,用于执行指定的命令。 +它接受两个参数:一个字符指针pszCmd,用于存储要执行的命令;一个常量字符指针pszMode,表示管道的读写模式。函数返回一个整数值,表示管道文件描述符。 +首先,函数检查pszCmd和pszMode指针是否为空,如果为空,则设置errno为EINVAL,并返回-1。 +然后,函数检查读写模式是否正确,即pszMode只能为'r'或'w',并且只能有一个字符。如果模式不正确,则设置errno为EINVAL,并返回-1。 +接下来,函数使用bbox_GetFreePid函数获取一个空闲的管道ID。如果获取失败,则返回-1。 +然后,函数使用sys_pipe函数创建一个管道,如果创建失败,则返回-1。 +接着,函数使用sys_fork函数创建子进程。如果创建子进程失败,则关闭管道的文件描述符,释放管道ID,并返回-1。如果创建成功,则在子进程中执行以下操作: + +- 获取待执行的命令和参数数组pArgv。 +- 恢复信号函数。 +- 关闭其他文件描述符,除了管道的读写文件描述符。 +- 根据读写模式设置标准输入输出文件描述符。 +- 使用sys_execve函数执行命令。如果执行失败,则打印错误信息,并使用sys_exit函数退出进程。 +最后,如果当前进程是父进程,则根据读写模式选择要返回的文件描述符,并将管道ID和子进程ID保存起来。 +函数sys_pclose的作用是关闭由sys_popen打开的管道。它接受一个整数值iFd,表示要关闭的文件描述符。函数返回一个整数值,表示进程的最终状态。 +首先,函数使用bbox_FindPid函数根据文件描述符查找对应的管道ID。如果查找失败,则返回-1。 +然后,函数使用sys_close函数关闭文件描述符。 +接着,函数使用sys_waitpid函数等待子进程的退出,并获取进程的状态。如果等待失败,则继续等待,直到成功或出现其他错误。 +最后,函数释放管道ID,并根据等待的结果返回相应的值。 +*/ s32 sys_popen(char* pszCmd, const char* pszMode) { struct PIPE_ID* volatile stCurPid = NULL; diff --git a/src/gausskernel/cbb/bbox/bbox_print.cpp b/src/gausskernel/cbb/bbox/bbox_print.cpp index ae7be6486..9521431c2 100644 --- a/src/gausskernel/cbb/bbox/bbox_print.cpp +++ b/src/gausskernel/cbb/bbox/bbox_print.cpp @@ -139,51 +139,59 @@ contact tel: 18720816902 */ s32 bbox_vsnprintf(BBOX_vnprintCallBack pCallback, void* ptr, s32 iSize, const char* pFmt, va_list ap) { + // 定义变量 + s32 iCount = 0; // 记录写入缓冲区的字符数 + char c; // 临时存储格式化字符串中的字符 + s32 iCheckFmt = 0; // 判断是否处于格式化标识符 '%' 的状态 + s32 iQualifier = 0; // 判断是否有 'l' 或 'z' 限定符 + s32 iSizeTConv = 0; // 判断是否有 'z' 限定符 + s32 iRet = 0; // 用于保存回调函数的返回值 - s32 iCount = 0; - char c; - s32 iCheckFmt = 0; - s32 iQualifier = 0; - s32 iSizeTConv = 0; - s32 iRet = 0; - + // 检查缓冲区大小是否合法 if (iSize <= 0) { return 0; } - /* traversal handles formatting strings */ + // 遍历格式化字符串 while (0 == iRet) { c = *(pFmt++); + + // 判断是否遍历完格式化字符串 if (!c) { break; } - /* judge format type if % */ + + // 判断是否为格式化标识符 '%' if (c == '%' && 0 == iCheckFmt) { + // 初始化限定符和转换说明符 iQualifier = 0; iSizeTConv = 0; iCheckFmt = 1; continue; } else if (0 == iCheckFmt) { - /* copy */ + // 复制非格式化标识符 iRet = pCallback(c, ptr, &iCount, iSize); continue; } - /* check whether the parameter has l */ + // 判断是否有 'l' 限定符或 'z' 限定符 if (c == 'l' && iQualifier == 0) { - iQualifier = 1; + iQualifier = 1; // 标记有 'l' 限定符 continue; } else if (c == 'z') { - iSizeTConv = 1; + iSizeTConv = 1; // 标记有 'z' 限定符 continue; } + // 根据格式化标识符的类型执行相应的操作 switch (c) { case 'c': { + // 处理字符类型 char ch = (char)va_arg(ap, int); - iRet = pCallback(ch, ptr, &iCount, iSize); + iRet = pCallback(ch, ptr, &iCount, iSize); // 调用回调函数处理字符 } break; case 'd': { + // 处理有符号十进制整数类型 signed long long n = 0; if (iSizeTConv) { #if (defined(__x86_64__)) || (defined(__aarch64__)) @@ -197,19 +205,22 @@ s32 bbox_vsnprintf(BBOX_vnprintCallBack pCallback, void* ptr, s32 iSize, const c s32 isNeg = (n < 0) ? 1 : 0; n = (isNeg) ? (-1 * n) : (n); - iRet = bbox_put_dox(pCallback, ptr, &iCount, iSize, (u64)n, 10, isNeg); + iRet = bbox_put_dox(pCallback, ptr, &iCount, iSize, (u64)n, 10, isNeg); // 调用回调函数处理数字 } break; case 'l': { + // 处理长整型类型 signed long long n = (iQualifier) ? va_arg(ap, long long) : va_arg(ap, long); s32 isNeg = (n < 0) ? 1 : 0; n = (isNeg) ? (-1 * n) : (n); - iRet = bbox_put_dox(pCallback, ptr, &iCount, iSize, (u64)n, 10, isNeg); + iRet = bbox_put_dox(pCallback, ptr, &iCount, iSize, (u64)n, 10, isNeg); // 调用回调函数处理数字 } break; case 'x': { + // 处理十六进制整数类型 unsigned long long n = (iQualifier) ? va_arg(ap, unsigned long int) : va_arg(ap, unsigned int); - iRet = bbox_put_dox(pCallback, ptr, &iCount, iSize, (u64)n, 16, 0); + iRet = bbox_put_dox(pCallback, ptr, &iCount, iSize, (u64)n, 16, 0); // 调用回调函数处理数字 } break; case 'u': { + // 处理无符号十进制整数类型 unsigned long long n = 0; if (iSizeTConv) { #if (defined(__x86_64__)) || (defined(__aarch64__)) @@ -221,37 +232,41 @@ s32 bbox_vsnprintf(BBOX_vnprintCallBack pCallback, void* ptr, s32 iSize, const c n = (iQualifier) ? va_arg(ap, unsigned long long) : va_arg(ap, unsigned int); } - iRet = bbox_put_dox(pCallback, ptr, &iCount, iSize, (u64)n, 10, 0); + iRet = bbox_put_dox(pCallback, ptr, &iCount, iSize, (u64)n, 10, 0); // 调用回调函数处理数字 } break; case 'p': { + // 处理指针类型 unsigned long long n = va_arg(ap, unsigned long); - iRet = bbox_put_dox(pCallback, ptr, &iCount, iSize, (u64)n, 16, 0); + iRet = bbox_put_dox(pCallback, ptr, &iCount, iSize, (u64)n, 16, 0); // 调用回调函数处理数字 } break; case 's': { + // 处理字符串类型 char* p = va_arg(ap, char*); if (p == NULL) { p = ""; } while (*p && (!iRet)) { - iRet = pCallback(*p, ptr, &iCount, iSize); + iRet = pCallback(*p, ptr, &iCount, iSize); // 调用回调函数处理字符 p++; } } break; default: - iRet = pCallback(c, ptr, &iCount, iSize); + iRet = pCallback(c, ptr, &iCount, iSize); // 调用回调函数处理字符 break; } + // 重置限定符和转换说明符 iQualifier = 0; iCheckFmt = 0; } + // 检查回调函数的返回值和终止符的写入情况 if (iRet != RET_OK || pCallback(0, ptr, &iCount, iSize) != RET_OK) { return RET_ERR; } - return iCount; + return iCount; // 返回写入缓冲区的字符数 } /* @@ -269,20 +284,23 @@ note: none date: 2022/8/3 contact tel: 18720816902 */ +/* +bbox_SnprintCallback函数是一个回调函数,用于处理bbox_snprintf函数中的格式化字符串。该函数的作用是将字符c写入到pszBuff指向的缓冲区中,并更新pszBuff和piCount的值。 +*/ s32 bbox_SnprintCallback(char c, void* pPtr, s32* piCount, s32 iSize) { char** pszBuff = (char**)pPtr; - /* return if the buffer length is exceeded */ + /* 如果超过了缓冲区的长度限制,则返回错误 */ if (*piCount >= iSize - 1) { - /* set the last bit to 0 and return err, means that exit snprintf_s function. */ + /* 将最后一位设置为0,并返回错误(表示退出snprintf_s函数) */ **pszBuff = 0; return RET_ERR; } - **pszBuff = c; - (*pszBuff)++; - (*piCount)++; + **pszBuff = c; // 将字符c写入到缓冲区中 + (*pszBuff)++; // 更新pszBuff的地址 + (*piCount)++; // 更新piCount的值 return RET_OK; } @@ -294,6 +312,7 @@ s32 bbox_SnprintCallback(char c, void* pPtr, s32* piCount, s32 iSize) * pFmt - string format * return : string length */ + //bbox_snprintf函数是一个简化版的snprintf函数,用于格式化输出字符串到指定的缓冲区中。 s32 bbox_snprintf(char* pszBuff, s32 iSize, const char* pFmt, ...) { va_list ap; @@ -313,6 +332,10 @@ s32 bbox_snprintf(char* pszBuff, s32 iSize, const char* pFmt, ...) * iSize - length limit * return : string length */ + /* + bbox_PrintCallback函数是一个回调函数,用于处理bbox_printf函数和bbox_print函数中的格式化字符串。 + 该函数的作用是将字符c写入到指定的文件描述符中,并更新g_pcCurWriteLogPos和g_iLastLogLen的值 + */ s32 bbox_PrintCallback(char c, void* pPtr, s32* piCount, s32 iSize) { s32* fd = (s32*)pPtr; @@ -321,14 +344,14 @@ s32 bbox_PrintCallback(char c, void* pPtr, s32* piCount, s32 iSize) return RET_OK; } - /* write */ + /* 写入文件描述符 */ if (fd != 0 && *fd >= 0) { sys_write(*fd, &c, 1); } - /* return if the buffer length is exceeded */ + /* 如果超过了缓冲区的长度限制,则返回错误 */ if (g_iLastLogLen <= 1) { - /* set the last bit to 0 and return err, means that exit snprintf_s function. */ + /* 将最后一位设置为0,并返回错误(表示退出snprintf_s函数) */ *g_pcCurWriteLogPos = 0; if (g_iLogScreen) { return RET_OK; @@ -337,9 +360,9 @@ s32 bbox_PrintCallback(char c, void* pPtr, s32* piCount, s32 iSize) return RET_ERR; } - *g_pcCurWriteLogPos = c; - (g_pcCurWriteLogPos)++; - (g_iLastLogLen)--; + *g_pcCurWriteLogPos = c; // 将字符c写入到缓冲区中 + (g_pcCurWriteLogPos)++; // 更新g_pcCurWriteLogPos的地址 + (g_iLastLogLen)--; // 更新g_iLastLogLen的值 return RET_OK; } @@ -347,6 +370,7 @@ s32 bbox_PrintCallback(char c, void* pPtr, s32* piCount, s32 iSize) /* * simple signal-safe function printf */ + //bbox_printf函数是一个简化版的printf函数,用于将格式化的字符串输出到标准输出。 void bbox_printf(const char* pFmt, ...) { s32 fd = 1; @@ -360,6 +384,7 @@ void bbox_printf(const char* pFmt, ...) /* * simple signal-safe function print */ + //bbox_print函数是一个简化版的printf函数,可以根据打印级别和屏幕打印级别来输出格式化的字符串到标准输出。 void bbox_print(EN_PRINT_TYPE enType, const char* pFmt, ...) { s32 fd = 1; @@ -383,6 +408,7 @@ void bbox_print(EN_PRINT_TYPE enType, const char* pFmt, ...) /* * set print level */ + //bbox_set_log_level函数用于设置日志级别,根据传入的enLevel参数来设置全局变量g_enLogLevel的值。 s32 bbox_set_log_level(EN_PRINT_TYPE enLevel) { if (enLevel < PRINT_DBG || enLevel > PRINT_ERR) { @@ -397,6 +423,7 @@ s32 bbox_set_log_level(EN_PRINT_TYPE enLevel) /* * set screen print level */ + //bbox_set_screen_log_level函数用于设置屏幕打印级别,根据传入的enLevel参数来设置全局变量g_enScreenLogLeven的值。 s32 bbox_set_screen_log_level(EN_PRINT_TYPE enLevel) { if (enLevel < PRINT_DBG || enLevel > PRINT_ERR) { diff --git a/src/gausskernel/cbb/bbox/bbox_threads.cpp b/src/gausskernel/cbb/bbox/bbox_threads.cpp index 4129973cf..b7a677a06 100644 --- a/src/gausskernel/cbb/bbox/bbox_threads.cpp +++ b/src/gausskernel/cbb/bbox/bbox_threads.cpp @@ -72,6 +72,14 @@ note: The stack this function creats is actually a character array. date: 2022/8/3 contact tel: 18720816902 */ + +/* +void BBOX_ReserveZeroStack(s32 count)函数是一个预留并清零堆栈的函数。具体实现如下: + +1. 创建一个大小为count的字符数组buff。 +2. 调用memset_s函数将buff的值全部设置为0。 +3. 调用sys_read函数将buff的值从文件描述符-1读取进来。实际上此处读取的操作是无效的,只是为了预留并使用堆栈空间。 +*/ void BBOX_ReserveZeroStack(s32 count) { char buff[count]; @@ -86,6 +94,13 @@ void BBOX_ReserveZeroStack(s32 count) * clone the current process and runs the specified function * return 0 if seccess else err code */ + /* + s32 BBOX_CloneRun(u32 uFlags, s32 (*pFn)(void*), void* pArg, ...)函数是一个克隆当前进程并运行指定函数的函数。具体实现如下: + +1. 首先判断pArg和pFn是否为NULL,如果为NULL则返回-1。 +2. 调用sys__clone函数克隆当前进程,并将指定的函数和参数传递给新创建的进程。 +3. 返回新创建进程的pid。 +*/ s32 BBOX_CloneRun(u32 uFlags, s32 (*pFn)(void*), void* pArg, ...) { /* reserve 4K when calling and running a function to protect waitpid can exit correct. */ @@ -153,6 +168,26 @@ note: none date: 2022/8/3 contact tel: 18720816902 */ +/* +这是一个用于获取指定路径下进程和线程ID的函数。 + +函数的参数包括指向TASK_ATTACH_INFO结构体的指针pstTaskInfo,整型变量iSize和指向字符数组的指针szTaskPath。 + +函数的主要步骤如下: + +1. 首先检查指针pstTaskInfo和szTaskPath是否为NULL,如果是则输出错误信息并返回-1。 +2. 使用sys_open函数以只读和目录模式打开指定的路径szTaskPath,得到的文件描述符保存在变量iProc中。 +3. 如果iProc小于0,则输出错误信息并返回-1。 +4. 使用循环遍历/proc/[pid]/task目录下的所有文件,其中iThreadCount表示已经获取的进程和线程ID的数量。 +5. 使用sys_getdents函数读取目录项,读取的结果保存在szBuff中,返回的字节数保存在nBytes中。 +6. 如果nBytes小于0,则输出错误信息并跳转到errout标签。 +7. 如果nBytes等于0,则使用sys_lseek函数将文件指针设置到目录的开头位置,然后跳出循环。 +8. 遍历目录项,判断当前项是否为进程或线程的目录。 +9. 提取目录名中的数字作为pid,并将pid存储到pstTaskInfo数组对应的元素中。 +10. 将iThreadCount加1,表示已经获取的进程和线程ID的数量。 +11. 使用sys_close函数关闭iProc。 +12. 返回成功获取的进程和线程ID的数量iThreadCount,如果出错则返回-1。 +*/ s32 BBOX_GetTaskId(struct TASK_ATTACH_INFO* pstTaskInfo, s32 iSize, char* szTaskPath) { s32 iProc = -1; @@ -248,6 +283,19 @@ note: none date: 2022/8/3 contact tel: 18720816902 */ +/* +s32 BBOX_PtraceAttachPid(struct TASK_ATTACH_INFO* pstTaskInfo, s32 iPidCount, s32 iDoPtraceCheck)函数是用于检查指定进程的跟踪状态是否正常的函数。 + +首先检查pstTaskInfo和iPidCount是否合法,如果不合法则返回指定的错误码。 +使用循环遍历pstTaskInfo数组中的每个任务。 +使用sys_ptrace函数将指定任务的跟踪状态设置为跟踪状态。 +使用sys_waitpid函数等待指定任务结束。 +如果等待失败并且错误码不是EINTR,则输出错误信息,然后使用sys_ptrace函数将跟踪状态取消。 +如果iDoPtraceCheck为真,则使用sys_ptrace函数检查跟踪状态是否有效。 +如果检查失败,输出错误信息并取消跟踪状态。 +将任务的cIsAttached成员变量设置为已跟踪状态。 +返回正常执行的结果码。 +*/ s32 BBOX_PtraceAttachPid(struct TASK_ATTACH_INFO* pstTaskInfo, s32 iPidCount, s32 iDoPtraceCheck) { u32 i; @@ -367,20 +415,26 @@ note: none date: 2022/8/3 contact tel: 18720816902 */ +/* +这段代码是一个函数,接受一个指向结构体BBOX_ListParams的指针pstArgs、一个最大线程数量iMaxThreadCount和一个指向char类型的指针pszProcSelfTask作为参数。 +代码的作用是使用ptrace操作来追踪并运行线程。 +*/ s32 BBOX_PtraceAndRun(struct BBOX_ListParams* pstArgs, s32 iMaxThreadCount, char* pszProcSelfTask) { - struct TASK_ATTACH_INFO stTaskInfo[iMaxThreadCount]; - pid_t thread_pids[iMaxThreadCount]; + // 声明一些变量 + struct TASK_ATTACH_INFO stTaskInfo[iMaxThreadCount]; // 存储线程信息的数组 + pid_t thread_pids[iMaxThreadCount]; // 存储线程PID的数组 + + struct TASK_CHECK_RESUME_ARGS stTaskCheck; // 追踪线程所需参数 + s32 iThreadCount = 0; // 线程数量 + s32 iAttachCount = 0; // 被追踪的线程数量 + s32 iRet = 0; // 返回值变量 + s32 iDoPtraceCheck = 1; // 是否需要使用ptrace检查 - struct TASK_CHECK_RESUME_ARGS stTaskCheck; - s32 iThreadCount = 0; - s32 iAttachCount = 0; - s32 iRet = 0; - s32 iDoPtraceCheck = 1; s32 i; - if (pstArgs == NULL || pszProcSelfTask == NULL || iMaxThreadCount <= 0) { - + // 参数检查,如果参数无效则打印错误信息并返回错误码 + if (pstArgs == NULL || pszProcSelfTask == NULL || iMax 线程数量小于等于0) { bbox_print(PRINT_ERR, "Parameter is invald, pstArgs or pszProcSelfTask is NULL, iMaxThreadCount = %d \n", iMaxThreadCount); @@ -388,6 +442,7 @@ s32 BBOX_PtraceAndRun(struct BBOX_ListParams* pstArgs, s32 iMaxThreadCount, char return RET_ERR; } + // 初始化stTaskInfo、thread_pids和stTaskCheck为0 errno_t rc = memset_s(stTaskInfo, sizeof(stTaskInfo), 0, sizeof(stTaskInfo)); securec_check_c(rc, "\0", "\0"); rc = memset_s(thread_pids, sizeof(thread_pids), 0, sizeof(thread_pids)); @@ -395,6 +450,7 @@ s32 BBOX_PtraceAndRun(struct BBOX_ListParams* pstArgs, s32 iMaxThreadCount, char rc = memset_s(&stTaskCheck, sizeof(stTaskCheck), 0, sizeof(stTaskCheck)); securec_check_c(rc, "\0", "\0"); + // 获取线程数量 iThreadCount = BBOX_GetTaskId(stTaskInfo, iMaxThreadCount, pszProcSelfTask); if (iThreadCount <= 0) { bbox_print(PRINT_ERR, "Get task id failed.\n"); @@ -402,19 +458,21 @@ s32 BBOX_PtraceAndRun(struct BBOX_ListParams* pstArgs, s32 iMaxThreadCount, char goto errout; } + // 根据pstArgs的enGetType判断是否需要使用ptrace检查 if (GET_TYPE_DUMP != pstArgs->enGetType) { iDoPtraceCheck = 0; } else { iDoPtraceCheck = 1; } + // 使用BBOX_PtraceAttachPid函数对线程进行ptrace追踪 iRet = BBOX_PtraceAttachPid(stTaskInfo, iThreadCount, iDoPtraceCheck); if (iRet != RET_OK) { bbox_print(PRINT_ERR, "Ptrace attache failed.\n"); goto errout; } - /* copy information of thread that have been attaching. */ + // 将已经被追踪的线程的PID复制到thread_pids数组中 for (i = 0; i < iThreadCount; i++) { if (!stTaskInfo[i].cIsAttached) { continue; @@ -423,23 +481,26 @@ s32 BBOX_PtraceAndRun(struct BBOX_ListParams* pstArgs, s32 iMaxThreadCount, char iAttachCount++; } + // 设置stTaskCheck的相应参数 stTaskCheck.pstTaskInfo = stTaskInfo; stTaskCheck.enType = pstArgs->enGetType; stTaskCheck.iThreadCount = iThreadCount; - /* run call back function. */ + // 调用回调函数进行处理 bbox_print(PRINT_TIP, "Thread count :%d\n", iThreadCount); bbox_print(PRINT_TIP, "Ptraced thread count :%d\n", iAttachCount); bbox_print(PRINT_LOG, "Run callback: thread count = %d\n", iThreadCount); - /* Callback to the thread information handler. */ + // 调用线程信息处理函数的回调函数 pstArgs->iResult = pstArgs->pCallBack(BBOX_CheckResumeThread, &stTaskCheck, iAttachCount, thread_pids, pstArgs->ap); pstArgs->iError = errno; + // 解除所有线程的追踪 BBOX_DetachAllThread(stTaskInfo, iThreadCount); return RET_OK; errout: + // 出错情况下也需要解除所有线程的追踪 BBOX_DetachAllThread(stTaskInfo, iThreadCount); return RET_ERR; } @@ -454,30 +515,38 @@ note: none date: 2022/8/3 contact tel: 18720816902 */ +/* +这段代码是一个函数,接受一个指向char类型的指针pFileName作为参数。代码的作用是将全局变量g_acBBoxLog中的内容输出到文件中。 +*/ +// 函数声明:将全局变量g_acBBoxLog中的内容输出到文件中 void BBOX_PrintFailedLog(const char* pFileName) { - ssize_t iRet = 0; - s32 iWriteSize = 0; - s32 iBboxLogFd = -1; + ssize_t iRet = 0; // 返回值变量 + s32 iWriteSize = 0; // 写入的数据大小变量 + s32 iBboxLogFd = -1; // 文件描述符变量,初始化为-1 + // 使用sys_open函数打开文件,以可读写和创建方式打开,文件的访问权限为0600 iBboxLogFd = sys_open(pFileName, O_RDWR | O_CREAT | O_TRUNC, 0600); if (iBboxLogFd < 0) { - - bbox_print(PRINT_ERR, "open failed, errno = %d\n", errno); + bbox_print(PRINT_ERR, "open failed, errno = %d\n", errno); // 打印错误信息 return; } + // 计算要写入的数据大小 iWriteSize = bbox_strnlen(g_acBBoxLog, BBOX_LOG_SIZE) + 1; iWriteSize = (iWriteSize > BBOX_LOG_SIZE) ? BBOX_LOG_SIZE : iWriteSize; + + // 使用sys_write函数将g_acBBoxLog中的内容写入到文件中 iRet = sys_write(iBboxLogFd, g_acBBoxLog, iWriteSize); if (iRet < 0) { - bbox_print(PRINT_ERR, "write failed, errno = %d\n", errno); + bbox_print(PRINT_ERR, "write failed, errno = %d\n", errno); // 打印错误信息 - sys_close(iBboxLogFd); + sys_close(iBboxLogFd); // 关闭文件描述符 return; } + // 使用sys_close函数关闭文件描述符 sys_close(iBboxLogFd); } @@ -491,59 +560,69 @@ note: none date: 2022/8/3 contact tel: 18720816902 */ +// 函数声明:获取进程的信息并进行一系列操作 void BBOX_ListThread(struct BBOX_ListParams* pstArgs) { - pid_t ppid = 0; - s32 iMaker = -1; - s32 iMaxThreadCount = 0; - s32 iRet = 0; + pid_t ppid = 0; // 父进程的进程ID + s32 iMaker = -1; // socket的文件描述符,初始化为-1 + s32 iMaxThreadCount = 0; // 最大线程数,初始化为0 + s32 iRet = 0; // 返回值变量 - struct kernel_stat stMarkerSB; - char szProcSelfTask[BBOX_PROC_PATH_LEN]; - char pszMarkPath[BBOX_PROC_PATH_LEN]; - stack_t altstack; - errno_t rc = EOK; + struct kernel_stat stMarkerSB; // 文件状态结构体 + char szProcSelfTask[BBOX_PROC_PATH_LEN]; // 存放进程任务路径的数组 + char pszMarkPath[BBOX_PROC_PATH_LEN]; // 存放标记路径的数组 + stack_t altstack; // 备用信号栈结构体 + errno_t rc = EOK; // 错误号变量,初始化为EOK + // 如果pstArgs为空指针,则打印错误信息并返回 if (pstArgs == NULL) { bbox_print(PRINT_ERR, "pstArgs is NULL.\n"); return; } + // 获取父进程的进程ID ppid = sys_getppid(); + // 创建一个socket,使用本地通信的地址族,数据报套接字类型,协议为0(自动选择协议) iMaker = sys_socket(PF_LOCAL, SOCK_DGRAM, 0); if (iMaker < 0) { bbox_print(PRINT_ERR, "sys_socket error, errno = %d\n", errno); goto errout; } + // 设置socket的关闭执行标记为FD_CLOEXEC,确保在exec族函数调用时关闭socket if (sys_fcntl(iMaker, F_SETFD, FD_CLOEXEC) < 0) { bbox_print(PRINT_ERR, "sys_fcntl error, errno = %d\n", errno); goto errout; } + // 使用bbox_snprintf函数将父进程的任务路径写入szProcSelfTask数组中 if (bbox_snprintf(szProcSelfTask, BBOX_PROC_PATH_LEN, "/proc/%d/task", ppid) <= 0) { bbox_print(PRINT_ERR, "bbox_snprintf is failed, errno = %d.\n", errno); goto errout; } + // 使用bbox_snprintf函数将标记路径写入pszMarkPath数组中 if (bbox_snprintf(pszMarkPath, BBOX_PROC_PATH_LEN, "/proc/%d/fd/%d", ppid, iMaker) <= 0) { bbox_print(PRINT_ERR, "bbox_snprintf is failed, errno = %d.\n", errno); goto errout; } + // 打印提示信息,显示正在获取pid为ppid的进程的信息 bbox_print(PRINT_TIP, "Get information for pid %d:\n", ppid); + // 使用memset_s函数将stMarkerSB结构体清零 rc = memset_s(&stMarkerSB, sizeof(stMarkerSB), 0, sizeof(stMarkerSB)); securec_check_c(rc, "\0", "\0"); + // 使用sys_stat函数获取pszMarkPath对应文件的状态信息并保存到stMarkerSB结构体中 if (sys_stat(pszMarkPath, &stMarkerSB) < 0) { bbox_print(PRINT_ERR, "sys_stat error, errno = %d, path = %s\n", errno, pszMarkPath); goto errout; } - /* switch stack pointer */ + // 切换栈指针为备用信号栈 rc = memset_s(&altstack, sizeof(altstack), 0, sizeof(altstack)); securec_check_c(rc, "\0", "\0"); altstack.ss_sp = pstArgs->pAltStackMem; @@ -551,48 +630,61 @@ void BBOX_ListThread(struct BBOX_ListParams* pstArgs) altstack.ss_size = BBOX_ALT_STACKSIZE; sys_sigaltstack(&altstack, (const stack_t*)NULL); - /* get max count of task. */ + // 获取进程的最大线程数 iMaxThreadCount = BBOX_GetTaskNumber(szProcSelfTask); if (iMaxThreadCount <= 0) { bbox_print(PRINT_ERR, "Get task number failed.\n"); goto errout; } - /* ptrace and run thread. */ + // 对线程执行ptrace并运行 iRet = BBOX_PtraceAndRun(pstArgs, iMaxThreadCount, szProcSelfTask); if (iRet != RET_OK) { bbox_print(PRINT_ERR, "ptrace task and run failed.\n"); goto errout; } + // 打印提示信息,获取信息成功 bbox_print(PRINT_TIP, "Get information success.\n"); + // 关闭socket sys_close(iMaker); + // 如果pstArgs的结果不等于RET_OK,则调用BBOX_PrintFailedLog函数打印失败日志 if (RET_OK != pstArgs->iResult) { BBOX_PrintFailedLog((char*)(((struct BBOX_ListDirParam*)(pstArgs->pDoneArgs))->pArg2)); } + // 如果pstArgs的回调函数不为空,则调用回调函数 if (pstArgs->pDoneCallback != NULL) { pstArgs->pDoneCallback(pstArgs->pDoneArgs); } + // 退出线程,返回值为0 sys_exit(0); + +// 错误处理 errout: if (iMaker > 0) { sys_close(iMaker); } + // 打印错误信息 bbox_print(PRINT_ERR, "Get information failed.\n"); + // 设置pstArgs的结果为-1并保存错误号 pstArgs->iResult = -1; pstArgs->iError = errno; + // 调用BBOX_PrintFailedLog函数打印失败日志 BBOX_PrintFailedLog((char*)(((struct BBOX_ListDirParam*)(pstArgs->pDoneArgs))->pArg2)); + + // 如果pstArgs的回调函数不为空,则调用回调函数 if (pstArgs->pDoneCallback != NULL) { pstArgs->pDoneCallback(pstArgs->pDoneArgs); } + // 退出线程,返回值为1 sys_exit(1); } @@ -609,6 +701,9 @@ note: none date: 2022/8/3 contact tel: 18720816902 */ +/* + * 获取克隆进程的结果并处理 + */ s32 BBOX_GetClonePidResult(pid_t iClonePid, struct BBOX_ListParams* pstArgs, s32 iCloneErrno) { s32 iStatus = 0; @@ -616,7 +711,7 @@ s32 BBOX_GetClonePidResult(pid_t iClonePid, struct BBOX_ListParams* pstArgs, s32 if (iClonePid < 0) { - bbox_print(PRINT_ERR, "Clone failed, can't create child process, errno = %d.\n", iCloneErrno); + bbox_print(PRINT_ERR, "克隆进程失败,无法创建子进程,errno = %d。\n", iCloneErrno); BBOX_PrintFailedLog((char*)(((struct BBOX_ListDirParam*)(pstArgs->pDoneArgs))->pArg2)); if (pstArgs->pDoneCallback != NULL) { @@ -626,12 +721,12 @@ s32 BBOX_GetClonePidResult(pid_t iClonePid, struct BBOX_ListParams* pstArgs, s32 return RET_ERR; } - /* wait child process exit. */ + /* 等待子进程退出 */ while ((iRet = sys_waitpid(iClonePid, &iStatus, __WALL)) < 0 && errno == EINTR) { continue; } - bbox_print(PRINT_LOG, "clone pid %d ret is %x, status = %x\n", iClonePid, iRet, WIFEXITED(iStatus)); + bbox_print(PRINT_LOG, "克隆进程的pid %d 返回值为 %x,状态为 %x\n", iClonePid, iRet, WIFEXITED(iStatus)); if (iRet < 0) { pstArgs->iError = errno; @@ -656,21 +751,22 @@ s32 BBOX_GetClonePidResult(pid_t iClonePid, struct BBOX_ListParams* pstArgs, s32 } else if (!WIFEXITED(iStatus)) { pstArgs->iError = EFAULT; pstArgs->iResult = -1; - bbox_print(PRINT_ERR, "WIFEXITED status failed"); + bbox_print(PRINT_ERR, "WIFEXITED 状态判断失败"); } else { pstArgs->iError = iCloneErrno; pstArgs->iResult = -1; - bbox_print(PRINT_ERR, "WIFEXITED error, errno = %d\n", iCloneErrno); + bbox_print(PRINT_ERR, "WIFEXITED 错误,errno = %d\n", iCloneErrno); } return RET_OK; } - /* * get all threads and run specify function */ -s32 BBOX_GetAllThreads( - GET_THREAD_TYPE enType, BBOX_GetAllThreadDone pDone, void* pDoneArgs, BBOX_GetAllThreadsCallBack pCallback, ...) +/* + * 获取所有线程并运行指定函数 + */ +s32 BBOX_GetAllThreads(GET_THREAD_TYPE enType, BBOX_GetAllThreadDone pDone, void* pDoneArgs, BBOX_GetAllThreadsCallBack pCallback, ...) { struct BBOX_ListParams stArgs; struct kernel_sigset_t stSigBlocked; @@ -685,14 +781,14 @@ s32 BBOX_GetAllThreads( if (BBOX_AtomicIncReturn(&g_isBusy) > 1) { BBOX_AtomicDec(&g_isBusy); - bbox_print(PRINT_ERR, "Dump task is running.\n"); + bbox_print(PRINT_ERR, "Dump任务正在运行中。\n"); errno = EALREADY; return -1; } if (enType >= GET_TYPE_BUTT || pCallback == NULL) { - bbox_print(PRINT_ERR, "Parameter is invalid, enType = %d, and maybe pCallback is NULL", enType); + bbox_print(PRINT_ERR, "参数无效,enType = %d,可能 pCallback 为空", enType); BBOX_AtomicDec(&g_isBusy); return -1; } @@ -706,19 +802,19 @@ s32 BBOX_GetAllThreads( va_start(stArgs.ap, pCallback); - /* clear new stack */ + /* 清空新栈 */ rc = memset_s(g_szAltStackMem, BBOX_ALT_STACKSIZE, 0, BBOX_ALT_STACKSIZE); securec_check_c(rc, "\0", "\0"); - /* reserve 32K */ + /* 保留 32K */ BBOX_ReserveZeroStack(1024 * 32); - /* check and set dump flag. */ + /* 检查并设置dump标志 */ iDumpable = sys_prctl(PR_GET_DUMPABLE, 0, 0, 0, 0); if (!iDumpable) { sys_prctl(PR_SET_DUMPABLE, 1, 0, 0, 0); } - /* set start parameter of dump thread. */ + /* 设置dump线程的启动参数 */ stArgs.iResult = -1; stArgs.iError = 0; stArgs.pAltStackMem = g_szAltStackMem; @@ -727,36 +823,35 @@ s32 BBOX_GetAllThreads( stArgs.pDoneArgs = pDoneArgs; stArgs.enGetType = enType; - /* suspend all signals */ + /* 暂停所有信号 */ sys_sigfillset(&stSigBlocked); for (iSigNo = 0; iSigNo < (s32)(sizeof(iSyncSignals) / sizeof(*iSyncSignals)); iSigNo++) { sys_sigdelset(&stSigBlocked, iSyncSignals[iSigNo]); } - /* block all signals */ + /* 阻塞所有信号 */ if (sys_sigprocmask(SIG_BLOCK, &stSigBlocked, &stSigOld)) { stArgs.iError = errno; stArgs.iResult = -1; - bbox_print(PRINT_ERR, "sys_sigprocmask error, errno = %d\n", errno); + bbox_print(PRINT_ERR, "sys_sigprocmask 错误,errno = %d\n", errno); goto errout; } - /* create child process and run function to export thread information. */ + /* 创建子进程并运行导出线程信息的函数 */ if (GET_TYPE_DUMP == enType) { - ClonePid = BBOX_CloneRun(CLONE_VM | CLONE_FS | CLONE_FILES, (s32(*)(void*))BBOX_ListThread, &stArgs); } else { - /* copy VMA if type is snapshoot. */ + /* 如果类型是快照,则复制VMA */ ClonePid = BBOX_CloneRun(CLONE_FS | CLONE_FILES, (s32(*)(void*))BBOX_ListThread, &stArgs); } iCloneErrno = errno; - /* restoring signal */ + /* 恢复信号 */ sys_sigprocmask(SIG_SETMASK, &stSigOld, &stSigOld); if (BBOX_GetClonePidResult(ClonePid, &stArgs, iCloneErrno) != RET_OK) { - bbox_print(PRINT_ERR, "BBOX_GetClonePidResult error\n"); + bbox_print(PRINT_ERR, "BBOX_GetClonePidResult 错误\n"); } errout: diff --git a/src/gausskernel/cbb/bbox/gs_bbox.cpp b/src/gausskernel/cbb/bbox/gs_bbox.cpp index 405a30b92..f094a4c8e 100644 --- a/src/gausskernel/cbb/bbox/gs_bbox.cpp +++ b/src/gausskernel/cbb/bbox/gs_bbox.cpp @@ -73,29 +73,50 @@ note: none date: 2022/8/4 contact tel: 18720816902 */ -static void coredump_handler(int sig, siginfo_t *si, void *uc) -{ +/** + * 此函数是用于核心转储信号的信号处理器。 + * 在接收到核心转储信号时,将调用此函数。 + * + * 参数: + * - sig:信号编号 + * - si:指向一个结构的指针,包含有关信号的附加信息 + * - uc:指向一个结构的指针,包含信号被触发时的机器上下文 + */ +static void coredump_handler(int sig, siginfo_t *si, void *uc) { + // 此变量存储第一个遇到致命错误的线程的线程ID。 static volatile int64 first_tid = INVALID_TID; + + // 获取当前线程的线程ID。 int64 cur_tid = (int64)pthread_self(); + // 检查是否为任何线程首次遇到的致命错误。 if (first_tid == INVALID_TID && __sync_bool_compare_and_swap(&first_tid, INVALID_TID, cur_tid)) { - /* Only first fatal error will set db state and generate fatal error log */ + /* 只有首个致命错误会设置数据库状态并生成致命错误日志 */ + // 将数据库状态文件设置为 COREDUMP_STATE,表示发生了核心转储。 (void)SetDBStateFileState(COREDUMP_STATE, false); + + // 如果启用了 FFIC 日志,则生成一个错误消息。 if (g_instance.attr.attr_common.enable_ffic_log) { (void)gen_err_msg(sig, si, (ucontext_t *)uc); } } else { /* - * Subsequent fatal error will go to here. If it comes from different thread, - * wait until first error handler end, and if it is a reentry, terminate process. + * 后续的致命错误将进入此处。如果来自不同的线程, + * 则等待第一个错误处理器结束,如果是重新进入,则终止进程。 */ + + // 如果这不是第一个致命错误并且来自不同的线程, + // 则等待第一个错误处理器结束,如果是重新进入,则终止进程。 if (first_tid != cur_tid) { (void)pause(); } } + // 恢复信号的默认处理器。 (void)pqsignal(sig, SIG_DFL); + + // 再次触发该信号,以调用默认的信号处理器。 (void)raise(sig); } @@ -162,30 +183,40 @@ note: none date: 2022/8/4 contact tel: 18720816902 */ -static void get_bbox_coredump_pattern_path(char* path, Size len) -{ +/** + * 此函数用于获取核心转储模式的路径。 + * + * 参数: + * - path:用于存储核心转储模式路径的缓冲区 + * - len:缓冲区的大小 + */ +static void get_bbox_coredump_pattern_path(char* path, Size len) { FILE* fp = NULL; char* p = NULL; struct stat stat_buf; if ((fp = fopen("/proc/sys/kernel/core_pattern", "r")) == NULL) { - write_stderr("cannot open file: /proc/sys/kernel/core_pattern.\n"); + // 打开文件失败,写入错误提示信息,并返回。 + write_stderr("无法打开文件:/proc/sys/kernel/core_pattern。\n"); return; } if (fgets(path, len, fp) == NULL) { + // 获取核心模式路径失败,关闭文件,写入错误提示信息,并返回。 fclose(fp); - write_stderr("failed to get the core pattern path.\n "); + write_stderr("无法获取核心模式路径。\n"); return; } fclose(fp); - if ((p = strrchr(path, '/')) == NULL) { /* a relative-path file */ + if ((p = strrchr(path, '/')) == NULL) { /* 相对路径文件 */ *path = '\0'; - } else { /* an absolute-path file */ + } else { /* 绝对路径文件 */ *(++p) = '\0'; + // 检查路径是否有效并且具有写权限。 if (stat(path, &stat_buf) != 0 || !S_ISDIR(stat_buf.st_mode) || access(path, W_OK) != 0) { - write_stderr("The core dump path is an invalid directory\n"); + // 核心转储路径是无效的目录,写入错误提示信息,并清空路径。 + write_stderr("核心转储路径是无效的目录。\n"); *path = '\0'; } } @@ -202,21 +233,28 @@ note: none date: 2022/8/4 contact tel: 18720816902 */ -static void build_bbox_corepath(char *bbox_core_path, Size path_size, char *config_path) -{ +/** + * 此函数用于构建 bbox 核心转储路径。 + * + * 参数: + * - bbox_core_path:用于存储 bbox 核心转储路径的缓冲区 + * - path_size:缓冲区的大小 + * - config_path:指定的配置路径,如果为 NULL,则使用默认路径 + */ +static void build_bbox_corepath(char *bbox_core_path, Size path_size, char *config_path) { struct stat stat_buf; /* - * the guc parameter bbox_dump_path is set to NULL as default. - * bbox_dump_path has to be a valid directory, if it is altered by users. + * 默认情况下,guc 参数 bbox_dump_path 设置为 NULL。 + * 如果用户修改了 bbox_dump_path,它必须是一个有效的目录。 */ if (config_path != NULL && config_path[0] != '\0') { if (stat(config_path, &stat_buf) != 0 || !S_ISDIR(stat_buf.st_mode) || access(config_path, W_OK) != 0) { ereport(WARNING, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("bbox_dump_path %s is an invalid directory!\n", config_path))); + errmsg("bbox_dump_path %s 是一个无效的目录!\n", config_path))); - /* if bbox_dump_path is invalid, the path of core dump will be set as default. */ + /* 如果 bbox_dump_path 是无效的,将使用默认的核心转储路径。 */ get_bbox_coredump_pattern_path(bbox_core_path, path_size); } else { errno_t rc = strcpy_s(bbox_core_path, path_size, config_path); @@ -224,29 +262,27 @@ static void build_bbox_corepath(char *bbox_core_path, Size path_size, char *conf } } else { /* - * default path of the core dump will be obtained - * by reading the file "/proc/sys/kernel/core_pattern" + * 默认情况下,核心转储路径将从文件 "/proc/sys/kernel/core_pattern" 中获取。 */ get_bbox_coredump_pattern_path(bbox_core_path, path_size); } } /* - * check_bbox_corepath - check coredump path for bbox + * check_bbox_corepath - 检查 bbox 的核心转储路径 */ -bool check_bbox_corepath(char** newval, void** extra, GucSource source) -{ +bool check_bbox_corepath(char** newval, void** extra, GucSource source) { if (t_thrd.proc_cxt.MyProcPid != PostmasterPid) return true; char core_dump_path[BBOX_PATH_SIZE] = {0}; - /* determine which path is used for bbox core dump file */ + /* 确定用于 bbox 核心转储文件的路径 */ build_bbox_corepath(core_dump_path, sizeof(core_dump_path), (newval != NULL) ? *newval : NULL); if (core_dump_path[0] != '\0' && BBOX_SetCoredumpPath(core_dump_path) == RET_OK) { ereport(LOG, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("bbox_dump_path is set to %s", core_dump_path))); + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("bbox_dump_path 设置为 %s", core_dump_path))); } char* result = (char*)MemoryContextAlloc(SESS_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_DFX), BBOX_PATH_SIZE); @@ -302,39 +338,59 @@ note: none date: 2022/8/4 contact tel: 18720816902 */ +/** + * 将一个字符串拆分为黑名单列表。 + * + * 参数: + * - source:要拆分的源字符串 + * + * 返回值: + * - 拆分后的黑名单列表 + */ static List* split_string_into_blacklist(const char* source) { - List *result = NIL; - char *str = pstrdup(source); - char *first_ch = str; - int len = strlen(str) + 1; + List *result = NIL; // 初始化列表为空 + char *str = pstrdup(source); // 复制源字符串 + char *first_ch = str; // 指向第一个字符的指针 + int len = strlen(str) + 1; // 字符串长度加1,包括结尾的空字符 for (int i = 0; i < len; i++) { if (str[i] == ',' || str[i] == '\0') { - /* replace ',' with '\0'. */ + /* 将 ',' 替换为 '\0' */ str[i] = '\0'; - /* copy this into result. */ + /* 将该字符串添加到结果列表中 */ result = lappend(result, pstrdup(first_ch)); - /* move to the head of next string. */ + /* 移动到下一个字符串的开头 */ first_ch = str + i + 1; i++; } } - pfree(str); + pfree(str); // 释放复制的字符串内存 - return result; + return result; // 返回拆分后的黑名单列表 } +/** + * 检查 bbox 黑名单配置项。 + * + * 参数: + * - newval:新设置的值 + * - extra:附加信息 + * - source:配置项的来源 + * + * 返回值: + * - 如果检查通过,返回 true;否则返回 false + */ bool check_bbox_blacklist(char** newval, void** extra, GucSource source) { if (t_thrd.proc_cxt.MyProcPid != PostmasterPid) return true; - List *result = split_string_into_blacklist(*newval); + List *result = split_string_into_blacklist(*newval); // 将配置项的值拆分为黑名单列表 ListCell *lc = NULL; - uint64 mask = 0; + uint64 mask = 0; // 用于保存黑名单掩码 size_t i; foreach(lc, result) { @@ -349,45 +405,62 @@ bool check_bbox_blacklist(char** newval, void** extra, GucSource source) } if (i == sizeof(g_blacklist_items) / sizeof(BlacklistItem)) { ereport(WARNING, - (errmsg("blacklist item %s does not exist, so it is ignored.", (char*)lfirst(lc)))); + (errmsg("黑名单项 %s 不存在,已忽略。", (char*)lfirst(lc)))); } } - list_free_deep(result); + list_free_deep(result); // 释放拆分后的黑名单列表内存 *extra = MemoryContextAlloc(SESS_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_DFX), sizeof(uint64)); if (*extra == NULL) return false; - *((uint64*)*extra) = (mask == 0) ? DEFAULT_BLACKLIST_MASK : mask; + *((uint64*)*extra) = (mask == 0) ? DEFAULT_BLACKLIST_MASK : mask; // 设置附加信息为黑名单掩码 return true; } +/** + * 设置 bbox 黑名单。 + * + * 参数: + * - newval:新设置的值 + * - extra:附加信息 + */ void assign_bbox_blacklist(const char* newval, void* extra) { if (t_thrd.proc_cxt.MyProcPid == PostmasterPid) { - g_instance.attr.attr_common.bbox_blacklist_mask = *((uint64*)extra); + g_instance.attr.attr_common.bbox_blacklist_mask = *((uint64*)extra); // 设置 bbox 黑名单掩码 } } +/** + * 显示当前的 bbox 黑名单。 + * + * 返回值: + * - 当前的 bbox 黑名单字符串 + */ const char* show_bbox_blacklist() { StringInfoData str; - initStringInfo(&str); + initStringInfo(&str); // 初始化字符串信息 for (size_t i = 0; i < sizeof(g_blacklist_items) / sizeof(BlacklistItem); i++) { if ((BLACKLIST_ITEM_MASK(g_blacklist_items[i].blacklist_ID) & BBOX_BLACKLIST) != 0) { appendStringInfo(&str, "%s,", g_blacklist_items[i].blacklist_name); } } if (str.len >= 0) { - str.data[--str.len] = '\0'; + str.data[--str.len] = '\0'; // 将最后一个逗号替换为结束符 } - return str.data; + return str.data; // 返回黑名单字符串 } -/* - * assign_bbox_coredump - set coredump for bbox or not +/** + * 设置是否进行 bbox 核心转储。 + * + * 参数: + * - newval:新设置的值 + * - extra:附加信息 */ void assign_bbox_coredump(const bool newval, void* extra) { @@ -395,7 +468,7 @@ void assign_bbox_coredump(const bool newval, void* extra) return; if (newval && !FencedUDFMasterMode) { - (void)install_signal(SIGABRT, bbox_handler); + (void)install_signal(SIGABRT, bbox_handler); // 安装信号处理程序 (void)install_signal(SIGBUS, bbox_handler); (void)install_signal(SIGILL, bbox_handler); (void)install_signal(SIGSEGV, bbox_handler); @@ -407,33 +480,33 @@ void assign_bbox_coredump(const bool newval, void* extra) } } -/* - * do initilaization for dumping core file +/** + * 初始化 bbox 核心转储。 */ void bbox_initialize() { - char core_dump_path[BBOX_PATH_SIZE] = {0}; + char core_dump_path[BBOX_PATH_SIZE] = {0}; // 存储核心转储路径的缓冲区 - /* determine path which is used for bbox core dump file */ + /* 确定用于 bbox 核心转储文件的路径 */ build_bbox_corepath(core_dump_path, sizeof(core_dump_path), u_sess->attr.attr_common.bbox_dump_path); if (u_sess->attr.attr_common.enable_bbox_dump && *core_dump_path != '\0' && CheckFilenameValid(core_dump_path) == RET_OK && BBOX_SetCoredumpPath(core_dump_path) == 0) { - write_stderr("bbox_dump_path is set to %s\n", core_dump_path); + write_stderr("bbox_dump_path 设置为 %s\n", core_dump_path); // 打印核心转储路径 } /* - * no matter bbox_dump_count is default (8) or set by users, call function BBOX_SetCoreFileCount. - * Note: bbox_dump_count cannot be smaller than 1. + * 无论 bbox_dump_count 是默认值 (8) 还是用户设置的值,都调用函数 BBOX_SetCoreFileCount。 + * 注意:bbox_dump_count 不能小于 1。 */ if (u_sess->attr.attr_common.bbox_dump_count != 0 && BBOX_SetCoreFileCount(u_sess->attr.attr_common.bbox_dump_count) != 0) { - write_stderr("failed to set coredump count.\n"); + write_stderr("设置核心转储文件计数失败。\n"); // 打印设置核心转储文件计数失败信息 } - assign_bbox_coredump(u_sess->attr.attr_common.enable_bbox_dump, NULL); + assign_bbox_coredump(u_sess->attr.attr_common.enable_bbox_dump, NULL); // 设置是否进行 bbox 核心转储 } /* @@ -452,8 +525,8 @@ void bbox_blacklist_add(BlacklistIndex item, void* addr, uint64 size) } /* - * remove an blacklist item. - * void *pAddress : the head address of excluded memory + * 移除一个黑名单项。 + * void *pAddress : 要排除的内存的起始地址 */ void bbox_blacklist_remove(BlacklistIndex item, void* addr) { @@ -464,14 +537,9 @@ void bbox_blacklist_remove(BlacklistIndex item, void* addr) } /* -function name: CheckFilenameValid -description: Check if the filename is in line with norms, or if dangerous characters appear - the filename is invalid. -arguments: A pointer to string indicating filename. -return value: An integer, if function works normally, the value is RET_OK, else it's RET_ERR. -note: none -date: 2022/8/4 -contact tel: 18720816902 +描述: 检查文件名是否符合规范,如果包含危险字符,则文件名无效。 +参数: 文件名的字符串指针。 +返回值: 整型,函数正常工作时返回 RET_OK,否则返回 RET_ERR。 */ int CheckFilenameValid(const char* inputEnvValue) { @@ -491,4 +559,3 @@ int CheckFilenameValid(const char* inputEnvValue) } return RET_OK; } - diff --git a/src/gausskernel/cbb/communication/libcomm.cpp b/src/gausskernel/cbb/communication/libcomm.cpp old mode 100755 new mode 100644 diff --git a/src/gausskernel/cbb/communication/libcomm_common.cpp b/src/gausskernel/cbb/communication/libcomm_common.cpp index 1743b43f2..9069de610 100644 --- a/src/gausskernel/cbb/communication/libcomm_common.cpp +++ b/src/gausskernel/cbb/communication/libcomm_common.cpp @@ -41,64 +41,64 @@ #include "libcomm_common.h" int binary_semaphore::init() { - atomic_set(&b_flag, 0); - atomic_set(&waiting_count, 0); - atomic_set(&b_destroy, 0); - atomic_set(&destroy_wait, 0); - int err = pthread_cond_init(&cond, NULL); + atomic_set(&b_flag, 0); // 初始化 b_flag 为 0 + atomic_set(&waiting_count, 0); // 初始化 waiting_count 为 0 + atomic_set(&b_destroy, 0); // 初始化 b_destroy 为 0 + atomic_set(&destroy_wait, 0); // 初始化 destroy_wait 为 0 + int err = pthread_cond_init(&cond, NULL); // 初始化 cond,若失败则返回错误码 if (err != 0) return err; - err = pthread_mutex_init(&mutex, NULL); + err = pthread_mutex_init(&mutex, NULL); // 初始化 mutex,若失败则销毁 cond,并返回错误码 if (err != 0) { LIBCOMM_PTHREAD_COND_DESTORY(&cond); return err; } - return err; + return err; // 返回错误码 } int binary_semaphore::destroy(bool do_destroy) { - const int ret = 0; - atomic_set(&b_destroy, 1); - while (destroy_wait != 0) { - post(); - usleep(100); - } - if (do_destroy) { - LIBCOMM_PTHREAD_COND_DESTORY(&cond); - LIBCOMM_PTHREAD_MUTEX_DESTORY(&mutex); - } - return ret; + const int ret = 0; + atomic_set(&b_destroy, 1); // 将 b_destroy 置为 1 表示要销毁 + while (destroy_wait != 0) { // 若存在等待销毁的线程,则发送信号,并休眠 100 微秒 + post(); + usleep(100); + } + if (do_destroy) { // 若指定要进行销毁,则销毁 cond 和 mutex + LIBCOMM_PTHREAD_COND_DESTORY(&cond); + LIBCOMM_PTHREAD_MUTEX_DESTORY(&mutex); + } + return ret; // 返回 0 } void binary_semaphore::reset() { - atomic_set(&b_flag, 0); - while (destroy_wait != 0) { + atomic_set(&b_flag, 0); // 将 b_flag 重置为 0 + while (destroy_wait != 0) { // 若存在等待销毁的线程,则发送信号,并休眠 100 微秒 post(); usleep(100); } } - void binary_semaphore::post() { - LIBCOMM_PTHREAD_MUTEX_LOCK(&mutex); +void binary_semaphore::post() { + LIBCOMM_PTHREAD_MUTEX_LOCK(&mutex); // 上锁 mutex /* thread will poll up when someone has posted before */ - atomic_set(&b_flag, 1); - if (waiting_count > 0) { + atomic_set(&b_flag, 1); // 将 b_flag 置为 1 表示已有线程发送信号 + if (waiting_count > 0) { // 若存在等待的线程,则发送一个信号给等待线程 LIBCOMM_PTHREAD_COND_SIGNAL(&cond); } - LIBCOMM_PTHREAD_MUTEX_UNLOCK(&mutex); + LIBCOMM_PTHREAD_MUTEX_UNLOCK(&mutex); // 解锁 mutex } void binary_semaphore::post_all() { - LIBCOMM_PTHREAD_MUTEX_LOCK(&mutex); - atomic_set(&b_flag, 1); - if (waiting_count > 0) { + LIBCOMM_PTHREAD_MUTEX_LOCK(&mutex); // 上锁 mutex + atomic_set(&b_flag, 1); // 将 b_flag 置为 1 表示已有线程发送信号 + if (waiting_count > 0) { // 若存在等待的线程,则发送广播给所有等待线程 LIBCOMM_PTHREAD_COND_BROADCAST(&cond); } - LIBCOMM_PTHREAD_MUTEX_UNLOCK(&mutex); + LIBCOMM_PTHREAD_MUTEX_UNLOCK(&mutex); // 解锁 mutex } int binary_semaphore::wait() { - if (b_flag) { + if (b_flag) { // 若 b_flag 为 1,则表示已有线程发送信号,不需要等待 /* reset b_flag is no one is waitting */ if (waiting_count == 0) atomic_set(&b_flag, 0); @@ -106,29 +106,29 @@ int binary_semaphore::wait() { } int ret = 0; - LIBCOMM_PTHREAD_MUTEX_LOCK(&mutex); - while (!b_flag) { - atomic_add(&waiting_count, 1); - ret = pthread_cond_wait(&cond, &mutex); + LIBCOMM_PTHREAD_MUTEX_LOCK(&mutex); // 上锁 mutex + while (!b_flag) { // 若 b_flag 为 0,则表示没有线程发送信号,需要等待 + atomic_add(&waiting_count, 1); + ret = pthread_cond_wait(&cond, &mutex); // 等待信号,并当有信号到来时解锁 mutex 并接收信号 atomic_sub(&waiting_count, 1); } - if (b_destroy) + if (b_destroy) // 若 b_destroy 为 1,则表示要销毁,返回错误码 ret = -1; /* reset b_flag is no one is waitting */ if (waiting_count == 0) atomic_set(&b_flag, 0); - LIBCOMM_PTHREAD_MUTEX_UNLOCK(&mutex); + LIBCOMM_PTHREAD_MUTEX_UNLOCK(&mutex); // 解锁 mutex - return ret; + return ret; // 返回错误码 } void binary_semaphore::destroy_wait_add() { - atomic_add(&destroy_wait, 1); + atomic_add(&destroy_wait, 1); // 销毁等待数 +1 } void binary_semaphore::destroy_wait_sub() { - atomic_sub(&destroy_wait, 1); + atomic_sub(&destroy_wait, 1); // 销毁等待数 -1 } /** The parameter timeout should be in second, if it is minus or zero, the function @@ -137,138 +137,139 @@ void binary_semaphore::destroy_wait_sub() { int binary_semaphore::timed_wait(int timeout) { int ret = -1; - if (timeout <= 0) { + if (timeout <= 0) { // 若超时时间小于等于 0,则与 _wait() 函数一样 ret = wait(); return ret; } - if (b_flag) { + if (b_flag) { // b_flag 为 1,表示已有线程发送信号,不需要等待 /* reset b_flag is no one is waitting */ if (waiting_count == 0) atomic_set(&b_flag, 0); return 0; } - LIBCOMM_PTHREAD_MUTEX_LOCK(&mutex); - if (b_flag) { + LIBCOMM_PTHREAD_MUTEX_LOCK(&mutex); // 上锁 mutex + if (b_flag) { // b_flag 为 1,表示已有线程发送信号,不需要等待 atomic_set(&b_flag, 0); LIBCOMM_PTHREAD_MUTEX_UNLOCK(&mutex); ret = 0; return ret; } struct timespec ts; - clock_gettime(CLOCK_REALTIME, &ts); - ts.tv_sec += timeout; + clock_gettime(CLOCK_REALTIME, &ts); // 获取当前时间 + ts.tv_sec += timeout; // 计算超时的时间点 ts.tv_nsec = 0; - atomic_add(&waiting_count, 1); + atomic_add(&waiting_count, 1); // 增加等待线程数 - ret = pthread_cond_timedwait(&cond, &mutex, &ts); + ret = pthread_cond_timedwait(&cond, &mutex, &ts); // 等待信号,若超过超时时间仍未接收到信号,则返回 ETIMEDOUT - atomic_sub(&waiting_count, 1); - if (b_flag) { + atomic_sub(&waiting_count, 1); // 减少等待线程数 + if (b_flag) { // b_flag 为 1,表示已有线程发送信号,不需要等待 /* reset b_flag is no one is waitting */ if (waiting_count == 0) atomic_set(&b_flag, 0); ret = 0; } - if (b_destroy) + if (b_destroy) // 若 b_destroy 为 1,则表示要销毁,返回错误码 ret = -1; - LIBCOMM_PTHREAD_MUTEX_UNLOCK(&mutex); + LIBCOMM_PTHREAD_MUTEX_UNLOCK(&mutex); // 解锁 mutex - return ret; + return ret; // 返回错误码 } int hash_entry::_init() { - return sem.init(); + return sem.init(); // 调用 binary_semaphore 对象 sem 的 init() 函数 } void hash_entry::_destroy() { - sem.destroy(true); + sem.destroy(true); // 调用 binary_semaphore 对象 sem 的 destroy() 函数 } void hash_entry::_signal() { - sem.post(); + sem.post(); // 调用 binary_semaphore 对象 sem 的 post() 函数 } void hash_entry::_signal_all() { - sem.post_all(); + sem.post_all(); // 调用 binary_semaphore 对象 sem 的 post_all() 函数 } void hash_entry::_wait() { - sem.wait(); + sem.wait(); // 调用 binary_semaphore 对象 sem 的 wait() 函数 } void hash_entry::_hold_destroy() { - sem.destroy_wait_add(); + sem.destroy_wait_add(); // 调用 binary_semaphore 对象 sem 的 destroy_wait_add() 函数 } void hash_entry::_release_destroy() { - sem.destroy_wait_sub(); + sem.destroy_wait_sub(); // 调用 binary_semaphore 对象 sem 的 destroy_wait_sub() 函数 } int hash_entry::_timewait(int timeout) { - return sem.timed_wait(timeout); + return sem.timed_wait(timeout); // 调用 binary_semaphore 对象 sem 的 timed_wait() 函数 } void node_sock::reset_all() { - ctrl_tcp_sock = -1; - ctrl_tcp_port = -1; - ctrl_tcp_sock_id = 0; - libcomm_reply_sock = -1; - libcomm_reply_sock_id = -1; + ctrl_tcp_sock = -1; // 重置 ctrl_tcp_sock 为 -1 表示未连接 + ctrl_tcp_port = -1; // 重置 ctrl_tcp_port 为 -1 + ctrl_tcp_sock_id = 0; // 重置 ctrl_tcp_sock_id 为 0 + libcomm_reply_sock = -1; // 重置 libcomm_reply_sock 为 -1 表示未连接 + libcomm_reply_sock_id = -1; // 重置 libcomm_reply_sock_id 为 -1 errno_t ss_rc = 0; - ss_rc = memset_s(remote_host, HOST_ADDRSTRLEN, 0x0, HOST_ADDRSTRLEN); + ss_rc = memset_s(remote_host, HOST_ADDRSTRLEN, 0x0, HOST_ADDRSTRLEN); // 清空 remote_host 数组 securec_check(ss_rc, "\0", "\0"); - ss_rc = memset_s(remote_nodename, NAMEDATALEN, 0x0, NAMEDATALEN); + ss_rc = memset_s(remote_nodename, NAMEDATALEN, 0x0, NAMEDATALEN); // 清空 remote_nodename 数组 securec_check(ss_rc, "\0", "\0"); - ss_rc = memset_s(&to_ss, sizeof(struct sockaddr_storage), 0x0, sizeof(struct sockaddr_storage)); + ss_rc = memset_s(&to_ss, sizeof(struct sockaddr_storage), 0x0, sizeof(struct sockaddr_storage)); // 清空 to_ss 结构体 securec_check(ss_rc, "\0", "\0"); } void node_sock::init() { - reset_all(); - LIBCOMM_PTHREAD_MUTEX_INIT(&_slock, 0); - LIBCOMM_PTHREAD_MUTEX_INIT(&_tlock, 0); + reset_all(); // 初始化相关成员变量 + LIBCOMM_PTHREAD_MUTEX_INIT(&_slock, 0); // 初始化锁 _slock + LIBCOMM_PTHREAD_MUTEX_INIT(&_tlock, 0); // 初始化锁 _tlock } void node_sock::clear() { - reset_all(); + reset_all(); // 清空相关成员变量 } void node_sock::destroy() { - clear(); - LIBCOMM_PTHREAD_MUTEX_DESTORY(&_slock); - LIBCOMM_PTHREAD_MUTEX_DESTORY(&_tlock); + clear(); // 清空相关成员变量 + LIBCOMM_PTHREAD_MUTEX_DESTORY(&_slock); // 销毁锁 _slock + LIBCOMM_PTHREAD_MUTEX_DESTORY(&_tlock); // 销毁锁 _tlock } void node_sock::lock() { - LIBCOMM_PTHREAD_MUTEX_LOCK(&_tlock); + LIBCOMM_PTHREAD_MUTEX_LOCK(&_tlock); // 上锁 _tlock } void node_sock::unlock() { - LIBCOMM_PTHREAD_MUTEX_UNLOCK(&_tlock); + LIBCOMM_PTHREAD_MUTEX_UNLOCK(&_tlock); // 解锁 _tlock } void node_sock::close_socket(int flag) { - lock(); - close_socket_nl(flag); + lock(); + close_socket_nl(flag); // 关闭 socket unlock(); } -void node_sock::close_socket_nl(int flag) { // close without lock +void node_sock::close_socket_nl(int flag) { // 关闭 socket,但不进行上锁 switch (flag) { case CTRL_TCP_SOCK: if (ctrl_tcp_sock >= 0) { - close(ctrl_tcp_sock); + close(ctrl_tcp_sock); // 关闭 ctrl_tcp_sock ctrl_tcp_sock = -1; ctrl_tcp_sock_id = -1; } break; default: - break; - } + break; + } } + void node_sock::set(int val, int flag) { lock(); @@ -276,49 +277,48 @@ void node_sock::set(int val, int flag) { unlock(); } -void node_sock::set_nl(int val, int flag) { // set without lock - switch (flag) { - case CTRL_TCP_SOCK: - ctrl_tcp_sock = val; - break; - case CTRL_TCP_PORT: - ctrl_tcp_port = val; - break; - case CTRL_TCP_SOCK_ID: - ctrl_tcp_sock_id = val; - break; - default: - break; +void node_sock::set_nl(int val, int flag) { + switch (flag) { // 根据flag的不同选择对应的操作 + case CTRL_TCP_SOCK: // 如果flag是CTRL_TCP_SOCK + ctrl_tcp_sock = val; // 将val赋值给ctrl_tcp_sock + break; // 结束该case + case CTRL_TCP_PORT: // 如果flag是CTRL_TCP_PORT + ctrl_tcp_port = val; // 将val赋值给ctrl_tcp_port + break; // 结束该case + case CTRL_TCP_SOCK_ID: // 如果flag是CTRL_TCP_SOCK_ID + ctrl_tcp_sock_id = val; // 将val赋值给ctrl_tcp_sock_id + break; // 结束该case + default: // 如果flag不是上述三种情况 + break; // 不执行任何操作 } } int node_sock::get(int flag, int* id) { - int val = -1; - lock(); - val = get_nl(flag, id); - unlock(); - return val; + int val = -1; // 初始化val为-1 + lock(); // 加锁 + val = get_nl(flag, id); // 调用get_nl函数获取val的值 + unlock(); // 解锁 + return val; // 返回val的值 } -int node_sock::get_nl(int flag, int* id) const { // get without lock - int val = -1; - switch (flag) { - case CTRL_TCP_SOCK: - val = ctrl_tcp_sock; - if (id != NULL) - *id = ctrl_tcp_sock_id; - break; - case CTRL_TCP_PORT: - val = ctrl_tcp_port; - break; - case CTRL_TCP_SOCK_ID: - val = ctrl_tcp_sock_id; - if (id != NULL) - *id = ctrl_tcp_sock_id; - break; - default: - break; +int node_sock::get_nl(int flag, int* id) const { + int val = -1; // 初始化val为-1 + switch (flag) { // 根据flag的不同选择对应的操作 + case CTRL_TCP_SOCK: // 如果flag是CTRL_TCP_SOCK + val = ctrl_tcp_sock; // 将ctrl_tcp_sock的值赋给val + if (id != NULL) // 如果id不为空指针 + *id = ctrl_tcp_sock_id; // 将ctrl_tcp_sock_id的值赋给id所指向的变量 + break; // 结束该case + case CTRL_TCP_PORT: // 如果flag是CTRL_TCP_PORT + val = ctrl_tcp_port; // 将ctrl_tcp_port的值赋给val + break; // 结束该case + case CTRL_TCP_SOCK_ID: // 如果flag是CTRL_TCP_SOCK_ID + val = ctrl_tcp_sock_id; // 将ctrl_tcp_sock_id的值赋给val + if (id != NULL) // 如果id不为空指针 + *id = ctrl_tcp_sock_id; // 将ctrl_tcp_sock_id的值赋给id所指向的变量 + break; // 结束该case + default: // 如果flag不是上述三种情况 + break; // 不执行任何操作 } - return val; + return val; // 返回val的值 } - diff --git a/src/gausskernel/process/datasource.cpp b/src/gausskernel/process/datasource.cpp new file mode 100644 index 000000000..e1bf788aa --- /dev/null +++ b/src/gausskernel/process/datasource.cpp @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2020 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * datasource.cpp + * support for data source + * + * IDENTIFICATION + * src/gausskernel/process/datasource/datasource.cpp + * + * ------------------------------------------------------------------------- + *//* + * data_source.c + * Support functions for operations on data sources. + * + * This module is responsible for interfacing with the pg_extension_data_source + * system catalog table, and providing a low-level API for working with these + * objects. It is also responsible for interpreting some of the common fields + * that are used throughout the system. + * + */ + +#include "postgres.h" +#include "access/htup_details.h" +#include "access/reloptions.h" +#include "catalog/catalog.h" +#include "catalog/dependency.h" +#include "catalog/indexing.h" +#include "catalog/pg_extension_data_source.h" +#include "utils/builtins.h" +#include "utils/fmgroids.h" +#include "utils/syscache.h" + + +/* + * get_data_source_oid - 通过数据源名称获取对应的oid + * + * @param sourcename: 数据源名称 + * @param missing_ok: 是否允许缺失,true表示允许,false表示不允许 + * @return 返回对应的oid,如果数据源不存在且不允许缺失,则抛出错误 + */ +Oid get_data_source_oid(const char* sourcename, bool missing_ok) +{ + Oid oid; + + oid = GetSysCacheOid1(DATASOURCENAME, CStringGetDatum(sourcename)); + + if (!OidIsValid(oid) && !missing_ok) + ereport(ERROR, + (errmodule(MOD_EC), errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("source \"%s\" does not exist", sourcename))); + return oid; +} + +/** + * GetDataSource - 查找数据源定义 + * + * @param sourceid: 数据源oid + * @return 返回数据源 + */ +DataSource* GetDataSource(Oid sourceid) +{ + Form_pg_extension_data_source sourceform = NULL; + DataSource* source = NULL; + HeapTuple tp = NULL; + Datum datum; + bool isnull = false; + + tp = SearchSysCache1(DATASOURCEOID, ObjectIdGetDatum(sourceid)); + + if (!HeapTupleIsValid(tp)) + ereport(ERROR, + (errmodule(MOD_EC), + errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("cache lookup failed for data source %u", sourceid))); + + sourceform = (Form_pg_extension_data_source)GETSTRUCT(tp); + + source = (DataSource*)palloc0(sizeof(DataSource)); + source->sourceid = sourceid; + source->srcname = pstrdup(NameStr(sourceform->srcname)); + source->owner = sourceform->srcowner; + + /* Extract source type */ + datum = SysCacheGetAttr(DATASOURCEOID, tp, Anum_pg_extension_data_source_srctype, &isnull); + source->srctype = isnull ? NULL : pstrdup(TextDatumGetCString(datum)); + + /* Extract source version */ + datum = SysCacheGetAttr(DATASOURCEOID, tp, Anum_pg_extension_data_source_srcversion, &isnull); + source->srcversion = isnull ? NULL : pstrdup(TextDatumGetCString(datum)); + + /* Extract the srcoptions */ + datum = SysCacheGetAttr(DATASOURCEOID, tp, Anum_pg_extension_data_source_srcoptions, &isnull); + if (isnull) + source->options = NIL; + else + source->options = untransformRelOptions(datum); + + ReleaseSysCache(tp); + + return source; +} + +/** + * GetDataSourceByName - 通过名称查找数据源定义 + * + * @param sourcename: 数据源名称 + * @param missing_ok: 是否允许缺失,true表示允许,false表示不允许 + * @return 返回数据源,如果数据源不存在且允许缺失,则返回NULL + */ +DataSource* GetDataSourceByName(const char* sourcename, bool missing_ok) +{ + Oid sourceid; + + if (sourcename == NULL) + return NULL; + + sourceid = get_data_source_oid(sourcename, missing_ok); + + if (!OidIsValid(sourceid)) + return NULL; + + return GetDataSource(sourceid); +} + diff --git a/src/gausskernel/process/datasource/datasource.cpp b/src/gausskernel/process/datasource/datasource.cpp index b26ab9d85..e1bf788aa 100644 --- a/src/gausskernel/process/datasource/datasource.cpp +++ b/src/gausskernel/process/datasource/datasource.cpp @@ -20,29 +20,35 @@ * src/gausskernel/process/datasource/datasource.cpp * * ------------------------------------------------------------------------- + *//* + * data_source.c + * Support functions for operations on data sources. + * + * This module is responsible for interfacing with the pg_extension_data_source + * system catalog table, and providing a low-level API for working with these + * objects. It is also responsible for interpreting some of the common fields + * that are used throughout the system. + * */ -#include "postgres.h" -#include "knl/knl_variable.h" +#include "postgres.h" +#include "access/htup_details.h" #include "access/reloptions.h" +#include "catalog/catalog.h" +#include "catalog/dependency.h" +#include "catalog/indexing.h" #include "catalog/pg_extension_data_source.h" -#include "datasource/datasource.h" -#include "lib/stringinfo.h" -#include "miscadmin.h" #include "utils/builtins.h" -#include "utils/memutils.h" -#include "utils/rel.h" -#include "utils/rel_gs.h" +#include "utils/fmgroids.h" #include "utils/syscache.h" + /* - * get_data_source_oid - * look up the OID by source name + * get_data_source_oid - 通过数据源名称获取对应的oid * - * @IN sourcename: source name - * @IN missing_ok: If missing_ok is false, throw an error if name not found. - * If true, just return InvalidOid. - * @RETURN: oid of the data source. + * @param sourcename: 数据源名称 + * @param missing_ok: 是否允许缺失,true表示允许,false表示不允许 + * @return 返回对应的oid,如果数据源不存在且不允许缺失,则抛出错误 */ Oid get_data_source_oid(const char* sourcename, bool missing_ok) { @@ -56,12 +62,11 @@ Oid get_data_source_oid(const char* sourcename, bool missing_ok) return oid; } -/* - * GetDataSource - * look up the data source definition +/** + * GetDataSource - 查找数据源定义 * - * @IN sourceid: data source oid - * @RETURN: a data source + * @param sourceid: 数据源oid + * @return 返回数据源 */ DataSource* GetDataSource(Oid sourceid) { @@ -106,13 +111,12 @@ DataSource* GetDataSource(Oid sourceid) return source; } -/* - * GetDataSourceByName - * look up the data source definition by name. +/** + * GetDataSourceByName - 通过名称查找数据源定义 * - * @IN sourcename: source name - * @IN missing_ok: missing source name ok - * @RETURN: data source + * @param sourcename: 数据源名称 + * @param missing_ok: 是否允许缺失,true表示允许,false表示不允许 + * @return 返回数据源,如果数据源不存在且允许缺失,则返回NULL */ DataSource* GetDataSourceByName(const char* sourcename, bool missing_ok) { @@ -128,3 +132,4 @@ DataSource* GetDataSourceByName(const char* sourcename, bool missing_ok) return GetDataSource(sourceid); } + diff --git a/src/gausskernel/process/globalplancache/globalplancache.cpp b/src/gausskernel/process/globalplancache/globalplancache.cpp index 96f6ecf75..467d1db60 100644 --- a/src/gausskernel/process/globalplancache/globalplancache.cpp +++ b/src/gausskernel/process/globalplancache/globalplancache.cpp @@ -50,35 +50,59 @@ template void GlobalPlanCache::RemovePlanSource(CachedPlanSourc template void GlobalPlanCache::RemovePlanSource(CachedPlanSource* plansource, const char* stmt_name); template void GlobalPlanCache::RemovePlanSource(CachedPlanSource* plansource, const char* stmt_name); - +/* + * 函数名:has_diff_schema + * 功能:判断两个列表是否有不同的schema(OID)。 + * 参数: + * - list1: 第一个列表 + * - list2: 第二个列表 + * 返回值: + * - bool类型,如果list1中有与list2不同的schema,则返回true;否则返回false。 + */ static bool has_diff_schema(const List *list1, const List *list2) { const ListCell *cell = NULL; + /* 如果list2为空,则只需判断list1是否非空即可 */ if (list2 == NIL) { return list1 != NULL; } + + /* 遍历list1中的每个元素,如果在list2中找不到对应的OID,表示有不同的schema */ foreach (cell, list1) { if (!list_member_oid(list2, lfirst_oid(cell))) { return true; } } + + /* 如果遍历结束后都没有找到不同的schema,返回false */ return false; } -static bool -CompareSearchPath(struct OverrideSearchPath* path1, struct OverrideSearchPath* path2) +/* + * 函数名:CompareSearchPath + * 功能:比较两个OverrideSearchPath结构体的内容是否相等。 + * 参数: + * - path1: 第一个OverrideSearchPath结构体指针 + * - path2: 第二个OverrideSearchPath结构体指针 + * 返回值: + * - bool类型,如果两个结构体的内容相等,则返回true;否则返回false。 + */ +static bool CompareSearchPath(struct OverrideSearchPath* path1, struct OverrideSearchPath* path2) { Assert(path1 != NULL); + /* 如果path2为空,则比较path1和当前搜索路径是否相等 */ if (path2 == NULL) { return OverrideSearchPathMatchesCurrent(path1); } + /* 如果path1和path2是同一个结构体的指针,则认为相等 */ if (path1 == path2) { return true; } + /* 逐个比较结构体中的字段值 */ if (path1->addTemp != path2->addTemp) { return false; } @@ -94,6 +118,16 @@ CompareSearchPath(struct OverrideSearchPath* path1, struct OverrideSearchPath* p return true; } +/* + * 函数名:GPCCompareParam + * 功能:比较两个Oid数组是否相等。 + * 参数: + * - params1: 第一个Oid数组 + * - params2: 第二个Oid数组 + * - paramNum: 数组长度 + * 返回值: + * - bool类型,如果两个数组的内容完全相等,则返回true;否则返回false。 + */ static bool GPCCompareParam(Oid* params1, Oid* params2, int paramNum) { for (int i = 0; i < paramNum; i++) { @@ -103,25 +137,29 @@ static bool GPCCompareParam(Oid* params1, Oid* params2, int paramNum) } return true; } - -/* - * Return false when the given compilation environment matches the current - * session compilation environment, mainly compares GUC parameter settings. +/* + * GPCCompareEnv - 用于比较两个GPCEnv结构体是否相等。 */ static bool GPCCompareEnv(GPCEnv *env1, GPCEnv *env2) { - Assert (env1 != NULL); - Assert (env2 != NULL); + Assert(env1 != NULL); + Assert(env2 != NULL); + // 比较plainenv字段 if (memcmp(&env1->plainenv, &env2->plainenv, sizeof(GPCPlainEnv)) == 0 + // 比较default_storage_nodegroup字段 && strncmp(env1->default_storage_nodegroup, env2->default_storage_nodegroup, NAMEDATALEN) == 0 - && strncmp(env1->expected_computing_nodegroup, env2->expected_computing_nodegroup, NAMEDATALEN) == 0 - && env1->num_params == env2->num_params) + // 比较expected_computing_nodegroup字段 + && strncmp(env1->expected_computing_nodegroup, env2->expected_computing_nodegroup, NAMEDATALEN) == 0 + // 比较param_types数组中的元素是否相等 + && env1->num_params == env2->num_params) { if (!GPCCompareParam(env1->param_types, env2->param_types, env1->num_params)) { return false; } + // 比较search_path字段 if (CompareSearchPath(env1->search_path, env2->search_path)) { + // 如果depends_on_role字段和user_oid字段不相等,则返回false if (env1->depends_on_role != env2->depends_on_role && env1->user_oid != env2->user_oid) { return false; } else if (env1->depends_on_role && env2->depends_on_role && env1->user_oid != env2->user_oid) { @@ -135,18 +173,28 @@ GPCCompareEnv(GPCEnv *env1, GPCEnv *env2) return false; } +/* + * GPCHashFunc - 计算GPCKey结构体的哈希值 + */ uint32 GPCHashFunc(const void *key, Size keysize) { const GPCKey *item = (const GPCKey *) key; - uint32 val1 = DatumGetUInt32(hash_any((const unsigned char *)item->query_string, item->query_length)); - uint32 val2 = DatumGetUInt32(hash_any((const unsigned char *)(&item->env.plainenv), sizeof(GPCPlainEnv))); - uint32 val3 = DatumGetUInt32(hash_any((const unsigned char *)(&item->spi_signature), sizeof(SPISign))); + // 对query_string字段进行哈希计算 + uint32 val1 = DatumGetUInt32(hash_any((const unsigned char *) item->query_string, item->query_length)); + // 对plainenv字段进行哈希计算 + uint32 val2 = DatumGetUInt32(hash_any((const unsigned char *) (&item->env.plainenv), sizeof(GPCPlainEnv))); + // 对spi_signature字段进行哈希计算 + uint32 val3 = DatumGetUInt32(hash_any((const unsigned char *) (&item->spi_signature), sizeof(SPISign))); + // 使用异或运算组合哈希结果 val1 ^= val2; val1 ^= val3; return val1; } +/* + * GPCKeyMatch - 比较两个GPCKey结构体是否相等 + */ int GPCKeyMatch(const void *left, const void *right, Size keysize) { GPCKey *leftItem = (GPCKey*)left; @@ -154,38 +202,41 @@ int GPCKeyMatch(const void *left, const void *right, Size keysize) Assert(NULL != leftItem); Assert(NULL != rightItem); - /* we just care whether the result is 0 or not. */ + // 判断query_length字段是否相等 if (leftItem->query_length != rightItem->query_length) { return 1; } - - if(strncmp(leftItem->query_string, rightItem->query_string, leftItem->query_length)) { + // 判断query_string字段是否相等 + if (strncmp(leftItem->query_string, rightItem->query_string, leftItem->query_length)) { return 1; } - - if(GPCCompareEnv(&(leftItem->env), &(rightItem->env)) == false) { + // 比较GPCEnv结构体是否相等 + if (GPCCompareEnv(&(leftItem->env), &(rightItem->env)) == false) { return 1; } return 0; } +/* + * GPCKeyDeepCopy - 创建给定GPCKey结构体的深拷贝 + */ void GPCKeyDeepCopy(const GPCKey *srcGpckey, GPCKey *destGpckey) { *destGpckey = *srcGpckey; if (srcGpckey->query_string) destGpckey->query_string = pstrdup(srcGpckey->query_string); - + // 拷贝param_types数组 if (destGpckey->env.num_params > 0) { - destGpckey->env.param_types = (Oid*)palloc(sizeof(Oid) * destGpckey->env.num_params); + destGpckey->env.param_types = (Oid*) palloc(sizeof(Oid) * destGpckey->env.num_params); errno_t rc = 0; rc = memcpy_s(destGpckey->env.param_types, sizeof(Oid) * destGpckey->env.num_params, srcGpckey->env.param_types, sizeof(Oid) * destGpckey->env.num_params); securec_check(rc, "", ""); } - + // 拷贝search_path字段 if (destGpckey->env.schema_name) { - destGpckey->env.search_path = (struct OverrideSearchPath *)palloc(sizeof(struct OverrideSearchPath)); + destGpckey->env.search_path = (struct OverrideSearchPath *) palloc(sizeof(struct OverrideSearchPath)); *destGpckey->env.search_path = *srcGpckey->env.search_path; destGpckey->env.search_path->schemas = list_copy(srcGpckey->env.search_path->schemas); } @@ -194,22 +245,33 @@ void GPCKeyDeepCopy(const GPCKey *srcGpckey, GPCKey *destGpckey) /***************** global plan cache *****************/ - +/** + * 构造函数:GlobalPlanCache + */ GlobalPlanCache::GlobalPlanCache() { Init(); } +/** + * 析构函数:~GlobalPlanCache + */ GlobalPlanCache::~GlobalPlanCache() { } +/** + * 初始化函数:Init + */ void GlobalPlanCache::Init() { + // 创建 HASHCTL 结构体 ctl HASHCTL ctl; errno_t rc = 0; rc = memset_s(&ctl, sizeof(ctl), 0, sizeof(ctl)); securec_check(rc, "\0", "\0"); + + // 设置 HASHCTL 结构体的字段值 ctl.keysize = sizeof(GPCKey); ctl.entrysize = sizeof(GPCEntry); ctl.hash = (HashValueFunc)GPCHashFunc; @@ -217,18 +279,20 @@ void GlobalPlanCache::Init() int flags = HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT | HASH_COMPARE | HASH_EXTERN_CONTEXT | HASH_NOEXCEPT; + // 分配 GPCHashCtl 数组的内存空间 m_array = (GPCHashCtl *) MemoryContextAllocZero(GLOBAL_PLANCACHE_MEMCONTEXT, sizeof(GPCHashCtl) * GPC_NUM_OF_BUCKETS); + // 遍历 GPCHashCtl 数组,进行初始化 for (uint32 i = 0; i < GPC_NUM_OF_BUCKETS; i++) { + // 初始化 count 字段和 lockId 字段 m_array[i].count = 0; m_array[i].lockId = FirstGPCMappingLock + i; /* - * Create a MemoryContext per hash bucket so that all entries, plans etc under the bucket will live - * under this Memory context. This is for performance purposes. We do not want everything to be under - * the shared GlobalPlanCacheContext because more threads would need to synchronize everytime it needs a chunk - * of memory and that would become a bottleneck. + * 为每个哈希桶创建一个内存上下文,以保证该桶下的所有条目、计划等都在这个内存上下文中。 + * 这样做是为了提高性能。我们不希望所有东西都在共享的 GlobalPlanCacheContext 下, + * 因为每次需要一块内存时,更多的线程需要进行同步,这将成为瓶颈。 */ m_array[i].context = AllocSetContextCreate(GLOBAL_PLANCACHE_MEMCONTEXT, "GPC_Plan_Bucket_Context", @@ -237,18 +301,25 @@ void GlobalPlanCache::Init() ALLOCSET_DEFAULT_MAXSIZE, SHARED_CONTEXT); - + // 设置 ctl 的 hcxt 字段为当前哈希桶的上下文 ctl.hcxt = m_array[i].context; + + // 创建哈希表 m_array[i].hash_tbl = hash_create("Global_Plan_Cache", GPC_HTAB_SIZE, &ctl, flags); - } m_invalid_list = NULL; } +/** + * 尝试存储函数:TryStore + * @param plansource CachedPlanSource指针 + * @param ps PreparedStatement指针 + * @return bool类型,表示存储是否成功 + */ bool GlobalPlanCache::TryStore(CachedPlanSource *plansource, PreparedStatement *ps) { Assert (plansource != NULL); @@ -265,10 +336,12 @@ bool GlobalPlanCache::TryStore(CachedPlanSource *plansource, PreparedStatement Assert (bucket_id >= 0 && bucket_id < GPC_NUM_OF_BUCKETS); int lock_id = m_array[bucket_id].lockId; + // 获取锁 (void)LWLockAcquire(GetMainLWLockByIndex(lock_id), LW_EXCLUSIVE); MemoryContext oldcontext = MemoryContextSwitchTo(m_array[bucket_id].context); bool found = false; + // 在哈希表中搜索对应的条目 GPCEntry *entry = (GPCEntry *)hash_search_with_hash_value(m_array[bucket_id].hash_tbl, (const void*)key, hashCode, HASH_ENTER, &found); if (entry == NULL) { @@ -282,23 +355,23 @@ bool GlobalPlanCache::TryStore(CachedPlanSource *plansource, PreparedStatement if (found == false) { START_CRIT_SECTION(); - /* Deep copy the query_string to the GPC entry's query_string */ + /* 深拷贝 query_string 到 GPC 条目的 query_string 字段 */ entry->key.query_string = key->query_string; entry->key.query_length = key->query_length; - /* Set the magic number. */ + /* 设置 magic number */ entry->val.plansource = plansource; entry->val.used_count = 0; INSTR_TIME_SET_CURRENT(entry->val.last_use_time); - /* off the link */ + /* 关闭链路 */ plansource->next_saved = NULL; plansource->is_checked_opfusion = true; if (plansource->opFusionObj != NULL) { OpFusion::SaveInGPC((OpFusion*)(plansource->opFusionObj)); } - /* initialize the ref count .*/ + /* 初始化引用计数 */ #ifdef ENABLE_MULTIPLE_NODES - /* dn only count reference on cur_stmt_psrc, no prepare statement. - cn count reference on prepare statement */ + /* 数据节点仅对 cur_stmt_psrc 进行引用计数,不对预处理语句进行计数。 + 协调器对预处理语句进行引用计数。 */ if (IS_PGXC_COORDINATOR) plansource->gpc.status.AddRefcount(); #else @@ -326,7 +399,7 @@ bool GlobalPlanCache::TryStore(CachedPlanSource *plansource, PreparedStatement END_CRIT_SECTION(); } else { - /* some guys win. */ + /* 有其他进程获取到了存储的权利 */ if (ps == NULL) { Assert (IS_PGXC_DATANODE); GPC_LOG("drop cache plan in try store", plansource, 0); @@ -338,7 +411,7 @@ bool GlobalPlanCache::TryStore(CachedPlanSource *plansource, PreparedStatement INSTR_TIME_SET_CURRENT(entry->val.last_use_time); u_sess->pcache_cxt.gpc_in_try_store = true; - /* purge old one. */ + /* 清除旧的计划 */ GPC_LOG("drop cache plan in try store", plansource, 0); #ifdef ENABLE_MULTIPLE_NODES if (IS_PGXC_COORDINATOR) { @@ -350,6 +423,7 @@ bool GlobalPlanCache::TryStore(CachedPlanSource *plansource, PreparedStatement } } + // 切换回旧的内存上下文,并释放锁 MemoryContextSwitchTo(oldcontext); LWLockRelease(GetMainLWLockByIndex(lock_id)); return true; @@ -358,6 +432,7 @@ bool GlobalPlanCache::TryStore(CachedPlanSource *plansource, PreparedStatement CachedPlanSource* GlobalPlanCache::Fetch(const char *query_string, uint32 query_len, int num_params, Oid* paramTypes, SPISign* spi_sign_ptr) { + // 构造缓存键 GPCKey key; key.env.filled = false; key.query_string = query_string; @@ -371,16 +446,18 @@ CachedPlanSource* GlobalPlanCache::Fetch(const char *query_string, uint32 query_ else key.spi_signature = {(uint32)-1, 0, (uint32)-1, -1}; + // 计算哈希值和桶ID uint32 hashCode = GPCHashFunc((const void *) &key, sizeof(key)); - uint32 bucket_id = GetBucket(hashCode); Assert (bucket_id >= 0 && bucket_id < GPC_NUM_OF_BUCKETS); int lock_id = m_array[bucket_id].lockId; + // 获取读锁,并切换内存上下文 (void)LWLockAcquire(GetMainLWLockByIndex(lock_id), LW_SHARED); MemoryContext oldcontext = MemoryContextSwitchTo(m_array[bucket_id].context); bool foundCachedEntry = false; + // 在哈希表中查找缓存的计划 GPCEntry *entry = (GPCEntry *) hash_search_with_hash_value(m_array[bucket_id].hash_tbl, (const void*)(&key), hashCode, @@ -388,22 +465,25 @@ CachedPlanSource* GlobalPlanCache::Fetch(const char *query_string, uint32 query_ &foundCachedEntry); if (!foundCachedEntry) { + // 未找到缓存的计划 MemoryContextSwitchTo(oldcontext); LWLockRelease(GetMainLWLockByIndex(lock_id)); return NULL; } else { + // 找到缓存的计划 CachedPlanSource* psrc = entry->val.plansource; - psrc->gpc.status.AddRefcount(); + psrc->gpc.status.AddRefcount(); // 计数加一,增加引用计数 if (!psrc->gpc.status.IsValid()) { + // 缓存的计划无效,将其移动到无效列表中 MemoryContextSwitchTo(oldcontext); LWLockRelease(GetMainLWLockByIndex(lock_id)); MoveIntoInvalidPlanList(psrc); - psrc->gpc.status.SubRefCount(); + psrc->gpc.status.SubRefCount(); // 计数减一,减少引用计数 return NULL; } if (ENABLE_DN_GPC) u_sess->pcache_cxt.private_refcount++; - pg_atomic_fetch_add_u32(&entry->val.used_count, 1); + pg_atomic_fetch_add_u32(&entry->val.used_count, 1); // 增加计划的使用计数 MemoryContextSwitchTo(oldcontext); LWLockRelease(GetMainLWLockByIndex(lock_id)); return psrc; @@ -414,12 +494,14 @@ CachedPlanSource* GlobalPlanCache::Fetch(const char *query_string, uint32 query_ void GlobalPlanCache::AddInvalidList(CachedPlanSource* plansource) { + // 获取清理锁,并切换内存上下文 (void)LWLockAcquire(GPCClearLock, LW_EXCLUSIVE); MemoryContext oldcontext = MemoryContextSwitchTo(GLOBAL_PLANCACHE_MEMCONTEXT); START_CRIT_SECTION(); + // 设置计划的状态为在共享表的无效列表中 plansource->gpc.status.SetLoc(GPC_SHARE_IN_SHARE_TABLE_INVALID_LIST); - m_invalid_list = dlappend(m_invalid_list, plansource); - plansource->gpc.status.SetStatus(GPC_INVALID); + m_invalid_list = dlappend(m_invalid_list, plansource); // 将计划添加到无效列表中 + plansource->gpc.status.SetStatus(GPC_INVALID); // 设置计划为无效状态 END_CRIT_SECTION(); MemoryContextSwitchTo(oldcontext); LWLockRelease(GPCClearLock); @@ -427,6 +509,7 @@ void GlobalPlanCache::AddInvalidList(CachedPlanSource* plansource) void GlobalPlanCache::DropInvalid() { + // 获取清理锁 (void)LWLockAcquire(GPCClearLock, LW_EXCLUSIVE); if (m_invalid_list != NULL) { DListCell *cell = m_invalid_list->head; @@ -436,6 +519,7 @@ void GlobalPlanCache::DropInvalid() Assert(curr->next_saved == NULL); DListCell *next = cell->next; GPC_LOG("drop invalid shared plancache", curr, curr->stmt_name); + // 从无效列表中移除计划,并执行相关清理操作 m_invalid_list = dlist_delete_cell(m_invalid_list, cell, false); DropCachedPlanInternal(curr); curr->magic = 0; @@ -530,6 +614,7 @@ void GlobalPlanCache::RemoveEntry(uint32 htblIdx, GPCEntry *entry) } + bool GlobalPlanCache::CheckRecreateCachePlan(CachedPlanSource* psrc, bool* hasGetLock) { /* @@ -539,7 +624,8 @@ bool GlobalPlanCache::CheckRecreateCachePlan(CachedPlanSource* psrc, bool* hasGe */ start_xact_command(); Assert(psrc->magic == CACHEDPLANSOURCE_MAGIC); - /* get lock before check plan is valid or not, release it if need recreate plan */ + + // 获取锁以检查计划是否有效,如果需要重新创建计划,则释放锁 if (psrc->gpc.status.InShareTable()) { AcquirePlannerLocks(psrc->query_list, true); if (psrc->gplan) { @@ -558,19 +644,24 @@ bool GlobalPlanCache::CheckRecreateCachePlan(CachedPlanSource* psrc, bool* hasGe } #endif + // GPC正在执行DDL if (u_sess->pcache_cxt.gpc_in_ddl == true) { return true; } + // GPC的计划无效 if (!psrc->gpc.status.IsValid()) { return true; } + // GPC依赖角色,但当前角色不一致 if (psrc->dependsOnRole && (psrc->rewriteRoleId != GetUserId())) { return true; } + // GPC的计划为僵化计划 if ((psrc->gplan != NULL && TransactionIdIsValid(psrc->gplan->saved_xmin))) { return true; } + // GPC的搜索路径已改变 if (psrc->search_path && !OverrideSearchPathMatchesCurrent(psrc->search_path)) { return true; } @@ -602,12 +693,14 @@ void GlobalPlanCache::RecreateSPICachePlan(SPIPlanPtr spiplan) { ListCell* cell = NULL; Assert(spiplan->magic == _SPI_PLAN_MAGIC); - /* push error context stack */ + + // push错误上下文栈 ErrorContextCallback spi_err_context; spi_err_context.callback = _SPI_error_callback; - spi_err_context.arg = NULL; /* we'll fill this below */ + spi_err_context.arg = NULL; spi_err_context.previous = t_thrd.log_cxt.error_context_stack; t_thrd.log_cxt.error_context_stack = &spi_err_context; + foreach(cell, spiplan->plancache_list) { CachedPlanSource* oldsource = (CachedPlanSource*)lfirst(cell); if (!oldsource->gpc.status.InShareTable()) @@ -615,7 +708,8 @@ void GlobalPlanCache::RecreateSPICachePlan(SPIPlanPtr spiplan) GPC_LOG("recreate spi cachedplan", oldsource, 0); RecreateCachePlan(oldsource, NULL, NULL, spiplan, cell, false); } - /* pop error context stack */ + + // pop错误上下文栈 t_thrd.log_cxt.error_context_stack = spi_err_context.previous; Assert(SPIPlanCacheTableLookup(u_sess->SPI_cxt._current->spi_hash_key)); } @@ -644,7 +738,8 @@ void GlobalPlanCache::RecreateCachePlan(CachedPlanSource* oldsource, const char* SPIPlanPtr spiplan, ListCell* spiplanCell, bool hasGetLock) { GPC_LOG("recreate plan", oldsource, oldsource->stmt_name); - /* these operator may throw error, make sure shared plan is invalid first */ + + // 这些操作可能会抛出错误,确保共享计划无效 CachedPlanSource *newsource = NULL; PG_TRY(); { @@ -660,8 +755,7 @@ void GlobalPlanCache::RecreateCachePlan(CachedPlanSource* oldsource, const char* u_sess->exec_cxt.CurrentOpFusionObj = NULL; Assert (oldsource->gpc.status.IsSharePlan()); newsource->gpc.status.ShareInit(); - // If the planSource is set to invalid, the AST must be analyzed again - // because the meta has changed. + // 如果计划源设置为无效,则必须重新分析AST,因为元数据已更改。 newsource->is_valid = false; bool has_lp = false; @@ -676,12 +770,12 @@ void GlobalPlanCache::RecreateCachePlan(CachedPlanSource* oldsource, const char* newsource->stmt_name = pstrdup(stmt_name); #ifdef ENABLE_MULTIPLE_NODES has_lp = (oldsource->single_exec_node != NULL && oldsource->gplan == NULL && oldsource->cplan == NULL); - /* clean session's datanode statment on cn */ + // 在CN上清除会话的Datanode语句 if (has_lp) { - /* no lp in newsource, delete old lp */ + // 新的计划中没有LP,删除旧的LP GPCDropLPIfNecessary(stmt_name, false, true, NULL); } else if (oldsource->gplan != NULL) { - /* Close any active planned Datanode statements, recreate in BuildCachedPlan later */ + // 关闭任何活动的计划DN语句,在BuildCachedPlan中重新创建 GPCCleanDatanodeStatement(oldsource->gplan->dn_stmt_num, stmt_name); } #endif @@ -691,13 +785,13 @@ void GlobalPlanCache::RecreateCachePlan(CachedPlanSource* oldsource, const char* } PG_CATCH(); { - /* catch only move invalid plansource into gpc invalid list when error occurs */ + // 仅在出现错误时将无效计划源移动到GPC无效列表中 MoveIntoInvalidPlanList(oldsource); PG_RE_THROW(); } PG_END_TRY(); - /* newsource has reference on session, forget resource owner */ + // 新计划已引用会话,忘记资源所有者 ResourceOwnerForgetGMemContext(t_thrd.utils_cxt.TopTransactionResourceOwner, newsource->context); newsource->next_saved = u_sess->pcache_cxt.first_saved_plan; u_sess->pcache_cxt.first_saved_plan = newsource; @@ -739,19 +833,25 @@ void GlobalPlanCache::Commit() * just drop it and update the prepare pointer to the shared glboal plancache. * @in num: void * @return - void - */ + */// 全局计划缓存类的DNCommit方法 void GlobalPlanCache::DNCommit() { CachedPlanSource *next_plansource = NULL; CachedPlanSource *plansource = u_sess->pcache_cxt.first_saved_plan; u_sess->pcache_cxt.first_saved_plan = NULL; CleanSessGPCPtr(u_sess); + + // 检查引用计数是否正确 if (u_sess->pcache_cxt.private_refcount != 0) { elog(PANIC, "wrong refcount"); } + while (plansource != NULL) { + // 断言检查magic字段和状态 Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC); Assert(!plansource->gpc.status.InShareTable()); + + // 如果magic字段异常,则报错 if (unlikely(plansource->magic != CACHEDPLANSOURCE_MAGIC)) { ereport(PANIC, (errcode(ERRCODE_UNDEFINED_PSTATEMENT), @@ -759,32 +859,43 @@ void GlobalPlanCache::DNCommit() } next_plansource = plansource->next_saved; + + // 处理私有计划 if (plansource->gpc.status.IsPrivatePlan()) { - /* private plan has reference on pointer like unname_stmt_psrc or spiplan */ + // 无效的计划,设置状态为GPC_INVALID并标记为不可用 GPC_LOG("invalid plan in commit", plansource, plansource->stmt_name); plansource->is_valid = false; plansource->next_saved = NULL; plansource->gpc.status.SetLoc(GPC_SHARE_IN_PREPARE_STATEMENT); plansource->gpc.status.SetStatus(GPC_INVALID); - } else if (!plansource->is_valid || plansource->gplan == NULL || !plansource->is_support_gplan) { + } + // 非法计划或者不支持全局计划缓存,则删除缓存 + else if (!plansource->is_valid || plansource->gplan == NULL || !plansource->is_support_gplan) { GPC_LOG("drop plan in commit", plansource, plansource->stmt_name); - /* no prepare statement on dn, so we just drop shared plansource if can't save it in gpc, in case leak */ DropCachedPlan(plansource); - } else { + } + // 存储计划 + else { TryStore(plansource, NULL); } + plansource = next_plansource; } } +// 全局计划缓存类的CNCommit方法 void GlobalPlanCache::CNCommit() { CachedPlanSource *next_plansource = NULL; CachedPlanSource *plansource = u_sess->pcache_cxt.first_saved_plan; u_sess->pcache_cxt.first_saved_plan = NULL; + while (plansource != NULL) { + // 断言检查magic字段和状态 Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC); Assert(!plansource->gpc.status.InShareTable()); + + // 如果magic字段异常,则报错 if (unlikely(plansource->magic != CACHEDPLANSOURCE_MAGIC)) { ereport(PANIC, (errcode(ERRCODE_UNDEFINED_PSTATEMENT), @@ -793,6 +904,8 @@ void GlobalPlanCache::CNCommit() next_plansource = plansource->next_saved; bool has_lp = false; + + // 判断是否存在轻量级代理计划 #ifdef ENABLE_MULTIPLE_NODES has_lp = plansource->single_exec_node && plansource->gplan == NULL && plansource->cplan == NULL && plansource->stmt_name; @@ -800,8 +913,9 @@ void GlobalPlanCache::CNCommit() has_lp = (lightProxy::locateLpByStmtName(plansource->stmt_name) != NULL); } #endif + + // 非共享计划或者存在cplan,则放入ungpc_save_plan列表 if (!plansource->gpc.status.IsSharePlan() || (plansource->gplan == NULL && plansource->cplan)) { - /* stream or private plan or cplan need put into ungpc_save_plan */ plansource->is_saved = true; if (!plansource->is_support_gplan && plansource->gpc.status.IsSharePlan()) plansource->gpc.status.SetKind(GPC_CPLAN); @@ -809,7 +923,9 @@ void GlobalPlanCache::CNCommit() plansource->gpc.status.SetLoc(GPC_SHARE_IN_LOCAL_UNGPC_PLAN_LIST); plansource->next_saved = u_sess->pcache_cxt.ungpc_saved_plan; u_sess->pcache_cxt.ungpc_saved_plan = plansource; - } else if (!plansource->is_valid || (plansource->gplan && !plansource->gplan->is_valid)) { + } + // 非法计划或者全局计划缓存为空(gplan和cplan都为NULL)的情况下,将计划放入保存列表 + else if (!plansource->is_valid || (plansource->gplan && !plansource->gplan->is_valid)) { plansource->is_valid = false; plansource->is_saved = true; Assert (plansource->gpc.status.IsSharePlan()); @@ -817,20 +933,25 @@ void GlobalPlanCache::CNCommit() plansource->gpc.status.SetLoc(GPC_SHARE_IN_LOCAL_SAVE_PLAN_LIST); plansource->next_saved = u_sess->pcache_cxt.first_saved_plan; u_sess->pcache_cxt.first_saved_plan = plansource; - } else if (plansource->gplan == NULL && plansource->cplan == NULL && !has_lp) { - /* get commit before create cachedplan or lp, not init gpckey. keep in first_saved_plan */ + } + // gplan和cplan都为NULL,并且不存在轻量级代理计划时,将计划放入保存列表 + else if (plansource->gplan == NULL && plansource->cplan == NULL && !has_lp) { plansource->is_saved = true; Assert (plansource->gpc.status.IsSharePlan()); plansource->gpc.status.SetLoc(GPC_SHARE_IN_LOCAL_SAVE_PLAN_LIST); plansource->next_saved = u_sess->pcache_cxt.first_saved_plan; u_sess->pcache_cxt.first_saved_plan = plansource; - } else { + } + // 处理SPI计划 + else { if (plansource->spi_signature.spi_key != INVALID_SPI_KEY) { Assert(!has_lp); Assert(plansource->gplan); Assert(plansource->is_support_gplan); g_instance.plan_cache->SPICommit(plansource); - } else { + } + // 获取预处理语句并存储计划 + else { PreparedStatement* ps = FetchPreparedStatement(plansource->stmt_name, true, false); if (unlikely(ps == NULL)) { #ifdef MEMORY_CONTEXT_CHECKING @@ -848,52 +969,51 @@ void GlobalPlanCache::CNCommit() plansource = next_plansource; } } - void GlobalPlanCache::SPITryStore(CachedPlanSource* plansource, SPIPlanPtr spiplan, int nth) { - Assert (plansource != NULL); - Assert (plansource->magic == CACHEDPLANSOURCE_MAGIC); - Assert (!plansource->gpc.status.InShareTable()); - Assert (plansource->gpc.status.IsSharePlan()); - Assert (spiplan->saved); + // 尝试将计划存储在全局计划缓存中 + + Assert(plansource != NULL); + Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC); + Assert(!plansource->gpc.status.InShareTable()); + Assert(plansource->gpc.status.IsSharePlan()); + Assert(spiplan->saved); GPCKey* key = plansource->gpc.key; - uint32 hashCode = GPCHashFunc((const void *) key, sizeof(*key)); + uint32 hashCode = GPCHashFunc((const void*)key, sizeof(*key)); uint32 bucket_id = GetBucket(hashCode); - Assert (bucket_id >= 0 && bucket_id < GPC_NUM_OF_BUCKETS); + Assert(bucket_id >= 0 && bucket_id < GPC_NUM_OF_BUCKETS); int lock_id = m_array[bucket_id].lockId; (void)LWLockAcquire(GetMainLWLockByIndex(lock_id), LW_EXCLUSIVE); MemoryContext oldcontext = MemoryContextSwitchTo(m_array[bucket_id].context); bool found = false; - GPCEntry *entry = (GPCEntry *)hash_search_with_hash_value(m_array[bucket_id].hash_tbl, - (const void*)key, hashCode, HASH_ENTER, &found); + GPCEntry* entry = (GPCEntry*)hash_search_with_hash_value(m_array[bucket_id].hash_tbl, + (const void*)key, hashCode, HASH_ENTER, &found); if (entry == NULL) { MemoryContextSwitchTo(oldcontext); LWLockRelease(GetMainLWLockByIndex(lock_id)); ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_PSTATEMENT), - errmsg("store global plan source failed due to memory allocation failed"))); + (errcode(ERRCODE_UNDEFINED_PSTATEMENT), + errmsg("由于内存分配失败,无法存储全局计划源"))); } if (found == false) { - /* Deep copy the query_string to the GPC entry's query_string */ + // 深拷贝查询字符串到GPC条目的查询字符串 entry->key.query_string = key->query_string; entry->key.query_length = key->query_length; entry->key.spi_signature = key->spi_signature; - /* Set the magic number. */ - entry->val.plansource = plansource; - //off the link - plansource->next_saved = NULL; - INSTR_TIME_SET_CURRENT(entry->val.last_use_time); - //initialize the ref count . - plansource->gpc.status.AddRefcount(); - - m_array[bucket_id].count++; - pg_atomic_fetch_add_u32((volatile uint32*)&plansource->gplan->global_refcount, 1); + + entry->val.plansource = plansource; // 设置计划源 + plansource->next_saved = NULL; // 关闭链接 + INSTR_TIME_SET_CURRENT(entry->val.last_use_time); // 初始化最后使用时间 + plansource->gpc.status.AddRefcount(); // 初始化引用计数 + + m_array[bucket_id].count++; // 增加计数 + pg_atomic_fetch_add_u32((volatile uint32*)&plansource->gplan->global_refcount, 1); // 计划全局引用计数增加 plansource->gplan->is_share = true; Assert(plansource->context->is_shared); MemoryContextSeal(plansource->context); @@ -904,16 +1024,16 @@ void GlobalPlanCache::SPITryStore(CachedPlanSource* plansource, SPIPlanPtr spipl plansource->gpc.status.SetLoc(GPC_SHARE_IN_SHARE_TABLE); } else { - //some guys win. + // 有其他进程已经存储了计划 CachedPlanSource* newsource = entry->val.plansource; ListCell* n_cell = list_nth_cell(spiplan->plancache_list, nth); n_cell->data.ptr_value = (void*)newsource; newsource->gpc.status.AddRefcount(); - // purge old one. - CN_GPC_LOG("drop cache plan in try store", plansource, 0); + // 清除旧的计划 + CN_GPC_LOG("在尝试存储时删除已缓存的计划", plansource, 0); DropCachedPlan(plansource); - CN_GPC_LOG("change to cache plan", newsource, 0); + CN_GPC_LOG("改为使用缓存的计划", newsource, 0); } MemoryContextSwitchTo(oldcontext); @@ -922,7 +1042,9 @@ void GlobalPlanCache::SPITryStore(CachedPlanSource* plansource, SPIPlanPtr spipl void GlobalPlanCache::SPICommit(CachedPlanSource* plansource) { - Assert (u_sess->SPI_cxt.SPICacheTable != NULL); + // 提交SPI计划 + + Assert(u_sess->SPI_cxt.SPICacheTable != NULL); plpgsql_SPIPlanCacheEnt* entry = SPIPlanCacheTableLookup(plansource->spi_signature.spi_key); Assert(entry != NULL); Assert(entry->func_oid != InvalidOid); @@ -945,7 +1067,7 @@ void GlobalPlanCache::SPICommit(CachedPlanSource* plansource) ereport(ERROR, #endif (errcode(ERRCODE_UNDEFINED_PSTATEMENT), - errmsg("In gpc spi finish stage, fail to get spi func: %u. hashkey: %u", + errmsg("在gpc spi完成阶段,无法获取spi函数: %u. hashkey: %u", plansource->spi_signature.func_oid, plansource->spi_signature.spi_key))); } @@ -956,36 +1078,45 @@ void GlobalPlanCache::SPICommit(CachedPlanSource* plansource) if (list_length(spi_plan->plancache_list) == 0) continue; foreach(cl, spi_plan->plancache_list) { - CachedPlanSource *cur = (CachedPlanSource*)(cl->data.ptr_value); + CachedPlanSource* cur = (CachedPlanSource*)(cl->data.ptr_value); Assert(cur->magic == CACHEDPLANSOURCE_MAGIC); } } #endif } - +/** + * 从SPIPlan中移除计划缓存 + */ void GlobalPlanCache::RemovePlanCacheInSPIPlan(SPIPlanPtr plan) { - Assert (plan->magic == _SPI_PLAN_MAGIC); + Assert(plan->magic == _SPI_PLAN_MAGIC); + if (list_length(plan->plancache_list) > 0) { ListCell* cell = NULL; foreach(cell, plan->plancache_list) { CachedPlanSource* plansource = (CachedPlanSource*)lfirst(cell); + + // 检查计划是否在共享表中 if (plansource->gpc.status.InShareTable()) { Assert(plan->saved); CN_GPC_LOG("drop shared spi plan, subrefcount", plansource, 0); - /* move plansource into invalid list if during delet func */ + + // 如果正在删除函数,则将plansource移动到无效列表中 if (u_sess->plsql_cxt.is_delete_function) { GPCKey* gpckey = plansource->gpc.key; uint32 hashCode = GPCHashFunc((const void *) gpckey, sizeof(*gpckey)); uint32 bucket_id = GetBucket(hashCode); int lock_id = m_array[bucket_id].lockId; (void)LWLockAcquire(GetMainLWLockByIndex(lock_id), LW_EXCLUSIVE); + + // 如果plansource不在无效列表中,则将其从哈希表中移除,并将其添加到无效列表中 if (!plansource->gpc.status.InShareTableInvalidList()) { bool found = false; (void)hash_search(m_array[bucket_id].hash_tbl, (void *)gpckey, HASH_REMOVE, &found); m_array[bucket_id].count--; AddInvalidList(plansource); } + plansource->gpc.status.SubRefCount(); DropInvalid(); LWLockRelease(GetMainLWLockByIndex(lock_id)); @@ -998,21 +1129,26 @@ void GlobalPlanCache::RemovePlanCacheInSPIPlan(SPIPlanPtr plan) } } } + if (plan->spi_key != INVALID_SPI_KEY) SPIPlanCacheTableDeletePlan(plan->spi_key, plan); } +/** + * 根据时间进行清理 + */ void GlobalPlanCache::CleanUpByTime() { List *gpckey_list = NULL; const int maxlen_gpckey_list = 100; instr_time curTime; INSTR_TIME_SET_CURRENT(curTime); + for (uint32 bucket_id = 0; bucket_id < GPC_NUM_OF_BUCKETS; bucket_id++) { int lock_id = m_array[bucket_id].lockId; - /* Step 1: Try to find the code plan cache */ + // 步骤1:尝试找到计划缓存 LWLockAcquire(GetMainLWLockByIndex(lock_id), LW_SHARED); if (m_array[bucket_id].count == 0) { LWLockRelease(GetMainLWLockByIndex(lock_id)); @@ -1023,21 +1159,29 @@ void GlobalPlanCache::CleanUpByTime() CachedPlanSource* cur_plansource = NULL; hash_seq_init(&hash_seq, m_array[bucket_id].hash_tbl); + + // 遍历哈希表中的每个项 while ((entry = (GPCEntry*)hash_seq_search(&hash_seq)) != NULL) { + + // 如果计划被使用过,则更新最后使用时间和使用次数 if (entry->val.used_count > 0) { entry->val.last_use_time = curTime; entry->val.used_count = 0; continue; } + cur_plansource = entry->val.plansource; + + // 如果计划的引用计数为0且超过了清理超时阈值,则将其加入gpckey_list中 if (cur_plansource->gpc.status.RefCountZero() && INSTR_TIME_GET_DOUBLE(curTime) - INSTR_TIME_GET_DOUBLE(entry->val.last_use_time) > u_sess->attr.attr_common.gpc_clean_timeout) { + GPCKey *dest_gpckey = (GPCKey *)palloc(sizeof(GPCKey)); GPCKeyDeepCopy(&entry->key, dest_gpckey); gpckey_list = lappend(gpckey_list, dest_gpckey); - /* should not be long */ + // 列表长度达到最大值时,结束遍历 if (gpckey_list->length >= maxlen_gpckey_list) { hash_seq_term(&hash_seq); break; @@ -1046,7 +1190,7 @@ void GlobalPlanCache::CleanUpByTime() } LWLockRelease(GetMainLWLockByIndex(lock_id)); - /* Step 2: Try to remove plan cache */ + // 步骤2:尝试移除计划缓存 if (gpckey_list && list_length(gpckey_list) > 0) { LWLockAcquire(GetMainLWLockByIndex(lock_id), LW_EXCLUSIVE); ListCell* l = NULL; @@ -1055,24 +1199,32 @@ void GlobalPlanCache::CleanUpByTime() GPCKey *key = (GPCKey *)lfirst(l); GPCEntry *entry = NULL; entry = (GPCEntry *)hash_search(m_array[bucket_id].hash_tbl, (void *)key, HASH_FIND, &found); + if (entry) { cur_plansource = entry->val.plansource; + + // 如果计划的引用计数为0且超过了清理超时阈值,则移除该计划缓存 if (cur_plansource->gpc.status.RefCountZero() && INSTR_TIME_GET_DOUBLE(curTime) - INSTR_TIME_GET_DOUBLE(entry->val.last_use_time) > u_sess->attr.attr_common.gpc_clean_timeout) { + GPC_LOG("drop shared plancache by time", cur_plansource, cur_plansource->stmt_name); DropCachedPlanInternal(cur_plansource); + hash_search(m_array[bucket_id].hash_tbl, (void *) key, HASH_REMOVE, &found); cur_plansource->magic = 0; MemoryContextUnSeal(cur_plansource->context); MemoryContextUnSeal(cur_plansource->query_context); + if (cur_plansource->opFusionObj) { OpFusion::DropGlobalOpfusion((OpFusion*)(cur_plansource->opFusionObj)); } + MemoryContextDelete(cur_plansource->context); m_array[bucket_id].count--; } } + pfree((void *)key->query_string); pfree_ext(key->env.param_types); pfree_ext(key->env.search_path->schemas); @@ -1086,47 +1238,63 @@ void GlobalPlanCache::CleanUpByTime() } } +/** + * 清理会话的GPC指针 + */ void CleanSessGPCPtr(knl_session_context* currentSession) { + // 获取当前会话中的当前语句计划 CachedPlanSource *psrc = currentSession->pcache_cxt.cur_stmt_psrc; currentSession->pcache_cxt.cur_stmt_psrc = NULL; + + // 检查当前计划是否正确 if (psrc && psrc->magic != CACHEDPLANSOURCE_MAGIC) elog(PANIC, "cur psrc wrong"); + + // 如果当前计划在共享表中,则减少引用计数和私有引用计数 if (psrc && psrc->gpc.status.InShareTable()) { psrc->gpc.status.SubRefCount(); currentSession->pcache_cxt.private_refcount--; } + + // 检查私有引用计数是否为0 if (unlikely(currentSession->pcache_cxt.private_refcount != 0)) elog(PANIC, "wrong refcount"); } +/** + * 清理会话的GPC指针并分离会话 + */ void CleanSessionGPCDetach(knl_session_context* currentSession) { + // 如果是PGXC_COORDINATOR节点,则直接返回 if (IS_PGXC_COORDINATOR) return; + + // 检查会话的当前语句计划是否为NULL if (currentSession->pcache_cxt.cur_stmt_psrc != NULL) { elog(PANIC, "session's cur_stmt_psrc should be null when detach"); } CachedPlanSource* plansource = currentSession->pcache_cxt.first_saved_plan; + + // 遍历会话中的每个保存的计划 while (plansource != NULL) { - /* - * When turing on the enable_global_plancache, there are some cases that - * we cannot insert the plancache in the shared HTAB. No Prepare Statement - * on DN, so we can just drop shared plan and wait for next parse message - * to create it again. - */ + CachedPlanSource* next_plansource = plansource->next_saved; + + // 对于私有计划,将其标记为无效 if (plansource->gpc.status.IsPrivatePlan()) { - /* private plan has reference on pointer like unname_stmt_psrc or spiplan */ GPC_LOG("invalid plan in sess detach", plansource, plansource->stmt_name); plansource->is_valid = false; plansource->next_saved = NULL; plansource->gpc.status.SetLoc(GPC_SHARE_IN_PREPARE_STATEMENT); plansource->gpc.status.SetStatus(GPC_INVALID); } else { + // 对于共享计划,删除计划缓存 DropCachedPlan(plansource); } + plansource = next_plansource; } diff --git a/src/gausskernel/process/globalplancache/globalplancache_inval.cpp b/src/gausskernel/process/globalplancache/globalplancache_inval.cpp index 9c2568249..273e3be59 100644 --- a/src/gausskernel/process/globalplancache/globalplancache_inval.cpp +++ b/src/gausskernel/process/globalplancache/globalplancache_inval.cpp @@ -40,6 +40,7 @@ bool GlobalPlanCache::MsgCheck(const SharedInvalidationMessage *msg) { + // 检查共享缓存清理消息的合法性 if (msg->id >= 0) { if (msg->cc.id == PROCOID || msg->cc.id == NAMESPACEOID || msg->cc.id == OPEROID || msg->cc.id == AMOPOPID) { return true; @@ -53,6 +54,7 @@ bool GlobalPlanCache::MsgCheck(const SharedInvalidationMessage *msg) bool GlobalPlanCache::NeedDropEntryByLocalMsg(CachedPlanSource* plansource, int tot, const int *idx, const SharedInvalidationMessage *msgs) { + // 检查本地缓存是否需要被清除 Oid database_id = plansource->gpc.key->env.plainenv.database_id; for (int j = 0; j < tot; j++) { @@ -92,6 +94,7 @@ bool GlobalPlanCache::NeedDropEntryByLocalMsg(CachedPlanSource* plansource, int void GlobalPlanCache::InvalMsg(const SharedInvalidationMessage *msgs, int n) { + // 处理共享缓存清理消息 int *idx = (int *)palloc0(n * sizeof(int)); int tot = 0; diff --git a/src/gausskernel/process/job/gs_job_calendar.cpp b/src/gausskernel/process/job/gs_job_calendar.cpp index a9a8f85a0..abd00eb3a 100644 --- a/src/gausskernel/process/job/gs_job_calendar.cpp +++ b/src/gausskernel/process/job/gs_job_calendar.cpp @@ -114,17 +114,29 @@ static TimestampTz evaluate_calendar_interval(Calendar calendar, TimestampTz sta * @param numeric_only * @return true legal * @return false illegal + *//* + * 函数名:IsLegalIntervalStr + * 功能:检查给定字符串是否是合法的时间间隔字符串。 + * 参数: + * - str:要检查的字符串。 + * - numeric_only:是否只允许数字输入。 + * 返回值: + * - 布尔类型,如果字符串是合法的则返回true,否则返回false。 */ + static bool IsLegalIntervalStr(const char* str, bool numeric_only) { size_t NBytes = (unsigned int)strlen(str); + + // 如果字符串长度超过最大允许长度,则认为不合法 if (NBytes > (MAX_CALENDAR_FIELD_LEN)) { return false; } - /* numeric input recognize comma, space and minus sign */ + /* 对于只允许数字输入的情况,认可逗号、空格和减号 */ if (numeric_only) { for (size_t i = 0; i < NBytes; i++) { + // 如果字符既不是数字也不是逗号、空格或减号,则认为不合法 if (!isdigit(str[i]) && str[i] != ',' && str[i] != ' ' && str[i] != '-') { return false; } @@ -133,7 +145,7 @@ static bool IsLegalIntervalStr(const char* str, bool numeric_only) } for (size_t i = 0; i < NBytes; i++) { - /* check whether the character is correct */ + /* 检查字符是否正确 */ if (IsIllegalIntervalCharacter(str[i])) { return false; } @@ -141,36 +153,44 @@ static bool IsLegalIntervalStr(const char* str, bool numeric_only) return true; } - -/* - * @brief get_calendar_clause - * Get calendar clause and return its value; - * @param tokens calendar interval tokens - * @param clause clause name - * @return char* value +/* + * 函数名:get_calendar_clause_val + * 功能:从tokens数组中获取与clause对应的值。 + * 参数: + * - tokens:存储了键值对的字符串数组。 + * - clause:要获取值的键。 + * - numeric_only:是否只允许数字输入。 + * 返回值: + * - char类型指针,如果找到与clause对应的值,则返回该值,否则返回NULL。 */ static char *get_calendar_clause_val(char **tokens, const char *clause, bool numeric_only) { char *val = NULL; for (int i = 0; i < MAX_CALENDAR_FIELDS; i += 2) { + // 如果找到与clause对应的键,则获取对应的值 if (tokens[i] != NULL && pg_strcasecmp(tokens[i], clause) == 0) { - val = tokens[i + 1]; /* get clause's value */ + val = tokens[i + 1]; break; } } + + // 如果未找到与clause对应的键,则返回NULL if (val == NULL) { return NULL; } + // 检查值是否合法 if (!IsLegalIntervalStr(val, numeric_only)) { - ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED), - errmsg("Fail to evaluate calendaring string."), - errdetail("Invalid value string for clause \'%s\'", clause), errcause("N/A"), - erraction("Please modify the calendaring string."))); + ereport(ERROR, + (errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED), + errmsg("Fail to evaluate calendaring string."), + errdetail("Invalid value string for clause \'%s\'", clause), + errcause("N/A"), + erraction("Please modify the calendaring string."))); } + return val; } - /* * @brief get_calendar_freqency * Get frequency_clause value. @@ -182,6 +202,7 @@ static char *get_calendar_clause_val(char **tokens, const char *clause, bool num * @return true clause exists * @return false clause absent */ + //功能:根据给定的字符串数组 tokens 中的键值对信息,解析出日历的频率并将其存储在 calendar 结构体中 static bool get_calendar_freqency(Calendar calendar, char **tokens) { char *val = get_calendar_clause_val(tokens, "freq", false); @@ -221,9 +242,12 @@ static bool get_calendar_freqency(Calendar calendar, char **tokens) * @param calendar * @param tokens */ -static void get_calendar_n_interval(Calendar calendar, char **tokens) + /*该函数用于解析日历的间隔值。首先将 calendar->interval 设置为默认值 1,然后调用 get_calendar_clause_val 函数获取键为 "interval" 的值,并将其转换为整数值。 + 如果获取到的值不在范围 [1, 99] 内,则释放 tokens 并抛出错误。最后将解析得到的间隔值赋值给 calendar->interval 字段。 + */ + static void get_calendar_n_interval(Calendar calendar, char **tokens) { - calendar->interval = 1; /* we ALWAYS set interval to 1 */ + calendar->interval = 1; /* 我们总是将 interval 设置为 1 */ char *val = get_calendar_clause_val(tokens, "interval", true); if (val == NULL) { return; @@ -240,31 +264,34 @@ static void get_calendar_n_interval(Calendar calendar, char **tokens) calendar->interval = num; } +/*该函数用于解析日历的月份规则。首先通过调用 get_calendar_clause_val 函数获取键为 "bymonth" 的值。 +如果获取到的值不为空指针,则应用该规则并直接返回该值。如果未指定 bymonth 规则,则根据日历的频率进行不同的处理 +*/ static char *get_calendar_bymonth_val(Calendar calendar, char **tokens) { char *val = get_calendar_clause_val(tokens, "bymonth", false); if (val != NULL) { - /* apply bymonth rule if bymonth is specified */ + /* 如果指定了 bymonth 规则,则应用该规则 */ return val; } if (calendar->frequency > MONTHLY) { - /* If we have higher frequency, every month is possible */ + /* 如果频率较高,将每个月都视为可能的选项 */ for (int i = 0; i < MONTHS_PER_YEAR; i++) { calendar->bymonth[i] = i + 1; } calendar->month_len = MONTHS_PER_YEAR; calendar->date_depth *= calendar->month_len; } else if (calendar->frequency < MONTHLY) { - /* If we have lower frquency, sync with start date value, fill it later */ + /* 如果频率较低,与开始日期的值同步,稍后填充 */ calendar->month_len = 0; } else { - /* Frequency is MONTHLY, try optimize */ + /* 频率为 MONTHLY,尝试优化 */ int mod = (calendar->interval >= MONTHS_PER_YEAR) ? calendar->interval % MONTHS_PER_YEAR : calendar->interval; if (mod == 0) { mod = MONTHS_PER_YEAR; } - /* We need the start month to figure out the ACTUAL month list, set it to negative and deal with it later */ + /* 我们需要开始月份才能确定实际的月份列表,将其设置为负数,稍后处理 */ calendar->month_len = (MONTHS_PER_YEAR % mod == 0) ? MONTHS_PER_YEAR / mod : MONTHS_PER_YEAR; calendar->date_depth *= calendar->month_len; calendar->month_len *= -1; @@ -284,6 +311,7 @@ static char *get_calendar_bymonth_val(Calendar calendar, char **tokens) * @param calendar * @param tokens */ + //该函数用于解析日历中的月份规则,并根据解析结果对日历的相应字段进行赋值。 static void get_calendar_bymonth(Calendar calendar, char **tokens) { const char *month_str[] = {"JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"}; @@ -348,6 +376,10 @@ static void get_calendar_bymonth(Calendar calendar, char **tokens) * @param calendar * @param tokens */ + /*该函数用于处理日历中的 "BYWEEKNO" 规则。 +首先,调用 get_calendar_clause_val 函数获取 "BYWEEKNO" 规则的值。 +如果规则的值不为空,则抛出错误,提示该规则当前不支持。 +*/ static void get_calendar_byweekno(Calendar calendar, char **tokens) { char *val = get_calendar_clause_val(tokens, "byweekno", true); @@ -369,6 +401,10 @@ static void get_calendar_byweekno(Calendar calendar, char **tokens) * @param calendar * @param tokens */ + /*该函数用于处理日历中的 "BYYEARDAY" 规则。 +首先,调用 get_calendar_clause_val 函数获取 "BYYEARDAY" 规则的值。 +如果规则的值不为空,则抛出错误,提示该规则当前不支持。 +*/ static void get_calendar_byyearday(Calendar calendar, char **tokens) { char *val = get_calendar_clause_val(tokens, "byyearday", true); @@ -389,6 +425,10 @@ static void get_calendar_byyearday(Calendar calendar, char **tokens) * @param calendar * @param tokens */ + /*该函数用于处理日历中的 "BYDATE" 规则。 +首先,调用 get_calendar_clause_val 函数获取 "BYDATE" 规则的值。 +如果规则的值不为空,则抛出错误,提示该规则当前不支持。 +*/ static void get_calendar_bydate(Calendar calendar, char **tokens) { char *val = get_calendar_clause_val(tokens, "byweekno", true); @@ -400,7 +440,14 @@ static void get_calendar_bydate(Calendar calendar, char **tokens) } } - +/* +该函数用于获取 "BYMONTHDAY" 规则的值,并根据该值进行相应的处理。 +首先,调用 get_calendar_clause_val 函数获取 "BYMONTHDAY" 规则的值。 +如果规则的值不为空,则应用 "BYMONTHDAY" 规则并返回该值。 +如果日历的频率大于等于每天或者是每周一次,那么每一天都是可能的。将每一天的值存入日历结构体中,并更新相关的长度和深度。 +如果日历的频率低于每天并且不是每周一次,那么与起始日期的值同步,并稍后填充。 +最后,返回空指针表示没有要应用的 "BYMONTHDAY" 规则。 +*/ static char *get_calendar_bymonthday_val(Calendar calendar, char **tokens) { char *val = get_calendar_clause_val(tokens, "bymonthday", true); @@ -435,6 +482,17 @@ static char *get_calendar_bymonthday_val(Calendar calendar, char **tokens) * @param calendar * @param tokens */ + /* + 该函数用于处理 "BYMONTHDAY" 规则的具体逻辑。 +首先,调用 get_calendar_bymonthday_val 函数获取 "BYMONTHDAY" 规则的值。 +如果规则的值为空,则直接返回。 +然后,使用逗号分隔得到每个月天的值,并进行相应的处理。 +对于每个月天的值,如果其超出了范围或为零,则抛出错误提示。 +如果月天的值为正数且已经使用过,则跳过。 +如果月天的值为负数且其绝对值已经使用过,则跳过。 +对于已经使用过的月天的值,将其存入日历结构体中,并更新相关的长度和深度。 +最后,设置相应的标志位表示已经应用了 "BYMONTHDAY" 规则。 +*/ static void get_calendar_bymonthday(Calendar calendar, char **tokens) { char *val = get_calendar_bymonthday_val(calendar, tokens); @@ -500,6 +558,16 @@ static void get_calendar_bymonthday(Calendar calendar, char **tokens) * day = "MON" | "TUE" | "WED" | "THU" | "FRI" | "SAT" | "SUN" * @param calendar * @param tokens + *//* + * 处理 "BYDAY" 规则的逻辑 + * + * 参数: + * - calendar: 日历结构体 + * - tokens: 分词后的字符串数组 + * + * 功能: + * - 获取 "BYDAY" 规则的值,并根据该值进行相应的处理。 + * - 当规则的值不为空时,抛出错误提示,因为当前不支持 "BYDAY" 规则。 */ static void get_calendar_byday(Calendar calendar, char **tokens) { @@ -512,32 +580,48 @@ static void get_calendar_byday(Calendar calendar, char **tokens) } } - +/* + * 处理 "BYHOUR" 规则的逻辑 + * + * 参数: + * - calendar: 日历结构体 + * - tokens: 分词后的字符串数组 + * + * 返回值: + * - NULL 表示没有要应用的 "BYHOUR" 规则,或已经应用过规则并且处理完毕。 + * - 非空指针表示需要应用 "BYHOUR" 规则,并返回规则的值。 + * + * 功能: + * - 获取 "BYHOUR" 规则的值,并根据该值进行相应的处理。 + * - 当规则的值不为空时,应用 "BYHOUR" 规则,并返回该值。 + * - 如果日历的频率大于每小时,那么每一小时都是可能的。将每一小时的值存入日历结构体中,并更新相关的长度和深度。 + * - 如果日历的频率低于每小时,并且不是每几小时一次,那么与起始日期的值同步,并稍后填充。 + */ static char *get_calendar_byhour_val(Calendar calendar, char **tokens) { char *val = get_calendar_clause_val(tokens, "byhour", true); if (val != NULL) { - /* apply byhour rule if byhour is specified */ + /* 当 byhour 规则被指定时应用规则 */ return val; } if (calendar->frequency > HOURLY) { - /* If we have higher frequency, every hour is possible */ + /* 如果频率大于每小时,那么每一小时都是可能的 */ for (int i = 0; i < HOURS_PER_DAY; i++) { calendar->byhour[i] = i; } calendar->hour_len = HOURS_PER_DAY; calendar->time_depth *= calendar->hour_len; } else if (calendar->frequency < HOURLY) { - /* If we have lower frequency, sync with start date value, fill it later */ + /* 如果频率低于每小时,并且不是每几小时一次,与起始日期的值同步,并稍后填充 */ calendar->hour_len = 0; } else { - /* frequency matched, try optimize */ + /* 频率匹配,尝试优化处理 */ int mod = (calendar->interval >= HOURS_PER_DAY) ? calendar->interval % HOURS_PER_DAY : calendar->interval; if (mod == 0) { mod = HOURS_PER_DAY; } - /* We need the start hour to figure out the ACTUAL hour list, set it to negative and deal with it later */ + /* 我们需要起始小时来确定实际的小时列表,将其设置为负数,并稍后处理 */ calendar->hour_len = (HOURS_PER_DAY % mod == 0) ? HOURS_PER_DAY / mod : HOURS_PER_DAY; calendar->time_depth *= calendar->hour_len; calendar->hour_len *= -1; @@ -546,6 +630,7 @@ static char *get_calendar_byhour_val(Calendar calendar, char **tokens) return NULL; } + /* * @brief get_calendar_byhour * Get byhour_clause. @@ -555,6 +640,11 @@ static char *get_calendar_byhour_val(Calendar calendar, char **tokens) * @param calendar * @param tokens */ + /* + 该函数的功能是处理日历的 "BYHOUR" 规则。 + 根据规则的值进行相应的处理,当规则的值不为空时,应用规则并返回该值; + 当规则的值为空时,根据日历的频率、起始日期和间隔等信息确定实际的小时列表,并存储在日历结构体中。 + */ static void get_calendar_byhour(Calendar calendar, char **tokens) { char *val = get_calendar_byhour_val(calendar, tokens); @@ -590,7 +680,10 @@ static void get_calendar_byhour(Calendar calendar, char **tokens) calendar->byfields |= INTERVAL_BYHOUR; } - +/*该函数的功能是处理日历的 "BYMINUTE" 规则。 +根据规则的值进行相应的处理,当规则的值不为空时,应用规则并返回该值; +当规则的值为空时,根据日历的频率、起始日期和间隔等信息确定实际的分钟列表,并存储在日历结构体中 +*/ static char *get_calendar_byminute_val(Calendar calendar, char **tokens) { char *val = get_calendar_clause_val(tokens, "byminute", true); @@ -633,6 +726,11 @@ static char *get_calendar_byminute_val(Calendar calendar, char **tokens) * @param calendar * @param tokens */ + /* + get_calendar_byminute函数的功能是处理日历的 "BYMINUTE" 规则。 + 根据规则的值进行相应的处理,当规则的值不为空时,应用规则并返回该值; + 当规则的值为空时,根据日历的频率、起始日期和间隔等信息确定实际的分钟列表,并存储在日历结构体中。 + */ static void get_calendar_byminute(Calendar calendar, char **tokens) { char *val = get_calendar_byminute_val(calendar, tokens); @@ -667,7 +765,11 @@ static void get_calendar_byminute(Calendar calendar, char **tokens) calendar->time_depth *= (calendar->minute_len == 0) ? 1 : calendar->minute_len; calendar->byfields |= INTERVAL_BYMINUTE; } - +/* + get_calendar_bysecond_val函数的功能是处理日历的 "BYSECOND" 规则。 + 根据规则的值进行相应的处理,当规则的值不为空时,应用规则并返回该值; + 当规则的值为空时,根据日历的频率、起始日期和间隔等信息确定实际的秒钟列表,并存储在日历结构体中。 + */ static char *get_calendar_bysecond_val(Calendar calendar, char **tokens) { char *val = get_calendar_clause_val(tokens, "bysecond", true); @@ -705,6 +807,11 @@ static char *get_calendar_bysecond_val(Calendar calendar, char **tokens) * @param interval * @param tokens */ + /* + get_calendar_bysecond_val函数的功能是处理日历的 "BYSECOND" 规则。 + 根据规则的值进行相应的处理,当规则的值不为空时,应用规则并返回该值; + 当规则的值为空时,根据日历的频率、起始日期和间隔等信息确定实际的秒钟列表,并存储在日历结构体中。 + */ static void get_calendar_bysecond(Calendar calendar, char **tokens) { char *val = get_calendar_bysecond_val(calendar, tokens); @@ -749,6 +856,10 @@ static void get_calendar_bysecond(Calendar calendar, char **tokens) * @param fields number of tokens needed * @return char** an array of tokens generated */ + /* + tokenize_str函数的功能是将字符串分割为多个子字符串,并存储在字符指针数组中。 + 根据给定的分隔符和字段数,使用strtok_s函数逐个分割源字符串,将分割得到的子字符串存储在tokens数组中,并返回tokens数组。 +*/ static char **tokenize_str(char *src, const char *delims, int fields) { char **tokens = (char **)palloc0(sizeof(char *) * fields); @@ -767,6 +878,11 @@ static char **tokenize_str(char *src, const char *delims, int fields) return tokens; } +/* +validate_field_names函数的功能是验证字段名称是否合法。 +通过比较每个字段名称与支持的字段名称列表,检查字段名称是否重复或不符合支持的字段列表。 +如果发现字段名称无效,则返回字段位置索引;如果所有字段名称都有效,则返回-1表示验证通过。 +*/ static int validate_field_names(char **toks) { bool valid = false; @@ -806,6 +922,12 @@ static int validate_field_names(char **toks) * The main interpreter of calendar interval. * @param interval_str * @return Calendar + *//* + * 解释日历字符串,生成对应的日历对象 + * 参数: + * calendar_str: 待解释的日历字符串 + * 返回值: + * 解释得到的日历对象,如果解释失败则返回NULL */ Calendar interpret_calendar_interval(char *calendar_str) { @@ -815,6 +937,7 @@ Calendar interpret_calendar_interval(char *calendar_str) /* Make token lists */ char **str_toks = tokenize_str(calendar_str, " =;", MAX_CALENDAR_FIELDS); if (str_toks == NULL) { + /* 解释失败,抛出错误 */ ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED), errmsg("Fail to evaluate calendaring string."), errdetail("Unable to parse calendaring string."), @@ -824,28 +947,28 @@ Calendar interpret_calendar_interval(char *calendar_str) int pos = validate_field_names(str_toks); if (pos >= 0) { + /* 解释失败,抛出错误 */ ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED), errmsg("Fail to evaluate calendaring string."), errdetail("Incorrect/duplicate clause name '%s'.", str_toks[pos]), errcause("N/A"), erraction("Please modify the calendaring string."))); } - /* Make Calendar */ + /* 创建日历对象 */ Calendar calendar = (Calendar)palloc0(sizeof(CalendarContext)); - /* Main interpreter */ + /* 主解释器 */ field = get_calendar_freqency(calendar, str_toks); if (!field || pg_strcasecmp(str_toks[0], "freq") != 0) { /* - * Return NULL if frequency clause is missing(or not the first), - * possibly not a calendaring syntax + * 如果缺少频率子句(或非第一个子句),可能不是日历语法,返回NULL */ pfree_ext(calendar); return NULL; } get_calendar_n_interval(calendar, str_toks); - /* by*** clauses */ + /* 解析by***子句 */ calendar->date_depth = 1; calendar->time_depth = 1; get_calendar_bymonth(calendar, str_toks); @@ -862,15 +985,24 @@ Calendar interpret_calendar_interval(char *calendar_str) return calendar; } + /* * @brief get_calendar_period * Get the calendar period in the form of Interval. * @param calendar * @param num_of_period * @return Interval* + *//* + * 根据日历的频率和数量生成时间间隔 + * 参数: + * calendar: 日历对象 + * num_of_period: 期间的数量 + * 返回值: + * 生成的时间间隔对象,如果无效的频率则返回NULL */ static Interval *get_calendar_period(Calendar calendar, int num_of_period) { + /* 根据频率确定时间单位 */ const char *freq_str = NULL; if (calendar->frequency == YEARLY) { freq_str = "years"; @@ -887,24 +1019,25 @@ static Interval *get_calendar_period(Calendar calendar, int num_of_period) } else if (calendar->frequency == SECONDLY) { freq_str = "seconds"; } else { - /* rely on upper level checks, which will eventually reach the MAX_CALENDAR_DEPTH and stop */ + /* 依赖于上层的检查,当达到最大日历深度时会停止 */ return NULL; } - + + /* 构造时间间隔对象并返回 */ return calendar_construct_interval(freq_str, calendar->interval * num_of_period); } /* - * @brief copy_calendar_dates - * copy batches of timestamps. - * @param dates target timestamp array - * @param nvals number of batches - * @param cnt number of existing values - * @param date_in optional, only valid when dates is empty + * 复制日期数组的批次数据 + * 参数: + * dates: 目标时间戳数组 + * nvals: 批次数量 + * cnt: 存在的值的个数 + * date_in: 可选,仅在dates为空时有效 */ static void copy_calendar_dates(TimestampTz *timeline, int nvals, int cnt) { - /* copy nvals number of existing timestamps */ + /* 复制现有时间戳的nvals个拷贝 */ if (nvals <= 1) { return; } @@ -916,24 +1049,30 @@ static void copy_calendar_dates(TimestampTz *timeline, int nvals, int cnt) } /* - * @brief validate_calendar_monthday - * Check if a given monthday is valid. - * @param year - * @param month - * @param mday - * @return true - * @return false + * 验证给定的月份日期是否有效 + * 参数: + * year: 年份 + * month: 月份 + * mday: 日期 + * 返回值: + * 如果是有效的月份日期返回true,否则返回false */ static bool validate_calendar_monthday(int year, int month, int mday) { + /* 每个月的天数 */ bool month_31[MONTHS_PER_YEAR] = {1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1}; + + /* 检查日期是否超出范围 */ if ((!month_31[month - 1] && mday > 30) || (month_31[month - 1] && mday > 31)) { return false; } + + /* 闰年的特殊处理 */ if ((!isleap(year) && month == 2 && mday > 29) || (isleap(year) && month == 2 && mday > 28)) { return false; } - /* monthday can be negative */ + + /* 月份日期可以为负数 */ if (!isleap(year) && month == 2 && mday < -28) { return false; } @@ -943,9 +1082,11 @@ static bool validate_calendar_monthday(int year, int month, int mday) if (!month_31[month - 1] && mday < -30) { return false; } + return true; } + /* * @brief evaluate_calendar_bymonth * Evaluate bymonth field. @@ -964,10 +1105,12 @@ static void evaluate_calendar_bymonth(Calendar calendar, TimestampTz *timeline, int chunk = *cnt; TimestampTz ref = truncate_calendar_date(CStringGetTextDatum("month"), calendar->ref_date); + // 检查是否存在bymonthday,如果不存在需要根据当前时间的月份构造bymonthday,保证至少有一个月份符合条件。 if (calendar->month_len == 0) { calendar->bymonth[0] = calendar->tm.tm_mon; calendar->month_len = 1; } else if (calendar->month_len < 0) { + // 如果bymonth为负数,则每隔abs(bymonth)个月执行一次。 int mod = calendar->bymonth[0]; int start = (calendar->tm.tm_mon - 1) - (((calendar->tm.tm_mon - 1) / mod) * mod) + 1; calendar->month_len = 0; @@ -992,17 +1135,33 @@ static void evaluate_calendar_bymonth(Calendar calendar, TimestampTz *timeline, } } +/** + * @brief evaluate_calendar_byweekno + * Evaluate byweekno field. + * @param calendar + * @param timeline + * @param cnt + */ static void evaluate_calendar_byweekno(Calendar calendar, TimestampTz *timeline, int *cnt) { + // 目前未实现byweekno计算,直接返回。 return; } +/** + * @brief evaluate_calendar_byyearday + * Evaluate byyearday field. + * @param calendar + * @param timeline + * @param cnt + */ static void evaluate_calendar_byyearday(Calendar calendar, TimestampTz *timeline, int *cnt) { + // 目前未实现byyearday计算,直接返回。 return; } -/* +/** * @brief evaluate_calendar_bymonthday * Evaluate bymonthday field. * @param calendar @@ -1020,6 +1179,7 @@ static void evaluate_calendar_bymonthday(Calendar calendar, TimestampTz *timelin Interval *interval = NULL; TimestampTz ref = truncate_calendar_date(CStringGetTextDatum("day"), calendar->ref_date); + // 检查是否存在bymonthday,如果不存在需要根据当前时间的月份构造bymonthday,保证至少有一个月份符合条件。 if (calendar->monthday_len == 0) { calendar->bymonthday[0] = calendar->tm.tm_mday; calendar->monthday_len = 1; @@ -1043,11 +1203,12 @@ static void evaluate_calendar_bymonthday(Calendar calendar, TimestampTz *timelin errcause("N/A"), erraction("Please modify the calendaring string."))); } if (!validate_calendar_monthday(tm->tm_year, tm->tm_mon, calendar->bymonthday[i])) { - /* skip if month day is invalid */ + // 无效的月天数,直接跳过。 continue; } timeline[*cnt] = DatumGetTimestampTz(timestamp_pl_interval(timeline[j], interval)); if (calendar->bymonthday[i] <= 0) { + // 如果月天数为负数,则表示倒数第n天。 pfree_ext(interval); interval = calendar_construct_interval("days", -(calendar->bymonthday[i])); timeline[*cnt] = DatumGetTimestampTz(timestamp_mi_interval(timeline[j], interval)); @@ -1059,13 +1220,12 @@ static void evaluate_calendar_bymonthday(Calendar calendar, TimestampTz *timelin pfree_ext(interval); } } - /* * @brief evaluate_calendar_byhour - * Evaluate byhour field. - * @param calendar - * @param timeline - * @param cnt + * 评估按小时的字段。 + * @param calendar 日历参数 + * @param timeline 时间线 + * @param cnt 计数器 */ static void evaluate_calendar_byhour(Calendar calendar, TimestampTz *timeline, int *cnt) { @@ -1108,10 +1268,10 @@ static void evaluate_calendar_byhour(Calendar calendar, TimestampTz *timeline, i /* * @brief evaluate_calendar_byminute - * Evaluate byminunte field. - * @param calendar - * @param timeline - * @param cnt + * 评估按分钟的字段。 + * @param calendar 日历参数 + * @param timeline 时间线 + * @param cnt 计数器 */ static void evaluate_calendar_byminute(Calendar calendar, TimestampTz *timeline, int *cnt) { @@ -1151,13 +1311,12 @@ static void evaluate_calendar_byminute(Calendar calendar, TimestampTz *timeline, pfree_ext(interval); } } - -/* +/* * @brief evaluate_calendar_bysecond - * Evaluate bysecond field. - * @param calendar - * @param timeline - * @param cnt + * 评估bysecond字段。 + * @param calendar 日历对象 + * @param timeline 时间线数组 + * @param cnt 计数器 */ static void evaluate_calendar_bysecond(Calendar calendar, TimestampTz *timeline, int *cnt) { @@ -1198,18 +1357,18 @@ static void evaluate_calendar_bysecond(Calendar calendar, TimestampTz *timeline, } } -/* +/* * @brief fastforward_calendar_period - * Fastforward to the date right before the date after to save some computing power. - * @param calendar - * @param start_date - * @param date_after + * 快速前进到日期后的前一天,以节省计算资源。 + * @param calendar 日历对象 + * @param start_date 起始日期 + * @param date_after 之后的日期 */ static void fastforward_calendar_period(Calendar calendar, TimestampTz *start_date, TimestampTz date_after) { Interval *interval = calendar_construct_interval("second", 1); if (timestamptz_cmp_internal(*start_date, date_after) >= 0) { - /* No rewind, no equal */ + /* 不需要倒回,也不相等 */ calendar->ref_date = DatumGetTimestampTz(timestamp_pl_interval(*start_date, interval)); pfree_ext(interval); return; @@ -1219,8 +1378,8 @@ static void fastforward_calendar_period(Calendar calendar, TimestampTz *start_da Interval *period = get_calendar_period(calendar); if (period == NULL) { ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), - errmsg("Cannot evaluate calendar clause."), errdetail("Broken interval clause."), - errcause("N/A"), erraction("Please modify the calendaring string."))); + errmsg("无法评估日历子句。"), errdetail("错误的间隔子句。"), + errcause("N/A"), erraction("请修改日历字符串。"))); } Datum pace_datum = DirectFunctionCall2(interval_part, CStringGetTextDatum("epoch"), PointerGetDatum(period)); @@ -1231,14 +1390,111 @@ static void fastforward_calendar_period(Calendar calendar, TimestampTz *start_da int elapsed_usec = 0; TimestampDifference(*start_date, date_after, &elapsed_sec, &elapsed_usec); - /* fastforward span in seconds = floor(elapsed / pace) * pace */ + /* 快速前进的秒数 = floor(elapsed / pace) * pace */ int num_of_periods = (elapsed_sec / pace); if (num_of_periods >= 1) { Interval *ff_span = get_calendar_period(calendar, num_of_periods); if (ff_span == NULL) { ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), - errmsg("Cannot evaluate calendar clause."), errdetail("Broken interval clause."), - errcause("N/A"), erraction("Please modify the calendaring string."))); + errmsg("无法评估日历子句。"), errdetail("错误的间隔子句。"), + errcause("N/A"), erraction("请修改日历字符串。"))); + } + new_start_date = DatumGetTimestampTz(timestamp_pl_interval(*start_date, ff_span)); + pfree_ext(ff_span); + *start_date = new_start_date; + } + calendar->ref_date = DatumGetTimestampTz(timestamp_pl_interval(date_after, interval)); + pfree_ext(interval); +}/* + * @brief evaluate_calendar_bysecond + * 对bysecond字段进行评估,生成时间线数组。 + * @param calendar 日历对象 + * @param timeline 时间线数组 + * @param cnt 计数器 + */ +static void evaluate_calendar_bysecond(Calendar calendar, TimestampTz *timeline, int *cnt) +{ + Assert(*cnt <= calendar->time_depth); + if (*cnt == 0) { + return; + } + + Interval *interval = NULL; + int chunk = *cnt; + TimestampTz ref = truncate_calendar_date(CStringGetTextDatum("second"), calendar->ref_date); + + // 如果bysecond长度为0,则默认使用当前秒作为bysecond值(长度为1) + if (calendar->second_len == 0) { + calendar->bysecond[0] = calendar->tm.tm_sec; + calendar->second_len = 1; + } + // 如果bysecond长度为负数,则以模值作为起点,生成连续的秒数 + else if (calendar->second_len < 0) { + int mod = calendar->bysecond[0]; + int start = calendar->tm.tm_sec - ((calendar->tm.tm_sec / mod) * mod); + calendar->second_len = 0; + while (start < SECS_PER_MINUTE) { + calendar->bysecond[calendar->second_len] = start; + start += mod; + calendar->second_len++; + } + } + + *cnt = 0; + copy_calendar_dates(timeline, calendar->second_len, chunk); + for (int i = 0; i < calendar->second_len; i++) { + interval = calendar_construct_interval("seconds", calendar->bysecond[i]); + for (int j = i * chunk; j < (i + 1) * chunk; j++) { + timeline[*cnt] = DatumGetTimestampTz(timestamp_pl_interval(timeline[j], interval)); + if (timestamp_cmp_internal(timeline[*cnt], ref) >= 0) { + (*cnt)++; + } + } + pfree_ext(interval); + } +} + +/* + * @brief fastforward_calendar_period + * 快速前进到日期后的前一天,以节省计算资源。 + * @param calendar 日历对象 + * @param start_date 起始日期 + * @param date_after 之后的日期 + */ +static void fastforward_calendar_period(Calendar calendar, TimestampTz *start_date, TimestampTz date_after) +{ + Interval *interval = calendar_construct_interval("second", 1); + if (timestamptz_cmp_internal(*start_date, date_after) >= 0) { + /* 不需要倒回,也不相等 */ + calendar->ref_date = DatumGetTimestampTz(timestamp_pl_interval(*start_date, interval)); + pfree_ext(interval); + return; + } + + TimestampTz new_start_date; + Interval *period = get_calendar_period(calendar); + if (period == NULL) { + ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("无法评估日历子句。"), errdetail("错误的间隔子句。"), + errcause("N/A"), erraction("请修改日历字符串。"))); + } + Datum pace_datum = DirectFunctionCall2(interval_part, CStringGetTextDatum("epoch"), + PointerGetDatum(period)); + int pace = (int)DatumGetFloat8(pace_datum); + pfree_ext(period); + + long elapsed_sec = 0; + int elapsed_usec = 0; + TimestampDifference(*start_date, date_after, &elapsed_sec, &elapsed_usec); + + /* 快速前进的秒数 = floor(elapsed / pace) * pace */ + int num_of_periods = (elapsed_sec / pace); + if (num_of_periods >= 1) { + Interval *ff_span = get_calendar_period(calendar, num_of_periods); + if (ff_span == NULL) { + ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("无法评估日历子句。"), errdetail("错误的间隔子句。"), + errcause("N/A"), erraction("请修改日历字符串。"))); } new_start_date = DatumGetTimestampTz(timestamp_pl_interval(*start_date, ff_span)); pfree_ext(ff_span); @@ -1250,12 +1506,12 @@ static void fastforward_calendar_period(Calendar calendar, TimestampTz *start_da /* * @brief recheck_calendar_period - * Usually we need to double check if a given timestamp resonate with original frequency. - * @param calendar - * @param start_date - * @param next_date - * @return true resonate - * @return false does not resonate + * 通常需要对给定的时间戳进行二次检查,以确定是否与原始频率相符。 + * @param calendar 日历对象 + * @param start_date 起始日期 + * @param next_date 下一个日期 + * @return true 相符 + * @return false 不相符 */ static bool recheck_calendar_period(Calendar calendar, TimestampTz start_date, TimestampTz next_date) { @@ -1293,10 +1549,10 @@ static bool recheck_calendar_period(Calendar calendar, TimestampTz start_date, T /* * @brief timestamp_cmp_func - * Compare func for qsort - * @param dt1 - * @param dt2 - * @return int + * 比较函数,用于qsort排序 + * @param dt1 时间戳1 + * @param dt2 时间戳2 + * @return int 比较结果 */ static int timestamp_cmp_func(const void *dt1, const void *dt2) { @@ -1308,23 +1564,23 @@ static int timestamp_cmp_func(const void *dt1, const void *dt2) /* * @brief find_nearest_calendar_time - * Find the nearest timestamp from all valid timestamps. - * @param calendar context - * @param timeline timestamp list - * @param start start time - * @param cnt number of valid final timestamps - * @param nearest out value - * @return true found - * @return false not found + * 从所有有效的时间戳中找到最近的时间戳。 + * @param calendar 日历对象 + * @param timeline 时间戳列表 + * @param start 起始时间 + * @param cnt 有效的最终时间戳数量 + * @param nearest 最近的时间戳 + * @return true 找到 + * @return false 未找到 */ static bool find_nearest_calendar_time(Calendar calendar, TimestampTz *timeline, TimestampTz start, int cnt, TimestampTz *nearest) { - /* sort all timestamp */ + /* 对时间戳进行排序 */ qsort(timeline, cnt, sizeof(TimestampTz), timestamp_cmp_func); for (int i = 0; i < cnt; i++) { - /* Must greater than ref date */ + /* 必须大于ref_date,并且与原始频率相符合 */ if (timestamp_cmp_internal(timeline[i], calendar->ref_date) >= 0 && recheck_calendar_period(calendar, start, timeline[i])) { *nearest = timeline[i]; @@ -1333,16 +1589,16 @@ static bool find_nearest_calendar_time(Calendar calendar, TimestampTz *timeline, } return false; } - /* * @brief evaluate_calendar_period - * - * @param calendar - * @param period - * @param start_date - * @param next_date - * @return true success - * @return false fail + * 评估日历的周期部分,生成时间线数组。 + * @param calendar 日历对象 + * @param timeline 时间线数组 + * @param sub_timeline 子时间线数组 + * @param start_date 起始日期 + * @param next_date 下一个日期 + * @return true 成功 + * @return false 失败 */ static bool evaluate_calendar_period(Calendar calendar, TimestampTz *timeline, TimestampTz *sub_timeline, TimestampTz start_date, TimestampTz *next_date) @@ -1353,16 +1609,16 @@ static bool evaluate_calendar_period(Calendar calendar, TimestampTz *timeline, T evaluate_calendar_byyearday(calendar, timeline, &date_cnt); evaluate_calendar_bymonthday(calendar, timeline, &date_cnt); - /* sort the timeline so that we can break immediately once we find any timestamps later */ + /* 对时间线进行排序,以便在找到晚于当前时间的时间戳后立即中止 */ qsort(timeline, date_cnt, sizeof(TimestampTz), timestamp_cmp_func); /* - * Use the sorted timeline (date part) to generate matching timestamp for each day. + * 使用已排序的时间线(日期部分)为每一天生成匹配的时间戳。 */ int time_cnt; for (int i = 0; i < date_cnt; i++) { time_cnt = 1; - sub_timeline[0] = timeline[i]; /* starting date */ + sub_timeline[0] = timeline[i]; /* 起始日期 */ evaluate_calendar_byhour(calendar, sub_timeline, &time_cnt); evaluate_calendar_byminute(calendar, sub_timeline, &time_cnt); evaluate_calendar_bysecond(calendar, sub_timeline, &time_cnt); @@ -1375,10 +1631,10 @@ static bool evaluate_calendar_period(Calendar calendar, TimestampTz *timeline, T /* * @brief prepare_calendar_period - * - * @param calendar - * @param base_date - * @param timeline + * 准备日历的周期部分。 + * @param calendar 日历对象 + * @param base_date 基准日期 + * @param timeline 时间线数组 */ static void prepare_calendar_period(Calendar calendar, TimestampTz base_date, TimestampTz *timeline) { @@ -1391,18 +1647,18 @@ static void prepare_calendar_period(Calendar calendar, TimestampTz base_date, Ti if (calendar->date_depth > MAX_CALENDAR_DATE_DEPTH || calendar->time_depth > SECS_PER_DAY) { ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), - errmsg("Cannot evaluate calendar clause."), - errdetail("The scheduler run out of attempts to find a valid date in the foreesable future."), - errcause("N/A"), erraction("Please modify the calendaring string."))); + errmsg("无法评估日历子句。"), + errdetail("调度程序尝试在可预见的未来找到有效日期的次数用尽。"), + errcause("N/A"), erraction("请修改日历字符串。"))); } fsec_t fsec; - struct pg_tm tt, *tm = &tt; /* POSIX time struct, see NOTE above */ + struct pg_tm tt, *tm = &tt; /* POSIX时间结构,参见上面的NOTE */ int tz; if (timestamp2tm(base_date, &tz, tm, &fsec, NULL, NULL) != 0) { ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), - errmsg("Cannot evaluate calendar clause."), errdetail("Fail to truncate start date."), - errcause("N/A"), erraction("Please modify the calendaring string."))); + errmsg("无法评估日历子句。"), errdetail("无法截断起始日期。"), + errcause("N/A"), erraction("请修改日历字符串。"))); } calendar->tm = tt; calendar->fsec = fsec; @@ -1410,14 +1666,12 @@ static void prepare_calendar_period(Calendar calendar, TimestampTz base_date, Ti if (timestamp_cmp_internal(timeline[0], calendar->ref_date) > 0) { calendar->ref_date = timeline[0]; } -} - -/* +}/* * @brief get_next_calendar_period - * Get the next calendar period. Usually a year, but can be multiple years if interval is large. - * @param calendar - * @param base_date - * @return TimestampTz + * 获取下一个日历周期。通常是一年,但如果间隔很大,可以是多年。 + * @param calendar 日历对象 + * @param base_date 基准日期 + * @return TimestampTz 下一个日历周期的起始日期 */ static TimestampTz get_next_calendar_period(Calendar calendar, TimestampTz base_date) { @@ -1434,10 +1688,10 @@ static TimestampTz get_next_calendar_period(Calendar calendar, TimestampTz base_ /* * @brief evaluate_calendar_interval - * Calculate next date base on start date. - * @param calendar - * @param start_date - * @return char* + * 根据起始日期计算下一个日期。 + * @param calendar 日历对象 + * @param start_date 起始日期 + * @return TimestampTz 下一个日期 */ static TimestampTz evaluate_calendar_interval(Calendar calendar, TimestampTz start_date) { @@ -1447,7 +1701,7 @@ static TimestampTz evaluate_calendar_interval(Calendar calendar, TimestampTz sta int try_count = 0; TimestampTz next_date = start_date; - TimestampTz base_date = start_date; /* base date of each period, update with loop */ + TimestampTz base_date = start_date; /* 每个周期的基准日期,在循环中更新 */ TimestampTz *timeline = (TimestampTz *)palloc0(calendar->date_depth * sizeof(TimestampTz)); TimestampTz *sub_timeline = (TimestampTz *)palloc0((calendar->time_depth + 1) * sizeof(TimestampTz)); while (try_count < YEARS_PER_CENTURY) { @@ -1464,18 +1718,16 @@ static TimestampTz evaluate_calendar_interval(Calendar calendar, TimestampTz sta pfree_ext(sub_timeline); if (try_count >= YEARS_PER_CENTURY) { ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), - errmsg("Cannot evaluate calendar clause."), errdetail("Calender clause too deep."), - errcause("N/A"), erraction("Please modify the calendaring string."))); + errmsg("无法评估日历子句。"), errdetail("日历子句太深。"), + errcause("N/A"), erraction("请修改日历字符串。"))); } return next_date; -} - -/* +}/* * @brief evaluate_repeat_interval - * Calculate next date base on start date and calendar string. - * @param calendar_in - * @param base_time - * @return Datum + * 根据起始日期和日历字符串计算下一个日期。 + * @param calendar_in 日历字符串 + * @param start_date 起始日期 + * @return Datum 下一个日期 */ Datum evaluate_repeat_interval(Datum calendar_in, Datum start_date, Datum date_after) { @@ -1488,12 +1740,12 @@ Datum evaluate_repeat_interval(Datum calendar_in, Datum start_date, Datum date_a TimestampTz start_date_raw = DatumGetTimestampTz(start_date); Calendar calendar = interpret_calendar_interval(calendar_str); - /* remove fsec part */ + /* 移除毫秒部分 */ start_date_raw = truncate_calendar_date(CStringGetTextDatum("second"), TimestampTzGetDatum(start_date_raw)); - /* fastforward start date if date_after is specified */ + /* 如果指定了date_after,则快进到start_date之后的日期 */ if (calendar != NULL) { - /* remove fsec part */ + /* 移除毫秒部分 */ date_after = truncate_calendar_date(CStringGetTextDatum("second"), TimestampTzGetDatum(date_after)); fastforward_calendar_period(calendar, &start_date_raw, DatumGetTimestampTz(date_after)); } @@ -1501,7 +1753,7 @@ Datum evaluate_repeat_interval(Datum calendar_in, Datum start_date, Datum date_a pfree_ext(calendar_str); if (calendar == NULL) { - /* return if not a calendar interval expression, `calendar` is already freed */ + /* 如果不是日历间隔表达式,则直接返回,不需要释放calendar */ return calendar_in; } else { pfree_ext(calendar); @@ -1511,14 +1763,14 @@ Datum evaluate_repeat_interval(Datum calendar_in, Datum start_date, Datum date_a /* * @brief evaluate_calendar_string_internal - * eval_calendar_string interface. + * eval_calendar_string的接口函数。 * @return Datum */ Datum evaluate_calendar_string_internal(PG_FUNCTION_ARGS) { - Datum string = PG_GETARG_DATUM(0); /* calendar string */ - Datum start_date = PG_GETARG_DATUM(1); /* start date */ - Datum date_after = PG_GETARG_DATUM(2); /* return date after */ + Datum string = PG_GETARG_DATUM(0); /* 日历字符串 */ + Datum start_date = PG_GETARG_DATUM(1); /* 起始日期 */ + Datum date_after = PG_GETARG_DATUM(2); /* 返回日期之后的日期 */ Datum new_next_date = evaluate_repeat_interval(string, start_date, date_after); PG_RETURN_DATUM(new_next_date); -} \ No newline at end of file +} diff --git a/src/gausskernel/process/job/gs_job_manager.cpp b/src/gausskernel/process/job/gs_job_manager.cpp index feca70a7d..b444b4f35 100644 --- a/src/gausskernel/process/job/gs_job_manager.cpp +++ b/src/gausskernel/process/job/gs_job_manager.cpp @@ -68,20 +68,21 @@ static bool run_sql_job(Datum job_name, StringInfoData *buf); static bool run_procedure_job(Datum job_name, StringInfoData *buf); static char *run_external_job(Datum job_name); - /* * @brief delete_by_syscache - * Perform a simple heap delete by searching syscache. - * @param rel Target relation - * @param object_name Delete by key - * @param cache_id Cache ID + * 通过搜索系统缓存执行简单的堆删除。 + * @param rel 目标关系 + * @param object_name 删除的键 + * @param cache_id 缓存ID */ static void delete_by_syscache(Relation rel, const Datum object_name, SysCacheIdentifier cache_id) { + /* 在系统缓存中查找对象 */ CatCList *tuples = SearchSysCacheList1(cache_id, object_name); if (tuples == NULL) { return; } + /* 循环遍历对象并删除 */ for (int i = 0; i < tuples->n_members; i++) { HeapTuple tuple = t_thrd.lsc_cxt.FetchTupleFromCatCList(tuples, i); simple_heap_delete(rel, &tuple->t_self); @@ -91,7 +92,7 @@ static void delete_by_syscache(Relation rel, const Datum object_name, SysCacheId /* * @brief delete_from_attribute - * Delete from gs_job_attribute. + * 从gs_job_attribute表中删除。 * @param object_name */ void delete_from_attribute(const Datum object_name) @@ -103,7 +104,7 @@ void delete_from_attribute(const Datum object_name) /* * @brief delete_from_argument - * Delete from gs_job_argument. + * 从gs_job_argument表中删除。 * @param job_name */ void delete_from_argument(const Datum object_name) @@ -115,7 +116,7 @@ void delete_from_argument(const Datum object_name) /* * @brief delete_from_job - * Delete from pg_job. + * 从pg_job表中删除。 * @param job_name */ void delete_from_job(const Datum job_name) @@ -130,7 +131,7 @@ void delete_from_job(const Datum job_name) /* * @brief delete_from_job_proc - * Delete from pg_job_proc. + * 从pg_job_proc表中删除。 * @param job_name */ void delete_from_job_proc(const Datum job_name) @@ -142,34 +143,35 @@ void delete_from_job_proc(const Datum job_name) } heap_close(rel, NoLock); } - HeapTuple search_from_pg_job(Relation pg_job_rel, Datum job_name) { ScanKeyInfo scan_key_info1; - scan_key_info1.attribute_value = job_name; - scan_key_info1.attribute_number = Anum_pg_job_job_name; - scan_key_info1.procedure = F_TEXTEQ; + scan_key_info1.attribute_value = job_name; // 设置扫描键的属性值为job_name + scan_key_info1.attribute_number = Anum_pg_job_job_name; // 设置扫描键的属性编号为Anum_pg_job_job_name + scan_key_info1.procedure = F_TEXTEQ; // 设置扫描键的比较函数为F_TEXTEQ + ScanKeyInfo scan_key_info2; - scan_key_info2.attribute_value = PointerGetDatum(u_sess->proc_cxt.MyProcPort->database_name); - scan_key_info2.attribute_number = Anum_pg_job_dbname; - scan_key_info2.procedure = F_NAMEEQ; + scan_key_info2.attribute_value = PointerGetDatum(u_sess->proc_cxt.MyProcPort->database_name); // 设置扫描键的属性值为当前数据库名称 + scan_key_info2.attribute_number = Anum_pg_job_dbname; // 设置扫描键的属性编号为Anum_pg_job_dbname + scan_key_info2.procedure = F_NAMEEQ; // 设置扫描键的比较函数为F_NAMEEQ - List *tuples = search_by_sysscan_2(pg_job_rel, &scan_key_info1, &scan_key_info2); + List *tuples = search_by_sysscan_2(pg_job_rel, &scan_key_info1, &scan_key_info2); // 在pg_job_rel上执行扫描操作,并返回符合条件的元组列表 if (tuples == NIL) { return NULL; } - Assert(list_length(tuples) == 1); + Assert(list_length(tuples) == 1); // 断言元组列表长度为1 if (list_length(tuples) != 1) { ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("find %d tuples match job_name %s in system table pg_job.", list_length(tuples), TextDatumGetCString(job_name)), errdetail("N/A"), errcause("job name is not exist"), erraction("Please check job_name"))); } - HeapTuple tuple = (HeapTuple)linitial(tuples); - list_free_ext(tuples); - return tuple; + HeapTuple tuple = (HeapTuple)linitial(tuples); // 获取元组列表中的第一个元组 + list_free_ext(tuples); // 释放元组列表的内存 + return tuple; // 返回元组 } + /* * @brief update_pg_job * Update pg_job. @@ -277,12 +279,18 @@ List *search_related_attribute(Relation gs_job_attribute_rel, Datum attribute_na /* * @brief disable_related_jobs_force + *//* + * @brief disable_related_jobs_force + * + * @param gs_job_attribute_rel + * @param disable_job_names 要禁用的作业名称列表 */ static void disable_related_jobs_force(Relation gs_job_attribute_rel, List *disable_job_names) { ListCell *lc = NULL; foreach (lc, disable_job_names) { Datum job_name = PointerGetDatum(lfirst(lc)); + // 禁用指定名称的作业 update_pg_job(job_name, Anum_pg_job_enable, BoolGetDatum(false)); } } @@ -291,8 +299,9 @@ static void disable_related_jobs_force(Relation gs_job_attribute_rel, List *disa * @brief reset_job_class * * @param gs_job_attribute_rel - * @param disable_job_names - * @param attribute_name + * @param disable_job_names 在设置默认 job_class 前需要先禁用这些作业 + * @param attribute_name 要重置为默认 job_class 的属性名称 + * @param force 是否强制执行重置操作 */ static void reset_job_class(Relation gs_job_attribute_rel, List *disable_job_names, Datum attribute_name, bool force) { @@ -306,10 +315,12 @@ static void reset_job_class(Relation gs_job_attribute_rel, List *disable_job_nam ListCell *lc = NULL; foreach (lc, disable_job_names) { Datum job_name = PointerGetDatum(lfirst(lc)); + // 将指定作业的 attribute_name 属性重置为默认 job_class update_attribute(job_name, attribute_name, CStringGetTextDatum("DEFAULT_JOB_CLASS")); } } + /* * @brief search_related_jobs * search all related jobs. @@ -318,6 +329,7 @@ static void reset_job_class(Relation gs_job_attribute_rel, List *disable_job_nam * @param force * @return List* */ + //search_related_jobs 函数:用于查找所有与给定对象和属性名相关的任务,并返回任务名称列表。 static List *search_related_jobs(Relation gs_job_attribute_rel, Datum object_name, Datum attribute_name, bool force) { List *tuples = search_related_attribute(gs_job_attribute_rel, attribute_name, object_name); @@ -352,6 +364,7 @@ static List *search_related_jobs(Relation gs_job_attribute_rel, Datum object_nam * Drop inline program if exists. * @param job_name */ + //drop_inline_program 函数:删除指定任务的内联程序(如果存在)及其相关信息。 void drop_inline_program(const Datum job_name) { Datum attribute_name = CStringGetTextDatum("program_name"); @@ -380,6 +393,7 @@ void drop_inline_program(const Datum job_name) * @param force * @param simple when set to true, do not disable relatied objects */ + //drop_single_object_name 函数:删除指定的对象,并禁用所有相关对象。 static void drop_single_object_name(Datum object_name, const char *object_type, bool force) { check_object_type_matched(object_name, object_type); @@ -417,6 +431,7 @@ static void drop_single_object_name(Datum object_name, const char *object_type, * Note: * Dropping a job class requires the MANAGE SCHEDULER system privilege. */ + //此函数用于删除单个作业类。 void drop_single_job_class_internal(PG_FUNCTION_ARGS) { check_object_is_visible(PG_GETARG_DATUM(0), false); @@ -433,6 +448,7 @@ void drop_single_job_class_internal(PG_FUNCTION_ARGS) * @brief drop_single_program_internal * Drop a single program. */ + //此函数用于删除单个程序。 void drop_single_program_internal(PG_FUNCTION_ARGS) { check_object_is_visible(PG_GETARG_DATUM(0), false); @@ -459,6 +475,7 @@ void drop_single_program_internal(PG_FUNCTION_ARGS) * @brief drop_single_schedule_internal * Drop a single schedule. */ + //此函数用于删除单个调度。 void drop_single_schedule_internal(PG_FUNCTION_ARGS) { check_object_is_visible(PG_GETARG_DATUM(0), false); @@ -471,6 +488,7 @@ void drop_single_schedule_internal(PG_FUNCTION_ARGS) * @brief drop_credential_internal * Drop a single credential. */ + //此函数用于删除单个凭据。 void drop_credential_internal(PG_FUNCTION_ARGS) { if (!superuser()) { @@ -1725,4 +1743,4 @@ void remove_scheduler_objects_from_owner(const char *user_str) } list_free_deep(drop_object_names); list_free_deep(drop_object_types); -} \ No newline at end of file +} diff --git a/src/gausskernel/process/job/job_scheduler.cpp b/src/gausskernel/process/job/job_scheduler.cpp old mode 100755 new mode 100644 index 1c5b2fb1b..6e129dd10 --- a/src/gausskernel/process/job/job_scheduler.cpp +++ b/src/gausskernel/process/job/job_scheduler.cpp @@ -72,11 +72,11 @@ #include "gssignal/gs_signal.h" /* the minimum allowed time between two awakenings of the launcher */ -#define MIN_JOB_SCHEDULE_SLEEPTIME 100 /* milliseconds */ -#define MILLISECOND_PER_SECOND 1000000L /* sleep 1s when encounter with error */ -#define MILLISECOND_JOB 1000 -#define JOB_QUEUE_INTERVAL 1 /* the interval for check pg_job */ -#define UNKNOW_PID ((ThreadId)(-1)) +#define MIN_JOB_SCHEDULE_SLEEPTIME 100 /* milliseconds *///运行器唤醒的最小允许时间间隔,单位为毫秒。 +#define MILLISECOND_PER_SECOND 1000000L /* sleep 1s when encounter with error *///遇到错误时休眠1秒的时间,单位为微秒。 +#define MILLISECOND_JOB 1000//作业执行的时间,单位为毫秒。 +#define JOB_QUEUE_INTERVAL 1 /* the interval for check pg_job *///检查pg_job的间隔时间,单位为秒。 +#define UNKNOW_PID ((ThreadId)(-1))//未知进程ID。 /***************************************************************************** * PRIVATE STRUCTURE DEFINE @@ -85,20 +85,20 @@ #define DLIsTail(list, elem) (DLGetTail(list) == (elem)) #define DLIsEmpty(list) ((list) == NULL || (DLGetHead(list) == NULL && DLGetTail(list) == NULL)) -typedef struct Dlelem* DlelemPtr; +typedef struct Dlelem* DlelemPtr;//指向Dlelem结构体的指针。 static void DLInsertByOrder(Dllist* l, Dlelem* e, int (*Comparator)(const void*, const void*)); /***************************************************************************** * PRIVATE FUNCTION DEFINE ****************************************************************************/ -static void jobschd_sighup_handler(SIGNAL_ARGS); -static void jobschd_sigusr2_handler(SIGNAL_ARGS); -static void jobschd_sigterm_handler(SIGNAL_ARGS); -static void SchedulerDetermineSleep(bool canlaunch, struct timeval* nap); -static void ScanExpireJobs(); -static int JobComparator(const void* a, const void* b); -static void ActivateWorker(); -static void check_jobinfo(); +static void jobschd_sighup_handler(SIGNAL_ARGS);//SIGHUP信号处理函数。 +static void jobschd_sigusr2_handler(SIGNAL_ARGS);//SIGUSR2信号处理函数。 +static void jobschd_sigterm_handler(SIGNAL_ARGS);//SIGTERM信号处理函数。 +static void SchedulerDetermineSleep(bool canlaunch, struct timeval* nap);//根据条件决定运行器的休眠时间。 +static void ScanExpireJobs();//扫描过期的作业。 +static int JobComparator(const void* a, const void* b);//作业比较函数。 +static void ActivateWorker();//激活工作线程。 +static void check_jobinfo();//检查作业信息。 /***************************************************************************** * JOB SCHEDULER IMPLEMENTS CODE : PRIVATE @@ -113,32 +113,32 @@ static void check_jobinfo(); */ NON_EXEC_STATIC void JobScheduleMain() { - sigjmp_buf local_sigjmp_buf; - char username[NAMEDATALEN]; - char* dbname = (char*)pstrdup(DEFAULT_DATABASE); + sigjmp_buf local_sigjmp_buf; // 保存信号跳转信息的变量 + char username[NAMEDATALEN]; // 存储用户名的数组 + char* dbname = (char*)pstrdup(DEFAULT_DATABASE);// 存储默认数据库名称的指针 - /* we are a postmaster subprocess now */ + /* we are a postmaster subprocess now */ // 我们现在是一个后台进程 IsUnderPostmaster = true; - t_thrd.role = JOB_SCHEDULER; + t_thrd.role = JOB_SCHEDULER; // 线程角色设置为JOB_SCHEDULER表示正在执行作业调度器的功能 - /* reset t_thrd.proc_cxt.MyProcPid */ + /* reset t_thrd.proc_cxt.MyProcPid */ // 重置当前进程的进程ID为当前线程的ID t_thrd.proc_cxt.MyProcPid = gs_thread_self(); - t_thrd.proc_cxt.MyProgName = "JobScheduler"; - u_sess->attr.attr_common.application_name = pstrdup("JobScheduler"); + t_thrd.proc_cxt.MyProgName = "JobScheduler"; // 当前进程的程序名称设置为"JobScheduler" + u_sess->attr.attr_common.application_name = pstrdup("JobScheduler"); // 当前会话的应用程序名称设置为"JobScheduler" - /* record Start Time for logging */ + /* record Start Time for logging */ // 记录当前进程的启动时间 t_thrd.proc_cxt.MyStartTime = time(NULL); - /* Identify myself via ps */ + /* Identify myself via ps */ // 通过ps命令显示当前进程的信息 init_ps_display("job scheduler process", "", "", ""); - elog(LOG, "job scheduler started"); + elog(LOG, "job scheduler started"); // 在日志中记录作业调度器已启动 - SetProcessingMode(InitProcessing); + SetProcessingMode(InitProcessing); // 将处理模式设置为初始化模式 bool isExit = IS_PGXC_COORDINATOR && IsPostmasterEnvironment; - if (isExit) { + if (isExit) {//布尔变量isExit,判断条件是当前环境为PGXC协调器并且处于Postmaster环境 /* * If we exit, first try and clean connections and send to * pooler thread does NOT exist any more, PoolerLock of LWlock is used instead. @@ -155,7 +155,7 @@ NON_EXEC_STATIC void JobScheduleMain() * and registers it after ProcKill(), and PGXCNodeCleanAndRelease() will * be called before ProcKill(). */ - on_shmem_exit(PGXCNodeCleanAndRelease, 0); + on_shmem_exit(PGXCNodeCleanAndRelease, 0);//注册到"on_shmem_exit_list"后的函数,将在进程退出时被调用 } /* @@ -163,29 +163,50 @@ NON_EXEC_STATIC void JobScheduleMain() * backend, so we use the same signal handling. See equivalent code in * tcop/postgres.c. */ - (void)gspqsignal(SIGHUP, jobschd_sighup_handler); - (void)gspqsignal(SIGINT, StatementCancelHandler); - (void)gspqsignal(SIGTERM, jobschd_sigterm_handler); + /* 设置SIGHUP信号处理函数为jobschd_sighup_handler */ + (void)gspqsignal(SIGHUP, jobschd_sighup_handler); + + /* 设置SIGINT信号处理函数为StatementCancelHandler */ + (void)gspqsignal(SIGINT, StatementCancelHandler); + + /* 设置SIGTERM信号处理函数为jobschd_sigterm_handler */ + (void)gspqsignal(SIGTERM, jobschd_sigterm_handler); + + /* 设置SIGQUIT信号处理函数为quickdie */ + (void)gspqsignal(SIGQUIT, quickdie); + + /* 设置SIGALRM信号处理函数为handle_sig_alarm */ + (void)gspqsignal(SIGALRM, handle_sig_alarm); + + /* 忽略SIGPIPE信号 */ + (void)gspqsignal(SIGPIPE, SIG_IGN); + + /* 设置SIGUSR1信号处理函数为procsignal_sigusr1_handler */ + (void)gspqsignal(SIGUSR1, procsignal_sigusr1_handler); + + /* 设置SIGUSR2信号处理函数为jobschd_sigusr2_handler */ + (void)gspqsignal(SIGUSR2, jobschd_sigusr2_handler); + + /* 设置SIGFPE信号处理函数为FloatExceptionHandler */ + (void)gspqsignal(SIGFPE, FloatExceptionHandler); + + /* SIGCHLD信号使用默认处理方式 */ + (void)gspqsignal(SIGCHLD, SIG_DFL); + + /* 如果在Postmaster进程之下,允许使用SIGQUIT (quickdie)信号 */ + if (IsUnderPostmaster) { + (void)sigdelset(&t_thrd.libpq_cxt.BlockSig, SIGQUIT); + } + + /* 设置阻塞信号集为t_thrd.libpq_cxt.UnBlockSig */ + gs_signal_setmask(&t_thrd.libpq_cxt.UnBlockSig, NULL); + + /* 解除对SIGUSR2信号的阻塞 */ + (void)gs_signal_unblock_sigusr2(); + + /* 早期初始化 */ + BaseInit(); - (void)gspqsignal(SIGQUIT, quickdie); - (void)gspqsignal(SIGALRM, handle_sig_alarm); - - (void)gspqsignal(SIGPIPE, SIG_IGN); - (void)gspqsignal(SIGUSR1, procsignal_sigusr1_handler); - (void)gspqsignal(SIGUSR2, jobschd_sigusr2_handler); - (void)gspqsignal(SIGFPE, FloatExceptionHandler); - (void)gspqsignal(SIGCHLD, SIG_DFL); - - if (IsUnderPostmaster) { - /* We allow SIGQUIT (quickdie) at all times */ - (void)sigdelset(&t_thrd.libpq_cxt.BlockSig, SIGQUIT); - } - - gs_signal_setmask(&t_thrd.libpq_cxt.UnBlockSig, NULL); - (void)gs_signal_unblock_sigusr2(); - - /* Early initialization */ - BaseInit(); /* * Create a per-backend PGPROC struct in shared memory, except in the @@ -193,303 +214,294 @@ NON_EXEC_STATIC void JobScheduleMain() * this before we can use LWLocks (and in the EXEC_BACKEND case we already * had to do some stuff with LWLocks). */ -#ifndef EXEC_BACKEND - InitProcess(); -#endif + #ifndef EXEC_BACKEND + InitProcess(); + #endif + + /* 初始化进程 */ + // 如果没有定义EXEC_BACKEND宏,则调用InitProcess()函数进行进程初始化的工作 + + /* 使用DEFAULT_DATABASE初始化openGauss,因为无法删除它 */ + t_thrd.proc_cxt.PostInit->SetDatabaseAndUser(dbname, InvalidOid, username); + t_thrd.proc_cxt.PostInit->InitJobScheduler(); + + #ifdef PGXC /* PGXC_COORD */ + /* + * 为备份时使用的咨询锁初始化键对。 + */ + t_thrd.postmaster_cxt.xc_lockForBackupKey1 = Int32GetDatum(XC_LOCK_FOR_BACKUP_KEY_1); + t_thrd.postmaster_cxt.xc_lockForBackupKey2 = Int32GetDatum(XC_LOCK_FOR_BACKUP_KEY_2); + #endif + + /* 设置处理模式为NormalProcessing */ + SetProcessingMode(NormalProcessing); + + /* + * 创建主循环中将使用的内存上下文。 + * + * t_thrd.mem_cxt.msg_mem_cxt在每次主循环迭代(即完成对客户端的每个命令消息处理后)时重置一次。 + */ + t_thrd.mem_cxt.msg_mem_cxt = AllocSetContextCreate(t_thrd.top_mem_cxt, + "MessageContext", + ALLOCSET_DEFAULT_MINSIZE, + ALLOCSET_DEFAULT_INITSIZE, + ALLOCSET_DEFAULT_MAXSIZE); + + /* + * 创建一个内存上下文,我们将在其中执行所有工作。 + * 我们这样做是为了在错误恢复期间重置上下文,从而避免可能的内存泄漏。 + */ + t_thrd.job_cxt.JobScheduleMemCxt = AllocSetContextCreate(t_thrd.top_mem_cxt, + "Job Scheduler", + ALLOCSET_DEFAULT_MINSIZE, + ALLOCSET_DEFAULT_INITSIZE, + ALLOCSET_DEFAULT_MAXSIZE); + + t_thrd.job_cxt.ExpiredJobListCtx = AllocSetContextCreate(t_thrd.job_cxt.JobScheduleMemCxt, + "Expired Job List", + ALLOCSET_DEFAULT_MINSIZE, + ALLOCSET_DEFAULT_INITSIZE, + ALLOCSET_DEFAULT_MAXSIZE); - /* Initialize openGauss with DEFAULT_DATABASE, since it cannot be dropped */ - t_thrd.proc_cxt.PostInit->SetDatabaseAndUser(dbname, InvalidOid, username); - t_thrd.proc_cxt.PostInit->InitJobScheduler(); -#ifdef PGXC /* PGXC_COORD */ - /* - * Initialize key pair to be used as object id while using advisory lock - * for backup - */ - t_thrd.postmaster_cxt.xc_lockForBackupKey1 = Int32GetDatum(XC_LOCK_FOR_BACKUP_KEY_1); - t_thrd.postmaster_cxt.xc_lockForBackupKey2 = Int32GetDatum(XC_LOCK_FOR_BACKUP_KEY_2); -#endif + /* + * 如果遇到异常,处理将从这里恢复。 + * + * 这段代码是PostgresMain错误恢复的简化版本。 + */ + int curTryCounter; + int* oldTryCounter = NULL; + + /* 使用sigsetjmp函数设置跳转点,并检查返回值以确定是否从跳转点返回 */ + if (sigsetjmp(local_sigjmp_buf, 1) != 0) { + gstrace_tryblock_exit(true, oldTryCounter); + + /* 保存错误信息 */ + (void)MemoryContextSwitchTo(t_thrd.job_cxt.JobScheduleMemCxt); + ErrorData* edata = CopyErrorData(); + + /* 由于没有使用PG_TRY,需要手动重置错误堆栈 */ + t_thrd.log_cxt.error_context_stack = NULL; + t_thrd.log_cxt.call_stack = NULL; + + /* 清理时防止中断 */ + HOLD_INTERRUPTS(); + + /* 取消任何待处理的QueryCancel请求 */ + t_thrd.int_cxt.QueryCancelPending = false; + (void)disable_sig_alarm(true); + t_thrd.int_cxt.QueryCancelPending = false; /* 再次取消,以防超时发生 */ + + /* 将错误报告记录到服务器日志 */ + EmitErrorReport(); + + /* 中止当前事务以进行恢复 */ + AbortCurrentTransaction(); + + /* 释放lsc持有的资源 */ + AtEOXact_SysDBCache(false); + + elog(LOG, "Job scheduler encounter abnormal, detail error msg: %s.", edata->message); + + /* + * 现在回到正常的顶层上下文,并清除ErrorContext以供下次使用。 + */ + (void)MemoryContextSwitchTo(t_thrd.job_cxt.JobScheduleMemCxt); + FlushErrorState(); + + /* 刷新顶层上下文中的任何泄漏的数据 */ + MemoryContextResetAndDeleteChildren(t_thrd.job_cxt.JobScheduleMemCxt); + t_thrd.job_cxt.ExpiredJobList = NULL; + t_thrd.job_cxt.ExpiredJobListCtx = NULL; + + t_thrd.job_cxt.ExpiredJobListCtx = AllocSetContextCreate(t_thrd.job_cxt.JobScheduleMemCxt, + "Expired Job List", + ALLOCSET_DEFAULT_MINSIZE, + ALLOCSET_DEFAULT_INITSIZE, + ALLOCSET_DEFAULT_MAXSIZE); + + /* 现在可以再次允许中断了 */ + RESUME_INTERRUPTS(); + + /* + * 在发生错误后至少休眠1秒。 + * 我们不希望错误日志文件被填满。 + */ + pg_usleep(MILLISECOND_PER_SECOND); + } - SetProcessingMode(NormalProcessing); + oldTryCounter = gstrace_tryblock_entry(&curTryCounter); // 记录当前try块被调用的次数 - /* - * Create the memory context we will use in the main loop. - * - * t_thrd.mem_cxt.msg_mem_cxt is reset once per iteration of the main loop, ie, upon - * completion of processing of each command message from the client. - */ - t_thrd.mem_cxt.msg_mem_cxt = AllocSetContextCreate(t_thrd.top_mem_cxt, - "MessageContext", - ALLOCSET_DEFAULT_MINSIZE, - ALLOCSET_DEFAULT_INITSIZE, - ALLOCSET_DEFAULT_MAXSIZE); + /* 设置异常处理跳转点 */ + t_thrd.log_cxt.PG_exception_stack = &local_sigjmp_buf; + t_thrd.job_cxt.JobScheduleShmem->jsch_pid = t_thrd.proc_cxt.MyProcPid; // 在JobScheduler共享内存中设置进程ID + + /* 在PgBackendStatus数组中报告该后台进程 */ + u_sess->proc_cxt.MyProcPort->SessionStartTime = GetCurrentTimestamp(); // 设置会话开始时间 + pgstat_bestart(); // 开始统计信息收集 + pgstat_report_appname("JobScheduler"); // 报告应用程序名称为"JobScheduler" + pgstat_report_activity(STATE_IDLE, NULL); // 报告活动状态为空闲 + + if (t_thrd.job_cxt.got_SIGTERM) { + /* 正常退出 */ + ereport(LOG, (errmsg("job scheduler is shutting down"))); + + t_thrd.job_cxt.JobScheduleShmem->jsch_pid = 0; // 将JobScheduler共享内存中的进程ID设置为0 + + proc_exit(0); // 进程退出 + } + + /* + * 创建资源所有者以跟踪资源(目前只有缓冲区引用)。 + */ + t_thrd.utils_cxt.CurrentResourceOwner = ResourceOwnerCreate(NULL, "Job Scheduler", + THREAD_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_EXECUTOR)); // 创建当前资源所有者,并命名为"Job Scheduler" + + /* 获取分类的节点OID列表,用于同步作业状态信息 */ + exec_init_poolhandles(); // 初始化节点的连接句柄 + + (void)MemoryContextSwitchTo(t_thrd.job_cxt.JobScheduleMemCxt); // 切换到JobScheduler内存上下文 + + /* + * 将所有作业的作业状态从'r'更新为'f', + * 当这些作业正在运行时,可能所有节点都已重置。 + */ + check_jobinfo(); // 检查作业信息 - /* - * Create a memory context that we will do all our work in. We do this so - * that we can reset the context during error recovery and thereby avoid - * possible memory leaks. - */ - t_thrd.job_cxt.JobScheduleMemCxt = AllocSetContextCreate(t_thrd.top_mem_cxt, - "Job Scheduler", - ALLOCSET_DEFAULT_MINSIZE, - ALLOCSET_DEFAULT_INITSIZE, - ALLOCSET_DEFAULT_MAXSIZE); - - t_thrd.job_cxt.ExpiredJobListCtx = AllocSetContextCreate(t_thrd.job_cxt.JobScheduleMemCxt, - "Expired Job List", - ALLOCSET_DEFAULT_MINSIZE, - ALLOCSET_DEFAULT_INITSIZE, - ALLOCSET_DEFAULT_MAXSIZE); - - /* - * If an exception is encountered, processing resumes here. - * - * This code is a stripped down version of PostgresMain error recovery. - */ - int curTryCounter; - int* oldTryCounter = NULL; - if (sigsetjmp(local_sigjmp_buf, 1) != 0) { - gstrace_tryblock_exit(true, oldTryCounter); - - /* Save error info */ - (void)MemoryContextSwitchTo(t_thrd.job_cxt.JobScheduleMemCxt); - ErrorData* edata = CopyErrorData(); - - /* since not using PG_TRY, must reset error stack by hand */ - t_thrd.log_cxt.error_context_stack = NULL; - - t_thrd.log_cxt.call_stack = NULL; - - /* Prevents interrupts while cleaning up */ - HOLD_INTERRUPTS(); - - /* Forget any pending QueryCancel request */ - t_thrd.int_cxt.QueryCancelPending = false; - (void)disable_sig_alarm(true); - t_thrd.int_cxt.QueryCancelPending = false; /* again in case timeout occurred */ - - /* Report the error to the server log */ - EmitErrorReport(); - - /* Abort the current transaction in order to recover */ - AbortCurrentTransaction(); - - /* release resource held by lsc */ - AtEOXact_SysDBCache(false); - - elog(LOG, "Job scheduler encounter abnormal, detail error msg: %s.", edata->message); - - /* - * Now return to normal top-level context and clear ErrorContext for - * next time. - */ - (void)MemoryContextSwitchTo(t_thrd.job_cxt.JobScheduleMemCxt); - FlushErrorState(); - - /* Flush any leaked data in the top-level context */ - MemoryContextResetAndDeleteChildren(t_thrd.job_cxt.JobScheduleMemCxt); - t_thrd.job_cxt.ExpiredJobList = NULL; - t_thrd.job_cxt.ExpiredJobListCtx = NULL; - - t_thrd.job_cxt.ExpiredJobListCtx = AllocSetContextCreate(t_thrd.job_cxt.JobScheduleMemCxt, - "Expired Job List", - ALLOCSET_DEFAULT_MINSIZE, - ALLOCSET_DEFAULT_INITSIZE, - ALLOCSET_DEFAULT_MAXSIZE); - - /* Now we can allow interrupts again */ - RESUME_INTERRUPTS(); - - /* - * Sleep at least 1 second after any error. We don't want to be - * filling the error logs as fast as we can. - */ - pg_usleep(MILLISECOND_PER_SECOND); - } - oldTryCounter = gstrace_tryblock_entry(&curTryCounter); - - /* We can now handle ereport(ERROR) */ - t_thrd.log_cxt.PG_exception_stack = &local_sigjmp_buf; - t_thrd.job_cxt.JobScheduleShmem->jsch_pid = t_thrd.proc_cxt.MyProcPid; - - /* report this backend in the PgBackendStatus array */ - u_sess->proc_cxt.MyProcPort->SessionStartTime = GetCurrentTimestamp(); - pgstat_bestart(); - pgstat_report_appname("JobScheduler"); - pgstat_report_activity(STATE_IDLE, NULL); - - if (t_thrd.job_cxt.got_SIGTERM) { - /* Normal exit */ - ereport(LOG, (errmsg("job scheduler is shutting down"))); - - t_thrd.job_cxt.JobScheduleShmem->jsch_pid = 0; - - proc_exit(0); - } - - /* - * Create a resource owner to keep track of our resources (currently only - * buffer pins). - */ - t_thrd.utils_cxt.CurrentResourceOwner = ResourceOwnerCreate(NULL, "Job Scheduler", - THREAD_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_EXECUTOR)); - - /* Get classified list of node Oids for syschronise th job status info. */ - exec_init_poolhandles(); - - (void)MemoryContextSwitchTo(t_thrd.job_cxt.JobScheduleMemCxt); - - /* - * Update all the jobs's job_status from 'r' to 'f', - * may be all nodes have reseted when these jobs is under running. - */ - check_jobinfo(); - - /* Main loop */ - for (;;) { - /* close xlog file fd if any */ - CloseXlogFilesAtThreadExit(); - struct timeval nap; - TimestampTz current_time = 0; - bool can_launch = false; - int ret; - ResourceOwner save = t_thrd.utils_cxt.CurrentResourceOwner; - int4 canceled_job_id = -1; - - /* calculate sleep time, we'd like to sleep before the first launch of a child process */ - can_launch = - t_thrd.job_cxt.JobScheduleShmem->jsch_freeWorkers != NULL && !DLIsEmpty(t_thrd.job_cxt.ExpiredJobList); - SchedulerDetermineSleep(can_launch, &nap); - - /* - * Wait until naptime expires or we get some type of signal (all the - * signal handlers will wake us by calling SetLatch). - */ - ret = WaitLatch(&t_thrd.proc->procLatch, - WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH, - (nap.tv_sec * 1000L) + (nap.tv_usec / 1000L)); - - ResetLatch(&t_thrd.proc->procLatch); - - /* Process sinval catchup interrupts that happened while sleeping */ - ProcessCatchupInterrupt(); - - /* - * Emergency bailout if postmaster has died. This is to avoid the - * necessity for manual cleanup of all postmaster children. - */ - if ((unsigned int)ret & WL_POSTMASTER_DEATH) { - elog(LOG, "Job scheduler shutting down with exit code 1"); - proc_exit(1); - } - - /* the normal shutdown case */ - if (t_thrd.job_cxt.got_SIGTERM) - break; - - pgstat_report_activity(STATE_RUNNING, NULL); - if (t_thrd.job_cxt.got_SIGHUP) { - t_thrd.job_cxt.got_SIGHUP = false; - (void)MemoryContextSwitchTo(t_thrd.job_cxt.JobScheduleMemCxt); - ProcessConfigFile(PGC_SIGHUP); - } - - /* A job worker finished, or postmaster signalled failure to start a worker */ - if (t_thrd.job_cxt.got_SIGUSR2) { - t_thrd.job_cxt.got_SIGUSR2 = false; - - /* if postmaster fork job_worker failed, we had better to try again */ - if (t_thrd.job_cxt.JobScheduleShmem->jsch_signal[ForkJobWorkerFailed]) { - t_thrd.job_cxt.JobScheduleShmem->jsch_signal[ForkJobWorkerFailed] = false; - pg_usleep(MILLISECOND_PER_SECOND); /* sleep 1s */ - SendPostmasterSignal(PMSIGNAL_START_JOB_WORKER); - continue; - } - } - if (u_sess->attr.attr_sql.enable_prevent_job_task_startup) { - /* prevent to active job worker in config file ? */ - continue; - } - current_time = GetCurrentTimestamp(); - LWLockAcquire(JobShmemLock, LW_SHARED); - - can_launch = (t_thrd.job_cxt.JobScheduleShmem->jsch_freeWorkers != NULL); - - if (t_thrd.job_cxt.JobScheduleShmem->jsch_startingWorker != NULL) { - int waittime; - JobWorkerInfo worker = t_thrd.job_cxt.JobScheduleShmem->jsch_startingWorker; - - /* - * We can't start another job worker when another one is still - * starting up (or failed while doing so), so just sleep for a bit - * more; that worker will wake us up again as soon as it's ready. - * We will only wait job_queue_interval seconds (up to a maximum - * of 60 seconds) for this to happen however. Note that failure - * to connect to a particular database is not a problem here, - * because the worker removes itself from the startingWorker - * pointer before trying to connect. Problems detected by the - * postmaster (like fork() failure) are also reported and handled - * differently. The only problems that may cause this code to - * fire are errors in the earlier sections of JobExecuteWorkerMain, - * before the worker removes the JobWorkerInfo from the - * startingWorker pointer. - */ - waittime = JOB_QUEUE_INTERVAL * MILLISECOND_JOB; - if (TimestampDifferenceExceeds(worker->job_launchtime, current_time, waittime)) { - LWLockRelease(JobShmemLock); - LWLockAcquire(JobShmemLock, LW_EXCLUSIVE); - - /* - * No other process can put a worker in starting mode, so if - * startingWorker is still INVALID after exchanging our lock, - * we assume it's the same one we saw above (so we don't - * recheck the launch time). - */ - if (t_thrd.job_cxt.JobScheduleShmem->jsch_startingWorker != NULL) { - canceled_job_id = worker->job_id; - worker = t_thrd.job_cxt.JobScheduleShmem->jsch_startingWorker; - worker->job_dboid = InvalidOid; - worker->job_id = 0; - worker->job_launchtime = 0; - worker->job_links.next = (SHM_QUEUE*)(t_thrd.job_cxt.JobScheduleShmem->jsch_freeWorkers); - t_thrd.job_cxt.JobScheduleShmem->jsch_freeWorkers = worker; - t_thrd.job_cxt.JobScheduleShmem->jsch_startingWorker = NULL; - } - } else { - can_launch = false; - } - } - LWLockRelease(JobShmemLock); /* either shared or exclusive */ - - if (canceled_job_id > 0) { - ereport(WARNING, - (errmsg("Job worker with job id:%d took too long " - "time to start, so canceled it", - canceled_job_id))); - } - /* If we can't do anything, just go back to sleep */ - if (!can_launch || u_sess->attr.attr_sql.enable_prevent_job_task_startup) { - continue; - } - - /* Get expired job */ - if (DLIsEmpty(t_thrd.job_cxt.ExpiredJobList)) { - ScanExpireJobs(); - } - - t_thrd.utils_cxt.CurrentResourceOwner = save; - /* To start a new worker thread for execute job. */ - ActivateWorker(); - } - - /* Normal exit */ - ereport(LOG, (errmsg("job scheduler is shutting down"))); - - t_thrd.job_cxt.JobScheduleShmem->jsch_pid = 0; - - proc_exit(0); +//------------------------------------------------------------------------------------------------------------------------------ + /* 主循环 */ + for (;;) { + /* 关闭已存在的xlog文件句柄 */ + CloseXlogFilesAtThreadExit(); + struct timeval nap; + TimestampTz current_time = 0; + bool can_launch = false; + int ret; + ResourceOwner save = t_thrd.utils_cxt.CurrentResourceOwner; + int4 canceled_job_id = -1; + + /* 计算睡眠时间,在启动子进程前我们希望先休眠一段时间 */ + can_launch = + t_thrd.job_cxt.JobScheduleShmem->jsch_freeWorkers != NULL && !DLIsEmpty(t_thrd.job_cxt.ExpiredJobList); + SchedulerDetermineSleep(can_launch, &nap); + + /* + * 等待直到naptime过期或者接收到信号(所有信号处理程序会调用SetLatch唤醒我们)。 + */ + ret = WaitLatch(&t_thrd.proc->procLatch, + WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH, + (nap.tv_sec * 1000L) + (nap.tv_usec / 1000L)); + + ResetLatch(&t_thrd.proc->procLatch); + + /* 处理在休眠期间发生的sinval追赶中断 */ + ProcessCatchupInterrupt(); + + /* + * 如果postmaster已经终止,紧急退出。这是为了避免手动清理所有postmaster子进程的必要性。 + */ + if ((unsigned int)ret & WL_POSTMASTER_DEATH) { + elog(LOG, "Job scheduler shutting down with exit code 1"); + proc_exit(1); + } + + /* 正常的关闭情况 */ + if (t_thrd.job_cxt.got_SIGTERM) + break; + + pgstat_report_activity(STATE_RUNNING, NULL); + if (t_thrd.job_cxt.got_SIGHUP) { + t_thrd.job_cxt.got_SIGHUP = false; + (void)MemoryContextSwitchTo(t_thrd.job_cxt.JobScheduleMemCxt); + ProcessConfigFile(PGC_SIGHUP); + } + + /* 一个作业工作者已经完成,或者postmaster发出启动工作者失败的信号 */ + if (t_thrd.job_cxt.got_SIGUSR2) { + t_thrd.job_cxt.got_SIGUSR2 = false; + + /* 如果postmaster fork job_worker失败,最好尝试重新启动 */ + if (t_thrd.job_cxt.JobScheduleShmem->jsch_signal[ForkJobWorkerFailed]) { + t_thrd.job_cxt.JobScheduleShmem->jsch_signal[ForkJobWorkerFailed] = false; + pg_usleep(MILLISECOND_PER_SECOND); /* 休眠1秒 */ + SendPostmasterSignal(PMSIGNAL_START_JOB_WORKER); + continue; + } + } + if (u_sess->attr.attr_sql.enable_prevent_job_task_startup) { + /* 在配置文件中禁止激活作业工作者吗? */ + continue; + } + current_time = GetCurrentTimestamp(); + LWLockAcquire(JobShmemLock, LW_SHARED); + + can_launch = (t_thrd.job_cxt.JobScheduleShmem->jsch_freeWorkers != NULL); + + if (t_thrd.job_cxt.JobScheduleShmem->jsch_startingWorker != NULL) { + int waittime; + JobWorkerInfo worker = t_thrd.job_cxt.JobScheduleShmem->jsch_startingWorker; + + /* + * 当另一个工作者正在启动(或者在启动过程中失败)时,我们不能启动另一个作业工作者,所以稍微休眠一会儿; + * 当他准备好后,他会再次唤醒我们。不过我们只会等待job_queue_interval秒钟(最多60秒)。 + * 请注意,连接到特定数据库失败不是问题,因为工作者在尝试连接之前会从startingWorker指针中删除自己。 + * 由postmaster检测到的问题(例如fork()失败)会以不同的方式报告和处理。 + * 只有在JobExecuteWorkerMain的早期部分发生错误时,此代码才会触发,即在工作者将JobWorkerInfo从startingWorker指针中删除之前。 + */ + waittime = JOB_QUEUE_INTERVAL * MILLISECOND_JOB; + if (TimestampDifferenceExceeds(worker->job_launchtime, current_time, waittime)) { + LWLockRelease(JobShmemLock); + LWLockAcquire(JobShmemLock, LW_EXCLUSIVE); + + /* + * 在我们获得锁之后,没有其他进程可以将工作者置于启动模式, + * 所以如果在交换锁之后startingWorker仍然无效,我们认为它与上面看到的相同(因此我们不重新检查启动时间)。 + */ + if (t_thrd.job_cxt.JobScheduleShmem->jsch_startingWorker != NULL) { + canceled_job_id = worker->job_id; + worker = t_thrd.job_cxt.JobScheduleShmem->jsch_startingWorker; + worker->job_dboid = InvalidOid; + worker->job_id = 0; + worker->job_launchtime = 0; + worker->job_links.next = (SHM_QUEUE*)(t_thrd.job_cxt.JobScheduleShmem->jsch_freeWorkers); + t_thrd.job_cxt.JobScheduleShmem->jsch_freeWorkers = worker; + t_thrd.job_cxt.JobScheduleShmem->jsch_startingWorker = NULL; + } + } else { + can_launch = false; + } + } + LWLockRelease(JobShmemLock); /* 释放共享锁或独占锁 */ + + if (canceled_job_id > 0) { + ereport(WARNING, + (errmsg("Job worker with job id:%d took too long " + "time to start, so canceled it", + canceled_job_id))); + } + /* 如果我们无法做任何事情,就继续休眠 */ + if (!can_launch || u_sess->attr.attr_sql.enable_prevent_job_task_startup) { + continue; + } + + /* 获取到期的作业 */ + if (DLIsEmpty(t_thrd.job_cxt.ExpiredJobList)) { + ScanExpireJobs(); + } + + t_thrd.utils_cxt.CurrentResourceOwner = save; + /* 启动一个新的工作者线程来执行作业 */ + ActivateWorker(); + } + + /* 正常退出 */ + ereport(LOG, (errmsg("job scheduler is shutting down"))); + + t_thrd.job_cxt.JobScheduleShmem->jsch_pid = 0; + + proc_exit(0); } - +//------------------------------------------------------------------------------------------------------------------------------ /* * Description: Receive SIGHUP and set flag to re-read config file at next convenient time. * @@ -497,11 +509,15 @@ NON_EXEC_STATIC void JobScheduleMain() * @in SIGNAL_ARGS: the args of signal. * Returns: void */ +/* SIGHUP信号处理程序 */ static void jobschd_sighup_handler(SIGNAL_ARGS) { int save_errno = errno; + /* 设置标志,表示接收到SIGHUP信号 */ t_thrd.job_cxt.got_SIGHUP = true; + + /* 如果当前进程存在,则设置进程的Latch,以便唤醒进程 */ if (t_thrd.proc) { SetLatch(&t_thrd.proc->procLatch); } @@ -516,12 +532,18 @@ static void jobschd_sighup_handler(SIGNAL_ARGS) * @in SIGNAL_ARGS: the args of signal. * Returns: void */ +/* SIGUSR2信号处理程序 */ static void jobschd_sigusr2_handler(SIGNAL_ARGS) { int save_errno = errno; + + /* 记录日志,表示接收到SIGUSR2信号并且作业工作者启动失败 */ elog(LOG, "Job scheduler received sigusr2 when job worker startup failed."); + /* 设置标志,表示接收到SIGUSR2信号 */ t_thrd.job_cxt.got_SIGUSR2 = true; + + /* 如果当前进程存在,则设置进程的Latch,以便唤醒进程 */ if (t_thrd.proc) { SetLatch(&t_thrd.proc->procLatch); } @@ -536,9 +558,13 @@ static void jobschd_sigusr2_handler(SIGNAL_ARGS) * @in SIGNAL_ARGS: the args of signal. * Returns: void */ + /* sigterm信号处理程序 */ static void jobschd_sigterm_handler(SIGNAL_ARGS) { + /* 设置标志,表示接收到SIGTERM信号 */ t_thrd.job_cxt.got_SIGTERM = true; + + /* 退出进程,并传递给die函数的信号参数 */ die(postgres_signal_arg); } @@ -553,12 +579,13 @@ static void jobschd_sigterm_handler(SIGNAL_ARGS) static void SchedulerDetermineSleep(bool canlaunch, struct timeval* nap) { if (!canlaunch) { + /* 如果无法启动作业工作者,则休眠 JOB_QUEUE_INTERVAL 秒 */ nap->tv_sec = JOB_QUEUE_INTERVAL; nap->tv_usec = 0; } else { - /* Sleep time should ensure the job scheduler send signal to pm to start jobworker. */ + /* 否则,休眠时间应该足够长,以确保作业调度程序发送信号给 postmaster 启动作业工作者 */ nap->tv_sec = 0; - nap->tv_usec = MIN_JOB_SCHEDULE_SLEEPTIME * 1000; /* 0.1s */ + nap->tv_usec = MIN_JOB_SCHEDULE_SLEEPTIME * 1000; /* 0.1秒 */ } } @@ -576,22 +603,25 @@ void DLInsertByOrder(Dllist* list, Dlelem* newElem, int (*Comparator)(const void DlelemPtr elem = DLGetHead(list); if (NULL == elem) { + /* 如果链表为空,则将新元素添加到链表头部 */ DLAddHead(list, newElem); return; } while (elem != NULL) { if (DLIsHead(list, elem) && Comparator(elem->dle_val, newElem->dle_val) < 0) { + /* 如果当前元素是链表头部元素,并且比新元素小,则将新元素插入到链表头部 */ DLAddHead(list, newElem); break; } if (DLIsTail(list, elem) && Comparator(elem->dle_val, newElem->dle_val) > 0) { + /* 如果当前元素是链表尾部元素,并且比新元素大,则将新元素插入到链表尾部 */ DLAddTail(list, newElem); break; } - /* Add new element next to current element. */ + /* 将新元素插入到当前元素的下一个位置 */ if (Comparator(elem->dle_val, newElem->dle_val) > 0 && Comparator(elem->dle_next->dle_val, newElem->dle_val) < 0) { newElem->dle_prev = elem; @@ -604,25 +634,27 @@ void DLInsertByOrder(Dllist* list, Dlelem* newElem, int (*Comparator)(const void elem = DLGetSucc(elem); } } - #define JOB_WORKER_RUNNING 2 #define JOB_WORKER_STARTING 1 #define JOB_WORKER_INACTIVE 0 +/* 获取作业状态的函数 */ static int GetJobStatus(int4 jobid) { SHM_QUEUE* queue = NULL; SHM_QUEUE* nextPtr = NULL; + /* 共享内存锁定 */ LWLockAcquire(JobShmemLock, LW_SHARED); - queue = &t_thrd.job_cxt.JobScheduleShmem->jsch_runningWorkers; + queue = &t_thrd.job_cxt.JobScheduleShmem->jsch_runningWorkers; // 获取正在运行的作业队列 + nextPtr = queue; do { JobWorkerInfo worker = (JobWorkerInfo)nextPtr; if (worker->job_id == jobid) { - /* job is executing */ + /* 作业正在执行 */ LWLockRelease(JobShmemLock); return JOB_WORKER_RUNNING; } @@ -631,36 +663,47 @@ static int GetJobStatus(int4 jobid) if (t_thrd.job_cxt.JobScheduleShmem->jsch_startingWorker && t_thrd.job_cxt.JobScheduleShmem->jsch_startingWorker->job_id == jobid) { - /* job is ready to execute */ + /* 作业准备执行 */ LWLockRelease(JobShmemLock); return JOB_WORKER_STARTING; } + /* 作业未激活 */ LWLockRelease(JobShmemLock); return JOB_WORKER_INACTIVE; } static inline bool IsExecuteOnCurrentNode(const char* executeNodeName) { + // 检查作业是否在当前节点执行 + + // 检查执行节点名称是否与当前节点名称相同 if (strcmp(executeNodeName, g_instance.attr.attr_common.PGXCNodeName) == 0) return true; + // 检查执行节点名称是否为特殊值"ALL" if (strcmp(executeNodeName, PGJOB_TYPE_ALL) == 0) return true; + // 如果当前节点是协调器 if (IS_PGXC_COORDINATOR) { + // 检查执行节点名称是否为特殊值"ALL_CN" if (strcmp(executeNodeName, PGJOB_TYPE_ALL_CN) == 0) { return true; - } else if (strcmp(executeNodeName, PGJOB_TYPE_CCN) == 0) { + } + // 检查执行节点名称是否为特殊值"CCN",并且当前节点是中心节点 + else if (strcmp(executeNodeName, PGJOB_TYPE_CCN) == 0) { return is_pgxc_central_nodename(g_instance.attr.attr_common.PGXCNodeName); } else { return false; } } + // 如果当前节点是数据节点,并且执行节点名称为特殊值"ALL_DN" if (IS_PGXC_DATANODE && strcmp(executeNodeName, PGJOB_TYPE_ALL_DN) == 0) return true; + // 默认情况下,作业不在当前节点执行 return false; } @@ -678,110 +721,110 @@ static bool SkipSchedulerJob(Datum *values, bool *nulls, Timestamp curtime) { Assert(values != NULL); Assert(nulls != NULL); - /* do not handle non-scheduler jobs */ + + // 不处理非调度作业 if (nulls[Anum_pg_job_job_name]) { return false; } - /* expired job, need to drop even it is disabled */ + // 过期的作业,即使已禁用也需要删除 if (DatumGetBool(DirectFunctionCall2(timestamp_ge, curtime, values[Anum_pg_job_end_date - 1]))) { return false; } - /* disabled jobs */ + // 禁用的作业 if (!nulls[Anum_pg_job_enable - 1] && !DatumGetBool(values[Anum_pg_job_enable - 1])) { - return true; /* skip here to avoid further overhead */ + return true; // 在此跳过以避免进一步开销 } return false; } - /* * Description: Find expire jobs and insert to job queue for execute. * * Returns: void */ + static void ScanExpireJobs() { - Relation pg_job_tbl = NULL; - TableScanDesc scan = NULL; - HeapTuple tuple = NULL; - MemoryContext oldCtx = NULL; - int jobStatus = JOB_WORKER_INACTIVE; - Datum curtime = DirectFunctionCall1(timestamptz_timestamp, GetCurrentTimestamp()); + Relation pg_job_tbl = NULL; // 声明关系变量 pg_job_tbl,初始化为 NULL + TableScanDesc scan = NULL; // 声明表扫描描述符 scan,初始化为 NULL + HeapTuple tuple = NULL; // 声明堆元组变量 tuple,初始化为 NULL + MemoryContext oldCtx = NULL; // 声明内存上下文变量 oldCtx,初始化为 NULL + int jobStatus = JOB_WORKER_INACTIVE; // 声明作业状态变量 jobStatus,初始化为 JOB_WORKER_INACTIVE + Datum curtime = DirectFunctionCall1(timestamptz_timestamp, GetCurrentTimestamp()); // 获取当前时间并转换为 Datum 类型 - StartTransactionCommand(); + StartTransactionCommand(); // 开始事务 - pg_job_tbl = heap_open(PgJobRelationId, AccessShareLock); - scan = tableam_scan_begin(pg_job_tbl, SnapshotNow, 0, NULL); + pg_job_tbl = heap_open(PgJobRelationId, AccessShareLock); // 打开表 PgJobRelationId 并获取关系对象 pg_job_tbl + scan = tableam_scan_begin(pg_job_tbl, SnapshotNow, 0, NULL); // 开始对关系进行表扫描,返回扫描描述符 scan - MemoryContextReset(t_thrd.job_cxt.ExpiredJobListCtx); - oldCtx = MemoryContextSwitchTo(t_thrd.job_cxt.ExpiredJobListCtx); - /* Build a new job list if it is null. */ - t_thrd.job_cxt.ExpiredJobList = DLNewList(); + MemoryContextReset(t_thrd.job_cxt.ExpiredJobListCtx); // 重置内存上下文 ExpiredJobListCtx + oldCtx = MemoryContextSwitchTo(t_thrd.job_cxt.ExpiredJobListCtx); // 切换到内存上下文 ExpiredJobListCtx + /* 如果已过期作业列表为空,则构建一个新的作业列表。 */ + t_thrd.job_cxt.ExpiredJobList = DLNewList(); // 创建一个新的双向链表作为已过期作业列表 while (HeapTupleIsValid(tuple = (HeapTuple) tableam_scan_getnexttuple(scan, ForwardScanDirection))) { - Form_pg_job pg_job = (Form_pg_job)GETSTRUCT(tuple); - Datum values[Natts_pg_job]; - bool nulls[Natts_pg_job]; - char status = pg_job->job_status; - int64 jobID = pg_job->job_id; + Form_pg_job pg_job = (Form_pg_job)GETSTRUCT(tuple); // 获取堆元组中的作业结构体 + Datum values[Natts_pg_job]; // 声明 values 数组,用于存储作业属性值 + bool nulls[Natts_pg_job]; // 声明 nulls 数组,用于存储作业属性是否为空的标志位 + char status = pg_job->job_status; // 获取作业状态 + int64 jobID = pg_job->job_id; // 获取作业ID - get_job_values(jobID, tuple, pg_job_tbl, values, nulls); + get_job_values(jobID, tuple, pg_job_tbl, values, nulls); // 获取作业的属性值和空值标志位 - /* dbms schedule creates a job but dont enable it */ + /* 如果是跳过的调度作业,则继续下一次循环 */ if (SkipSchedulerJob(values, nulls, curtime)) { continue; } - /* handle cases - ALL_NODE/ALL_CN/ALL_DN/CCN specific node */ + /* 处理 ALL_NODE/ALL_CN/ALL_DN/CCN 特定节点的情况 */ if (!IsExecuteOnCurrentNode(pg_job->node_name.data)) { continue; } + /* 如果当前时间小于下次运行时间,则跳过 */ if (false == DatumGetBool(DirectFunctionCall2(timestamp_gt, curtime, values[Anum_pg_job_next_run_date - 1]))) { - /* skip since it doesnot reach book time */ continue; } - jobStatus = GetJobStatus(jobID); - if (PGJOB_RUN_STATUS == status) { - if (JOB_WORKER_INACTIVE != jobStatus) { - /* skip since job is active */ + jobStatus = GetJobStatus(jobID); // 获取作业的状态 + if (PGJOB_RUN_STATUS == status) { // 如果作业状态为运行状态 + if (JOB_WORKER_INACTIVE != jobStatus) { // 如果作业不是非活跃状态,则跳过 continue; } - /* ready to execute the job since it is not on executing */ - } else if (PGJOB_ABORT_STATUS == status) { - /* skip since the job is broken */ + /* 准备执行作业,因为它不在执行中 */ + } else if (PGJOB_ABORT_STATUS == status) { // 如果作业状态为中止状态,则跳过 continue; } else { - /* ready to execute the job */ + /* 准备执行作业 */ Assert(PGJOB_FAIL_STATUS == pg_job->job_status || PGJOB_SUCC_STATUS == pg_job->job_status); - if (JOB_WORKER_RUNNING == jobStatus) { + if (JOB_WORKER_RUNNING == jobStatus) { // 如果作业状态为运行中 /* - * 1. skip long time job check since job will be finished soon - * 2. skip do the job since job is running + * 1. 跳过长时间作业检查,因为作业即将完成 + * 2. 跳过作业执行,因为作业正在运行 */ continue; - } else if (JOB_WORKER_STARTING == jobStatus) { + } else if (JOB_WORKER_STARTING == jobStatus) { // 如果作业状态为启动中 ereport(WARNING, (errmsg("[job id %ld] worker is in risk of startup timeout", jobID))); - /* skip since job woker is starting */ + /* 跳过,因为作业工作者正在启动 */ continue; } else { Assert(JOB_WORKER_INACTIVE == jobStatus); - /* ready to execute the job since it is not on executing */ + /* 准备执行作业,因为它不在执行中 */ } } - Oid dboid = get_database_oid(NameStr(pg_job->dbname), true); + Oid dboid = get_database_oid(NameStr(pg_job->dbname), true); // 获取作业所属数据库的 OID if (!OidIsValid(dboid)) { - /* skip since the database of job does not exist */ + /* 跳过,因为作业所属的数据库不存在 */ ereport(LOG, (errcode(ERRCODE_UNDEFINED_DATABASE), errmsg("database \"%s\" of job %ld does not exist", NameStr(pg_job->dbname), jobID))); continue; } + // 创建 JobInfo 对象并进行初始化 JobInfo jobInfo = (JobInfoData*)palloc0(sizeof(JobInfoData)); jobInfo->job_id = jobID; jobInfo->job_oid = HeapTupleGetOid(tuple); @@ -790,16 +833,19 @@ static void ScanExpireJobs() jobInfo->node_name = pg_job->node_name; jobInfo->last_start_date = (nulls[Anum_pg_job_last_start_date - 1] ? 0 : values[Anum_pg_job_last_start_date - 1]); + + // 将 JobInfo 对象按照顺序插入已过期作业列表中 DLInsertByOrder(t_thrd.job_cxt.ExpiredJobList, DLNewElem(jobInfo), JobComparator); } - (void)MemoryContextSwitchTo(oldCtx); - tableam_scan_end(scan); - heap_close(pg_job_tbl, AccessShareLock); + (void)MemoryContextSwitchTo(oldCtx); // 切换回旧的内存上下文 + tableam_scan_end(scan); // 结束表扫描 + heap_close(pg_job_tbl, AccessShareLock); // 关闭关系对象 pg_job_tbl - CommitTransactionCommand(); + CommitTransactionCommand(); // 提交事务 } + /* * Description: Compare with last_start_date and decide the smaller will insert previes. * @@ -810,30 +856,33 @@ static void ScanExpireJobs() */ static int JobComparator(const void* baseOne, const void* newOne) { + // 比较两个 JobInfo 对象的 last_start_date 属性值 if (((const JobInfo)newOne)->last_start_date <= ((const JobInfo)baseOne)->last_start_date) { - return -1; + return -1; // 如果 newOne 的 last_start_date 小于等于 baseOne 的 last_start_date,返回 -1 } else { - return 1; + return 1; // 如果 newOne 的 last_start_date 大于 baseOne 的 last_start_date,返回 1 } } + /* * Description: Send SIGUSR2 to postmaster and start a new job worker. * * Returns: void */ + //激活作业工作者 static void ActivateWorker() { JobWorkerInfo worker = NULL; JobInfo jobInfo = NULL; DlelemPtr head_job = NULL; + // 如果已过期作业列表为空,则立即返回,主循环会在一段时间后再次获取作业 if (DLIsEmpty(t_thrd.job_cxt.ExpiredJobList)) { - /* return immediately, after a period, main loop will fetch jobs again */ return; } - /* return quickly when there are no free job workers */ + // 当没有空闲的作业工作者时,快速返回 LWLockAcquire(JobShmemLock, LW_SHARED); worker = t_thrd.job_cxt.JobScheduleShmem->jsch_freeWorkers; if (NULL == worker) { @@ -843,13 +892,11 @@ static void ActivateWorker() } LWLockRelease(JobShmemLock); - /* remove and return the head job from ExpiredJobList */ + // 从过期作业列表中移除并返回首个作业 head_job = DLRemHead(t_thrd.job_cxt.ExpiredJobList); if (NULL == head_job || NULL == head_job->dle_val) { /* - * just throw an error if ExpiredJobList is invalid., and execution - * environment of the scheduler will be reset in function - * JobScheduleMain + * 如果过期作业列表无效,抛出错误,调度器的执行环境将在 JobScheduleMain 函数中重置 */ ereport(ERROR, ((errcode(ERRCODE_INVALID_STATUS), @@ -859,17 +906,18 @@ static void ActivateWorker() LWLockAcquire(JobShmemLock, LW_EXCLUSIVE); - /* Get a worker from freelist, and start it */ + // 从空闲作业工作者列表中获取一个工作者,并启动它 worker = t_thrd.job_cxt.JobScheduleShmem->jsch_freeWorkers; if (NULL == worker) { LWLockRelease(JobShmemLock); - /* log error, and proc exit */ + // 记录错误日志,并退出进程 ereport(FATAL, (errmsg("no free slot when start job worker"))); return; } t_thrd.job_cxt.JobScheduleShmem->jsch_freeWorkers = (JobWorkerInfo)worker->job_links.next; + // 设置工作者的相关属性 worker->job_dboid = jobInfo->job_dboid; worker->job_id = jobInfo->job_id; worker->job_oid = jobInfo->job_oid; @@ -881,107 +929,142 @@ static void ActivateWorker() LWLockRelease(JobShmemLock); - /* Tell postmaster start a new job worker. */ + // 通知 postmaster 启动一个新的作业工作者 SendPostmasterSignal(PMSIGNAL_START_JOB_WORKER); DLFreeElem(head_job); elog(LOG, "Job scheduler send signal to postmaster to start job worker, jobid=%d.", worker->job_id); } + /* * Description: Check job's status is 'r' and update to 'f' when start job scheduler thread. * * Returns: void */ +/* + * 检查作业信息 + */ static void check_jobinfo() { ResourceOwner save = t_thrd.utils_cxt.CurrentResourceOwner; + // 开启一个事务 StartTransactionCommand(); (void)GetTransactionSnapshot(); PG_TRY(); { - /* Check if have job which status is 'r', and update job_status as 'f'. */ + // 检查状态为 'r' 的作业,并将其状态更新为 'f' update_run_job_to_fail(); CommitTransactionCommand(); } PG_CATCH(); { + // 处理异常并记录错误日志 FlushErrorState(); elog(LOG, "Check job info failed"); AbortCurrentTransaction(); } PG_END_TRY(); + // 恢复当前资源拥有者 t_thrd.utils_cxt.CurrentResourceOwner = save; } + /* * Description: Shared memory size. * * Returns: Size */ +/* + * 计算作业信息共享内存大小 + */ Size JobInfoShmemSize(void) { Size size; - /* Need the fixed struct and the array of JobWorkerInfoData */ + /* 需要固定结构体和 JobWorkerInfoData 数组的内存空间 */ + + // 计算 JobScheduleShmemStruct 结构体的大小,并按需对齐 size = sizeof(JobScheduleShmemStruct); size = MAXALIGN(size); + + // 计算 JobWorkerInfoData 数组的大小,并添加到总大小中 size = add_size(size, mul_size(g_instance.attr.attr_sql.job_queue_processes, sizeof(JobWorkerInfoData))); + return size; } + /* * Description: Init shared memory. * * Returns: void */ +/* + * 初始化作业信息共享内存 + */ void JobInfoShmemInit(void) { bool found = false; + + // 通过 ShmemInitStruct 函数获取共享内存指针 t_thrd.job_cxt.JobScheduleShmem = (JobScheduleShmemStruct*)ShmemInitStruct("Job Scheduler Data", JobInfoShmemSize(), &found); if (!IsUnderPostmaster) { - JobWorkerInfo worker; + // 如果是在 Postmaster 进程中,则初始化共享内存结构体 + // 确保共享内存尚未分配 AssertEreport(!found, MOD_EXECUTOR, ""); + // 初始化 JobScheduleShmemStruct 结构体 t_thrd.job_cxt.JobScheduleShmem->jsch_pid = 0; t_thrd.job_cxt.JobScheduleShmem->jsch_freeWorkers = NULL; SHMQueueInit(&t_thrd.job_cxt.JobScheduleShmem->jsch_runningWorkers); t_thrd.job_cxt.JobScheduleShmem->jsch_startingWorker = NULL; - worker = (JobWorkerInfo)((char*)t_thrd.job_cxt.JobScheduleShmem + MAXALIGN(sizeof(JobScheduleShmemStruct))); - - /* Create new freeworker queue. */ + // 初始化 JobWorkerInfoData 数组 + JobWorkerInfo worker = (JobWorkerInfo)((char*)t_thrd.job_cxt.JobScheduleShmem + MAXALIGN(sizeof(JobScheduleShmemStruct))); for (int i = 0; i < g_instance.attr.attr_sql.job_queue_processes; ++i) { worker[i].job_links.next = (SHM_QUEUE*)(t_thrd.job_cxt.JobScheduleShmem->jsch_freeWorkers); t_thrd.job_cxt.JobScheduleShmem->jsch_freeWorkers = &worker[i]; } } else { + // 如果不是在 Postmaster 进程中,则确保共享内存已分配 AssertEreport(found, MOD_EXECUTOR, ""); } } + /* * Description: return true if the thread is job scheduler. * * Returns: bool */ +/* + * 判断当前进程是否为作业调度器进程 + */ bool IsJobSchedulerProcess(void) { + // 判断当前进程的角色是否为 JOB_SCHEDULER return t_thrd.role == JOB_SCHEDULER; } + /* * RecordForkJobWorkerFailed: Called from postmaster when a worker could not be forked. * * Returns: void */ +/* + * 记录 fork 子进程作业工作者失败状态 + */ void RecordForkJobWorkerFailed(void) { + // 将 ForkJobWorkerFailed 信号置为 true t_thrd.job_cxt.JobScheduleShmem->jsch_signal[ForkJobWorkerFailed] = true; } + diff --git a/src/gausskernel/process/job/job_worker.cpp b/src/gausskernel/process/job/job_worker.cpp old mode 100755 new mode 100644 index 28330be1f..0853f0b7a --- a/src/gausskernel/process/job/job_worker.cpp +++ b/src/gausskernel/process/job/job_worker.cpp @@ -93,6 +93,7 @@ static void FreeJobWorkerInfo(int code, Datum arg); * * Returns: bool */ + //此函数用于判断当前线程是否为作业工作进程。如果是作业工作进程,则返回true;否则返回false。 bool IsJobWorkerProcess(void) { return t_thrd.role == JOB_WORKER; @@ -103,6 +104,7 @@ bool IsJobWorkerProcess(void) * * Returns: void */ + //此函数用于为作业工作进程注册信号处理程序。 static void SetupSignalHook(void) { (void)gspqsignal(SIGHUP, SIG_IGN); @@ -122,6 +124,7 @@ static void SetupSignalHook(void) * * Returns: void */ + //此函数用于在线程退出时释放作业工作进程的信息。 static void FreeJobWorkerInfo(int code, Datum arg) { if (t_thrd.job_cxt.MyWorkerInfo != NULL) { @@ -148,6 +151,20 @@ static void FreeJobWorkerInfo(int code, Datum arg) * @in argv: detail info for each args. * Returns: void */ + /* + 这个函数是一个作业执行器的主函数。它实现了以下功能: + +初始化进程和线程上下文。 +创建内存上下文并设置处理模式。 +设置信号处理钩子和信号屏蔽。 +处理异常和错误,记录日志并清理资源。 +从共享内存获取作业信息,并将自己添加到运行中的作业工作者列表中。 +设置会话用户名和数据库名。 +执行作业初始化操作。 +报告自己的状态和活动到PgBackendStatus和pg_stat_activity。 +执行作业具体的逻辑。 +清理资源并退出进程。 +*/ void JobExecuteWorkerMain() { sigjmp_buf local_sigjmp_buf; diff --git a/src/gausskernel/process/main/main.cpp b/src/gausskernel/process/main/main.cpp old mode 100755 new mode 100644 index 6a6ca1027..c5e4659d5 --- a/src/gausskernel/process/main/main.cpp +++ b/src/gausskernel/process/main/main.cpp @@ -76,228 +76,245 @@ extern int encrypte_main(int argc, char* const argv[]); */ int main(int argc, char* argv[]) { - char* mmap_env = NULL; - syscall_lock_init(); + char* mmap_env = NULL; + syscall_lock_init(); + + // 从环境变量中获取GAUSS_MMAP_THRESHOLD的值,并设置mmap_threshold变量 + mmap_env = gs_getenv_r("GAUSS_MMAP_THRESHOLD"); + if (mmap_env != NULL) { + check_backend_env(mmap_env); + mmap_threshold = (size_t)atol(mmap_env); + } + + // 初始化KNL实例 + knl_instance_init(); + + // 创建增量检查点上下文 + g_instance.increCheckPoint_context = AllocSetContextCreate( + INSTANCE_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_STORAGE), + "IncreCheckPointContext", + ALLOCSET_DEFAULT_MINSIZE, + ALLOCSET_DEFAULT_INITSIZE, + ALLOCSET_DEFAULT_MAXSIZE, + SHARED_CONTEXT); + + // 创建备机帐号上下文 + g_instance.account_context = AllocSetContextCreate(g_instance.instance_context, + "StandbyAccontContext", + ALLOCSET_DEFAULT_MINSIZE, + ALLOCSET_DEFAULT_INITSIZE, + ALLOCSET_DEFAULT_MAXSIZE, + SHARED_CONTEXT); + + // 创建通信全局内存上下文 + g_instance.comm_cxt.comm_global_mem_cxt = AllocSetContextCreate(g_instance.instance_context, + "CommunnicatorGlobalMemoryContext", + ALLOCSET_DEFAULT_MINSIZE, + ALLOCSET_DEFAULT_INITSIZE, + ALLOCSET_DEFAULT_MAXSIZE, + SHARED_CONTEXT); + + // 创建内置过程内存上下文 + g_instance.builtin_proc_context = AllocSetContextCreate(g_instance.instance_context, + "builtin_procGlobalMemoryContext", + ALLOCSET_DEFAULT_MINSIZE, + ALLOCSET_DEFAULT_INITSIZE, + ALLOCSET_DEFAULT_MAXSIZE, + SHARED_CONTEXT); + + /* + * Fire up essential subsystems: error and memory management + * + * Code after this point is allowed to use elog/ereport, though + * localization of messages may not work right away, and messages won't go + * anywhere but stderr until GUC settings get loaded. + */ + // 初始化内存上下文 + MemoryContextInit(); + + // 设置全局变量PmTopMemoryContext为顶层内存上下文 + PmTopMemoryContext = t_thrd.top_mem_cxt; + + // 初始化线程 + knl_thread_init(MASTER_THREAD); + + // 创建虚拟会话 + t_thrd.fake_session = create_session_context(t_thrd.top_mem_cxt, 0); + t_thrd.fake_session->status = KNL_SESS_FAKE; + + // 将当前会话设置为虚拟会话 + u_sess = t_thrd.fake_session; + + // 设置SelfMemoryContext为默认内存上下文组 + SelfMemoryContext = THREAD_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_DEFAULT); + + // 切换到默认内存上下文组 + MemoryContextSwitchTo(THREAD_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_DEFAULT)); + + // 获取程序名称 + progname = get_progname(argv[0]); - mmap_env = gs_getenv_r("GAUSS_MMAP_THRESHOLD"); - if (mmap_env != NULL) { - check_backend_env(mmap_env); - mmap_threshold = (size_t)atol(mmap_env); - } - - knl_instance_init(); - - g_instance.increCheckPoint_context = AllocSetContextCreate( - INSTANCE_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_STORAGE), - "IncreCheckPointContext", - ALLOCSET_DEFAULT_MINSIZE, - ALLOCSET_DEFAULT_INITSIZE, - ALLOCSET_DEFAULT_MAXSIZE, - SHARED_CONTEXT); - - g_instance.account_context = AllocSetContextCreate(g_instance.instance_context, - "StandbyAccontContext", - ALLOCSET_DEFAULT_MINSIZE, - ALLOCSET_DEFAULT_INITSIZE, - ALLOCSET_DEFAULT_MAXSIZE, - SHARED_CONTEXT); - - g_instance.comm_cxt.comm_global_mem_cxt = AllocSetContextCreate(g_instance.instance_context, - "CommunnicatorGlobalMemoryContext", - ALLOCSET_DEFAULT_MINSIZE, - ALLOCSET_DEFAULT_INITSIZE, - ALLOCSET_DEFAULT_MAXSIZE, - SHARED_CONTEXT); - - g_instance.builtin_proc_context = AllocSetContextCreate(g_instance.instance_context, - "builtin_procGlobalMemoryContext", - ALLOCSET_DEFAULT_MINSIZE, - ALLOCSET_DEFAULT_INITSIZE, - ALLOCSET_DEFAULT_MAXSIZE, - SHARED_CONTEXT); - /* - * Fire up essential subsystems: error and memory management - * - * Code after this point is allowed to use elog/ereport, though - * localization of messages may not work right away, and messages won't go - * anywhere but stderr until GUC settings get loaded. - */ - MemoryContextInit(); - - PmTopMemoryContext = t_thrd.top_mem_cxt; - - knl_thread_init(MASTER_THREAD); - - t_thrd.fake_session = create_session_context(t_thrd.top_mem_cxt, 0); - t_thrd.fake_session->status = KNL_SESS_FAKE; - - u_sess = t_thrd.fake_session; - - SelfMemoryContext = THREAD_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_DEFAULT); - - MemoryContextSwitchTo(THREAD_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_DEFAULT)); - - progname = get_progname(argv[0]); /* - * Platform-specific startup hacks - */ - startup_hacks(progname); + * Platform-specific startup hacks + */ + startup_hacks(progname); + + /* if gaussdb's name is gs_encrypt, so run in encrypte_main() */ + if (!strcmp(progname, "gs_encrypt")) { + return encrypte_main(argc, argv); + } + + // 初始化plog全局内存 + init_plog_global_mem(); + + /* + * Remember the physical location of the initially given argv[] array for + * possible use by ps display. On some platforms, the argv[] storage must + * be overwritten in order to set the process title for ps. In such cases, + * save_ps_display_args makes and returns a new copy of the argv[] array. + * + * save_ps_display_args may also move the environment strings to make + * extra room. Therefore this should be done as early as possible during + * startup, to avoid entanglements with code that might save a getenv() + * result pointer. + */ + argv = save_ps_display_args(argc, argv); + + /* + * If supported on the current platform, set up a handler to be called if + * the backend/postmaster crashes with a fatal signal or exception. + */ + #if defined(WIN32) && defined(HAVE_MINIDUMP_TYPE) + pgwin32_install_crashdump_handler(); + #endif + + /* + * Set up locale information from environment. Note that LC_CTYPE and + * LC_COLLATE will be overridden later from pg_control if we are in an + * already-initialized database. We set them here so that they will be + * available to fill pg_control during initdb. LC_MESSAGES will get set + * later during GUC option processing, but we set it here to allow startup + * error messages to be localized. + */ + set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("gaussdb")); + + #ifdef WIN32 + + /* + * Windows uses codepages rather than the environment, so we work around + * that by querying the environment explicitly first for LC_COLLATE and + * LC_CTYPE. We have to do this because initdb passes those values in the + * environment. If there is nothing there we fall back on the codepage. + */ + { + char* env_locale = NULL; + + if ((env_locale = gs_getenv_r("LC_COLLATE")) != NULL) { + check_backend_env(env_locale); + pg_perm_setlocale(LC_COLLATE, env_locale); + } else + pg_perm_setlocale(LC_COLLATE, ""); + + if ((env_locale = gs_getenv_r("LC_CTYPE")) != NULL) { + check_backend_env(env_locale); + pg_perm_setlocale(LC_CTYPE, env_locale); + } else + pg_perm_setlocale(LC_CTYPE, ""); + } + #else + pg_perm_setlocale(LC_COLLATE, ""); + pg_perm_setlocale(LC_CTYPE, ""); + #endif - /* if gaussdb's name is gs_encrypt, so run in encrypte_main() */ - if (!strcmp(progname, "gs_encrypt")) { - return encrypte_main(argc, argv); - } - - init_plog_global_mem(); - - /* - * Remember the physical location of the initially given argv[] array for - * possible use by ps display. On some platforms, the argv[] storage must - * be overwritten in order to set the process title for ps. In such cases - * save_ps_display_args makes and returns a new copy of the argv[] array. - * - * save_ps_display_args may also move the environment strings to make - * extra room. Therefore this should be done as early as possible during - * startup, to avoid entanglements with code that might save a getenv() - * result pointer. - */ - argv = save_ps_display_args(argc, argv); - - /* - * If supported on the current platform, set up a handler to be called if - * the backend/postmaster crashes with a fatal signal or exception. - */ -#if defined(WIN32) && defined(HAVE_MINIDUMP_TYPE) - pgwin32_install_crashdump_handler(); -#endif - - /* - * Set up locale information from environment. Note that LC_CTYPE and - * LC_COLLATE will be overridden later from pg_control if we are in an - * already-initialized database. We set them here so that they will be - * available to fill pg_control during initdb. LC_MESSAGES will get set - * later during GUC option processing, but we set it here to allow startup - * error messages to be localized. - */ - set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("gaussdb")); - -#ifdef WIN32 - - /* - * Windows uses codepages rather than the environment, so we work around - * that by querying the environment explicitly first for LC_COLLATE and - * LC_CTYPE. We have to do this because initdb passes those values in the - * environment. If there is nothing there we fall back on the codepage. - */ - { - char* env_locale = NULL; - - if ((env_locale = gs_getenv_r("LC_COLLATE")) != NULL) { - check_backend_env(env_locale); - pg_perm_setlocale(LC_COLLATE, env_locale); - } else - pg_perm_setlocale(LC_COLLATE, ""); - - if ((env_locale = gs_getenv_r("LC_CTYPE")) != NULL) { - check_backend_env(env_locale); - pg_perm_setlocale(LC_CTYPE, env_locale); - } else - pg_perm_setlocale(LC_CTYPE, ""); - } -#else - pg_perm_setlocale(LC_COLLATE, ""); - pg_perm_setlocale(LC_CTYPE, ""); -#endif - - /* - * We keep these set to "C" always, except transiently in pg_locale.c; see - * that file for explanations. - */ - pg_perm_setlocale(LC_MONETARY, "C"); - pg_perm_setlocale(LC_NUMERIC, "C"); - pg_perm_setlocale(LC_TIME, "C"); - - /* - * Now that we have absorbed as much as we wish to from the locale - * environment, remove any LC_ALL setting, so that the environment - * variables installed by pg_perm_setlocale have force. - */ - (void)unsetenv("LC_ALL"); - - /* - * Catch standard options before doing much else - */ - if (argc > 1) { - if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - help(progname); - exit(0); - } - if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) { - puts("gaussdb " DEF_GS_VERSION); - exit(0); - } - } - - /* - * Make sure we are not running as root. - */ - check_root(progname); - - /* - * Dispatch to one of various subprograms depending on first argument. - */ -#ifdef WIN32 - - /* - * Start our win32 signal implementation - * - * SubPostmasterMain() will do this for itself, but the remaining modes - * need it here - */ - pgwin32_signal_initialize(); -#endif - - t_thrd.mem_cxt.gs_signal_mem_cxt = AllocSetContextCreate( - t_thrd.top_mem_cxt, "gs_signal", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); - if (NULL == t_thrd.mem_cxt.gs_signal_mem_cxt) { - ereport(LOG, (errmsg("could not start a new thread, because of no enough system resource. "))); - proc_exit(1); - } - - /* - * @BuiltinFunc - * Create a global BuiltinFunc object shared among threads - */ - if (g_sorted_funcs[0] == NULL) { - initBuiltinFuncs(); - } - - bool isBoot = (argc > 1 && strcmp(argv[1], "--boot") == 0); - if (isBoot) { - IsInitdb = true; - gs_signal_monitor_startup(); - gs_signal_slots_init(1); - (void)gs_signal_unblock_sigusr2(); - gs_signal_startup_siginfo("AuxiliaryProcessMain"); - BootStrapProcessMain(argc, argv); /* does not return */ - } - - if (argc > 1 && strcmp(argv[1], "--describe-config") == 0) - exit(GucInfoMain()); - - if (argc > 1 && strcmp(argv[1], "--single") == 0) { - IsInitdb = true; - gs_signal_monitor_startup(); - gs_signal_slots_init(1); - (void)gs_signal_unblock_sigusr2(); - gs_signal_startup_siginfo("PostgresMain"); - - exit(PostgresMain(argc, argv, NULL, get_current_username(progname))); - } - - exit(PostmasterMain(argc, argv)); -} + /* + * We keep these set to "C" always, except transiently in pg_locale.c; see + * that file for explanations. + */ + pg_perm_setlocale(LC_MONETARY, "C"); // 将货币格式化设置为"C"语言环境 + pg_perm_setlocale(LC_NUMERIC, "C"); // 将数字格式化设置为"C"语言环境 + pg_perm_setlocale(LC_TIME, "C"); // 将时间格式化设置为"C"语言环境 + + /* + * Now that we have absorbed as much as we wish to from the locale + * environment, remove any LC_ALL setting, so that the environment + * variables installed by pg_perm_setlocale have force. + */ + (void)unsetenv("LC_ALL"); // 移除LC_ALL设置,以确保pg_perm_setlocale设置的环境变量生效 + + /* + * Catch standard options before doing much else + */ + if (argc > 1) { + if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { + help(progname); // 显示帮助信息 + exit(0); + } + if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) { + puts("gaussdb " DEF_GS_VERSION); // 显示版本号 + exit(0); + } + } + + /* + * Make sure we are not running as root. + */ + check_root(progname); // 检查是否以root用户身份运行 + + /* + * Dispatch to one of various subprograms depending on first argument. + */ + #ifdef WIN32 + + /* + * Start our win32 signal implementation + * + * SubPostmasterMain() will do this for itself, but the remaining modes + * need it here + */ + pgwin32_signal_initialize(); // 在Windows平台上启动信号处理 + + #endif + + t_thrd.mem_cxt.gs_signal_mem_cxt = AllocSetContextCreate( + t_thrd.top_mem_cxt, "gs_signal", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); + if (NULL == t_thrd.mem_cxt.gs_signal_mem_cxt) { + ereport(LOG, (errmsg("could not start a new thread, because of no enough system resource. "))); + proc_exit(1); + } + + /* + * @BuiltinFunc + * Create a global BuiltinFunc object shared among threads + */ + if (g_sorted_funcs[0] == NULL) { + initBuiltinFuncs(); // 初始化内置函数相关的全局变量 + } + + bool isBoot = (argc > 1 && strcmp(argv[1], "--boot") == 0); + if (isBoot) { + IsInitdb = true; + gs_signal_monitor_startup(); // 启动信号监控工作线程 + gs_signal_slots_init(1); // 初始化信号插槽 + (void)gs_signal_unblock_sigusr2(); // 解除SIGUSR2信号的阻塞 + gs_signal_startup_siginfo("AuxiliaryProcessMain"); // 记录启动信息 + BootStrapProcessMain(argc, argv); /* does not return */ + } + + if (argc > 1 && strcmp(argv[1], "--describe-config") == 0) + exit(GucInfoMain()); // 打印GUC参数信息 + + if (argc > 1 && strcmp(argv[1], "--single") == 0) { + IsInitdb = true; + gs_signal_monitor_startup(); // 启动信号监控工作线程 + gs_signal_slots_init(1); // 初始化信号插槽 + (void)gs_signal_unblock_sigusr2(); // 解除SIGUSR2信号的阻塞 + gs_signal_startup_siginfo("PostgresMain"); // 记录启动信息 + + exit(PostgresMain(argc, argv, NULL, get_current_username(progname))); // 进入PostgreSQL主循环 + } + + exit(PostmasterMain(argc, argv)); // 进入Postmaster主循环 /* * Place platform-specific startup hacks here. This is the right @@ -496,43 +513,55 @@ static void check_root(const char* progname) } #endif /* WIN32 */ } +/** + * get_current_username - 获取当前操作系统的用户名 + * @progname: 程序名称 + * + * 返回当前操作系统的用户名。 + */ + static char* get_current_username(const char* progname) + { + #ifndef WIN32 // 非Windows平台代码 + struct passwd* pw = NULL; + char* pRet = NULL; + + (void)syscalllockAcquire(&getpwuid_lock); // 获取线程锁 + pw = getpwuid(geteuid()); // 获取与实际用户ID关联的密码记录 + if (pw == NULL) { // 如果无法获取则报错 + (void)syscalllockRelease(&getpwuid_lock); + write_stderr("%s: invalid effective UID: %d\n", progname, (int)geteuid()); + exit(1); + } + /* Allocate new memory because later getpwuid() calls can overwrite it. */ + pRet = MemoryContextStrdup(SESS_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_CBB), pw->pw_name); // 分配新内存来储存用户名,并返回该内存地址 + (void)syscalllockRelease(&getpwuid_lock); // 释放线程锁 + return pRet; // 返回用户名 + #else // Windows平台代码 + unsigned long namesize = 256 /* UNLEN */ + 1; + char* name = NULL; + + name = MemoryContextAlloc( + SESS_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_CBB), namesize); // 分配内存来储存用户名,返回该内存地址 + if (!GetUserName(name, &namesize)) { // 获取当前用户的用户名 + write_stderr("%s: could not determine user name (GetUserName failed)\n", progname); + exit(1); + } + + return name; // 返回用户名 + #endif + } + + /** + * syscall_lock_init - 初始化系统调用锁 + * + * 初始化用于保护某些系统调用(如 getenv)的线程锁。 + */ + static void syscall_lock_init(void) + { + syscalllockInit(&getpwuid_lock); // 初始化获取用户ID对应密码记录的线程锁 + syscalllockInit(&env_lock); // 初始化 getenv 的线程锁 + syscalllockInit(&dlerror_lock); // 初始化 dlerror 的线程锁 + syscalllockInit(&kerberos_conn_lock); // 初始化 Kerberos 连接相关的线程锁 + syscalllockInit(&read_cipher_lock); // 初始化加密算法相关的线程锁 + } -static char* get_current_username(const char* progname) -{ -#ifndef WIN32 - struct passwd* pw = NULL; - char* pRet = NULL; - - (void)syscalllockAcquire(&getpwuid_lock); - pw = getpwuid(geteuid()); - if (pw == NULL) { - (void)syscalllockRelease(&getpwuid_lock); - write_stderr("%s: invalid effective UID: %d\n", progname, (int)geteuid()); - exit(1); - } - /* Allocate new memory because later getpwuid() calls can overwrite it. */ - pRet = MemoryContextStrdup(SESS_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_CBB), pw->pw_name); - (void)syscalllockRelease(&getpwuid_lock); - return pRet; -#else - unsigned long namesize = 256 /* UNLEN */ + 1; - char* name = NULL; - - name = MemoryContextAlloc(SESS_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_CBB), namesize); - if (!GetUserName(name, &namesize)) { - write_stderr("%s: could not determine user name (GetUserName failed)\n", progname); - exit(1); - } - - return name; -#endif -} - -static void syscall_lock_init(void) -{ - syscalllockInit(&getpwuid_lock); - syscalllockInit(&env_lock); - syscalllockInit(&dlerror_lock); - syscalllockInit(&kerberos_conn_lock); - syscalllockInit(&read_cipher_lock); -} diff --git a/src/gausskernel/process/postmaster/alarmchecker.cpp b/src/gausskernel/process/postmaster/alarmchecker.cpp index 2ffe93696..87c8fddc7 100644 --- a/src/gausskernel/process/postmaster/alarmchecker.cpp +++ b/src/gausskernel/process/postmaster/alarmchecker.cpp @@ -1,27 +1,4 @@ -/* - * Copyright (c) 2020 Huawei Technologies Co.,Ltd. - * - * openGauss is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * - * http://license.coscl.org.cn/MulanPSL2 - * - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, - * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - * ------------------------------------------------------------------------- - * - * alarmchecker.cpp - * - * openGauss Alarm checker thread Implementation - * - * IDENTIFICATION - * src/gausskernel/process/postmaster/alarmchecker.cpp - * - * ------------------------------------------------------------------------- - */ +//这些是各种头文件的引用,包含了一些系统库、PostgreSQL内部模块和自定义的模块 #include "postgres.h" #include "knl/knl_variable.h" @@ -46,18 +23,15 @@ #include "postmaster/alarmchecker.h" #include "gssignal/gs_signal.h" #include "replication/walsender.h" +//定义一些全局变量 +int g_alarmReportInterval;//报警上报的时间间隔 +char g_alarmComponentPath[MAXPGPATH];// 报警组件的路径 +int g_alarmReportMaxCount;//报警上报的最大次数 -// declare the global variable of alarm module -int g_alarmReportInterval; -char g_alarmComponentPath[MAXPGPATH]; -int g_alarmReportMaxCount; -/* seconds, interval of alarm check loop. */ -static const int AlarmCheckInterval = 1; - -bool enable_alarm = false; - +static const int AlarmCheckInterval = 1;//定义了一个静态常量 AlarmCheckInterval,值为1,表示报警检查的时间间隔(单位:秒) +bool enable_alarm = false;//定义并初始化了一个bool型变量 enable_alarm,初始值为 false,表示是否启用报警功能。 +//定义了静态变量 DataInstAlarmList 和 DataInstAlarmListSize,用于存储报警项的列表和列表大小 static Alarm* DataInstAlarmList = NULL; - static int DataInstAlarmListSize = 0; AlarmCheckResult DataOrRedoDirNotExistChecker(Alarm* alarm, AlarmAdditionalParam* additionalParam); @@ -70,15 +44,17 @@ static void acSigquitHandler(SIGNAL_ARGS); extern AlarmCheckResult DataInstArchChecker(Alarm* alarm, AlarmAdditionalParam* additionalParam); extern AlarmCheckResult ConnAuthMethodChecker(Alarm* alarm, AlarmAdditionalParam* additionalParam); extern AlarmCheckResult DataInstConnToGTMChecker(Alarm* alarm, AlarmAdditionalParam* additionalParam); - +//用于初始化报警项列表 void DataInstAlarmItemInitialize(void) { - DataInstAlarmListSize = 6; - DataInstAlarmList = (Alarm*)AlarmAlloc(sizeof(Alarm) * DataInstAlarmListSize); + DataInstAlarmListSize = 6;//设置列表大小为6 + DataInstAlarmList = (Alarm*)AlarmAlloc(sizeof(Alarm) * DataInstAlarmListSize);//给 DataInstAlarmList分配内存 + //如果分配失败 if (NULL == DataInstAlarmList) { - AlarmLog(ALM_LOG, "Out of memory: DataInstAlarmItemInitialize failed."); + AlarmLog(ALM_LOG, "Out of memory: DataInstAlarmItemInitialize failed.");//记录错误日志并退出程序 exit(1); } + //调用函数AlarmItemInitialize对每个报警项进行初始化,每个报警项由一个Alarm结构体表示,包含报警项的类型、报警状态和报警检查函数 // ALM_AI_MissingDataInstDataOrRedoDir AlarmItemInitialize( &(DataInstAlarmList[0]), ALM_AI_MissingDataInstDataOrRedoDir, ALM_AS_Normal, DataOrRedoDirNotExistChecker); @@ -96,50 +72,38 @@ void DataInstAlarmItemInitialize(void) AlarmItemInitialize( &(DataInstAlarmList[5]), ALM_AI_AbnormalDataInstConnToGTM, ALM_AS_Normal, DataInstConnToGTMChecker); } - +//用于启动报警检查进程 ThreadId startAlarmChecker(void) { - if (!IsPostmasterEnvironment || !enable_alarm) { + //看是否处于Postmaster环境并且是否启用了报警功能 + if (!IsPostmasterEnvironment || !enable_alarm) {//如果不满足条件,则返回0。 return 0; } - + //否则,调用initialize_util_thread函数来启动报警检查器线程。 return initialize_util_thread(ALARMCHECK); } - +//定义了一个名为AlarmCheckerMain的静态函数。 +//NON_EXEC_STATIC用于指定函数不会被直接执行,而是作为子进程在PostgreSQL中运行。 NON_EXEC_STATIC void AlarmCheckerMain() { + IsUnderPostmaster = true;//设置变量IsUnderPostmaster为true,表示当前进程是一个后台进程 - /* we are a postmaster subprocess now */ - IsUnderPostmaster = true; - - /* reset t_thrd.proc_cxt.MyProcPid */ - t_thrd.proc_cxt.MyProcPid = gs_thread_self(); - - /* record Start Time for logging */ - t_thrd.proc_cxt.MyStartTime = time(NULL); - - /* reord my name */ - t_thrd.proc_cxt.MyProgName = "AlarmChecker"; - - /* Identify myself via ps */ - init_ps_display("AlarmChecker", "", "", ""); - + t_thrd.proc_cxt.MyProcPid = gs_thread_self();//重置t_thrd.proc_cxt.MyProcPid为当前线程 + + t_thrd.proc_cxt.MyStartTime = time(NULL);//记录当前时间为t_thrd.proc_cxt.MyStartTime + + t_thrd.proc_cxt.MyProgName = "AlarmChecker";//将当前进程名设置为AlarmChecker + + init_ps_display("AlarmChecker", "", "", "");//调用函数init_ps_display来显示初始化进程状态 + //调用函数AlarmLog记录日志,表示报警检查程序已经启动 AlarmLog(ALM_LOG, "alarm checker started."); - + //调用函数InitializeLatchSupport来初始化latch支持。 InitializeLatchSupport(); /* needed for latch waits */ - /* Initialize private latch for use by signal handlers */ + //初始化一个latch对象,用于处理信号处理程序中的同步等待。 InitLatch(&t_thrd.alarm_cxt.AlarmCheckerLatch); - - /* - * Properly accept or ignore signals the postmaster might send us - * - * Note: we deliberately ignore SIGTERM, because during a standard Unix - * system shutdown cycle, init will SIGTERM all processes at once. We - * want to wait for the backends to exit, whereupon the postmaster will - * tell us it's okay to shut down (via SIGUSR2). - */ - (void)gspqsignal(SIGHUP, acSighupHandler); /* set flag to read config file */ + //使用gspqsignal函数设置了一些信号的处理行为,如SIGHUP、SIGINT、SIGTERM等。 + (void)gspqsignal(SIGHUP, acSighupHandler); (void)gspqsignal(SIGINT, SIG_IGN); (void)gspqsignal(SIGTERM, SIG_IGN); (void)gspqsignal(SIGQUIT, acSigquitHandler); @@ -147,160 +111,141 @@ NON_EXEC_STATIC void AlarmCheckerMain() (void)gspqsignal(SIGPIPE, SIG_IGN); (void)gspqsignal(SIGUSR1, SIG_IGN); (void)gspqsignal(SIGUSR2, SIG_IGN); + //对于某些信号通过SIG_IGN忽略,而对于其他信号通过SIG_DFL恢复为默认行为。 - /* - * Reset some signals that are accepted by postmaster but not here - */ (void)gspqsignal(SIGCHLD, SIG_DFL); (void)gspqsignal(SIGTTIN, SIG_DFL); (void)gspqsignal(SIGTTOU, SIG_DFL); (void)gspqsignal(SIGCONT, SIG_DFL); (void)gspqsignal(SIGWINCH, SIG_DFL); - + //设置信号掩码使得非阻塞的信号可用 gs_signal_setmask(&t_thrd.libpq_cxt.UnBlockSig, NULL); + //解除SIGUSR2信号的阻塞 (void)gs_signal_unblock_sigusr2(); - - /* all is done info top memory context. */ + //切换到默认内存上下文中 (void)MemoryContextSwitchTo(THREAD_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_DEFAULT)); - + + //调用初始化报警项列表 DataInstAlarmItemInitialize(); - for (;;) { - /* Clear any already-pending wakeups */ + //清除任何已经挂起的唤醒信号 ResetLatch(&t_thrd.alarm_cxt.AlarmCheckerLatch); - - /* the normal shutdown case */ - if (t_thrd.alarm_cxt.gotSigdie) + if (t_thrd.alarm_cxt.gotSigdie)// 接收到终止信号,跳出循环,结束线程 break; - - /* - * reload the postgresql.conf - */ + //检查是否接收到了SIGDIE信号 if (t_thrd.alarm_cxt.gotSighup) { - t_thrd.alarm_cxt.gotSighup = false; + t_thrd.alarm_cxt.gotSighup = false;//接收到,重新加载postgresql.conf配置文件 ProcessConfigFile(PGC_SIGHUP); } - + //进行报警检查 AlarmCheckerLoop(DataInstAlarmList, DataInstAlarmListSize); - - /* - * Sleep until there's something to do - */ + //进入休眠状态,等待下一次循环 (void)WaitLatch(&t_thrd.alarm_cxt.AlarmCheckerLatch, WL_LATCH_SET | WL_TIMEOUT, AlarmCheckInterval * 1000); } - + //调用AlarmLog函数记录日志,表示报警检查程序即将关闭 AlarmLog(ALM_LOG, "alarm checker shutting down..."); - + //结束进程 proc_exit(0); } - -/* - * signal handle functions - */ -/* - * @@GaussDB@@ - * Brief : handle SIGHUP signal and set t_thrd.alarm_cxt.gotSighup flag - * Description : - * Notes : - */ +//定义了一个名为acSighupHandler的静态函数,用于处理SIGHUP信号,SIGNAL_ARGS是用于接收信号处理程序的参数。 static void acSighupHandler(SIGNAL_ARGS) { - int save_errno = errno; - - t_thrd.alarm_cxt.gotSighup = true; + int save_errno = errno;//保存当前错误码 + t_thrd.alarm_cxt.gotSighup = true;//置为true,表示接收到了SIGHUP信号 + //调用SetLatch函数设置latch对象,以唤醒等待该latch的进程 SetLatch(&t_thrd.alarm_cxt.AlarmCheckerLatch); - + //恢复之前保存的错误码 errno = save_errno; } - -/* - * @@GaussDB@@ - * Brief : handle SIGTERM, SIGINT signal and set t_thrd.alarm_cxt.gotSigdie flag - * Description : - * Notes : - */ +//定义了一个名为acSigquitHandler的静态函数,用于处理SIGQUIT信号。SIGNAL_ARGS用于接收信号处理程序的参数。 static void acSigquitHandler(SIGNAL_ARGS) { - int save_errno = errno; - - t_thrd.alarm_cxt.gotSigdie = true; + int save_errno = errno;//保存当前错误码 + t_thrd.alarm_cxt.gotSigdie = true;//置为true,表示接收到了SIGQUIT信号 + //调用SetLatch函数设置latch对象,以唤醒等待该latch的进程 SetLatch(&t_thrd.alarm_cxt.AlarmCheckerLatch); - + //恢复之前保存的错误码 errno = save_errno; } - +//定义了一个名为isDirExist的函数,用于判断指定目录是否存在。参数dir表示要检查的目录路径。 bool isDirExist(const char* dir) { - struct stat stat_buf; - - if (stat(dir, &stat_buf) != 0) + struct stat stat_buf;//定义了一个stat结构体用于存储目录的属性信息 + //使用stat函数获取目录的属性信息 + if (stat(dir, &stat_buf) != 0)//如果返回值不为0 + return false;//获取失败,说明目录不存在,返回false + //判断获取到的目录的属性中的st_mode字段是否为目录类型 + if (!S_ISDIR(stat_buf.st_mode))//若不是目录类型,则返回false return false; - - if (!S_ISDIR(stat_buf.st_mode)) - return false; - + //这是对非Windows和非Cygwin系统上的额外检查: #if !defined(WIN32) && !defined(__CYGWIN__) - if (stat_buf.st_uid != geteuid()) + if (stat_buf.st_uid != geteuid())//检查目录的拥有者是否与当前用户ID相同 return false; - if ((stat_buf.st_mode & S_IRWXU) != S_IRWXU) + if ((stat_buf.st_mode & S_IRWXU) != S_IRWXU)//检查目录的权限是否设置为用户可读、写、执行的权限 return false; #endif - + //目录存在且满足所有条件,返回true,否则返回false return true; } - +//定义了一个名为DataOrRedoDirNotExistChecker的函数,用于检查数据目录和pg_xlog目录是否存在。 +//有两个参数:alarm表示报警对象,additionalParam表示额外的参数。 AlarmCheckResult DataOrRedoDirNotExistChecker(Alarm* alarm, AlarmAdditionalParam* additionalParam) { - if (isDirExist(t_thrd.proc_cxt.DataDir) && isDirExist("pg_xlog")) { - // fill the alarm message - WriteAlarmAdditionalInfo(additionalParam, + //调用isDirExist函数判断数据目录和pg_xlog目录是否都存在 + if (isDirExist(t_thrd.proc_cxt.DataDir) && isDirExist("pg_xlog")) {//如果两个目录都存在,则执行以下操作: + // fill the alarm message //- 使用WriteAlarmAdditionalInfo函数填充报警消息的额外信息。 + WriteAlarmAdditionalInfo(additionalParam, g_instance.attr.attr_common.PGXCNodeName, "", "", alarm, ALM_AT_Resume, g_instance.attr.attr_common.PGXCNodeName); - return ALM_ACR_Normal; - } else { + return ALM_ACR_Normal;//- 返回ALM_ACR_Normal,表示检查结果正常。 + } else { //如果两个目录有任何一个不存在,则执行以下操作 // fill the alarm message - WriteAlarmAdditionalInfo(additionalParam, + WriteAlarmAdditionalInfo(additionalParam, //使用WriteAlarmAdditionalInfo函数填充报警消息的额外信息 g_instance.attr.attr_common.PGXCNodeName, "", "", alarm, ALM_AT_Fault, g_instance.attr.attr_common.PGXCNodeName); - return ALM_ACR_Abnormal; + return ALM_ACR_Abnormal;// 返回ALM_ACR_Abnormal,表示检查结果异常 } } /* implementation of alarm module. */ +//定义了一个名为AlarmFree的函数,用于释放内存。pointer表示要释放的内存指针。 void AlarmFree(void* pointer) { + //检查指针是否为空 if (pointer != NULL) - pfree(pointer); + pfree(pointer);//如果不为空,则调用pfree函数释放内存 } - +//定义了一个名为AlarmAlloc的函数,用于分配内存。size表示要分配的内存大小 void* AlarmAlloc(size_t size) { - return palloc(size); + return palloc(size);//调用palloc函数分配内存,并将分配的内存地址返回 } - +//定义了一个名为AlarmLogImplementation的函数,用于记录报警日志。 +//有三个参数:level表示日志级别,prefix表示日志前缀,logtext表示要记录的日志文本。 void AlarmLogImplementation(int level, const char* prefix, const char* logtext) { + //使用switch语句根据日志级别执行不同的操作。 switch (level) { - case ALM_DEBUG: - ereport(DEBUG3, (errmsg("%s%s", prefix, logtext))); + case ALM_DEBUG://如果级别是ALM_DEBUG + ereport(DEBUG3, (errmsg("%s%s", prefix, logtext)));//调用ereport函数使用DEBUG3级别记录日志 break; - case ALM_LOG: - ereport(LOG, (errmsg("%s%s", prefix, logtext))); + case ALM_LOG://如果级别是ALM_LOG + ereport(LOG, (errmsg("%s%s", prefix, logtext)));//调用ereport函数使用LOG级别记录日志。 break; default: - break; + break;//其他情况不执行任何操作。 } }