Featured image of post xorg 代码琉理

xorg 代码琉理

遇到了一个 S3 后切 tty 或者关机或者重启会出现短暂的白屏的现象。但是只要不做 S3 就不会有问题。

现在通过 kill 掉所有的其它显示的组件,比如 dde 的应用 startdde , dde-greeter , kwin_x11 (窗口管理器)。这样的话,重启 lightdm 进程,也不会出现。可以用 startx 来手动打开 xorg 来接管进程。

这样子测试,已经确定了问题出在驱动。去掉 xorg 的执行权限,startdde 会起动 kwin_wayland ,同样有白屏的问题。肯定是显卡驱动的问题了,现在的问题是这个短暂的白屏问题应该如何解决呢?

虽然问题是驱动的,但是出问题的时候,xorg 是否已经成功退出了呢?这块可以绐 xorg 下一个断点来看看。如果是 xorg 完全退出了,那就看内核的显卡驱动由 amdgpu 切换为 tty 驱动的过程;如果 xorg 没有完全退出,那就看内核退出部分的上下文。这样也是能够缩小范围的。

现在的问题是不知道 xorg 退出时执行的代码在哪里,从哪里下断点。那么就先来学习一下 xorg 的代码吧。

总结一下相关的应用:

  • kwin_x11
  • dde_lock
  • startdde
  • kwin_wayland
  • deepin-greeter
  • xorg

xorg 的合成器可以选 xrandr , opengl 和无三个选项。这些判断条件作为很多问题的排查手段是足够了的。内核负责的是 xorg 部分的代码和内核显示驱动部分的代码。熟练掌握 xorg 的调试也是必须要会的。

xorg 的代码目录

uos@guolongji:~/gg/xorg-server$ tree -d -L 1
.
├── composite
├── config
├── damageext
├── dbe
├── debian
├── dix
├── doc
├── dri3
├── exa
├── fb
├── glamor
├── glx
├── hw
├── include
├── m4
├── man
├── mi
├── miext
├── os
├── present
├── pseudoramiX
├── randr
├── record
├── render
├── test
├── Xext
├── xfixes
├── Xi
└── xkb

从重要到不重要,一个一个来说:

dix 目录,这个是最重要的,是 xorg 的 server 的核心代码实现的部分。

mi 目录,这个是与平台无关的绘制基础图形的目录代码。

miext 目录,是 mi 的扩展,如伸缩绘图等,应该是稍微复杂一点的绘图函数。

hw 上目录,与硬件操作有关联的代码。

os: 这个目录包含了与操作系统相关的代码,例如对不同操作系统的抽象接口、进程管理、时间等。

randr: 这个目录包含了与 Xorg 的 Resize and Rotate 扩展相关的代码,该扩展允许动态调整显示器分辨率和旋转屏幕方向。

xkb: 这个目录包含了与 X 键盘布局相关的代码,包括键盘布局的解析、管理等。

xcb: 这个目录包含了 XCB(X protocol C-language Binding)库的代码,XCB 是一个轻量级的 X 协议库,用于与 X 服务器通信。

config: 这个目录包含了 Xorg 的配置文件,包括默认的 Xorg 配置文件、模块加载配置文件等。

include: 这个目录包含了 Xorg 的头文件,用于在开发中引用 Xorg 的数据结构和函数接口。

programs: 这个目录包含了一些 Xorg 相关的工具和程序,例如 Xorg 服务器本身、X 窗口管理器等。

doc: 这个目录包含了 Xorg 的文档和说明,用于帮助开发者理解 Xorg 的设计和使用方法。

看完这部分,感觉 xorg 退出部分的代码应该在 dix 部分,也就是核心部分。

dix/main.c 部分

dix_main 这个函数就是了。可以在这里打一个断点。之前调试过 xorg 。不知道能不能直接使用 dbgsym 包来调试,如果不能的话至少可以重新编译一个 xorg ,用 ssh 来做调试。

为了方便调试,我把之前的环境恢复一下。全部变成可以执行,重命名的文件也恢复回来。

todo …

这是一个尝试,还有一些

tty 部分的怀疑

