Linux源代码阅读——环境准备

转自:http://home.ustc.edu.cn/~boj/courses/linux_kernel/0_prepare.html

目录

  1. Linux 系统环境准备

    • 定制安装 Ubuntu
    • 安装工具链
  2. 编译 Linux 内核
    • 默认编译
    • 自定义编译
  3. 模拟执行 Linux
    • 用 qemu 模拟 Hello World 系统
  4. 准备源码阅读环境
    • vim 基本设置
    • 在 vim 中使用 cscope
    • 在 vim 中使用 ctags
    • 使用 taglist 显示 symbol 窗口
  5. Linux 内核源代码结构

1 Linux系统环境准备

1.1 定制安装Ubuntu

  1. 从科大源下载 Ubuntu Alternative CD,校验 MD5。
  2. 挂载 ISO,使用 preseed 文件来进行一些预定制,参考 http://pxe.ustc.edu.cn/boot/conf/preseed.debian.ustc.cfg(当然其中的 debian 需要改为 ubuntu,hostname 改成自己希望的主机名),指定安装所使用的源,以及其他一些预定义设置。
  3. 卸载ISO,使用 usb-creator-gtk 制作启动U盘。
  4. 使用 parted 分区:将硬盘的一个空闲 NTFS 分区删除;mkpartfs 建立一个ext2(用于/boot)、一个swap、一个ext4(用于根分区)。
  5. 从U盘启动,选择 Install a command-line system,设置键盘、分区等(其他设置已经写在 preseed 中了),安装基本系统,设置用户名密码。
  6. grub-mkimage 制作包含 reiserfs 模块的 grub2(原有的 linux 所在分区是 reiserfs 的),grub-mkconfig 更新 grub.cfg 配置文件。
  7. 安装 X(xserver-xorg)、GNOME 桌面环境(gdm、gnome-core、xinit、gnome-utils、synaptic)
  8. 安装中文语言包(language-pack-zh-base、language-support-fonts-zh、xfonts-wqy、language-selector),修改 locale
  9. 安装输入法(fcitx)、vim等
  10. 安装、设置声卡驱动(alsa)(特定硬件的驱动问题,采用网上的办法)
  11. 安装 ntfs-3g、wine 等系统工具,tcpdump、firefox、filezilla、amule 等网络工具,okular、chmsee、libreoffice 等办公工具
  12. 安装 ssh、nginx、php5-fpm、MySQL、memcached、vsftpd 等服务器需要的 daemon(网络开发用)
  13. apt-get clean 清除缓存
  14. 编辑 /etc/modules,开机加载 fuse(用户态文件系统)模块,以便使用 ntfs-3g
  15. 编辑 /etc/fstab 将 /dev/sda4(NTFS)自动挂载上,为方便 Windows 使用,学习资料等数据放在这个分区。
    # <file system> <mount point>   <type>  <options>       <dump>  <pass>
    proc            /proc           proc    nodev,noexec,nosuid 0       0
    # / was on /dev/sda2 during installation
    UUID=d993c54c-0602-48d2-935f-be9ac654f9a0 /               ext4    errors=remount-ro 0       1
    /dev/sda1       none            swap    sw              0       0
    /dev/sda4   /media/DATA ntfs-3g silent,umask=0,locale=zh_CN.utf8  0   0
    

1.2 安装工具链

  • 编译内核所需工具链:

    • build-essential (gcc, make, ld等)
    • binutils
    • libncurses-dev (make menuconfig 时需要)
    • kernel-package
  • 模拟内核所需工具:
    • qemu
    • gdb
    • fakeroot (chroot)
    • initramfs-tools
    • module-init-tools

2 编译Linux内核

