分类 技术 下的文章

工作原因,最近在看如何自定义initramfs的脚本,稍微了解了一下相关的知识。

首先,维基百科的介绍:
Initrd ramdisk或者initrd是指一个临时文件系统,它在启动阶段被Linux内核调用。initrd主要用于当“根”文件系统被挂载之前,进行准备工作。
所以我简单的认为,这东西就是个在真正的硬盘上的文件系统被挂载之前,装载到内存里的临时文件系统。

那它,可以用来做啥呢?
initramfs可以用来在真正启动硬盘上的Linux系统之前,趁着硬盘的文件系统还没有挂载到根的时候,做一些苟且的事情。

关于具体用途,这里有一篇很好的文章:
debian系发行版initramfs调试方法

其中详细描述了修改方法,我这里简单摘要一下:

  1. 可以通过debian自带的initramfs-tools工具来修改制作initramfs
  2. 大部分修改是对 hooks、modules 和 scripts 目录下的文件进行操作

    • hooks下的脚本,在mkinitramfs命令执行时会被自动调用
    • scripts 目录下的脚本,是在 initramfs 挂载之后的不同阶段执行的
    • modules 文件中定义了在构建 initramfs 时将会被包含的内核模块
  3. 写完脚本,配置完文件后,用 sudo update-initramfs -u 命令,来更新initramfs文件系统

update-initramfs和mkinitramfs是两个脚本,如果你编译过内核应该有印象。update-initramfs会调用mkinitramfs,做了些往临时initrd目录copy文件的繁琐过程,具体如下:

  1. 在临时initrd目录下构建FHS规定的文件系统
  2. 按/etc/initramfs-tools/module和/etc/modules文件的配置,往lib/modules/目录拷贝模块,同时生成模块依赖文件modules.dep,以后内核启动后会从initramfs中(initrd.img被解压到内存中)按模块依赖关系modprobe模块
  3. 拷贝/etc/initramfs-tools/scripts和/usr/share/initramfs-tools/scripts下的配置文件到conf/目录下,以后内核启动,创建第一个进程init(initrd.img根目录下init.sh文件)会从conf/*读取配置,按一定的顺序加载模块/执行程序
  4. 模块的加载离不开modprobe工具集,因此需要拷贝modprobe工具集及其他工具到initrd目录结构下,同时解决这些工具的依赖关系(依赖的so文件的路径)
  5. 所有步骤完成,调用cpio和gzip工具打包压缩临时initrd目录结构

以上关于update-initramfs的描述,抄袭自:
制作initrd

讲完,收工

下面这个命令

lsusb -d VID:PID -v

可以看到设备的详细信息,其中 bDeviceClass 是设备类型,如果bDeviceClass为0,说明设备类型由接口层定义,可以看下面的 bInterfaceClass。
所以通过

lsusb -d VID:PID -v|grep bDeviceClass 
lsusb -d VID:PID -v|grep bInterfaceClass

综合判断可以获取USB设备类型。

具体USB设备类型可以看下面这个网站:
https://www.usb.org/defined-class-codes

BaseClassDescriptor UsageDescription
00hDeviceUse class information in the Interface Descriptors
01hInterfaceAudio
02hBothCommunications and CDC Control
03hInterfaceHID (Human Interface Device)
05hInterfacePhysical
06hInterfaceImage
07hInterfacePrinter
08hInterfaceMass Storage
09hDeviceHub
0AhInterfaceCDC-Data
0BhInterfaceSmart Card
0DhInterfaceContent Security
0EhInterfaceVideo
0FhInterfacePersonal Healthcare
10hInterfaceAudio/Video Devices
11hDeviceBillboard Device Class
12hInterfaceUSB Type-C Bridge Class
DChBothDiagnostic Device
E0hInterfaceWireless Controller
EFhBothMiscellaneous
FEhInterfaceApplication Specific
FFhBothVendor Specific

最近做个东西,需要在X11环境下创建系统右下角的图标,在网上搜了一些资料,记录一下,也许会有用。
首先搜索system tray X11,找到了这个协议:
https://specifications.freedesktop.org/systemtray-spec/systemtray-spec-0.3.html
这个协议应该是X11协议的一部分,X11的基础知识和协议就不多说了(因为我也不太明白),这个system tray的协议基本上的内容有这么几点:

  1. system tray是X11窗口管理器中的组件(在Xubuntu上是Panel里的Notification Area)需要满足的协议
  2. 要创建右下角system tray 图标,首先需要创建一个窗口(通过X11),然后获取桌面管理器的 system tray 组件,向system tray 发送 SYSTEM_TRAY_REQUEST_DOCK 的消息,将窗口嵌入到消息栏中。

获取桌面管理器system tray的代码:

Atom net_system_tray = XInternAtom(dis,"_NET_SYSTEM_TRAY_S0",False);
Window system_tray = XGetSelectionOwner(dis,net_system_tray);

向system tray发送消息的代码:

XEvent ev;

memset (&ev, 0, sizeof (ev));
ev.xclient.type = ClientMessage;
ev.xclient.window = system_tray;  //获取到的SystemTray窗口句柄
ev.xclient.message_type = XInternAtom (dpy, "_NET_SYSTEM_TRAY_OPCODE", False);
ev.xclient.format = 32;
ev.xclient.data.l[0] = CurrentTime;
ev.xclient.data.l[1] = SYSTEM_TRAY_REQUEST_DOCK;
ev.xclient.data.l[2] = win;       //需要隐藏到系统消息栏的窗口句柄
ev.xclient.data.l[3] = data2;
ev.xclient.data.l[4] = data3;

XSendEvent (dpy, w, False, NoEventMask, &ev);

从找到的资料上看,到这一步,应该就可以在system tray里创建了隐藏窗口对应的图标,可是在我的Xubuntu 16.04 上,窗口被隐藏了,但图标却没有出现。

======= update 2020.4.14 ======
X11上的问题是特别小众的问题,google能找到的资料也很少,大部分是gtk或QT如何创建system tray的资料。经过了两天的调试,最终发现了问题:

  1. 在XSendEvent之后,必须要sleep一段时间,才能调用XMapWindow把创建的图标窗口Map到系统中,否则图标就是死活看不到。
  2. 创建的窗口需要设置XSizeHints,并且把窗口设置为 _XEMBED_INFO 类型
  3. 右下角出现的图标实际就是创建出的窗口,并不是窗口本身的图标

这里就把完整代码贴上来,方便需要的人吧。

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include "unistd.h"

#define WIDTH 22
#define HEIGHT 22

Display *dis;
int s;
Window win;
GC gc;

void send_message( Display* dpy, Window w,    long message, long data1 , long
data2 , long data3 )
{
    XEvent ev;

    memset (&ev, 0, sizeof (ev));
    ev.xclient.type = ClientMessage;
    ev.xclient.window = w;
    ev.xclient.message_type =
        XInternAtom (dpy, "_NET_SYSTEM_TRAY_OPCODE", False);
    ev.xclient.format = 32;
    ev.xclient.data.l[0] = CurrentTime;
    ev.xclient.data.l[1] = message;
    ev.xclient.data.l[2] = data1;
    ev.xclient.data.l[3] = data2;
    ev.xclient.data.l[4] = data3;

    XSendEvent (dpy, w, False, NoEventMask, &ev);
    XSync (dpy, False);
}

void close_x() {
/* it is good programming practice to return system resources to the 
   system...
*/
    Display *dis = XOpenDisplay(0);
    XFreeGC(dis, gc);
    XDestroyWindow(dis,win);
    XCloseDisplay(dis);    
    exit(1);                
}