commit ccf8a6af43ac4b34a388e222e71f9c15a6962563
Author: wenlunpeng <[email protected]>
Date:   Thu Dec 8 18:09:07 2022 +0800

    fbcon: support Chinese charset in tty

    task: https://pms.uniontech.com/task-view-218695.html

    apply cjktty-4.19.patch, 为tty显示中文字符添加支持

    Signed-off-by: wenlunpeng <[email protected]>
    Change-Id: If85aa3a0a074e341a3b02bbfc20ac0dd1041bf68

我认为这个 patch 导致了 tty 的页面刷新的速度非常地慢。切换过程当中的白屏也有类似的刷白屏现从上到下刷出来,黑屏再从上到下刷出来的一个过程。

解 bug 过程记录

https://pms.uniontech.com/bug-view-235603.html

【问题场景】【1070 第二轮】【浪潮 CE720F 】待机唤醒后重启会闪现绿屏现象

【概率问题】必现

【问题影响】体验

【问题根因】

1 、1060U2 也有同样问题存在,但 1060 版本正常

2 、需求修改暴露问题:原本在关机的时候 startdde 会调用

org.freedesktop.login1.Manager.Reboot(0) 调用

org.freedesktop.login1.Session.Terminate() 将自身退出;后面就改成调用 .Reboot(0) 后,不再调用 Terminate(). 让 systemd 自行处理关闭进程;

3 、1060 待机唤醒后,进入桌面直接执行 org.freedesktop.login1.Manager.Reboot(0) ,也会出现这个现象

4 、modesetting 不能复现,对比 modesetting 和 amdgpu_drv 的实现可能是一个思吃点,但短时间无法解决此问题;

【评审结论】遗留

【评审人员】

【评审时间】2024.04.02

把思路汇总一下

  • 明哥的思路是对的。他找到了代码的不同之处,这些地方的实验是要做的。
  • x86 下的安装也要试一下。可以简化分析(xorg 不复现,无法分析)

1.同样的显卡使用 x86 超翔 E500 不会有花屏的现象。arm 使用世恒 KF716-Y(GREATWALL GW001M1A-FTF)是概率性的,而 loongarch M540Z 则必现。分析问题最好用 loongarch,但是 loongarch 只能用 ftrace 和加日志来分析。

2.现在尝试加一些打印,找到出现花屏的时候是在哪个函数的执行过程当中。

通过对准秒表可以用延时加打印的方式来做。

是在这个函数的后面出现的花屏,针对这个函数做分析,是不是内核当中的做了 S3 之后 fb_helper->delayed_hotplug 的这个状态的值发生了变化呢?这个值的作用是什么呢?最好的方法是按照明哥的方法。对好秒表,然后看看是从哪个函数到哪个函数调用的中间出现的黑屏。

可以桌面开一个终端看着内核日志,tty 也同时打开一个内核日志,然后,切换的过程在不同的地方加 msleep(2000) ,这样的话通过代码二分的方法可以找到内核函数执行到哪里出现了花屏。

但是首先要弄清楚 s3 到 tty 的过程用到了哪些函数,这个过程可以用 x86 的机器来试(因为驱动的流程应该是一致的),最好再找一块 radeon 520 的显卡。这样子就比较好了,或者至少也是一块 a 卡。用 bpftrace 来分析。只有一块卡的话就先在 x86 上搞清楚流程,搞清楚了之后,再用 loongarch 来二分。

drm_fb_helper_restore_fbdev_mode_unlocked+0
drm_fb_helper_hotplug_event.part.29+152
drm_fb_helper_restore_fbdev_mode_unlocked+152
drm_fb_helper_set_par+44
fb_set_var+552
fbcon_blank+132
do_unblank_screen+312
complete_change_console+156
vt_ioctl+4584
tty_ioctl+464
do_vfs_ioctl+164
ksys_ioctl+120
__arm64_sys_ioctl+28
el0_svc_common+144
el0_svc_handler+156

drm_fb_helper_restore_fbdev_mode_unlocked 这个函数是哪里调用的?

都是这个 helper 类当中的。