2.1 默认编译

  1. 从 kernel.org 下载 Linux 2.6.26 源码,tar -zvxf
  2. make mrproper 清除所有已编译文件和配置文件
  3. make i386_defconfig 生成x86体系结构的默认配置。此过程首先HOSTCC、SHIPPED、HOSTLD一些配置文件,然后进入 Linux Kernel Configuration,与 make config 的命令行界面相同,只是所有选项都被自动选择了(具体参见 arch/x86/configs/i386_defconfig)。
  4. make 编译代码。由CC、LD、AR、AS、GEN、HOSTCC、OBJCOPY、BUILD等过程组成。其中有一些 warning,是因为代码不符合当前C标准,可以忽略。
    BUILD   arch/x86/boot/bzImage
    Root device is (8, 2)
    Setup is 10140 bytes (padded to 10240 bytes).
    System is 2651 kB
    CRC e3791c7
    Kernel: arch/x86/boot/bzImage is ready  (#1)
    

2.2 自定义编译

  1. 每次更改配置重新编译前,make clean 清除上次编译的目标文件和中间文件,但保留配置文件以便在其基础上修改。
  2. make menuconfig 进入终端下的菜单选择。相比完全命令行界面的 config,用起来更方便;而图形界面的 xconfig 或 gconfig 虽然比较直观,但不便于使用键盘。
  3. 设置完毕,保存到 .config
  4. make 编译代码,生成的内核映像是 arch/x86/boot/bzImage
    Root device is (8, 2)
    Setup is 10140 bytes (padded to 10240 bytes).
    System is 3063 kB
    CRC aa68b290
    Kernel: arch/x86/boot/bzImage is ready  (#3)Building modules, stage 2.MODPOST 26 modules
    
  5. 由于我们是在虚拟机内运行而不是安装到本地系统,因此不需要 make install

具体设置参考:Linux 2.6.19.x 内核编译配置选项简介(金步国)

  • General Setup

    • 为 local version 赋予一个惟一的自定义版本号(如boj1),以免不同的内核发生混淆
    • 选中 Auditing Support,以方便以后试验 SELinux
    • 要选中 Initramfs,现在 Linux 启动一般都用 initramfs(原来的 initrd)
  • Loadable Module Support
    • modprobe 时需要使用
    • 选中 Automatic kernel module loading,让内核通过运行 modprobe 来自动加载所需要的模块,自动解决模块的依赖关系
  • Enable the block layer
    • 块设备需要使用
    • Large Block Device 暂时不需要,没有 2TB 以上的块设备
  • Processor Type and Features
    • 根据 /etc/cpuinfo,当前的CPU是 Intel(R) Core(TM) Duo CPU T2250 @ 1.73GHz
      因此选择 Core 2/Newer Xeon。
    • 由于只在自己的计算机上使用,取消 Generic x86 support。
    • 由于自己的CPU是双核的,Maximum number of CPUs 设为2,每个CPU在内核映像中占据8KB。
    • High Memory Support 选择 4G(由于内存是2.5G的)
    • Timer Frequency 由于是桌面系统,对实时性要求较高,设为 1000Hz。
  • Power management options
    • 选中 Hibernation,以实现休眠功能
  • Bus options
    • 选中 PCI Express Support,显卡和网卡都需要
  • Executable file formats
    • Kernel support for MISC binaries 允许插入二进制的封装层到内核中,使用 Java 等语言编写的程序时需要它
  • Networking Support
    • Networking options

      • 选中 Packet Socket 和 mmaped IO,以便使用网络监控工具。
      • 选中 TCP Syncookie support,抵抗 SYN flood 攻击。需要同时启用 /proc 文件系统和 Sysctl support,然后在系统启动并挂载了 /proc 之后执行 "echo 1 >/proc/sys/net/ipv4/tcp_syncookies" 命令。
      • 选中 Network packet filtering,Netfilter 对数据包进行过滤和修改。
        • 选中 IPv4/IPv6 connection tracking support,以使用 NAT。
        • 选中 IP tables 和 ARP tables,经常需要用。其中选中 Full NAT 以便使用 iptables 中的 NAT 功能。
        • 选中 IP6 tables support,监控 IPv6 协议需要用。
      • 选中 Network Testing 中的 Packet Generator(编译为 pktgen 模块),方便网络测试。
    • 选中 Ameteur Radio Support,因为我们要玩无线电。AX.25 协议编译为 ax25 模块。
    • 将 IrDA (infrared) subsystem support 编译为 irda 模块,以便使用无线鼠标键盘。
    • 选中 Bluetooth subsystem support,蓝牙经常要用。
    • 选中 Generic IEEE 802.11 Networking Stack,wifi 等都是基于 802.11 的协议。
  • Device Drivers
    • Block Devices

      • 取消 Normal Floppy Disk support,没有软驱
      • 需要选中 RAM block device support,initrd 需要用。
        Menuconfig Help 中说

          │ Saying Y here will allow you to use a portion of your RAM memory as│ a block device, so that you can make file systems on it, read and│ write to it and do all the other things that you can do with normal│ block devices (such as hard drives). It is usually used to load and│ store a copy of a minimal root file system off of a floppy into RAM│ during the initial install of Linux.Most normal users won't need the RAM disk functionality, and can thus say N here.
        

        因此第一次配置时没有编译进内核,导致虚拟机无法启动:

        [   0.776881] Unpacking initramfs...<0>Kernel panic - not syncing: bad gzip magic numbers
    • ATA/ATAPI/MFM/RLL support
      • 取消 Include IDE/ATA-2 DISK support
    • SCSI device support
      • 选中 RAID Transport Class,将来可能折腾 RAID
    • Multiple devices driver support (RAID and LVM)
      • 将 RAID support 及相关选项编译为模块,以使用软RAID。
      • 选中 Device Mapper support,以使用 LVM。
    • 取消 Macintosh device drivers
    • Multimedia devices
      • 选中 Video for Linux,以便进行摄像头编程
    • Sound
      • 选中 Advanced Linux Sound Architechture,计算机上的声卡需要用 ALSA。
      • 取消 Open Sound System
    • 选中 Real Time Clock,以便获取系统时间
    • 选中 DMA Engine Support,以便使用 Direct Memory Access
  • Firmware Drivers
    没有特殊固件,因此保留默认配置。
  • File Systems
    • ext2、ext3、ext4 都是 Linux 经典文件系统,经常需要用到,编译进内核
    • ReiserFS 对于小文件的处理效率很高,有个存放各种源代码的分区用这个文件系统,编译进内核
    • 选中 Filesystem in Userspace Support,ntfs-3g 是基于 fuse 的,这样不用每次开机都加载 fuse 模块。
    • 微软文件系统:不选 NTFS 文件系统支持,内核中对于 NTFS 写的支持不好,使用用户态的 ntfs-3g 代替。
    • 本地语言支持:默认本地语言设为 utf-8,选中 Codepage 437, GB2312, Big5, ASCII, ISO 8859-1, UTF-8.
  • Kernel Hacking
    • 选中 Show timing information on printks,方便内核调试
    • 选中 Magic SysRq key,在紧急状态时使用 reisub 夺回系统控制权
  • Security Options
    暂时不需要其他安全模型,保留默认值,全不选。
  • Cryptographic options
    将 MD5、SHA1、Blowfish、DES 等常用加密算法编译为模块备用。
  • Virtualization
    电脑硬件不支持 KVM,因此不选。
  • Library routines
    保留默认值。

3 模拟执行 Linux

3.1 用 qemu 模拟 Hello World 系统

  1. 写一个 Hello World 程序:

    [boj@boj-laptop:~/test]$ cat test.c
    #include<stdio.h>
    int main() {printf("Hello World!");return 0;
    }
    
  2. 静态链接编译
    [boj@boj-laptop:~/test]$ gcc -static -o init test.c
  3. 建立目标根目录映像
    [boj@boj-laptop:~/test]$ dd if=/dev/zero of=initrd4M.img bs=4096 count=1024
    [boj@boj-laptop:~/test]$ mke2fs initrd4M.img
    [boj@boj-laptop:~/test]$ mkdir rootfs
    [boj@boj-laptop:~/test]$ sudo mount -o loop initrd4M.img rootfs
    
  4. 将init拷贝到目标根目录下
    [boj@boj-laptop:~/test]$ cp init rootfs/
  5. 准备 /dev 目录、console 设备、linux 根设备(ram)
    [boj@boj-laptop:~/test]$ sudo mkdir rootfs/dev
    [boj@boj-laptop:~/test]$ sudo mknod rootfs/dev/console c 5 1
    [boj@boj-laptop:~/test]$ sudo mknod rootfs/dev/ram b 1 0
    [boj@boj-laptop:~/test]$ sudo umount rootfs
    
  6. 用 qemu 模拟运行
    [boj@boj-laptop:~]$ qemu -kernel linux-2.6.26/arch/x86/boot/bzImage -initrd test/initrd4M.img -append "root=/dev/ram init=/init"
    

    一段初始化后,输出了 Hello World,随即 Kernel Panic(因为 init 在输出字符串后就自行退出了)

    [    4.270006] Freeing unused kernel memory: 244k freed
    Hello World![    4.326627] Kernel panic - not syncing: Attempted to kill init!

4 准备源码阅读环境

4.1 vim 基本设置

[boj@boj-laptop:~]$ cat ~/.vimrc
set encoding=UTF-8
set langmenu=zh_CN.UTF-8
language message zh_CN.UTF-8
set fileencodings=ucs-bom,utf-8,cp936,gb18030,big5,euc-jp,euc-kr,latin1
set fileencoding=utf-8
sy on
set ai
set nu
set shiftwidth=2
set tabstop=4
set softtabstop=4
set ic "搜索时忽略大小写,以便在某些“骆驼”变量名风格的源码中查找

4.2 在 vim 中使用 cscope

  1. 安装 CScope:

    sudo apt-get install cscope
  2. 确认 vim 支持 cscope:
    vim --version | grep cscope
  3. 给源代码建立索引:
    cscope -Rbq

    其中,ctags 递归的在每个目录下生成 tags 文件,供 vim 读取;cscope 生成 cscope.out

    cscope.out: cscope reference data version 15 with inverted index
  4. 将以下内容添加到 ~/.vimrc 中,以自动加载 cscope.out。
    """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
    " cscope settingif has("cscope")set csprg=/usr/bin/cscope              "指定用来执行 cscope 的命令set csto=1                             "先搜索tags标签文件,再搜索cscope数据库set cst                                "使用|:cstag|(:cs find g),而不是缺省的:tagset nocsverb                           "不显示添加数据库是否成功" add any database in current directoryif filereadable(ncscope.out")cs add cscope.out                   "添加cscope数据库endifset csverb                             "显示添加成功与否
    endifnmap <C-@>s :cs find s <C-R>=expand("<cword>")<CR><CR>
    nmap <C-@>g :cs find g <C-R>=expand("<cword>")<CR><CR>
    nmap <C-@>c :cs find c <C-R>=expand("<cword>")<CR><CR>
    nmap <C-@>t :cs find t <C-R>=expand("<cword>")<CR><CR>
    nmap <C-@>e :cs find e <C-R>=expand("<cword>")<CR><CR>
    nmap <C-@>f :cs find f <C-R>=expand("<cfile>")<CR><CR>
    nmap <C-@>i :cs find i ^<C-R>=expand("<cfile>")<CR>$<CR>
    nmap <C-@>d :cs find d <C-R>=expand("<cword>")<CR><CR>
    """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
    
  5. 根据上面的 .vimrc,使用简单的组合键和字母即可使用 cscope 的查找功能。

    例如,<C-@>g 先按 ctrl+@,再按 g,即可查看当前光标所在符号的定义。

    :cs help

    • s: 查找 C 语言符号,即查找函数名、宏、枚举值等出现的地方
    • g: 查找函数、宏、枚举等定义的位置,类似 ctags 所提供的功能
    • d: 查找本函数调用的函数
    • c: 查找调用本函数的函数
    • t: 查找指定的字符串
    • e: 查找 egrep 模式,相当于 egrep 功能,但查找速度快多了
    • f: 查找并打开文件,类似 vim 的 find 功能
    • i: 查找包含本文件的文件

4.3 在 vim 中使用 ctags

在源代码根目录下执行 ctags -R 命令用来为程序源代码生成标签文件,-R 选项表示递归操作,同时为子目录也生成标签文件。vim 利用生成的标签文件,可以进行相应检索、并在不同的文件中的C语言元素之间来回切换。

需要在 tags 文件所在的目录下运行 vim。否则,要用 :set tags=xxx 指定 tags 文件的路径。

使用 tag 命令时,可以使用 TAB 键进行匹配查找,继续按 TAB 键向下切换。

  • 跳到函数或数据结构xxx处

    :tag xxx
  • 跳到第一个定义处,优先跳转到当前文件
    :tnext
  • 跳到第一个
    :tfirst
  • 跳到前 count 个
    :[count]tprevious
  • 跳到后 count 个
    :[count]tnext
  • 跳到最后一个
    :tlast
  • 在所有 tagname 中选择
    :tselect tagname
  • 跳到包含 block 的标识符
    :tag /block 

    (这里 '/' 就是告诉 vim,'block' 是一个语句块标签。)

  • 跳转到光标所在函数标识符的定义处
    Ctrl+]
  • 使用"ctrl+t"退回上层
    Ctrl+t
  • 在以 write_ 开头的标识符中选择
    :tselect /^write_

    这里,'^'表示开头,同理,'$'表示末尾。

在函数中移动光标的快捷键:

  • [{ 转到上一个位于第一列的"{"
  • }] 转到下一个位于第一列的"{"
  • { 转到上一个空行
  • } 转到下一个空行
  • gd 转到当前光标所指的局部变量的定义
  • * 转到当前光标所指的单词下一次出现的地方
  • # 转到当前光标所指的单词上一次出现的地方

4.4 使用 taglist 显示 symbol 窗口

taglist 插件可以像 Source Insight 那样将当前文件中的宏、全局变量、函数等 tag 显示在 Symbol 窗口,用鼠标点上述 tag,就跳到该 tag 定义的位置;可以按字母序、该tag所属的类或scope,以及该 tag 在文件中出现的位置进行排序;如果切换到另外一个文件,Symbol 窗口更新显示这个文件中的 tag。taglist 依赖于 ctags。

  1. 打开 vim 的文件类型自动检测功能;
  2. 系统中装了 Exuberant ctags 工具,并且 taglist 能够找到此工具(因为 taglist 需要调用它来生成 tag 文件);
  3. vim 支持 system() 调用;
  4. 在 ~/.vimrc 中加入
    """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
    " ctags setting
    set tags=./tags,./../tags,./*/tags;" Tag list (ctags)filetype on                            "文件类型自动检测if MySys() == "windows"                "设定windows系统中ctags程序的位置let Tlist_Ctags_Cmd = 'ctags'
    elseif MySys() == "linux"              "设定linux系统中ctags程序的位置let Tlist_Ctags_Cmd = '/usr/bin/ctags'
    endiflet Tlist_Show_One_File = 1            "不同时显示多个文件的tag,只显示当前文件的
    let Tlist_Exit_OnlyWindow = 1          "如果taglist窗口是最后一个窗口,则退出vim
    let Tlist_Use_Right_Window = 1         "在右侧窗口中显示taglist窗口 map <silent> <F8> :TlistToggle<cr>     "在映射F8键打开tags窗口
    """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
    

上述配置过程参考了:http://www.cnblogs.com/sunblackshine/archive/2011/08/25/2152962.html

5 Linux 内核源代码结构

Linux 2.6.26 源码各目录大致作用:

  • Documentation/ 内核文档,包括 CodingStyle 等
  • include/子目录包含了建立内核代码时所需的大部分包含文件,这个模块利用其它模块重建内核。每种体系结构有相应的子目录。
  • init/ 子目录包含了内核的初始化代码,这是内核开始工作的起点。(main.c, function start_kernel)
  • arch/ 子目录包含了所有硬件结构特定的内核代码,在 x86 体系结构下,包括 kernel、mm、lib 等子目录,分别是与体系结构相关的核心、内存管理、库代码。
  • drivers/ 目录包含了内核中所有的设备驱动程序,如字符设备、块设备。
  • fs/ 目录包含了所有文件系统的代码,如:ext2、vfat、proc。
  • net/ 目录包含了内核的网络代码。
  • mm/ 目录包含了所有的内存管理代码。
  • ipc/ 目录包含了进程间通信的代码。
  • kernel/ 目录包含了主内核代码
  • lib/ 目录包含了与体系结构无关的内核库代码
  • modules/ 目录包含了可动态加载的模块。
  • Scripts/ 目录包含了配置核心的脚本文件。

Copyright © 2012 李博杰 PB10000603

This document is available from http://home.ustc.edu.cn/~boj/courses/linux_kernel/0_prepare.html

【作者】张昺华
【出处】http://www.cnblogs.com/sky-heaven/
【博客园】 http://www.cnblogs.com/sky-heaven/
【新浪博客】 http://blog.sina.com.cn/u/2049150530
【知乎】 http://www.zhihu.com/people/zhang-bing-hua
【我的作品---旋转倒立摆】 http://v.youku.com/v_show/id_XODM5NDAzNjQw.html?spm=a2hzp.8253869.0.0&from=y1.7-2
【我的作品---自平衡自动循迹车】 http://v.youku.com/v_show/id_XODM5MzYyNTIw.html?spm=a2hzp.8253869.0.0&from=y1.7-2
【新浪微博】 张昺华--sky
【twitter】 @sky2030_
【facebook】 张昺华 zhangbinghua
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.

非常好!!!Linux源代码阅读——环境准备【转】相关推荐

  1. Linux 源代码阅读知识点及要求

    说明:1.本次源代码阅读,以Linux 最新的稳定版本(2.6)为主: 2.源代码下载地址: 在官方站点 www.kernel.org 上最新稳定版本是 2.6.13.2: 在清华的 ftp 上随时都 ...

  2. 非常好!!!Linux源代码阅读——内核引导【转】

    Linux源代码阅读--内核引导 转自:http://home.ustc.edu.cn/~boj/courses/linux_kernel/1_boot.html 目录 Linux 引导过程综述 BI ...

  3. linux源代码阅读组合vim tags taglist

    vim+ctags+taglist 在linux下构建类似于windows sourceinsight的源码阅读环境 /***********************************/ /** ...

  4. linux 内核round-robin scheduler代码,LINUX源代码阅读报告

    进程调度代码分析 --关于LINUX源代码中进程调度部分的读书报告 在多进程的操作系统中,进程调度是一个全局性.关键性的问题,它对系统的总体设计.系统的的实现.功能设置以及各个方面的性能都有着决定性的 ...

  5. linux源代码阅读笔记 find_entry分析

    78 static struct buffer_head * find_entry(struct m_inode * dir,79 const char * name, int namelen, st ...

  6. linux源码阅读神器,Ubuntu下安装LXR Linux源代码阅读利器

    1.安装apache2 sudo apt-get install apache2 2.安装lxr sudo apt-get install lxr 3. 在/etc/apache2/httpd.con ...

  7. 配置Linux虚拟机编译环境

    主要平台和工具简介 x86模拟器Bochs Bochs是一个免费且开放源代码的IA-32(x86)架构PC机模拟器.在它模拟出的环境中可以运行Linux.DOS和各种版本的Windows等多种操作系统 ...

  8. Linux 下源代码阅读工具 —— vim + TagList + CTags

    为什么不采用 Windows 下较为著名的源代码阅读软件 SourceInsight, 其一,其在 Linux 下的安装较为繁琐: 其二,切换代码时背景色的变化会为人的眼部产生极为不舒服的感觉: 其三 ...

  9. linux下阅读源代码的工具

    最近刚刚安装了Ubuntu,打开源码之后,突然发现阅读代码有些不习惯,故上网搜一下linux的源码阅读工具,做一个记录.一下内容来自https://www.cnblogs.com/lidabo/p/4 ...

最新文章

  1. 镜像打包工具clonezilla
  2. 计算距离torch.nn.PairwiseDistance
  3. linux cron指定用户,Centos下crontab指定执行用户
  4. 修改Linux终端命令行字体颜色(对比明显,超炫酷)
  5. 查看http的并发请求数与其TCP连接状态
  6. AngularJS-Hello World
  7. 利用Session实现一次性验证码(多学一招)
  8. SQL Server与Oracle中的隔离级别
  9. python连接阿里云服务器_阿里云服务器python
  10. 编辑器漏洞、越权、逻辑漏洞(不安全的对象引用、功能级别访问控制缺失)
  11. 飞信2009_从飞信倒下的八个原因看运营商的复兴之路
  12. 怎样用电脑收发短信?
  13. Unity3D空战游戏模板 Air Warfare Pro
  14. 传华虹NEC重启赴港上市计划 可能融资3亿美元
  15. mongoDB Ops Manager
  16. 从LCN的两阶段提交到TCC补偿事务方案
  17. 10.statement对象实例(executeUpdate方法以及executeQuery方法),JDBC工具类编写
  18. 用条件编译方法实现以下功能:输入一行电报文字,可以任选两种输出,一为原文输出;一为将字母变成其下一个字母。用define命令控制
  19. 解决:AttributeError: can't set attribute
  20. Qt按ESC关闭模态对话框不触发closeEvent()问题解析(转)

热门文章

  1. HttpClient 中文官方教程----第一章基础知识-只收录,未测试
  2. Android PullToRefreshListView和ViewPager的结合使用
  3. JUnit 3.8 通过反射测试私有方法
  4. linux运维实战练习及linux shell脚本、awk、sed工具命令学习总结
  5. Javascript:必须知道的Javascript知识点之“字面量和对应类型”
  6. IBM磁带库中更换磁带的步骤
  7. vue页面自适应屏幕宽高_Vue+Element UI 高度实时自适应
  8. java 向量上的坐标点_新高三知识点-点的平移公式
  9. 5G 信令流程 — UE 状态机
  10. python_控制台输出带颜色的文字方法