void redraw() {
    //这里实际绘制右下角图标!!!
       XSetForeground(dis,gc,0xffff00ff);
    XFillRectangle(dis,win,gc,0,0,WIDTH,HEIGHT);
    XSync(dis,s);
    XFlush(dis);
}


int main( int argc, char **argv )
{
    unsigned long buffer[(2+WIDTH * HEIGHT)];
    dis = XOpenDisplay(0);
    s = DefaultScreen(dis);
    Atom net_wm_icon = XInternAtom(dis, "_NET_WM_ICON", False);
    Atom cardinal = XInternAtom(dis, "CARDINAL", False);
    XEvent e;
    XSizeHints *  size_hints;

    if ( !( size_hints  = XAllocSizeHints() )) {
        fprintf(stderr, "Couldn't allocate memory.\n");
        return 0;
    }


    //创建一个窗口,大小就是图标大小,用于绘制右下角图标
    win = XCreateWindow(dis,RootWindow(dis,0), 0,0,16,16,1, CopyFromParent, CopyFromParent, CopyFromParent, 0, 0);   
    gc = XCreateGC(dis, win, 0,0);


    size_hints->flags       = PWinGravity | PMinSize;
    size_hints->min_width   = 22;
    size_hints->min_height  = 22;
    size_hints->win_gravity = NorthWestGravity;
    XSetWMProperties(dis, win, 0, 0, NULL, 0, size_hints, 0, 0);

    char * wm_name = "trayicon";
    XChangeProperty( dis, win,XInternAtom(dis, "_NET_WM_NAME", False),XInternAtom(dis, "UTF8_STRING", False),8, PropModeReplace, (unsigned char *) wm_name,strlen(wm_name));

    //窗口接受的事件
    XSelectInput(dis, win, ExposureMask | KeyPressMask | KeyReleaseMask |ButtonPressMask | ButtonReleaseMask  | EnterWindowMask | LeaveWindowMask | StructureNotifyMask);


    //把窗口设置为XEmbed模式
    long data[2];
    data[0] = 0;
    data[1] = 1;
    Atom embed_type = XInternAtom(dis,"_XEMBED_INFO",False);
    XChangeProperty(dis,win,embed_type,embed_type,32,PropModeReplace,(const unsigned char *)data,2);

    //获取system_tray窗口管理器,向system_tray窗口发送DOCK消息,将之前创建的窗口嵌入system_tray中
    Atom net_system_tray = XInternAtom(dis,"_NET_SYSTEM_TRAY_S0",False);
    Window tray_owner;
    tray_owner = XGetSelectionOwner(dis,net_system_tray);
    printf("tray_owner:0x%0x win:0x%0x  _NET_SYSTEM_TRAY_S0 %d \n",tray_owner,win,net_system_tray);
    send_message(dis,tray_owner,0,win,0,0);
    //一定要sleep,之后再XMapWindow,否则出不来
    usleep(10000);
    XMapWindow(dis, win);
    XSync(dis,False);
    XFlush(dis);

    //窗口事件处理,在Expose事件中绘制窗口,即右下角图标
    while(1) {
        XNextEvent(dis, &e);
        printf("event type:%d\n",e.type);

        if (e.type==Expose && e.xexpose.count==0) {
            /* the window was exposed redraw it! */
            redraw();
        }
    } 
}

每次用xshell等工具连到linux主机上看到不同的文件有各种各样的颜色,觉得很神奇,抽空整理了一下:
默认用xterm类型的终端,连接到了一台CentOS主机,不同类型的文件颜色如下:

文件类型颜色
普通文件白色
可执行文件绿色
软链接浅蓝色
失效的软链接闪烁红色
压缩包(根据扩展名判断)红色
目录深蓝色
设备黄色
Socket文件粉红
任何人具有可写权限绿色背景

差不多就这些了,如果发现新的,我再来更新