/**
 * drm_fb_helper_set_par - implementation for &fb_ops.fb_set_par
 * @info: fbdev registered by the helper
 *
 * This will let fbcon do the mode init and is called at initialization time by
 * the fbdev core when registering the driver, and later on through the hotplug
 * callback.
 */
int drm_fb_helper_set_par(struct fb_info *info)
{
        struct drm_fb_helper *fb_helper = info->par;
        struct fb_var_screeninfo *var = &info->var;

        if (oops_in_progress)
                return -EBUSY;

        if (var->pixclock != 0) {
                DRM_ERROR("PIXEL CLOCK SET\n");
                return -EINVAL;
        }

        // 这里调用
        drm_fb_helper_restore_fbdev_mode_unlocked(fb_helper);

        return 0;
}
EXPORT_SYMBOL(drm_fb_helper_set_par);

核心的结构体是这个 fb_ops 这个,还有 fb_info ,这些都是干什么用的?

fb_info

fb_info 这个结构体是存储 fb 的信息的,其中的 lock 是 open release ioctl 的时候用的。这里面的“par ”代表私有。

在 Linux 内核中,fbcon_par 结构体通常包含与帧缓冲控制台相关的私有数据,这些数据可能包括控制台的配置信息、显存地址、显示模式等。这些数据对于控制台的初始化、显示内容的渲染等操作都是必要的。

fbcon_par 结构体的具体内容和用途会根据不同的帧缓冲设备驱动程序而有所不同,但通常它会包含一些与控制台操作相关的信息,以便内核能够正确地管理和显示控制台内容。

struct fb_info {
        atomic_t count;
        int node;
        int flags;
        /*
         * -1 by default, set to a FB_ROTATE_* value by the driver, if it knows
         * a lcd is not mounted upright and fbcon should rotate to compensate.
         */
        int fbcon_rotate_hint;
        struct mutex lock;		/* Lock for open/release/ioctl funcs */
        struct mutex mm_lock;		/* Lock for fb_mmap and smem_* fields */
        struct fb_var_screeninfo var;	/* Current var */
        struct fb_fix_screeninfo fix;	/* Current fix */
        struct fb_monspecs monspecs;	/* Current Monitor specs */
        struct work_struct queue;	/* Framebuffer event queue */
        struct fb_pixmap pixmap;	/* Image hardware mapper */
        struct fb_pixmap sprite;	/* Cursor hardware mapper */
        struct fb_cmap cmap;		/* Current cmap */
        struct list_head modelist;      /* mode list */
        struct fb_videomode *mode;	/* current mode */

#ifdef CONFIG_FB_BACKLIGHT
        /* assigned backlight device */
        /* set before framebuffer registration,
           remove after unregister */
        struct backlight_device *bl_dev;

        /* Backlight level curve */
        struct mutex bl_curve_mutex;
        u8 bl_curve[FB_BACKLIGHT_LEVELS];
#endif
#ifdef CONFIG_FB_DEFERRED_IO
        struct delayed_work deferred_work;
        struct fb_deferred_io *fbdefio;
#endif

        struct fb_ops *fbops;
        struct device *device;		/* This is the parent */
        struct device *dev;		/* This is this fb device */
        int class_flag;                    /* private sysfs flags */
#ifdef CONFIG_FB_TILEBLITTING
        struct fb_tile_ops *tileops;    /* Tile Blitting */
#endif
        union {
                char __iomem *screen_base;	/* Virtual address */
                char *screen_buffer;
        };
        unsigned long screen_size;	/* Amount of ioremapped VRAM or 0 */
        void *pseudo_palette;		/* Fake palette of 16 colors */
#define FBINFO_STATE_RUNNING	0
#define FBINFO_STATE_SUSPENDED	1
        u32 state;			/* Hardware state i.e suspend */
        void *fbcon_par;                /* fbcon use-only private area */
        /* From here on everything is device dependent */
        void *par;
        /* we need the PCI or similar aperture base/size not
           smem_start/size as smem_start may just be an object
           allocated inside the aperture so may not actually overlap */
        struct apertures_struct {
                unsigned int count;
                struct aperture {
                        resource_size_t base;
                        resource_size_t size;
                } ranges[0];
        } *apertures;

        bool skip_vt_switch; /* no VT switch on suspend/resume required */
};

