分类 其他 下的文章

"Hack" Ruff开发板(一)

"Hack"是加了引号的,并没有发现什么问题。至于里面JavaScript解释器有没有问题,那就是另外一回事了。

Ruff是一个使用JavaScript进行开发的的嵌入式开发板,官网是 https://ruff.io/zh-cn/。好多天之前就买了一个,一直在玩自带的几个传感器、开关等等。今天突然想了解一下它的内部的系统,就简单的分析了一下。

- 阅读剩余部分 -

最近遇到的几个技术问题

docker MySQL的binlog无法开启

binlog需要配合server-id一起使用,默认是65536,但是docker镜像不知道为什么没有这个值,所以直接添加log-bin的选项会出错,但是连一个错误提示都没有,浪费了好久。

解决方案

FROM mysql:latest
RUN printf "\nserver-id=1\nlog-bin=binlog\n" >> /etc/mysql/my.cnf

这里硬编码了一个server-id,如果MySQL是主从复制的模式运行,请自行配置server-id

全局变量与锁的问题

有一个Python的C语言拓展模块,大致的用法是

module.run(foo=bar, log_path="run.log")
module.run(foo=bar, log_path="test.log")

但是发现多次连续的运行并没有写入两个文件,而是都在第一个文件中,而这部分是由一个现成的日志模块完成的,查看日志模块的代码

static FILE *log_fp                 = NULL;
static char *log_filename           = NULL;
static int  log_opened              = 0;

int log_open(const char* filename)
{
    if (log_opened == 1)
    {
        return 0;
    }
    log_fp = fopen(filename, "a");
    if (log_fp == NULL)
    {
        exit(1);
    }
    atexit(log_close);
    log_opened = 1;
    log_extra_info[0] = 0;
    return 1;
}

很明显使用了全局变量,而这些变量是在Python代码中import这个模块的时候没有初始化的,而调用过一次模块方法后就被赋值了,这时候再次调用的时候,全局变量log_opened是1了,所以没有重新打开新的日志文件。

里面还有一个方法是

void log_close(void)
{
    if (log_opened)
    {
        fclose(log_fp);
        log_fp       = NULL;
        log_filename = NULL;
        log_opened   = 0;
    }
}

如果在模块的最后面主动调用这个方法的话,那很可能产生竞态条件,因为都是取得全局变量。所以最终需要全部改为局部变量加指针传值的方法。

但是还有一个小的竞态条件问题,就是在写入日志的时候,因为可能多线程运行的时候同时写入一个文件,所以要把写入文件的操作变成原子操作,最简单的应该就是给文件加锁了。

int log_fd = log_fp->_fileno;
if (flock(log_fd, LOCK_EX) == 0)
{
    if (write(log_fd, buffer, count) < 0)
    {
        perror("write error");
        exit(1);
    }
    flock(log_fd, LOCK_UN);
}
else
{
    perror("flock error");
    exit(1);
}

具体用法是 http://man7.org/linux/man-pages/man2/flock.2.html

当然还可以在写文件的时候,使用自旋锁等轻量级的锁,毕竟这个写几行日志是很快的操作,如果使用了信号量之类的睡眠锁,就有点重量级了。

RAID

今天给一台IBM x3850 x6装系统,首先要进入BIOS设置中组RAID,服务器共有4块硬盘,可选的有RAID0、RAID1和RAID5。

RAID0是整个逻辑盘的数据是被分条分布在多个磁盘上,可以并行读/写,提供最快的速度,但没有冗余能力。

RAID1只有一半的磁盘容量是有效的,可以认为一半读写数据,一半备份数据,有冗余能力。

RAID5把数据和相对应的奇偶校验信息存储到组成RAID5的各个磁盘上,并且奇偶校验信息和相对应的数据分别存储于不同的磁盘上,其中任意N-1块磁盘上都存储完整的数据。

网卡

还是上面的服务器,网卡不能用,ifconfig -a能看到,有4块网卡,但是直接运行ifup 网卡名提示出错。

发现/etc/network/interfaces中根本没有配置,然后添加了

auto p10p1
iface p10p1 inet dhcp

后再启用就好了。

git使用GPG签名

生成gpg key

运行gpg --gen-key前面选项可以保持默认,最后填写你的名字和邮箱。注意邮箱要和Github等平台上的一致。

导出key,添加到Github

运行gpg --list-keys就可以看到你的key id了。类似

