使用 overlayfs+chroot 搭建临时环境

最近在写一个小工具,依赖的库比较多,但是这些依赖都不常用,不想为了开发而装在常用的机器上。想用以前用 chroot 搞的一个开发环境,可惜更新源验证总出问题,搜索了几个方案后还是没解决。我只是想搞个隔离的测试环境,不需要限制资源,docker 又庞大配置又麻烦(其实根本原因是我讨厌 golang),又不想重新配置一份环境,于是搜了下 docker 相关的底层技术,最后发现 overlayfs+chroot 就能完美解决我的需求,在这里记录一下,内核版本是 5.6。

overlayfs 介绍

顾名思义,overlayfs 就是把多个目录组合在一起形成一个新的文件系统,在 2014 年被合并进 kernel v3.18。先来看下使用时的命令行参数:

mount -t overlay overlay -o lowerdir=<lowerdir1>:<lowerdir2>:<...>,upperdir=<upperdir>,workdir=<workdir> <mountpoint>

大概介绍下这里的三个参数:

  • lowerdir:只读目录,多个目录用 ":" 分隔。
  • upperdir:可读可写目录。如果 upperdirlowerdir 中有同名的文件,会优先展示 upperdir 中的文件(换言之就是 lowerdir 中的文件被屏蔽了)。

阅读全文…

Linux 异步 IO 之 eventfd

经过 3 个月的休(tou)息(lan),是时候写点东西了……

eventfd

较新版本的 Linux 内核(2.6.22 之后)提供了一个新的系统调用 eventfd() 来实现事件通知(参考资料 [1]):

#include <sys/eventfd.h>

int eventfd(unsigned int initval, int flags);

函数返回一个可用于 read()/write()/epoll()/close() 的 fd,fd 包含一个由内核维护的 8 字节的无符号整数,初始值由第一个参数 initval 制定。write() 的 buffer 大小都限定为 8 字节,如果 buffer 大小不等于 8 字节则会返回错误,如果提供给 read() 的大小小于 8 …

阅读全文…

Linux 异步 IO 之 Native AIO

Linux Native AIO

来看看 Linux 提供的 AIO 系统调用(自行封装的头文件 native_aio.h):

#ifndef __NATIVE_AIO_H__
#define __NATIVE_AIO_H__

#define _GNU_SOURCE

#include <unistd.h>
#include <sys/syscall.h>
#include <sys/time.h>
#include <linux/aio_abi.h>

static inline int io_setup(unsigned nr_events, aio_context_t* ctx_idp)
{
    return syscall(__NR_io_setup, nr_events, ctx_idp);
}