fb_ops

fb_ops 这个结构体是操作 fb 的接口。

/*
 * Frame buffer operations
 *
 * LOCKING NOTE: those functions must _ALL_ be called with the console
 * semaphore held, this is the only suitable locking mechanism we have
 * in 2.6. Some may be called at interrupt time at this point though.
 *
 * The exception to this is the debug related hooks.  Putting the fb
 * into a debug state (e.g. flipping to the kernel console) and restoring
 * it must be done in a lock-free manner, so low level drivers should
 * keep track of the initial console (if applicable) and may need to
 * perform direct, unlocked hardware writes in these hooks.
 */

struct fb_ops {
        /* open/release and usage marking */
        struct module *owner;
        int (*fb_open)(struct fb_info *info, int user);
        int (*fb_release)(struct fb_info *info, int user);

        /* For framebuffers with strange non linear layouts or that do not
         * work with normal memory mapped access
         */
        ssize_t (*fb_read)(struct fb_info *info, char __user *buf,
                           size_t count, loff_t *ppos);
        ssize_t (*fb_write)(struct fb_info *info, const char __user *buf,
                            size_t count, loff_t *ppos);

        /* checks var and eventually tweaks it to something supported,
         * DO NOT MODIFY PAR */
        int (*fb_check_var)(struct fb_var_screeninfo *var, struct fb_info *info);

        /* set the video mode according to info->var */
        int (*fb_set_par)(struct fb_info *info);

        /* set color register */
        int (*fb_setcolreg)(unsigned regno, unsigned red, unsigned green,
                            unsigned blue, unsigned transp, struct fb_info *info);

        /* set color registers in batch */
        int (*fb_setcmap)(struct fb_cmap *cmap, struct fb_info *info);

        /* blank display */
        int (*fb_blank)(int blank, struct fb_info *info);

        /* pan display */
        int (*fb_pan_display)(struct fb_var_screeninfo *var, struct fb_info *info);

        /* Draws a rectangle */
        void (*fb_fillrect) (struct fb_info *info, const struct fb_fillrect *rect);
        /* Copy data from area to another */
        void (*fb_copyarea) (struct fb_info *info, const struct fb_copyarea *region);
        /* Draws a image to the display */
        void (*fb_imageblit) (struct fb_info *info, const struct fb_image *image);

        /* Draws cursor */
        int (*fb_cursor) (struct fb_info *info, struct fb_cursor *cursor);

        /* wait for blit idle, optional */
        int (*fb_sync)(struct fb_info *info);

        /* perform fb specific ioctl (optional) */
        int (*fb_ioctl)(struct fb_info *info, unsigned int cmd,
                        unsigned long arg);

        /* Handle 32bit compat ioctl (optional) */
        int (*fb_compat_ioctl)(struct fb_info *info, unsigned cmd,
                        unsigned long arg);

        /* perform fb specific mmap */
        int (*fb_mmap)(struct fb_info *info, struct vm_area_struct *vma);

        /* get capability given var */
        void (*fb_get_caps)(struct fb_info *info, struct fb_blit_caps *caps,
                            struct fb_var_screeninfo *var);

        /* teardown any resources to do with this framebuffer */
        void (*fb_destroy)(struct fb_info *info);

        /* called at KDB enter and leave time to prepare the console */
        int (*fb_debug_enter)(struct fb_info *info);
        int (*fb_debug_leave)(struct fb_info *info);
};

代码流程梳理

明哥定位到的位置是这些函数调用的地方,我也出一个内核,把串口调好,然后看看花屏的地方是不是这些函数。首选这些地方可以加上打印,还有就是对 hotplug 的过程进行一下梳理。

drm_fb_helper_hotplug_event

/**
 * drm_fb_helper_hotplug_event - respond to a hotplug notification by
 *                               probing all the outputs attached to the fb
 * @fb_helper: driver-allocated fbdev helper, can be NULL
 *
 * Scan the connectors attached to the fb_helper and try to put together a
 * setup after notification of a change in output configuration.
 *
 * Called at runtime, takes the mode config locks to be able to check/change the
 * modeset configuration. Must be run from process context (which usually means
 * either the output polling work or a work item launched from the driver's
 * hotplug interrupt).
 *
 * Note that drivers may call this even before calling
 * drm_fb_helper_initial_config but only after drm_fb_helper_init. This allows
 * for a race-free fbcon setup and will make sure that the fbdev emulation will
 * not miss any hotplug events.
 *
 * RETURNS:
 * 0 on success and a non-zero error code otherwise.
 */
