内存管理-43-Swap-1-命令行工具实现


一、mkswap

基于 A13 + msm-5.4

1. 简介

源码位置: /android/external/toybox/toys/other/mkswap.c

该命令对要成为swap后端的设备的首页写一些格式化的内容(也就意味着用于swap的少一页),没有调用swap相关系统调用。

使用:

/ # mkswap --help
Toybox 0.8.4-android multicall binary: https://landley.net/toybox (see toybox --help)

usage: mkswap [-L LABEL] DEVICE

Set up a Linux swap area on a device or file.

2. 用户空间实现

void mkswap_main(void)
{
    /* (1) 以可读写方式打开目标设备/文件(参数 DEVICE), *toys.optargs 应该是参数如“/dev/block/zram0” */
    int fd = xopen(*toys.optargs, O_RDWR), pagesize = sysconf(_SC_PAGE_SIZE);
    /* (2) 获取目标总长度(普通文件走 st_size,块设备优先走 ioctl(fd, BLKGETSIZE64, &size)), 可对块设备的fd进行ioctl */
    off_t len = fdlength(fd);
    /* (3) 计算可用于 swap 的页数: pages = 总页数 - 1(预留首页用于 swap header 等元数据) */ 
    unsigned int pages = (len / pagesize) - 1;
    /* (4)) toybuf 作为临时 header 缓冲区,按 unsigned int 为单位写入 */
    unsigned int*swap = (unsigned int *)toybuf;
    /* (5) header 内部偏移:  uuid 起始在 swap+3,label 起始在 swap+7 */
    char *label = (char *)(swap+7), *uuid = (char *)(swap+3);

    /*
     * 写入头部。请注意,旧版本的内核在交换过程中会检查磁盘上的签名(而不是缓存中的签名),
     * 因此写入后需要进行同步。
     *
     * (6) 填写 header 关键字段
     * swap[0] = version(1)
     * swap[1] = 总页数 - 1
     */
    swap[0] = 1;
    swap[1] = pages;
    /* (7) 跳到 1024 偏移写 header(兼容传统 swap header 布局) */
    xlseek(fd, 1024, SEEK_SET);
    /* (8) 生成 UUID(RFC4122 v4 风格随机 UUID) */
    create_uuid(uuid);
    /* (9) 若指定 -L,还可以在mkswap时写入最多 15 个字符的标签 */
    if (TT.L) strncpy(label, TT.L, 15);
    /* (10) 在偏移1024位置写 header(129 个 unsigned int),即version写的是1,last_page写的是pages */
    xwrite(fd, swap, 129 * sizeof(unsigned int));
    /* (11) 跳到第一页末尾写签名字符串 “SWAPSPACE2”, 这是内核 swapon 校验 swap 区是否合法的关键 */
    xlseek(fd, pagesize - 10, SEEK_SET);
    xwrite(fd, "SWAPSPACE2", 10); //即 magic 写的是"SWAPSPACE2"
    /* (12) 强制落盘(旧内核在 swapon 时会直接检查磁盘内容) */
    fsync(fd);

    /* 此宏为0,不执行 */
    if (CFG_TOYBOX_FREE) close(fd);

    /* (13) 组织输出信息(大小、label、UUID) */
    if (TT.L) sprintf(toybuf, ", LABEL=%s", label);
    else *toybuf = 0;
    printf("Swapspace size: %luk%s, UUID=%s\n", pages * (unsigned long)(pagesize/1024), toybuf, show_uuid(uuid));
}

交换区的第一页是交换区首部,内核使用数据结构 swap_header 描述交换区首部,上面主要写这个首页:

//include/linux/swap.h

union swap_header {
    struct {
        char reserved[PAGE_SIZE - 10];
        char magic[10];            /* SWAP-SPACE or SWAPSPACE2 */
    } magic;
    struct {
        char        bootbits[1024];    /* Space for disklabel etc. */
        __u32        version;
        __u32        last_page;
        __u32        nr_badpages;
        unsigned char    sws_uuid[16];
        unsigned char    sws_volume[16];
        __u32        padding[117];
        __u32        badpages[1];
    } info;
};

成员介绍:

前面1024字节空闲,为引导程序预留空间,这种做法使得交换区可以处在磁盘的起始位置。
成员 version 是交换区的版本号。
成员 last_page 是最后一页的页号。
成员 nr_badpages 是坏页的数量,从成员 badpages 的位置开始存放坏页的页号。
最后10字节是魔幻数,用来区分交换区格式,内核已经不支持旧的格式"SWAPSPACE",只支持格式"SWAPSPACE2"。