static inline int io_destroy(aio_context_t 

阅读全文…

Linux 异步 IO 之 POSIX AIO

先来看一个简单的读写文件程序,程序读取源文件,然后把内容打印到屏幕上:

#include <unistd.h>
#include <fcntl.h>

#define BUFSIZE 4096

int main(void)
{
    int fd, nbytes;
    char buf[BUFSIZE];

    fd = open(__FILE__, O_RDONLY);
    while ((nbytes = read(fd, buf, BUFSIZE)) > 0)
        write(1, buf, nbytes);

    close(fd);
    return 0;
}

在这个例子中的 read()/write() 都是同步阻塞 IO,即如果没有数据可读或写入未完成时当前线程会一直等待,直到读写操作完成或出错才会返回。

同步 …

阅读全文…

Linux 异步 IO 之 epoll

epoll 是 Linux 内核在 2.5 引入的一个 I/O 事件通知机制,用来取代旧的 select(2) 和 poll(2)。

select() 的工作模式

从通知机制上说,select() 的方式和 epoll 的方式类似,都是阻塞在某个函数上,如果有指定的 I/O 事件发生的话函数就会返回。这样的通知机制的好处在于,在没有事件发生的时候系统可以去干别的事情。不过 select() 只告诉你有事件发生了,但是没有说清楚是哪些 fd 触发了事件通知,你需要去遍历所有的 fd 来找出哪些真正需要处理。select() 的函数原型如下:

int select(int nfds, fd_set *readfds, fd_set *writefds,
           fd_set *exceptfds, struct timeval *timeout);

fd_set 是一个 …

阅读全文…

关于 so 的一些笔记

现在的程序越来越复杂,由多个模块构成,如果把所有的模块和依赖都编译到一个单一的可执行文件中,不仅文件体积很大,而且也不利于模块更新;而且有些基础模块可以被多个程序共用,没必要各个程序都打包一份,因此就有了动态链接库。顾名思义,动态链接库就是可以动态地进行链接,在程序需要的时候才会进行加载,并且这份代码在内存里是共享的,在 Windows 中叫“Dynamic Link Library”,后缀是 dll,Linux 上叫“Shared Object”,后缀一般是“so”。

Hello, world!

下面是经典的打印“Hello, world!”的例子:

#ifndef __HELLO_H__
#define __HELLO_H__

void print(void);

#endif
/* hello.c */

#include <stdio.h>

void print(void)
{
    printf("Hello, world!\n");
}
/* main.c */

#include "hello.h"

int main(void)
{
    print();

    return 

阅读全文…

使用 inotify 监控目录和文件的状态

概述

很多时候程序需要对文件状态进行监控,例如在命令行中删掉一个文件,图形界面中的文件管理器也要把这个文件去掉(如果文件管理器正在显示被删除文件所在的目录);如果试图用 vim 保存一个文件,而该文件在修改的时候被其它程序修改过,那么 vim 会给出警告说文件已经发生改变。如果需要对文件状态进行实时监控,一个方法是保存文件的状态,然后不停地扫描目录或文件,如果和上一次保存的状态不一致则发出信号,但是这样的做法效率很低。最好的做法是能够向系统注册一个回调函数,当我们需要监控的目录或文件发生改变时调用回调函数,这样程序就能马上得到通知。

根据参考资料 [1],inotify 之前的文件状态监控机制是 dnotify。dnotify 中的“d”指的是目录,只能监控目录事件的变化,即在该目录下的创建/删除文件引起的事件,但是却不能监控文件本身状态的改变(如读写/访问文件),如果需要实现这样的功能需要应用程序自己比较文件状态。

在 2.6.13 的时候引入的 inotify 提供了比 dnotify 更强大的功能,除了目录还可以对文件状态进行监控。并且它不像 dnotify,每监控一个目录/文件都需要打开一个文件描述符,因此不会影响移动介质的 unmount。从接口形式和功能上看,inotify 和 epoll 很像,只是两者监控的事件类型不一样。

09 年出现了 fanotify(参考资料 [2]),据说是 inotify 的下一代,不过目前来看功能还不如 inotify。

对目录的监控

作为 inotify 的第一个示例,程序会在 /tmp/abc 被创建/删除/移动时打印相应的信息:

#include <stdio.h>
#include <string.h>

阅读全文…

debian 硬盘安装 linuxmint

debian7 出来后马上试用了一下,gnome3 不出意外地令人失望。本来想换 xfce 的,忽然想起 linuxmint 搞的 mate 和 cinnamon,其中 mate 是基于 gnome2 改的,而 cinnamon 基于 gnome3。本着与时俱进的原则,网上搜了下 cinnamon 发现挺好看的,而且发展了有一段时间了(最重要是这个,谁都不想一个软件用了两天作者就说不干了)。在 kvm 上试用了一下 linuxmint 打包的 debian cinnamon 201303 版,越用越爽,果断连论文都不改了,先换了系统再说。

官网的 iso 是 dvd 的,而我只有一张 cdrw,很不幸地光驱又抽风了,于是想用硬盘安装。用 google 搜“linuxmint 硬盘安装”,第一条结果是参考资料 [1],但是试了几次都出错了,说什么“getpwuid_r(): failed due to …

阅读全文…

bash 实现目录遍历

脚本的最初原型是当年用 fvwm 的时候为了山寨一个生成家目录下的树形结构菜单写的一个广度优先遍历;后来在把文件系统从 ext3 转到 ext4 的时候重新分区,把文件备份到 windows 上,结果还原回来的时候权限都乱了,于是把 queue 整理了一下写成一个单独的脚本用来改权限;后来还实现了一个深度优先遍历的版本。今天把这两个函数都整理一下。一般来说简单的操作用 find + 各种工具就行了,除非是对遍历顺序有要求或操作比较复杂。

#!/bin/bash

# http://ouonline.net/

IFS=$(echo -en "\n\b") # in case of space(s) in dentry name

# -------------------------------------------------------------------------- #

function dfs()
{
    #if [ $# -ne 2 ]; 

阅读全文…

使用 fuse 编写文件系统 (2)

读写文件

在第一个程序的基础上增加读写 hello-world 的功能:

#define FUSE_USE_VERSION 26

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <fuse.h>

#define BUFFSIZE 8192

static int content_size;
static char content[BUFFSIZE];
static const char* fname = "hello-world";

static int ou_readdir(const char* path, void* buf, fuse_fill_dir_t filler,
                      off_t 

阅读全文…