int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper)
{
        int err = 0;

        if (!drm_fbdev_emulation || !fb_helper)
                return 0;

        mutex_lock(&fb_helper->lock);
        if (fb_helper->deferred_setup) {
                err = __drm_fb_helper_initial_config_and_unlock(fb_helper,
                                fb_helper->preferred_bpp);
                return err;
        }

        if (!fb_helper->fb || !drm_fb_helper_is_bound(fb_helper)) {
                fb_helper->delayed_hotplug = true;
                mutex_unlock(&fb_helper->lock);
                return err;
        }

        DRM_DEBUG_KMS("\n");

        drm_setup_crtcs(fb_helper, fb_helper->fb->width, fb_helper->fb->height);
        drm_setup_crtcs_fb(fb_helper);
        mutex_unlock(&fb_helper->lock);

        drm_fb_helper_set_par(fb_helper->fbdev);

        return 0;
}
EXPORT_SYMBOL(drm_fb_helper_hotplug_event);

在 S3 当中,热插拔检查的逻辑是什么样的?与我现在分析的 A 卡相关的函数有三个,分别是:

int drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper *fb_helper);

void drm_fb_helper_output_poll_changed(struct drm_device *dev);

static int drm_fbdev_client_hotplug(struct drm_client_dev *client);
  • 先来看第一个 drm_fb_helper_restore_fbdev_mode_unlocked
/**
 * drm_fb_helper_restore_fbdev_mode_unlocked - restore fbdev configuration
 * @fb_helper: driver-allocated fbdev helper, can be NULL
 *
 * This should be called from driver's drm &drm_driver.lastclose callback
 * when implementing an fbcon on top of kms using this helper. This ensures that
 * the user isn't greeted with a black screen when e.g. X dies.
 *
 * RETURNS:
 * Zero if everything went ok, negative error code otherwise.
 */

这是切换到 fbcon 的过程时会调用的吗?这个看代码 lastclose 只是在 amdgpu 当中有使用,那先看一下过程。分别是一处定义和三处调用:

amdgpu_kms.c:815: * amdgpu_driver_lastclose_kms - drm callback for last close
amdgpu_kms.c:821:void amdgpu_driver_lastclose_kms(struct drm_device *dev)
amdgpu_kms.c:823:	drm_fb_helper_lastclose(dev);
amdgpu.h:1822:void amdgpu_driver_lastclose_kms(struct drm_device *dev);

amdgpu 用实现了 drm_driver ,drm_driver 用 kms_driver 来命名。drm_driver 有一些函数指针。比如这个 lastclose 和 postclose 。没事的时候就看代码,梳理流程并画图是非常地有用的。

display/modeset 只能被一个 drm_file 和 drm_device 所拥有。drm_file 和 drm_device 都有 is_master 属性。

/**
 * @delayed_hotplug:
 *
 * A hotplug was received while fbdev wasn't in control of the DRM
 * device, i.e. another KMS master was active. The output configuration
 * needs to be reprobe when fbdev is in control again.
 */
bool delayed_hotplug;

这个是什么意思呢? drm 不再控制 fbdev 的时候会收到一个热插拔信号。举例就是另一个 KMS 主被激活。当 fbdev 被重新控制的时候 output 配置需要被重新被重新检测。

别想一口吃成一个胖子,做调试的次数是不可能避免的。先用 WARN_ON 看一下调用。打印一些变量。调用多处没法看,也不知道在哪里加上变量的打印,所以先 WARN_ON 为敬。或者 ftrace ?

现在知道了大概率是 tty 唤醒时出现的白屏

那么可以用代码二分法的方式找问题的,首先要知道有哪些过程:

当系统从 Xorg 切换到 TTY(例如按下 Ctrl + Alt + F1 切换到 TTY1)时,涉及到以下主要步骤和函数:

