Segmentation Fault

コアダンプの数だけ強くなれるよ、納期と戦うエンジニアのように。

WordpressをNginx + redisキャッシュで高速化する

Wordpressは今までApache + mysql + phpの構成で構築してたけど、NginxとRedisを使ってみたかったで試してみた。

下記を参考にさせていただいた。

qiita.com

www.jeedo.net


構築環境は下記(さくらVPSの1Gプラン)
・CentOS7 (64bit)
・RAM 1G
・仮想コア 2コア (Xeon)


事前準備

必要なパッケージをインストール

# sudo yum install php php-fpm nginx redis mariadb-server


Wordpressのインストール

本家からtarballをダウンロードして展開する
ファイル名については下記で確認しておく

f:id:segmentation-fault:20170717195157p:plain

# wget http://ja.wordpress.org/wordpress-4.8-ja.tar.gz
# mkdir /var/www/wordpress/
# tar zxvf wordpress-4.8-ja.tar.gz -C /var/www/wordpress


Redisキャッシュ対応

必要なファイルをダウンロードして展開する

# cd /var/www/wordpress/
# wget http://uploads.staticjw.com/ji/jim/predis.php
# chmod 644 predis.php
# wget http://www.jeedo.net/downloads/wp-index-redis.zip
# unzip wp-index-redis.php
# chmod 644 wp-index-redis.php

index.phpをwp-index-redis.phpに置き換える。

# mv index.php _index.php # 初期ファイルはリネームしてバックアップ
# mv wp-index-redis.php index.php


Wordpressの準備

mysqlの設定

mysql-serverの起動

# systemctl enable mariadb.service
# systemctl start mariadb.service


MySQLの基本設定(*1)

# mysql_secure_installation

NOTE: RUNNING ALL PARTS OF THIS SCRIPT IS RECOMMENDED FOR ALL MariaDB
      SERVERS IN PRODUCTION USE!  PLEASE READ EACH STEP CAREFULLY!

In order to log into MariaDB to secure it, we'll need the current
password for the root user.  If you've just installed MariaDB, and
you haven't set the root password yet, the password will be blank,
so you should just press enter here.

Enter current password for root (enter for none):
OK, successfully used password, moving on...

Setting the root password ensures that nobody can log into the MariaDB
root user without the proper authorisation.

You already have a root password set, so you can safely answer 'n'.

Change the root password? [Y/n] n
 ... skipping.
# (以降はひたすらEnter)


データベースの作成

# mysql -u root -p
MariaDB [(none)]> CRATE DATABASE (お好きなデータベース名)
MariaDB [(none)]> CREATE USER (お好きなユーザ名)@localhost IDENTIFIED BY '(お好きなパスワード)';
MariaDB [(none)]> GRANT ALL ON wordpress.* TO (お好きなユーザ名)@localhost;
MariaDB [(none)]> exit
Bye


作成したデータベース名、ユーザ名、パスワードをwp-config.phpに記載する。

# cp wp-config-sample.php wp-config.php
# vim wp-config.php
# diff -u wp-config-sample.php wp-config.php
--- wp-config-sample.php        2017-06-09 01:38:02.000000000 +0900
+++ wp-config.php       2017-07-09 17:03:16.814058312 +0900
@@ -26,13 +26,13 @@

 // ** MySQL 設定 - この情報はホスティング先から入手してください。 ** //
 /** WordPress のためのデータベース名 */
-define('DB_NAME', 'database_name_here');
+define('DB_NAME', '(お好きなデータベース名)');

 /** MySQL データベースのユーザー名 */
-define('DB_USER', 'username_here');
+define('DB_USER', '(お好きなユーザ名)');

 /** MySQL データベースのパスワード */
-define('DB_PASSWORD', 'password_here');
+define('DB_PASSWORD', '(お好きなパスワード)');

 /** MySQL のホスト名 */
 define('DB_HOST', 'localhost');


PHPの設定

下記コンフィグファイルを編集してuserをnginxに変更する

# cp /etc/php-fpm.d/www.conf /etc/php-fpm.d/www.conf.orig
# diff -u /etc/php-fpm.d/www.conf.orig  /etc/php-fpm.d/www.conf
--- /etc/php-fpm.d/www.conf.orig        2017-07-09 16:20:28.104075951 +0900
+++ /etc/php-fpm.d/www.conf     2017-07-09 16:20:42.242076156 +0900
@@ -36,9 +36,9 @@
 ; Note: The user is mandatory. If the group is not set, the default user's group
 ;       will be used.
 ; RPM: apache Choosed to be able to access some dir as httpd
-user = apache
+user = nginx
 ; RPM: Keep a group allowed to write in log dir.
-group = apache
+group = nginx

 ; Choose how the process manager will control the number of child processes.
 ; Possible Values:
#
# systemctl enable php-fpm.service
# systemctl start php-fpm.service


Nginxの設定

コンフィグを編集

# cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.orig
# vim /etc/nginx/nginx.conf
# diff -u /etc/nginx/nginx.conf.orig /etc/nginx/nginx.conf
--- /etc/nginx/nginx.conf.orig  2017-07-09 15:09:20.002991898 +0900
+++ /etc/nginx/nginx.conf       2017-07-09 15:03:53.841982845 +0900
@@ -38,13 +38,18 @@
     server {
         listen       80 default_server;
         listen       [::]:80 default_server;
-        server_name  _;
-        root         /usr/share/nginx/html;
+        server_name  (あなたのサーバ名);
+        root         /var/www/wordpress;

         # Load configuration files for the default server block.
         include /etc/nginx/default.d/*.conf;

-        location / {
+        location ~ \.php$ {
+               root            /var/www/wordpress;
+               fastcgi_pass    127.0.0.1:9000;
+               fastcgi_index   index.php;
+               fastcgi_param   SCRIPT_FILENAME $document_root$fastcgi_script_name;
+               include         fastcgi_params;
         }

         error_page 404 /404.html;


/var/www/wordpress配下の所有者をNginxに変更
>||
# chown -R nginx.nginx /var/www/wordpress/


Nginxを起動

# systemctl enable nginx.service
# systemctl start nginx.service


ファイアウォールの設定

ファイアウォールにhttpの通過を許可させる

# firewall-cmd --add-service=http --zone=public --permanen
# firewall-cmd --reload


実行結果

ブラウザでサーバにアクセスしてWordpressのインストールが実行できればOK
アクセス先はhttp://(あなたのサーバ)/wp-admin/install.php

f:id:segmentation-fault:20170717195926p:plain


インストール後にWordpressにログインできれば完了
f:id:segmentation-fault:20170717200108p:plain



実際にWordpressの表示が高速化されたのかは比較対象がないので不明(^_^;)


---------------------
(*1)
mysql_secure_installationコマンド実行で下記エラーが発生。
mysqladminコマンドでパスワードを設定すると解消した。

# mysql_secure_installation

NOTE: RUNNING ALL PARTS OF THIS SCRIPT IS RECOMMENDED FOR ALL MariaDB
      SERVERS IN PRODUCTION USE!  PLEASE READ EACH STEP CAREFULLY!

In order to log into MariaDB to secure it, we'll need the current
password for the root user.  If you've just installed MariaDB, and
you haven't set the root password yet, the password will be blank,
so you should just press enter here.

Enter current password for root (enter for none):
ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: YES)
#
# mysqladmin -u root password
New password:
Confirm new password:

CentOS7(VPS)にminergate-cliをインストールしてアイドルタイムで仮想通貨を掘る

お勉強用にさくらVPSを利用しているが、平日の日中と夜間はリソースの使用率が低く余っており、このアイドルタイムで何か出来ないと探っていたところMinerGateのマイニングソフトで仮想通貨の採掘が可能そうなので試してみた。

実行環境はCentOS 7, 仮想2コア, RAM 1G。

ユーザー登録

まずは下記からMinerGateにユーザーの登録を行う。ここに登録したメールアドレスを後ほど利用する。

https://minergate.com/

f:id:segmentation-fault:20170709175307p:plain


マイニングソフトのインストール

mingergate-cli.rpmをダウンロード

$ wget https://minergate.com/download/rpm-cli -O minergate-cli.rpm


インストールに必要なソフト群(依存関係)を調べて解消

$ sudo rpm -ivh minergate-cli.rpm
error: Failed dependencies:
        libEGL.so.1()(64bit) is needed by minergate-cli-6.2_gcc4-1.x86_64
        libGL.so.1()(64bit) is needed by minergate-cli-6.2_gcc4-1.x86_64
        libICE.so.6()(64bit) is needed by minergate-cli-6.2_gcc4-1.x86_64
        libSM.so.6()(64bit) is needed by minergate-cli-6.2_gcc4-1.x86_64
        libXext.so.6()(64bit) is needed by minergate-cli-6.2_gcc4-1.x86_64
        libXrender.so.1()(64bit) is needed by minergate-cli-6.2_gcc4-1.x86_64
        libfontconfig.so.1()(64bit) is needed by minergate-cli-6.2_gcc4-1.x86_64
        libmtdev.so.1()(64bit) is needed by minergate-cli-6.2_gcc4-1.x86_64
        libxcb-icccm.so.4()(64bit) is needed by minergate-cli-6.2_gcc4-1.x86_64
        libxcb-image.so.0()(64bit) is needed by minergate-cli-6.2_gcc4-1.x86_64
        libxcb-keysyms.so.1()(64bit) is needed by minergate-cli-6.2_gcc4-1.x86_64
        libxcb-sync.so.0()(64bit) is needed by minergate-cli-6.2_gcc4-1.x86_64
$
$ #不足しているライブラリが梱包されているパッケージ名を調べる
$ yum provides libEGL.so.1 \
 libGL.so.1 \
 libICE.so.6 \
 libSM.so.6 \
 libXext.so.6 \
 libXrender.so.1 \
 libfontconfig.so.1 \
 libmtdev.so.1 \
 libxcb-icccm.so.4 \
 libxcb-image.so.0 \
 libxcb-keysyms.so.1 \
 libxcb-sync.so.0
$
$ #前コマンドで調べたパッケージをインストールする
$ sudo yum install mesa-libEGL \
 mesa-libGL \
 libICE \
 libSM \
 libXext \
 libXrender \
 fontconfig \
 mtdev \
 xcb-util-wm \
 xcb-util-image \
 xcb-util-keysyms \
 compat-libxcb


依存関係が解消されたらインストール実行

$ sudo rpm -ivh minergate-cli.rpm
Preparing...                          ################################# [100%]
Updating / installing...
   1:minergate-cli-6.2_gcc4-1         ################################# [100%]

マイニングお試し

環境が整ったので適当な仮想通貨を指定してマイニングしてみる。
ここではMonero(XMR)を指定してみた。

$ sudo nohup minergate-cli --user <登録したメールアドレス> --xmr 2 &
$ tail -f nohup.out
XMR hashrate: 68.40 H/s
XMR hashrate: 70.10 H/s
XMR hashrate: 68.40 H/s
XMR hashrate: 68.20 H/s


ハッシュレートはこんな感じ。1日ぶん回して0.003程度。

サーバの利用頻度が高い時間帯はcpulimit等でCPU使用率を制限してアイドルタイムは制限なく頑張ってもらうような運用になるのかな。

Androidスマホ(Nexus5X)からapkファイルを取り出す

Androidスマホ(Nexus5X)からインストール済みのアプリのapkファイルを取り出してみた。

大した内容は書いてませんがお約束として下記。

[前置き]
法律で保護されたプログラムに対してリバースエンジニアリング等を行うことは法律に違反する可能性があります。
本ページで得た情報を悪用しないでください。

[実行環境]
・取り出し元 : Nexus5X(Android 7.1.2)
・取り出し先 : WindowsのPC(Windows7 + adbコマンドが使える)


取り出し対象アプリ名の確認

まずは取り出すアプリのファイル名が分からないと検索できないのでGoogle Playから確認する。
アドレスバーの'id='以降の文字列(今回だとjp.naver.line.android)が対象である。


f:id:segmentation-fault:20170701203307p:plain



apkファイルの検索

ファイル名が分かったので早速スマホをPCに接続して検索してみる。
ちなみにNexus5XのUSB端子はType-Cなのでご注意。

スマホを接続時にはUSB接続用途をファイル転送して、開発者向けオプションのUSBデバッグを有効にする。


f:id:segmentation-fault:20170701203210p:plain


f:id:segmentation-fault:20170701203218p:plain


f:id:segmentation-fault:20170701203220p:plain






スマホを接続後、コマンドプロンプトから下記コマンドで検索する。

$ adb shell pm list packages -f | findstr jp.naver.line.android


が、なにやらエラーメッセージが表示されてしまった。

f:id:segmentation-fault:20170701202324p:plain

C:\Users\work>adb shell pm list packages -f | findstr jp.naver.line.android
error: device unauthorized.
This adb server's $ADB_VENDOR_KEYS is not set
Try 'adb kill-server' if that seems wrong.
Otherwise check for a confirmation dialog on your device.

スマホ側をみるとUSBデバッグの許可するか?を聞かれていたのでOKをする。


f:id:segmentation-fault:20170701204255p:plain



再度、adb kill-serverを実行してから同コマンドを実行してみる。

f:id:segmentation-fault:20170701204415p:plain

C:\Users\work>adb kill-server

C:\Users\work>adb shell pm list packages -f | findstr jp.naver.line.android
package:/data/app/jp.naver.line.android-1/base.apk=jp.naver.line.android


今度は問題なく検索が完了。



apkファイルの取り出し

あとは、検索したパスをadb pullコマンドに渡して取り出せばOK。
カレントディレクトリを確認してbase.apkが存在すれば完了。

f:id:segmentation-fault:20170701204636p:plain

C:\Users\work> adb pull /data/app/jp.naver.line.android-1/base.apk

NETLINKでネットワークインタフェースの状態変化を検知する

LinuxでNETLINKを使ったネットワークインタフェースの状態変化(up/down, IPアドレスの追加/削除)を検知してみる。

NETLINKはネットワークインタフェースの状態変化をカーネルからユーザランドのアプリケーションに対してソケットインタフェース経由で通知してくれる仕組み。
詳細についてはMan page of NETLINKを参照。


ソースコード

netlink.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <netinet/in.h>
#include <asm/types.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <errno.h>

#define EPOLL_SIZE (1)
#define CTRLNL_OK  (0)
#define CTRLNL_NG  (-1)

struct rtnl_handle
{
    int         fd;
    struct sockaddr_nl  local;
    struct sockaddr_nl  peer;
    __u32           seq;
    __u32           dump;
};

static int ctrlnl_open(struct rtnl_handle* rth, unsigned subscriptions, int protocol)
{
    int ret = CTRLNL_NG;
    int rc = 0;
    int addr_len = 0;

    memset(rth, 0, sizeof(rth));

    do {
        rth->fd = socket(AF_NETLINK, SOCK_RAW, protocol);
        if (rth->fd < 0) {
            perror("Cannot open netlink socket");
            break;
        }

        memset(&rth->local, 0, sizeof(rth->local));
        rth->local.nl_family = AF_NETLINK;
        rth->local.nl_groups = subscriptions;

        if (bind(rth->fd, (struct sockaddr*)&rth->local, sizeof(rth->local)) < 0) {
            perror("Cannot bind netlink socket");
            close(rth->fd);
            break;
        }
        
        addr_len = sizeof(rth->local);
        if (getsockname(rth->fd, (struct sockaddr*)&rth->local, &addr_len) < 0) {
            perror("Cannot getsockname");
            close(rth->fd);
            break;
        }
        
        if (addr_len != sizeof(rth->local)) {
            fprintf(stderr, "Wrong address length %d\n", addr_len);
            close(rth->fd);
            break;
        }
        
        if (rth->local.nl_family != AF_NETLINK) {
            fprintf(stderr, "Wrong address family %d\n", rth->local.nl_family);
            close(rth->fd);
            break;
        }

        rth->seq = time(NULL);

        ret = CTRLNL_OK;

    } while(0);

    return ret;
}

static void ctrlnl_getifname(int ifindex, char* ifname, int length)
{
    int fd = -1;
    struct ifreq ifr = {
        .ifr_ifindex = ifindex,
    };

    fd = socket(AF_INET, SOCK_DGRAM, 0);
    if (fd < 0) {
        perror("socket");
    } else {

        if (ioctl(fd, SIOCGIFNAME, &ifr) < 0) {
            perror("ioctl");
        } else {
            snprintf(ifname, length-1, "%s", ifr.ifr_name);
        }

        close(fd);
    }
}

static void ctrlnl_getrtaddr(struct rtattr** rtas, struct ifaddrmsg* ifa, int length)
{
    struct rtattr* rta = IFA_RTA(ifa);

    while(RTA_OK(rta, length)) {
        if (rta->rta_type <= IFA_MAX) {
            rtas[rta->rta_type] = rta;
        }

        rta = RTA_NEXT(rta, length);
    }
}

static int ctrlnl_showinfo(struct nlmsghdr* h)
{
    int ret = CTRLNL_OK;

    char  ifname[64] = {0};
    struct ifaddrmsg* ifa = NLMSG_DATA(h);
    struct rtattr*  rtas[IFA_MAX+1];

    printf("######### Recv Netlink Message ##########\n");

    ctrlnl_getifname(ifa->ifa_index, ifname, sizeof(ifname));
    printf("ifname = %s\n", ifname);
    printf("nlmsg_type = %#x\n", h->nlmsg_type);

    switch(h->nlmsg_type) {
    case RTM_NEWLINK:
    case RTM_DELLINK:
        break;
    case RTM_NEWADDR:
    case RTM_DELADDR:
        {
            ctrlnl_getrtaddr(rtas, ifa, (h->nlmsg_len - NLMSG_SPACE(sizeof(*ifa))));

            printf("ifa_family = %#x\n", ifa->ifa_family);

            char addr[128] = {0};
            if (rtas[IFA_LOCAL]) {
                inet_ntop(ifa->ifa_family, RTA_DATA(rtas[IFA_LOCAL]), addr, sizeof(addr));
            }

            if (rtas[IFA_ADDRESS]) {
                inet_ntop(ifa->ifa_family, RTA_DATA(rtas[IFA_ADDRESS]), addr, sizeof(addr));
            }

            printf("address = %s\n", addr);
        }
        break;
    default:
        fprintf(stderr, "illegal message type.\n");
        ret = CTRLNL_NG;
        break;
    }

    printf("\n");

    return ret;
}

static int ctrlnl_recv(int fd)
{
    int ret = CTRLNL_NG;
    struct nlmsghdr* h;
    char buf[4096] = {0};
    ssize_t rs = 0;

    rs = recv(fd, buf, sizeof(buf), 0);
    if (rs < 0) {
        perror("recv");
    } else {
        for (h = (struct nlmsghdr*)buf; NLMSG_OK(h, rs); h = NLMSG_NEXT(h, rs)) {
            ret = ctrlnl_showinfo(h);
            if (CTRLNL_OK != ret) {
                fprintf(stderr, "ctrlnl_showinfo(%d) failed.\n", ret);
                break;
            }
        }
    }

    return ret;
}

static int ctrlnl_close(int fd)
{
    int ret = CTRLNL_OK;
    int rc = 0;

    rc = close(fd);
    if (rc < 0) {
        perror("close");
        ret = CTRLNL_NG;
    }

    return ret;
}

static int ctrlnl_poll(int fd)
{
    int rc = 0;
    int ret = CTRLNL_NG;
    struct sockaddr_in addr;
    int epfd = -1;
    int nfds = -1;
    struct epoll_event events[EPOLL_SIZE];
    struct epoll_event evt = {
        .events = EPOLLIN,
        .data.fd = fd,
    };

    epfd = epoll_create(EPOLL_SIZE);
    if (epfd < 0) {
        perror("epoll_create");
        goto error_end;
    }

    rc = epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &evt);
    if (rc < 0) {
        perror("epoll_ctl");
        close(epfd);
        goto error_end;
    }

    while(1) {

        nfds = epoll_wait(epfd, events, EPOLL_SIZE, -1);
        if (nfds < -1) {
            perror("epoll_wait");
            close(epfd);
            goto error_end;
        }

        int i;
        for (i = 0; i < nfds; i++) {

            if (events[i].data.fd == fd) {

                ret = ctrlnl_recv(fd);
                if (CTRLNL_OK != ret) {
                    fprintf(stderr, "ctrlnl_recv(%d) failed.\n", ret);
                }
            }
        }
    }

 error_end:

    return ret;
}

int main(void)
{
    int ret = -1;
    struct rtnl_handle  rth;

    do {
        ret = ctrlnl_open(&rth,
                          RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR,
                          NETLINK_ROUTE);
        if (CTRLNL_OK != ret) {
            fprintf(stderr, "ctrlnl_open(%d) failed.\n", ret);
            break;
        }

        ret = ctrlnl_poll(rth.fd);
        if (CTRLNL_OK != ret) {
            fprintf(stderr, "ctrlnl_poll(%d) failed.\n", ret);
        }

        ret = ctrlnl_close(rth.fd);
        if (CTRLNL_OK != ret) {
            fprintf(stderr, "ctrlnl_close(%d) failed.\n", ret);
        }

    } while(0);

    return 0;
}

実行結果

ビルドしたバイナリを実行後にdown/up, IPv4付与をやってみる

user@user-VirtualBox ~ $ sudo ifconfig enp0s9
enp0s9    Link encap:イーサネット  ハードウェアアドレス 08:00:27:7c:40:f4
          UP BROADCAST RUNNING MULTICAST  MTU:1500  メトリック:1
          RXパケット:0 エラー:0 損失:0 オーバラン:0 フレーム:0
          TXパケット:3635 エラー:0 損失:0 オーバラン:0 キャリア:0
          衝突(Collisions):0 TXキュー長:1000
          RXバイト:0 (0.0 B)  TXバイト:601190 (601.1 KB)

user@user-VirtualBox ~ $ 
user@user-VirtualBox ~ $ sudo ifconfig enp0s9 down
user@user-VirtualBox ~ $ sudo ifconfig enp0s9 up
user@user-VirtualBox ~ $ sudo ifconfig enp0s9 172.16.1.1


表示結果

user@user-VirtualBox ~ $ gcc -o netlink netlink.c
user@user-VirtualBox ~ $ ./netlink
######### Recv Netlink Message ##########
ifname = enp0s9
nlmsg_type = 0x10

######### Recv Netlink Message ##########
ifname = enp0s9
nlmsg_type = 0x15
ifa_family = 0xa
address = fe80::5e20:28f4:aaab:aade

######### Recv Netlink Message ##########
ifname = enp0s9
nlmsg_type = 0x10

######### Recv Netlink Message ##########
ifname = enp0s9
nlmsg_type = 0x10

######### Recv Netlink Message ##########
ifname = enp0s9
nlmsg_type = 0x10

######### Recv Netlink Message ##########
ifname = enp0s9
nlmsg_type = 0x10

######### Recv Netlink Message ##########
ifname = enp0s9
nlmsg_type = 0x14
ifa_family = 0xa
address = fe80::5e20:28f4:aaab:aade

######### Recv Netlink Message ##########
ifname = enp0s9
nlmsg_type = 0x14
ifa_family = 0xa
address = fe80::5e20:28f4:aaab:aade

######### Recv Netlink Message ##########
ifname = enp0s9
nlmsg_type = 0x14
ifa_family = 0x2
address = 172.16.1.1

適当に参考になりそうなソースコードを集めて作ってみたが何とか動いた。
コマンド操作に対して通知回数が多い気がするのはIPv6のRAが動いているからか?(よく分かってない)

ifconfig,ipコマンドとかNet-SNMPのソースを見ればもう少し理解が深まりそう。

Raspberry Pi で Hello World

前回に引き続きラズパイ用の開発環境を構築し実機でHello Worldを表示(シリアル経由)するまでをやってみる。


実機は初期モデルからメモリが2倍の512MBになったVer。
最近のラズパイに比べたら装備も性能も劣るけれど個人的にはこっちのシンプルな方が好き。
Raspberry Pi Type B 512MB



環境構築続き

gatewayの設定変更

前回の作業ではホストオンリーアダプタにgatewayの設定を入れてしまい、
NAT経由で名前解決ができずapt-getが出来ない凡ミスを犯していたため修正した。


◆ネットワーク設定ファイルの修正とdefault gatewayの確認

user@user-VirtualBox ~ $ cat /etc/network/interfaces
# interfaces(5) file used by ifup(8) and ifdown(8)
auto lo
iface lo inet loopback

auto enp0s8
iface enp0s8 inet static
  address 192.168.100.101
  netmask 255.255.255.0
user@user-VirtualBox ~ $
user@user-VirtualBox ~ $ ip route show
default via 10.0.2.2 dev enp0s3  proto static  metric 100
10.0.2.0/24 dev enp0s3  proto kernel  scope link  src 10.0.2.15  metric 100
169.254.0.0/16 dev enp0s3  scope link  metric 1000
192.168.100.0/24 dev enp0s8  proto kernel  scope link  src 192.168.100.101
クロスコンパイラ(+その他)の取得

下記を参考にクロスコンパイラをgitで取ってくる。取得先は/usr/local/配下とした。

www.raspberrypi.org

user@user-VirtualBox ~ $ sudo apt-get install git
user@user-VirtualBox ~ $ cd /usr/local
user@user-VirtualBox ~ $
user@user-VirtualBox /usr/local $ sudo git clone https://github.com/raspberrypi/
tools
[sudo] user のパスワード:
Cloning into 'tools'...
remote: Counting objects: 25343, done.
remote: Total 25343 (delta 0), reused 0 (delta 0), pack-reused 25343
Receiving objects: 100% (25343/25343), 606.96 MiB | 1.48 MiB/s, done.
Resolving deltas: 100% (15035/15035), done.
Checking connectivity... done.
Checking out files: 100% (19058/19058), done.


クロスコンパイラが取ってこれたらパスを追記しておく。

user@user-VirtualBox ~ $ vim ~/.bashrc
PATH=$PATH:/usr/local/tools/arm-bcm2708/arm-bcm2708-linux-gnueabi/bin を追記


その他、ビルドしたファイルのファイル転送用にFTPサーバと純粋なviが使いずらいのでvimを追加する。

user@user-VirtualBox ~ $ sudo apt-get install vsftpd
user@user-VirtualBox ~ $ sudo apt-get install vim

起動イメージの作成

ビルド環境が整ったので早速 "Hello World" してみる。
具体的には電源ON後にHello Worldのメッセージを表示して終了するだけのバイナリ(kernel.img)を作る。

ソースコード

main.c

#include "uart.h"

#define UNUSED(x) (void)(x)

const char hello[] = "\r\nHello World\r\n";
const char halting[] = "\r\n*** system halting ***";

// kernel main function, it all begins here
void kernel_main(void) {

    uart_init();

    uart_puts(hello);

    // Wait a bit
    for(volatile int i = 0; i < 10000000; ++i) { }

    uart_puts(halting);
}


uart.c

/* uart.c - UART initialization & communication */
/* Reference material:
 * http://www.raspberrypi.org/wp-content/uploads/2012/02/BCM2835-ARM-Peripherals.pdf
 * Chapter 13: UART
 */

#include "mmio.h"
#include "uart.h"

enum {
    // The GPIO registers base address.
    GPIO_BASE = 0x20200000,

    // The offsets for reach register.

    // Controls actuation of pull up/down to ALL GPIO pins.
    GPPUD = (GPIO_BASE + 0x94),

    // Controls actuation of pull up/down for specific GPIO pin.
    GPPUDCLK0 = (GPIO_BASE + 0x98),

    // The base address for UART.
    UART0_BASE = 0x20201000,

    // The offsets for reach register for the UART.
    UART0_DR     = (UART0_BASE + 0x00),
    UART0_RSRECR = (UART0_BASE + 0x04),
    UART0_FR     = (UART0_BASE + 0x18),
    UART0_ILPR   = (UART0_BASE + 0x20),
    UART0_IBRD   = (UART0_BASE + 0x24),
    UART0_FBRD   = (UART0_BASE + 0x28),
    UART0_LCRH   = (UART0_BASE + 0x2C),
    UART0_CR     = (UART0_BASE + 0x30),
    UART0_IFLS   = (UART0_BASE + 0x34),
    UART0_IMSC   = (UART0_BASE + 0x38),
    UART0_RIS    = (UART0_BASE + 0x3C),
    UART0_MIS    = (UART0_BASE + 0x40),
    UART0_ICR    = (UART0_BASE + 0x44),
    UART0_DMACR  = (UART0_BASE + 0x48),
    UART0_ITCR   = (UART0_BASE + 0x80),
    UART0_ITIP   = (UART0_BASE + 0x84),
    UART0_ITOP   = (UART0_BASE + 0x88),
    UART0_TDR    = (UART0_BASE + 0x8C),
};

/*
 * delay function
 * int32_t delay: number of cycles to delay
 *
 * This just loops <delay> times in a way that the compiler
 * wont optimize away.
 */
static void delay(int32_t count) {
    int32_t i;
    for (i = 0; i < count * 1000000; i++) {
    }
    //__asm volatile("__delay_%=: subs %[count], %[count], #1; bne __delay%=\n"
    //     : : [count]"r"(count) : "cc");
}

/*
 * Initialize UART0.
 */
void uart_init() {
    // Disable UART0.
    mmio_write(UART0_CR, 0x00000000);
    // Setup the GPIO pin 14 && 15.

    // Disable pull up/down for all GPIO pins & delay for 150 cycles.
    mmio_write(GPPUD, 0x00000000);
    delay(150);

    // Disable pull up/down for pin 14,15 & delay for 150 cycles.
    mmio_write(GPPUDCLK0, (1 << 14) | (1 << 15));
    delay(150);

    // Write 0 to GPPUDCLK0 to make it take effect.
    mmio_write(GPPUDCLK0, 0x00000000);

    // Clear pending interrupts.
    mmio_write(UART0_ICR, 0x7FF);

    // Set integer & fractional part of baud rate.
    // Divider = UART_CLOCK/(16 * Baud)
    // Fraction part register = (Fractional part * 64) + 0.5
    // UART_CLOCK = 3000000; Baud = 115200.

    // Divider = 3000000/(16 * 115200) = 1.627 = ~1.
    // Fractional part register = (.627 * 64) + 0.5 = 40.6 = ~40.
    mmio_write(UART0_IBRD, 1);
    mmio_write(UART0_FBRD, 40);

    // Enable FIFO & 8 bit data transmissio (1 stop bit, no parity).
    mmio_write(UART0_LCRH, (1 << 4) | (1 << 5) | (1 << 6));

    // Mask all interrupts.
    mmio_write(UART0_IMSC, (1 << 1) | (1 << 4) | (1 << 5) |
            (1 << 6) | (1 << 7) | (1 << 8) |
            (1 << 9) | (1 << 10));

    // Enable UART0, receive & transfer part of UART.
    mmio_write(UART0_CR, (1 << 0) | (1 << 8) | (1 << 9));
}

/*
 * Transmit a byte via UART0.
 * uint8_t Byte: byte to send.
 */
void uart_putc(uint8_t byte) {
    // wait for UART to become ready to transmit
    while (1) {
        if (!(mmio_read(UART0_FR) & (1 << 5))) {
            break;
        }
    }
    mmio_write(UART0_DR, byte);
}

/*
 * print a string to the UART one character at a time
 * const char *str: 0-terminated string
 */
void uart_puts(const char *str) {
    while (*str) {
        uart_putc(*str++);
    }
}


uart.h

/* uart.h - UART initialization & communication */

#ifndef UART_H
#define UART_H

#include "local.h"

/*
 * Initialize UART0.
 */
void uart_init(void);

/*
 * Transmit a byte via UART0.
 * uint8_t Byte: byte to send.
 */
void uart_putc(uint8_t byte);

/*
 * print a string to the UART one character at a time
 * const char *str: 0-terminated string
 */
void uart_puts(const char *str);

#endif // #ifndef UART_H


mmio.h

/* mmio.h - access to MMIO registers */

#ifndef MMIO_H
#define MMIO_H

#include "local.h"

// write to MMIO register
static inline void mmio_write(uint32_t reg, uint32_t data) {
    uint32_t *ptr = (uint32_t*)reg;
    __asm volatile("str %[data], [%[reg]]" : : [reg]"r"(ptr), [data]"r"(data));
}

// read from MMIO register
static inline uint32_t mmio_read(uint32_t reg) {
    uint32_t *ptr = (uint32_t*)reg;
    uint32_t data;

    __asm volatile("ldr %[data], [%[reg]]"
         : [data]"=r"(data) : [reg]"r"(ptr));
    return data;
}

#endif // #ifndef MMIO_H


boot.S

/* boot.S - assembly startup code */

// To keep this in the first portion of the binary.
.section ".text.boot"

// Make Start global.
.globl Start

// Entry point for the kernel.
// r15 -> should begin execution at 0x8000.
// r0 -> 0x00000000
// r1 -> 0x00000C42
// r2 -> 0x00000100 - start of ATAGS
// preserve these registers as argument for kernel_main
Start:
        // Setup the stack.
        mov     sp, #0x8000

        // Clear out bss.
        ldr     r4, =_bss_start
        ldr     r9, =_bss_end
        mov     r5, #0
        mov     r6, #0
        mov     r7, #0
        mov     r8, #0
    b   Second

First:
        // store multiple at r4.
        stmia   r4!, {r5-r8}

        // If we are still below bss_end, loop.
Second:
        cmp     r4, r9
        blo     First

        // Call kernel_main
        ldr     r3, =kernel_main
        blx     r3

        // halt
halt:
        wfe
        b       halt
バイナリのビルド

Makefile

# Makefile - build script */

# build environment
PREFIX ?= /usr
ARMGNU ?= $(PREFIX)/local/tools/arm-bcm2708/arm-bcm2708-linux-gnueabi/bin/arm-bcm2708-linux-gnueabi
# source files
SOURCES_ASM := $(wildcard *.S)
SOURCES_C   := $(wildcard *.c)

# object files
OBJS        := $(patsubst %.S,%.o,$(SOURCES_ASM))
OBJS        += $(patsubst %.c,%.o,$(SOURCES_C))

# Build flags
DEPENDFLAGS := -MD -MP
INCLUDES    := -I./
BASEFLAGS   := -O2 -fpic -pedantic -pedantic-errors -nostdlib
BASEFLAGS   += -nostartfiles -ffreestanding -nodefaultlibs
BASEFLAGS   += -fno-builtin -fomit-frame-pointer -mcpu=arm1176jzf-s
WARNFLAGS   := -Wall -Wextra -Wshadow -Wcast-align -Wwrite-strings
WARNFLAGS   += -Wredundant-decls -Winline
WARNFLAGS   += -Wno-attributes -Wno-deprecated-declarations
WARNFLAGS   += -Wno-div-by-zero -Wno-endif-labels -Wfloat-equal
WARNFLAGS   += -Wformat=2 -Wno-format-extra-args -Winit-self
WARNFLAGS   += -Winvalid-pch -Wmissing-format-attribute
WARNFLAGS   += -Wmissing-include-dirs -Wno-multichar
WARNFLAGS   += -Wredundant-decls -Wshadow
WARNFLAGS   += -Wno-sign-compare -Wswitch -Wsystem-headers -Wundef
WARNFLAGS   += -Wno-pragmas -Wno-unused-but-set-parameter
WARNFLAGS   += -Wno-unused-but-set-variable -Wno-unused-result
WARNFLAGS   += -Wwrite-strings -Wdisabled-optimization -Wpointer-arith
WARNFLAGS   += -Werror
ASFLAGS     := $(INCLUDES) $(DEPENDFLAGS) -D__ASSEMBLY__
CFLAGS      := $(INCLUDES) $(DEPENDFLAGS) $(BASEFLAGS) $(WARNFLAGS)
CFLAGS      += -std=c99

# build rules
all: kernel.img

include $(wildcard *.d)

kernel.elf: $(OBJS) link-arm-eabi.ld
        $(ARMGNU)-ld $(OBJS) -Tlink-arm-eabi.ld -o $@

kernel.img: kernel.elf
        $(ARMGNU)-objcopy kernel.elf -O binary kernel.img

clean:
        $(RM) -f $(OBJS) kernel.elf kernel.img

dist-clean: clean
        $(RM) -f *.d

# C.
%.o: %.c Makefile
        $(ARMGNU)-gcc $(CFLAGS) -c $< -o $@

# AS.
%.o: %.S Makefile
        $(ARMGNU)-gcc $(ASFLAGS) -c $< -o $@

コンパイル

user@user-VirtualBox ~ $ make
user@user-VirtualBox ~ $ ls
Makefile  boot.o      link-arm-eabi.ld  main.d  tags    uart.h
boot.S    kernel.elf  local.h           main.o  uart.c  uart.o
boot.d    kernel.img  main.c            mmio.h  uart.d
その他必要なファイル

SDカードにkernel.imgと合わせてbootcode.binとstart.elfが必要なので下記から拝借する。

github.com



実機起動

SDカードに下記3ファイルを配置する。SDをラズパイに挿入し電源を入れると上から順に実行されていく。

  • bootcode.bin
  • start.elf
  • kernel.img
実行結果

結線はこんな感じ。

f:id:segmentation-fault:20170619232223j:plain


Teratermの画面にメッセージが表示されれば成功。(ボーレートは115200)
f:id:segmentation-fault:20170619231814p:plain

Virtualbox上にLinux Mintをインストール

Windows7でVirtualBox上にLinux Mintの環境(sshでログインできるまで)を構築する。
用途はRaspberry-pi用のソフトウェア開発環境。

[材料]
・Windows7-32bit
・Linux Mint 18.1(Mate 32bit)
・VirtualBox
・Teraterm (ssh接続用)

事前準備

TeratermとVirtualBoxは下記からダウンロードしてWindowsにインストールしておく。

Teraterm
https://ja.osdn.net/projects/ttssh2/releases/

VirtualBox
https://www.virtualbox.org/wiki/Downloads


イメージァイル(iso)の取得

下記からイメージファイルをダウンロード
linuxmint.com


Mate 32bitを選択

f:id:segmentation-fault:20170611223857p:plain

VMの作成

isoファイルがダウンロードできたらVirtualBoxを起動。
設定をクリックして新規VMを作成していきます。

f:id:segmentation-fault:20170611223957p:plain


表示に従って必要項目を入力していきます。

f:id:segmentation-fault:20170611224042p:plain


マシン名とハードディスク容量以外は大体デフォルトでOK

f:id:segmentation-fault:20170611224217p:plain


f:id:segmentation-fault:20170611224259p:plain


f:id:segmentation-fault:20170611224336p:plain


f:id:segmentation-fault:20170611224342p:plain


ハードディスク容量はデフォルト8GBですが、Mintはインストール時に9~10GB程度の容量を要求してくるので、今回は余裕を持たせて32GBほど割り当てます。

f:id:segmentation-fault:20170611224355p:plain


これでVMが作成されました。

f:id:segmentation-fault:20170611224712p:plain

VMへMintをインストール

VMを起動しCDドライブにダウンロードしたisoを選択。

f:id:segmentation-fault:20170611225119p:plain


下記画面が表示されたら「Start Linux Mint」を選択してEnter。

f:id:segmentation-fault:20170611225225p:plain


起動後デスクトップ画面が表示されたら「Install Linux Mint」をクリック。

f:id:segmentation-fault:20170611225530p:plain


表示に従って必要項目を入力していきます。

f:id:segmentation-fault:20170611225721p:plain
f:id:segmentation-fault:20170611225726p:plain
f:id:segmentation-fault:20170611225728p:plain
f:id:segmentation-fault:20170611225730p:plain
f:id:segmentation-fault:20170611225733p:plain
f:id:segmentation-fault:20170611225731p:plain
f:id:segmentation-fault:20170611225829p:plain


ここからしばらくインストール処理が走ります。

f:id:segmentation-fault:20170611230011p:plain


インストールが終ったら一度VMを停止。
(閉じるボタン or 端末でsudo halt/shutdownの実行)

f:id:segmentation-fault:20170611230122p:plain


f:id:segmentation-fault:20170611230259p:plain


SSHログイン対応

VMのウィンドウでは操作しづらいのでWindows7からTeratermを使ってMintにSSHログイン可能にしていきます。

まず、VMにネットワークインタフェースを2つ(NATとホストオンリーアダプタを)設定。

f:id:segmentation-fault:20170611230543p:plain

f:id:segmentation-fault:20170611230633p:plain


VM起動前にCDドライブからisoファイルを抜いておきます。

f:id:segmentation-fault:20170611230759p:plain


設定後、VMを起動してインストール時に作成したユーザでログイン。

f:id:segmentation-fault:20170611231008p:plain


ログイン後、端末を開きます。(Ctrl+Alt+T)
(Ubuntu,MintのようなDebianベースのLinuxは大抵このショートカットで端末が開く)

f:id:segmentation-fault:20170611231049p:plain


ここで、lsを実行するとディレクトリが日本語表記になっていることに気づきます。
これは気に食わない(パス指定とか面倒な)ので英語に変更します。

f:id:segmentation-fault:20170611231303p:plain


"LANG=C xdg-user-dirs-gtk-update"で表記を英語に変更できます。
コマンドを実行すると本当に変更してよいか聞かれるので「Update Names」でリネーム。

f:id:segmentation-fault:20170611231515p:plain

f:id:segmentation-fault:20170611231824p:plain


これでめでたく変更できました。

f:id:segmentation-fault:20170611231851p:plain


続いてインタフェースの設定をしていきます。
ipコマンドでインタフェースが認識されているか確認。
この例では、enp0s3がNAT線、enp0s8がホストオンリーアダプタとなります。

f:id:segmentation-fault:20170611232015p:plain



ホストオンリーアダプタ線に固定のIPアドレスを設定するため、/etc/network/interfacesに下記の記述を追記。
今回はMint側:192.168.100.101, Windows側: 192.168.100.1としました。

auto enp0s8
iface enp0s8 inet static
  address 192.168.100.101
  netmask 255.255.255.0
  gateway 192.168.100.1

f:id:segmentation-fault:20170611232224p:plain

f:id:segmentation-fault:20170611232311p:plain


ネットワークインタフェースの再起動だけでは何故か上手くいかなかたったのでVMごと再起動。

f:id:segmentation-fault:20170611232334p:plain


再起動中にWindows側のホストオンリーアダプタにも固定IPの設定をしておきます。

f:id:segmentation-fault:20170611232801p:plain

f:id:segmentation-fault:20170611232803p:plain


VMが起動したら、固定IPが設定されているかipコマンドで確認。

f:id:segmentation-fault:20170611233748p:plain


固定IPの設定が確認できたらapt-getでSSHサーバをインストール。

sudo apt-get install openssh-server
#パスワードを聞かれた場合はuserのパスワードを入力

f:id:segmentation-fault:20170611233205p:plain


インストール後、sshdのプロセスが起動しているかpsコマンドで確認。

f:id:segmentation-fault:20170611233825p:plain


WindowsからSSHログイン出来ればめでたく完了です。

f:id:segmentation-fault:20170611234053p:plain

f:id:segmentation-fault:20170611234115p:plain

f:id:segmentation-fault:20170611234122p:plain

f:id:segmentation-fault:20170611234130p:plain

32bit-UEFI環境でLinux-MintをUSB-bootする

手持ちのノートPCであるASUS x205TAでLinux-Mint(インストール用のライブ版)をUSB-Bootしてみる。

[材料]
・ASUS x205TA
・USBメモリ(BUFFALO 16GB)
・Linux-Mint(18.1-mate-32bit)

[注意点]
・従来BIOSではなくUEFI(詳しくは勉強中)環境
・CPUが32bitであること


どうやらRufus等のOSのイメージファイルをUSBメモリに書込みツールを実行するだけでは手順が不十分のよう。

PCは下記↓
www.asus.com


USB-Boot可能なUSBメモリの作成

Linux-Mintのダウンロード

まずはBoot対象のOSイメージをダウンロードする。今回はMATE(32bit)を選択。
www.linuxmint.com

RufusでOSイメージを書込み

下記からダウンロードしてLinux-Mint(isoファイル)をUSBに書き込む
https://www.linuxmint.com/edition.php?id=227www.gigafree.net


f:id:segmentation-fault:20170528225112p:plain


途中syslinuxについてメッセージが表示されるが今回は関係ないので気にせずOKボタンを実行。

ブートローダのダウンロード

対象のPCではRufusでUSBにインストールしたsyslinuxによる起動ができないため別途OSをキックする手段を用意する必要がある。
下記、先人の成果物(bootia32.efi)をお借りすることにした。

github.com


f:id:segmentation-fault:20170528225423p:plain



USBメモリ直下に「EFI」の名前でフォルダを新規作成し、配下に「BOOT」のフォルダを作成し、そこにダウンロードしたbootia32.efiを配置する。

f:id:segmentation-fault:20170528230049p:plain



これでUSBメモリは準備完了。USBメモリを挿入した状態でPCを再起動する。


BIOSの設定

セキュアブート無効化とUSB起動の優先度設定


PCを起動直後に「F2」を連打してBIOSを起動する。

f:id:segmentation-fault:20170528230326p:plain



念のためUSB Controller Selectを「EHCI」に変更する。

f:id:segmentation-fault:20170528230613p:plain



Secure Boot Controlを無効にする。

f:id:segmentation-fault:20170528230711p:plain



Boot Optionで「USB Hard Drive」の起動優先度を1番にする。

f:id:segmentation-fault:20170528231000p:plain



上記設定後、Boot Overrideで「USB Hard Drive」を選択しブートローダが起動するか確認する。

f:id:segmentation-fault:20170528231155p:plain



下記画面が起動すれば成功!

f:id:segmentation-fault:20170528231237p:plain