OpenGauss线程管理-轻量锁监控线程-LWlockmonitor

OpenGauss线程管理-轻量锁监控线程-LWlockmonitor

轻量级锁(LWLock)主要提供对共享内存的互斥访问,比如Clog buffer(事务提交状态缓存)、Shared buffers(数据页缓存)、Substran buffer(子事务缓存)等。该轻量级锁监控线程主要检测轻量级锁(LWLock)产生的死锁。

术语解释

  • 无锁:指没有对资源进行锁定,所有的线程都能访问并修改同一个资源,但同时只有一个线程能修改成功。
  • 偏向锁:在开启偏向锁机制的情况下,某个线程获得锁,当该线程下次再想要获得锁时,不需要再获得锁,直接就可以执行同步代码。
  • 轻量级锁(LWLock):当锁是偏向锁的时候,却被另外的线程所访问,此时偏向锁就会升级为轻量级锁,其他线程会通过自旋的形式尝试获取锁,线程不会阻塞,从而提高性能。在没有多线程竞争的情况下,使用轻量级锁能够减少性能消耗,但是当多个线程同时竞争锁时,轻量级锁会膨胀为重量级锁。
  • 重量级锁:即当有其他线程占用锁时,当前线程会进入阻塞状态。
  • 自旋锁:在自旋状态下,当一个线程A尝试进入同步代码块,但是当前的锁已经被线程B占有时,线程A不进入阻塞状态,而是不停的空转,等待线程B释放锁。

LWlockmonitor线程

路径:openGauss-server/src/gausskernel/process/postmaster/lwlockmonitor.cpp
该线程将检测到lwlock死锁的发生,并进行后期处理。

在主线程中的使用

在GaussDbThreadMain中通过thread_role进入LWlockmonitor线程
在这里插入图片描述

代码理解