用户输入触发: 用户按下 Ctrl + Alt + F1 或类似组合键,通知 Xorg 切换到 TTY1。

Xorg 暂停: Xorg 会暂停其运行,并释放对图形设备的控制权。

TTY 切换处理: 内核会处理 TTY 切换事件,并执行相应的操作。这通常由内核中的输入子系统处理。

TTY 设备重新激活: 内核会重新激活 TTY1 设备,以便用户可以在控制台中继续操作。这包括重新激活键盘、鼠标等输入设备,并初始化控制台显示。

Console 切换通知: 内核可能会触发一个控制台切换通知,以便用户空间程序或驱动程序可以相应地调整。

在 Linux 内核中,与 TTY 切换相关的函数通常位于 drivers/tty/tty_io.c 文件中。以下是一些可能涉及的函数:

tty_switch_to_vc(): 切换到指定的虚拟控制台。

tty_reset():重置当前的 TTY 设备状态。

con_switch():执行控制台切换操作。

vt_activate():激活指定的虚拟终端。

这些函数负责处理控制台切换事件并执行必要的操作,以便用户可以在 TTY 控制台中继续操作。

后续的测试可以用代码二分法

临时修改

int
DPMSSet(ClientPtr client, int level)
{
    int rc, i;

    DPMSPowerLevel = level;

    if (level != DPMSModeOn) {
        if (isUnblank(screenIsSaved)) {
            rc = dixSaveScreens(client, SCREEN_SAVER_FORCER, ScreenSaverActive);
            if (rc != Success)
                return rc;
        }
    } else if (!isUnblank(screenIsSaved)) {
        rc = dixSaveScreens(client, SCREEN_SAVER_OFF, ScreenSaverReset);
        if (rc != Success)
            return rc;
    }

    for (i = 0; i < screenInfo.numScreens; i++)
        if (screenInfo.screens[i]->DPMS != NULL)
            screenInfo.screens[i]->DPMS(screenInfo.screens[i], level);

    for (i = 0; i < screenInfo.numGPUScreens; i++)
        if (screenInfo.gpuscreens[i]->DPMS != NULL)
            screenInfo.gpuscreens[i]->DPMS(screenInfo.gpuscreens[i], level);

    return Success;
}


static int
ProcDPMSDisable(ClientPtr client)
{
    /* REQUEST(xDPMSDisableReq); */

    REQUEST_SIZE_MATCH(xDPMSDisableReq);

    DPMSSet(client, DPMSModeOn);

    DPMSEnabled = FALSE;

    return Success;
}

static int
ProcDPMSDispatch(ClientPtr client)
{
    REQUEST(xReq);

    switch (stuff->data) {
    case X_DPMSGetVersion:
        return ProcDPMSGetVersion(client);
    case X_DPMSCapable:
        return ProcDPMSCapable(client);
    case X_DPMSGetTimeouts:
        return ProcDPMSGetTimeouts(client);
    case X_DPMSSetTimeouts:
        return ProcDPMSSetTimeouts(client);
    case X_DPMSEnable:
        return ProcDPMSEnable(client);
    case X_DPMSDisable:
        return ProcDPMSDisable(client);
    case X_DPMSForceLevel:
        return ProcDPMSForceLevel(client);
    case X_DPMSInfo:
        return ProcDPMSInfo(client);
    default:
        return BadRequest;
    }
}

static int _X_COLD
SProcDPMSDisable(ClientPtr client)
{
    REQUEST(xDPMSDisableReq);

    swaps(&stuff->length);
    REQUEST_SIZE_MATCH(xDPMSDisableReq);

    return ProcDPMSDisable(client);
}
void
mieqProcessInputEvents(void)
{
    if (DPMSPowerLevel != DPMSModeOn)
            DPMSSet(serverClient, DPMSModeOn);
}

尝试一

DPMSSet(serverClient, DPMSModeOn);
UndisplayDevices();
DisableAllDevices();

在关闭的时候这样设置是正确的,不要用 FatalError 来打印,用 LogMessage(X_INFO, “”); 来打印。

尝试二