pub   2048R/779EEEEE 2016-04-21
uid                  LiYang <user@example.com>
sub   2048R/88EEEEE 2016-04-21

下文使用{KEY ID}代替779EEEEE

然后运行gpg --armor --export {KEY ID}就可以看到-----BEGIN PGP PUBLIC KEY BLOCK-----开头的一堆字符串了,复制出来,添加到Github。

设置git

运行git config user.signingkey {KEY ID}

设置git commit默认签名

运行git config commit.gpgsign true这就相当于每次commit的时候就添加-S参数。-s参数没找到默认设置项目,这个可以显示Signed-off-by

git tag也是使用-s参数,没有找到默认设置。

请输入图片描述

其他

  • git log --show-signature 查看带签名的log
  • git mergegit pull 可以使用 --verify-signatures

docker volume 的用法

docker container 设计上就是非持久化的,可以随时创建和删除,所以说在 container 里面存储数据并不太让人放心,万一哪天 docker rm -f 了。

docker volume 包括 data volume 和 data volume container,其中 data volume 是类似将主机上的文件夹或者文件挂载到 container 里面,而 data volume container 是将文件保存在一个单独的数据容器中。

data volume

data volume有两种创建方法,一个是docker run -d -P --name web -v /webapp training/webapp python app.py,这样会自动把/webapp文件夹映射到主机上某一个自动生成的文件夹中,使用docker inspect web命令可以看到,

...
Mounts": [
    {
        "Name": "fac362...80535",
        "Source": "/var/lib/docker/volumes/fac362...80535/_data",
        "Destination": "/webapp",
        "Driver": "local",
        "Mode": "",
        "RW": true,
        "Propagation": ""
    }
]
...

当然我们还可以手动指定主机上要映射的文件夹,这应该是最常用的,类似docker run -d -P --name web -v /src/webapp:/opt/webapp training/webapp python app.py 就会把主机上的/src/webapp映射到容器中的/opt/webapp里面。

要注意的是

  • 挂载主机上的文件夹的时候,需要容器内对应的路径也是存在的,比如说你需要首先去创建/opt/webapp,docker 其实是可以自动创建的,但是这个功能已经被废弃了。
  • 挂载主机上的文件夹的时候,容器内对应的路径上如果有文件,那在容器内就无法访问这些文件了,所有文件和主机目录内是一样的,而且主机上或者容器内对文件权限进行修改都是两边都同步生效的。当然文件还在,取消挂载后就出现了。

data volume container

创建 data volume container 的时候可以复用已有的镜像,比如docker create -v /dbdata --name dbstore training/postgres /bin/true,然后使用--volumes-from参数来把/dbdata目录挂载到其他的容器内。docker run -d --volumes-from dbstore --name db1 training/postgres

虽然说这样可以方便的在多个容器之间共享数据,但是和挂载主机文件夹的方法对比上并没觉得有太多的优点,进行文件迁移备份也挺麻烦的。当然 docker 要是能简单的实现跨主机的 volume 共享,我还是很支持的~ 在 https://jaxenter.com/how-to-share-docker-volumes-across-hosts-119602.html 有一些实现的 trick,而实际上和 docker 并没啥关系,还是传统的同步机制。在 docker 的 github 也有讨论 https://github.com/docker/docker/issues/7249

开源的 OnlineJudge

qduoj 是 青岛大学开源的一个 OnlineJudge,GitHub 地址 https://github.com/QingdaoU/OnlineJudge

acmer 常用的 oj 是 hduoj 、 poj 等等,但是有些不是很满意的地方。

首先是界面,国内的前辈 oj 大多数都很丑很丑, 10 多年前的风格,可能因为 oj 就是 10 多年前写的吧。

用起来也有些不是很方便的地方,比如提交题目要新开页面,要再选择题号,再手动刷新看结果。自己内部比赛有些规则也没办法去设置,自己没法去添加题目去查看测试用例等等。

当然,qduoj 的定位不仅仅是 ACM 训练,还有学校平时教学的作业考试等也可以在上面进行。 老师作为普通管理员可以创建小组,相当于一个班级,内部举办比赛,创建修改题目,外人不可见,超级管理员才可以管理公开的题目和比赛。

oj 后端涉及到的技术有 Python 、 Django 、 Docker 、 MySQL 、 Redis 、 Celery 等,后台的前端是一个 SPA 页面,使用 avalon js 。

欢迎感兴趣的小伙伴搭建试用~