FaultMonitorMain
NON_EXEC_STATIC void FaultMonitorMain()
{
    sigjmp_buf localSigjmpBuf;
    MemoryContext lwm_context = NULL;

    lwm_light_detect* prev_snapshot = NULL;
    lwm_light_detect* curr_snapshot = NULL;
    long cur_timeout = 0;

    /* 我们现在是postmaster子流程 */
    IsUnderPostmaster = true;

    t_thrd.proc_cxt.MyProcPid = gs_thread_self();

    ereport(DEBUG5, (errmsg("lwlockmonitor process is started: %lu", t_thrd.proc_cxt.MyProcPid)));

    (void)gspqsignal(SIGHUP, LWLockMonitorSigHupHandler);    /* 设置标志以读取配置文件 */
    (void)gspqsignal(SIGINT, LWLockMonitorShutdownHandler);  /* 请求关闭 */
    (void)gspqsignal(SIGTERM, LWLockMonitorShutdownHandler); /* 请求关闭 */
    (void)gspqsignal(SIGQUIT, SIG_IGN);
    (void)gspqsignal(SIGALRM, SIG_IGN);
    (void)gspqsignal(SIGPIPE, SIG_IGN);
    (void)gspqsignal(SIGUSR1, SIG_IGN);
    (void)gspqsignal(SIGUSR2, SIG_IGN); /* not used */

    /* 重置一些postmaster接受但不在此接受的信号 */
    (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);

    /* 我们始终允许SIGQUIT(快速模具) */
    (void)sigdelset(&t_thrd.libpq_cxt.BlockSig, SIGQUIT);

    /*
     * 创建一个我们将在其中完成所有工作的内存上下文。
     * 我们这样做是为了在错误恢复期间重置上下文,从而避免可能的内存泄漏。
     * 以前,此代码只在t_thrd中运行。topmemcxt,但重置它将是一个非常糟糕的主意。
     */
    lwm_context = AllocSetContextCreate(t_thrd.top_mem_cxt,
        "LWLock Monitor",
        ALLOCSET_DEFAULT_MINSIZE,
        ALLOCSET_DEFAULT_INITSIZE,
        ALLOCSET_DEFAULT_MAXSIZE);
    (void)MemoryContextSwitchTo(lwm_context);

#ifdef ENABLE_UT
    /* 单元测试用例 */
    ut_test_find_deadlock_cycle();
#endif /* ENABLE_UT */

    int curTryCounter;
    int* oldTryCounter = NULL;
    if (sigsetjmp(localSigjmpBuf, 1) != 0) {
        gstrace_tryblock_exit(true, oldTryCounter);

        /* 由于未使用PG_TRY,因此必须手动重置错误堆栈 */
        t_thrd.log_cxt.error_context_stack = NULL;

        t_thrd.log_cxt.call_stack = NULL;

        /* 清理时防止中断 */
        HOLD_INTERRUPTS();

        /* 向服务器日志报告错误 */
        EmitErrorReport();
        /* lsc持有的释放资源 */
        AtEOXact_SysDBCache(false);

        /*
         * 现在返回正常的顶级上下文,并清除ErrorContext以备下次使用。
         */
        (void)MemoryContextSwitchTo(lwm_context);
        FlushErrorState();

        /* 刷新顶级上下文中的所有泄漏数据 */
        MemoryContextResetAndDeleteChildren(lwm_context);

        /* 现在我们可以再次允许中断 */
        RESUME_INTERRUPTS();

        /*
         * 出现任何错误后至少休眠1秒。写入错误可能会重复出现,我们不希望尽快填写错误日志。
         */
        pg_usleep(1000000L);
    }
    oldTryCounter = gstrace_tryblock_entry(&curTryCounter);

    /* 我们现在可以处理安装(错误) */
    t_thrd.log_cxt.PG_exception_stack = &localSigjmpBuf;

    /* 解除封锁信号(当邮政局长分叉我们时,信号被封锁) */
    gs_signal_setmask(&t_thrd.libpq_cxt.UnBlockSig, NULL);
    (void)gs_signal_unblock_sigusr2();

    pgstat_report_appname("LWLock Monitor");
    pgstat_report_activity(STATE_IDLE, NULL);

    /* 设置当前监视器超时 */
    cur_timeout = (long)u_sess->attr.attr_common.fault_mon_timeout * 60 * 1000;
    prev_snapshot = NULL;
    curr_snapshot = NULL;

    for (;;) {
        int rc = 0;

        /* 清除所有已挂起的唤醒 */
        ResetLatch(&t_thrd.proc->procLatch);

        /* 处理最近收到的任何请求或信号。 */
        if (t_thrd.lwm_cxt.got_SIGHUP) {
            t_thrd.lwm_cxt.got_SIGHUP = false;
            ProcessConfigFile(PGC_SIGHUP);

            long newTimeout = (long)u_sess->attr.attr_common.fault_mon_timeout * 60 * 1000;

            if (newTimeout != cur_timeout) {
                /* 用于lwlock调试信息 */
                DumpLWLockInfoToServerLog();
            }

            /* 更新监视器超时 */
            cur_timeout = newTimeout;
        }

        if (t_thrd.lwm_cxt.shutdown_requested) {
            /* lwlockmonitor的正常出口在这里 */
            proc_exit(0);
        }

        /* 如果设置u_sess->attr.attrcommon,则禁用此功能。fault_mon_timeout为0 */
        if (u_sess->attr.attr_common.fault_mon_timeout > 0) {
            /* 开始做主要工作 */
            if (NULL != prev_snapshot) {
                lwm_deadlock deadlock = {NULL, 0, 0, 0};
                bool continue_next = false;

                /* 第1阶段:使用快速变化计数进行轻量化检测 */
                curr_snapshot = pgstat_read_light_detect();
                continue_next = lwm_compare_light_detect(prev_snapshot, curr_snapshot);

                if (continue_next) {
                    /* 第2阶段(如果需要):lwlock死锁的重量级诊断 */
                    int candidates_num = 0;
                    int* candidates_pos = lwm_find_candidates(prev_snapshot, curr_snapshot, &candidates_num);
                    lwm_lwlocks* backend_locks =
                        pgstat_read_diagnosis_data(curr_snapshot, candidates_pos, candidates_num);
                    pfree_ext(candidates_pos);
                    continue_next = lwm_heavy_diagnosis(&deadlock, backend_locks, candidates_num);

                    /* 清理*/
                    for (int i = 0; i < candidates_num; i++) {
                        lwm_lwlocks* lwlock = backend_locks + i;
                        pfree_ext(lwlock->held_lwlocks);
                    }
                    pfree_ext(backend_locks);
                }

                if (continue_next) {
                    /* 阶段3(如果需要):lwlock死锁的自动修复 */
                    lw_deadlock_auto_healing(&deadlock);
                }

                /* 准备下一个监视器,并保留当前快照 */
                if (NULL != deadlock.info) {
                    pfree_ext(deadlock.info);
                }
                pfree_ext(prev_snapshot);
                prev_snapshot = curr_snapshot;
                curr_snapshot = NULL;
            } else {
                /* 第一次获取快照 */
                prev_snapshot = pgstat_read_light_detect();
                curr_snapshot = NULL;
            }
        } else {
            /* 只需设置默认超时:10分钟 */
            cur_timeout = 10 * 60 * 1000;
        }

        pgstat_report_activity(STATE_IDLE, NULL);
        rc = WaitLatch(&t_thrd.proc->procLatch, WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH, cur_timeout);

        /*
         * 如果postmaster去世,紧急救援。这是为了避免手动清理所有邮政局长的孩子。
         */
        if (((unsigned int)rc) & WL_POSTMASTER_DEATH) {
            gs_thread_exit(1);
        }
    }
}
其他函数
函数功能
bool lwm_compare_light_detect(lwm_light_detect* olds, lwm_light_detect* news)比较两个光检测数据,检查是否有任何线程可能被其他线程或其自身阻塞。可能会发生死锁吗?
int* lwm_find_candidates(lwm_light_detect* olds, lwm_light_detect* news, int* out_num)找到所有候选人,记住他们的职位和号码
static inline void init_entry_in_the_first_insert(lock_entry* entry, LWLockAddr *entry_key)如果是第一次插入则初始化条目
static inline void map_from_lock_to_holder(HTAB* map, lwlock_id_mode* held_lwlocks, int n, lock_entry_id* holder)记住这把锁的持有人。现在持有者持有编号为n的锁
static void build_holder_and_waiter_map(HTAB* map, lwm_lwlocks* candidates, int num_candidates)为这两个关系构建哈希映射(1)lwlock id及其服务员;(2)lwlock id及其持有人;
static void destroy_lock_hashtbl(HTAB* lock_map)销毁锁哈希表
static void build_map_from_threadid_to_lockid(HTAB* map, lwm_lwlocks* candidates, int num_candidates)在线程id与其获取的lwlock之间建立映射
static bool find_lock_cycle_recurse(thread_entry* check_thread, HTAB* lock_map, HTAB* tid_map, lwm_visited_thread* visited, lwm_deadlock* deadlock, int depth)查找锁定周期的安全版本
static bool find_cycle_start_point(lwm_deadlock* deadlock)找到这个死锁循环的起点
static bool find_lock_cycle( lwm_lwlocks* lock, HTAB* lock_map, HTAB* tid_map, lwm_visited_thread* visited, lwm_deadlock* deadlock)输入点以查找死锁循环
void lwm_deadlock_report(lwm_deadlock* deadlock)Report a detected deadlock, with available details.
bool lwm_heavy_diagnosis(lwm_deadlock* deadlock, lwm_lwlocks* candidates, int num_candidates)lwlock死锁检查并在需要时报告
static int choose_one_victim(lwm_deadlock* deadlock, int* out_idx)为这个死锁循环选择一个牺牲品
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值