/**
 * Implement the screensaver by just calling down into the driver DPMS hooks.
 *
 * Even for monitors with no DPMS support, by the definition of our DPMS hooks,
 * the outputs will still get disabled (blanked).
 */
Bool
xf86SaveScreen(ScreenPtr pScreen, int mode)
{
    ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);

    if (xf86IsUnblank(mode))
        xf86DPMSSet(pScrn, DPMSModeOn, 0);
    else
        xf86DPMSSet(pScrn, DPMSModeOff, 0);

    return TRUE;
}

尝试三

DPMSForceLevel(dmxScreen->beDisplay, DPMSModeOn);

修改是没有问题的,但是却不能解决问题。现在这个问题不是说要解掉,因为了解的不足,又缺乏调试手段。那么最好的方式就是学习和总结 A 卡的代码和流程。之前对 A 卡的代码可以说是没有了解的。这一次全方位的对 A 卡的相关的流程做一下总结。看看一个 drm 的显卡到底都做哪些事情,都是怎么做的。好久没好好总结代码了。

重要的事情说三遍:总结代码,总结代码,总结代码。

路由器的高级使用方法

安装 Alist 可以方便的使用网盘。

使用 nekoray 可以方便的代理。使用非全局配置,默认就分的很好了。

我的 ubuntu-to-go 的优化

关闭 ubuntu 当中的辅助特效,可以减少撕裂。

关闭硬盘的显示。设置外观中配置 dock 行为,关闭显示卷和设备。

ubuntu 使用一个好看的 xorg 主题(没必要)。

输入法把中州韵放到第一位,再添加一个键盘英语放到第二位,这样的话,默认使用英文,可以方便打开命令。

B 站当中的一分钟变最简洁桌面,去掉顶部状态栏与左边面板。很简单,就是 setting 用鼠标点一点。会了命令也不要放弃使用简单的 UI 工具,哪个容易使用,就用哪个。关键的问题是用,而不是单纯为了炫技。

emacs 的画图

方案一

https://excalidraw.com/

和 org-excalidraw 这两个重新用起来。因为看到了高英杰画的图非常地好看,我也想画出一些有价值的图。这些高级的图和文档可以让我的思路更加的清晰。让我的效率更高。

方案二

https://emacs-china.org/t/org-drawio-drawio-svg-orgmode-buffer/26456

https://github.com/kimim/org-drawio

https://www.bilibili.com/video/BV1Gg4y1279s/?vd_source=e0fc96fdd47a5495cc13b102051c8234

综合来看,第二个方案更好,也集成在了 melpa 的源当中。需要下载一个 drawio 的应用程序,有 deb 的包可以直接下载现。

综合来看好用的东西,evince ,drawio 。emacs 和其它的应用程序分屏使用没有什么问题,而且以后一定会有类似 eaf 的更为好用的程序出现。一个东西可以用一辈子。人的一辈子是很短的,这当然是一个好的选择。

这个方案太完美了,可以在 orgmode 当中指定导出图片的位置,这样子的话应该可以和我 ox-hugo 写的博客完美的集成到一起。而且这个生成的图片也非常小。以后不管是写算法还是画图都解决了。

emacs 的日程管理

之前的日程管理方案有些复杂,主要是文件太多了,这样就会非常地让人头晕。后面只用 mydata/orgmode/gtd/_next.org 这个文件。只用一个文件会让我思路更清晰。人要选择长路大道,这样会随着时间的增长,人的能力会逐渐提高。年轻的时候可能因为懒散,智力一般没有取得什么了不起的成就,但是随着年龄的增长,一定能超越平常人。

有的事需要取巧,有些事需要漫长的积累。找到符和实际的方法,一定能得道。

emacs 的 recentf

这个是很有用的,但是被污染了。 ~/.emacs.d/bookmarks 文件和 ~/.elfeed/index 这两个文件要过滤掉,是干扰。

AI 编程

https://www.zelinai.com/model

定时任务

写一个晚上 12 更新代理配置的脚本,并重启程序。当然 12 点如果没有开机也是不生效的。再写一个开机启动的脚本,和这个类似。有了这两个东西可以做到开机无感知的使用代理。有白嫖的服务可以用,真是不好意思呢。