3. 内核空间实现

不涉及。

4. 小结

(1) swap可用空间大小是指定大小减去1个page=4k的大小。

 

二、swapon

1. 简介

启用swap后端设备。

源码位置: /android/external/toybox/toys/other/swapon.c

用法:

/ # swapon --help
Toybox 0.8.4-android multicall binary: https://landley.net/toybox (see toybox --help)

usage: swapon [-d] [-p priority] filename

Enable swapping on a given device/file.

-d      Discard freed SSD pages
-p      Priority (highest priority areas allocated first)

-p指定优先级,高优先级设备被优先使用。

2. 用户空间实现

#define FLAG_d (1<<0)
#define FLAG_p (1<<1)

void swapon_main(void)
{
    /* 0x70000 = SWAP_FLAG_DISCARD|SWAP_FLAG_DISCARD_ONCE|SWAP_FLAG_DISCARD_PAGES */
  /* 如果&的结果是0,结果就是0。如果&的结果是1,结果是0x70000 */
    int flags = (toys.optflags & FLAG_d) * 0x70000;

    /* 若指定了 -p 优先级参数(0..32767),flags 参数或上 0x8000 | <prio> << 0 */
    /* 这里若只指定了 -d 不也会执行吗 */
    if (toys.optflags)
        flags |= SWAP_FLAG_PREFER | (TT.p << SWAP_FLAG_PRIO_SHIFT);

    /* 调用swapon系统调用 */
    if (swapon(*toys.optargs, flags))
        perror_exit("Couldn't swapon '%s'", *toys.optargs);
}

相关标志位:

SWAP_FLAG_DISCARD: 表示启用 swap discard 机制.
SWAP_FLAG_DISCARD_ONCE: 表示 swapon 时整块 swap 区域 discard 一次。可理解为上线时整盘清一遍。
SWAP_FLAG_DISCARD_PAGES: 表示运行时每当 swap 页释放时,对应块做 discard 或按页 discard。可理解为运行时边回收边清。

3. 内核空间实现

对应系统调用:
SYSCALL_DEFINE2(swapon, const char __user *, specialfile, int, swap_flags) //mm/swapfile.c

TODO

 


三、swapoff

1. 简介

关闭swap后端设备。

源码位置: android/external/toybox/toys/other/swapoff.c

/ # swapoff --help
Toybox 0.8.4-android multicall binary: https://landley.net/toybox (see toybox --help)

usage: swapoff swapregion

Disable swapping on a given swapregion.

2. 用户空间实现

void swapoff_main(void)
{
    if (swapoff(toys.optargs[0]))
        perror_exit("failed to remove swaparea");
}

3. 内核空间实现

系统调用:
SYSCALL_DEFINE1(swapoff, const char __user *, specialfile) //mm/swapfile.c

TODO

 

四、实验

1. file-swap

/ # dd if=/dev/zero of=/data/vendor/swap/swapfile bs=1m count=200
200+0 records in
200+0 records out
209715200 bytes (200 M) copied, 0.276512 s, 723 M/s
/ #
/ # mkswap /data/vendor/swap/swapfile
Swapspace size: 204796k, UUID=54b045bd-2b52-430b-8dda-04a7946ae4b2
/ #
/ # swapon /data/vendor/swap/swapfile -p 32759
/ # cat /proc/swaps
Filename                                Type            Size    Used    Priority
/dev/block/zram0                        partition       6291452 723888  32758
/data/vendor/swap/swapfile              file            204796  0       32759
/ #
/ # swapoff /data/vendor/swap/swapfile //这样可以关闭
/ # cat /proc/swaps
Filename                                Type            Size    Used    Priority
/dev/block/zram0                        partition       6291452 811068  32758


2. zram-swap

/ # swapoff /dev/block/zram0
/ # cat /proc/swaps
Filename                                Type            Size    Used    Priority
/ #
/ # mkswap /dev/block/zram0
Swapspace size: 6291452k, UUID=c90e5ef6-2bde-40eb-9f7b-86dd027628a7
/ #
/ # swapon /dev/block/zram0 -p 32758
/ # cat /proc/swaps
Filename                                Type            Size    Used    Priority
/dev/block/zram0                        partition       6291452 0       32758

 

posted on 2026-03-31 11:29  Hello-World3  阅读(26)  评论(0)    收藏  举报

导航