pyenv/pyenv-virtualenv環境を構築する

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

pyenvはpythonをダウンロードしてきてコンパイルするけど、その際に必要となるパッケージを入れておく。Fedora以外はこちらを参照。

dnf install -y gcc zlib-devel bzip2 bzip2-devel readline-devel sqlite sqlite-devel openssl-devel tk-devel xz

pyenvとpyenv-virtualenvのインストール

pyenv開発者がpyenv installerを公開しているのでそれを利用してインストールする。このインストーラがpyenv-virtualenvまで入れてくれるからラクチン。

$ curl -L https://raw.githubusercontent.com/pyenv/pyenv-installer/master/bin/pyenv-installer | bash
....
<snip>
...
WARNING: seems you still have not added 'pyenv' to the load path.

# Load pyenv automatically by adding
# the following to ~/.bash_profile:

export PATH="/home/hoge/.pyenv/bin:$PATH"
eval "$(pyenv init -)"
eval "$(pyenv virtualenv-init -)"

ということなので、.bashrcに下記を追加しておく。

export PATH="/home/hoge/.pyenv/bin:$PATH"
eval "$(pyenv init -)"
eval "$(pyenv virtualenv-init -)"

pythonのバージョンをインストールしてみる

インストール可能なバージョンを確認

$ pyenv install --list
...
3.6.4
...

特定バージョンのpythonをインストールする

$ python install 3.6.4
$ pyenv versions
* system (set by /home/hoge/.pyenv/version)
  3.6.4

新しい環境を作成してみる

システムのpythonのバージョンの確認

fedora 27のpythonのバージョンは2.7.14

$ python --version
Python 2.7.14

/home/hoge/hoge以下に3.6.4環境を作成してみる

$ cd /home/hoge/hoge
$ pyenv virtulenv 3.6.4 my_3.6.4
$ pyenv local my_3.6.4
$ python --version
Python 3.6.4
$ cd ..
$ python --version
Python 2.7.14

ということで、/home/hoge/hoge以下だけ3.6.4を使う環境をつくれた

起動失敗してgrubプロンプト(grub >)が出た場合の対処

調べるのが面倒なので、メモを残しておく。

1. パーティションの確認 (sda2から起動したい)

grub> ls
(hd0) (hd0,msdos3) (hd0,msdos2) (hd0,msdos1)

2. パーティションの中身の確認

grub> ls (hd0,msdos2)/
bin/ boot/ dev/ ....
grub> cat (hd0,msdos2)/etc/redhat-releases
Fedora release 27 (Twenty Seven)
grub> ls (hd0,msdos)/boot/
.... initramfs-4.14-11-300.fc27.x86_64.img .... vmlinuz-4.14.11-300.fc27.x86_64 ...

3. 起動

grub> set root=(hd0,msdos2)
grub> linux /boot/vmlinux-4.14.11-300.fc27.x86_64 root=/dev/sda2
grub> initrd /boot/initramfs-4.14.11-300.fc27.x86_64.img
grub> boot

4 (必要に応じて) grubの再インストール

# grub2-install /dev/sda

5 (必要に応じて) ファイルシステムの復旧

今回はxfsが壊れていたので下記コマンドを実行。

# xfs_repair /dev/sda2

参考

https://jp.linux.com/news/linuxcom-exclusive/418274-lco20140625 26.7. GRUB 2 の再インストール Red Hat Enterprise Linux 7 | Red Hat Customer Portal

psコマンドでプロセスの親子関係を表示する

今までps aux使ってたけど、f付けるだけで分かり易くなるの知らなかった。

$ ps auxf

​USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
​...

hoge 1981 0.0 0.1 534500 53764 tty2 S+ 23:19 0:00 \_ /opt/google/chrome/chrome --type=zygote
hoge 1982 0.0 0.0 148140 13112 tty2 S+ 23:19 0:00 | \_ /opt/google/chrome/nacl_helper
hoge 1985 0.0 0.0 534500 12308 tty2 S+ 23:19 0:00 | \_ /opt/google/chrome/chrome --type=zygote
hoge 2163 0.0 0.2 991548 83072 tty2 Sl+ 23:19 0:00 | \_ /opt/google/chrome/chrome --type=renderer --field-trial-handle=1715498460

...

インスタンスにパスワードでログインする

Fedora等のイメージはデフォルトではパスワードログインが無効になってて、パスワードでログインすることができません。 しかし、CloudInit使って設定を変えることでパスワードログインできるようになります。

以下をユーザデータとして渡せばOK。 Horizonでインスタンスを作る際、Post-Creation -> Customization Script Source -> Direct Inputとして下記を入力。

#cloud-config
password: <password>
chpasswd: { expire: False }
ssh_pwauth: True

を好きなパスワードに変更すればOK。 Fedoraクラウドイメージなら、fedoraアカウントに上記のパスワードでログインできるようになります。

pthread_once()について

pthread_once()の使い方がよくわからなかったので調べてみた。 このページ(*)が分り易かったからまとめておく。

1. 用途

-初期化に使う --通常初期化はプログラムの開始時に実施*1 --でも、できない場合もある ---例えば、ライブラリを作成する場合、初期化してもらえるかわからん ---仕方ないから呼ばれた時に呼ばれた側で初期化しよう ---エントリポイントのすべてで初期化されたかを判定して初期化処理を書くのは馬鹿らしい --一回だけ実行する仕組みを作ろうぜ

→ pthread_once()

2. 定義

int pthread_once( pthread_once_t * once_control, void (*init_routine)(void));
once_control:制御変数。PTHREAD_ONCE_INIT で初期化。
init_routine:1回だけ呼ばれる初期化用関数。

3. 使い方の例

(*)にあるプログラムを実行しようと思ったんだけど、once_main.cが見つからなかったので、書いてみた。

#include <stdio.h>
#include <pthread.h>

extern void *thread_func1(void *);
extern void *thread_func2(void *);
extern void *thread_func3(void *);
extern void *thread_func4(void *);

int main(void)
{
        int i = 0, j = 0;
        int ret;
        pthread_t th[4];

        fprintf(stderr, "main starts!\n");

        ret = pthread_create(&th[i], NULL, thread_func1, NULL);
        if (ret != 0) {
                perror("pthread_create");
                goto out;
        }

        ret = pthread_create(&th[++i], NULL, thread_func2, NULL);
        if (ret != 0) {
                perror("pthread_create");
                goto out;
        }

        ret = pthread_create(&th[++i], NULL, thread_func3, NULL);
        if (ret != 0) {
                perror("pthread_create");
                goto out;
        }

        ret = pthread_create(&th[++i], NULL, thread_func4, NULL);
        if (ret != 0) {
                perror("pthread_create");
                goto out;
        }

out:
        while (j < i)
                pthread_join(th[j++], NULL);

        fprintf(stderr, "main ends\n");
        return 0;
}

これとonce_thread.cをビルドして実行するとこんな感じ。

$ ./once 
main starts!
in thread_func2, before once_func.
in thread_func3, before once_func.
in thread_func1, before once_func.
in thread_func4, now start.
initializing function Start!!
in thread_func4, now end.
initializing function End...
in thread_func2, after once_func.
in thread_func3, after once_func.
in thread_func2, mutex_lock OK.
in thread_func1, after once_func.
in thread_func2, mutex_unlock OK.
in thread_func2, timesec:[5.000698]
in thread_func3, mutex_lock OK.
in thread_func3, mutex_unlock OK.
in thread_func3, timesec:[6.001000]
in thread_func1, mutex_lock OK.
in thread_func1, mutex_unlock OK.
in thread_func1, timesec:[7.001423]
main ends

pthread_once()で実行するonce_func()は一度のみ実行されているし、同時に実行している他のスレッドはblockしてるのがわかる。

今日はここまで。

*1:コンストラクタとか

_smp_mflags

kernelのSRPMを展開して得られるSPECファイルを見るとビルドのところに、_smp_mflagsというのが出てくる。例えば、kernel.specだとこんな感じ。

BuildKernel() {
    MakeTarget=$1
    KernelImage=$2
    Flavour=$3
    InstallName=${4:-vmlinuz}

<snip>

    make -s ARCH=$Arch oldnoconfig >/dev/null
    make -s ARCH=$Arch V=1 %{?_smp_mflags} $MakeTarget %{?sparse_mflags}
    make -s ARCH=$Arch V=1 %{?_smp_mflags} modules %{?sparse_mflags} || exit 1

<snip>

で、この_smp_mflagsなんだが-jX*1に展開されるのだけど、気が向いた時、ビルドの時間を短くするためにXをCPU数の2倍位にしたりすることがあったり。で、本当気が向いた時だけやってるので、SPECを手動で書き換えているのだけど、まあよく忘れるし、適当過ぎよねということで、さらっと調べてみたのでメモしておく*2

まず、_smp_mflagsrpmのマクロになります。で、rpmのマクロの定義は/usr/lib/rpm/にある。マクロを定義しているファイルはいろいろあるので、grepしてみた。

$ grep -r _smp_mflags /usr/lib/rpm/
/usr/lib/rpm/platform/pentium3-linux/macros:%_smp_mflags %([ -z "$RPM_BUILD_NCPUS" ] \\\
/usr/lib/rpm/platform/x86_64-linux/macros:%_smp_mflags %([ -z "$RPM_BUILD_NCPUS" ] \\\
/usr/lib/rpm/platform/athlon-linux/macros:%_smp_mflags %([ -z "$RPM_BUILD_NCPUS" ] \\\
/usr/lib/rpm/platform/noarch-linux/macros:%_smp_mflags %([ -z "$RPM_BUILD_NCPUS" ] \\\
/usr/lib/rpm/platform/i486-linux/macros:%_smp_mflags %([ -z "$RPM_BUILD_NCPUS" ] \\\
/usr/lib/rpm/platform/i686-linux/macros:%_smp_mflags %([ -z "$RPM_BUILD_NCPUS" ] \\\
/usr/lib/rpm/platform/pentium4-linux/macros:%_smp_mflags %([ -z "$RPM_BUILD_NCPUS" ] \\\
/usr/lib/rpm/platform/ia32e-linux/macros:%_smp_mflags %([ -z "$RPM_BUILD_NCPUS" ] \\\
/usr/lib/rpm/platform/geode-linux/macros:%_smp_mflags %([ -z "$RPM_BUILD_NCPUS" ] \\\
/usr/lib/rpm/platform/amd64-linux/macros:%_smp_mflags %([ -z "$RPM_BUILD_NCPUS" ] \\\
/usr/lib/rpm/platform/i386-linux/macros:%_smp_mflags %([ -z "$RPM_BUILD_NCPUS" ] \\\
/usr/lib/rpm/platform/i586-linux/macros:%_smp_mflags %([ -z "$RPM_BUILD_NCPUS" ] \\\
/usr/lib/rpm/redhat/macros:%_smp_mflags %([ -z "$RPM_BUILD_NCPUS" ] \\\

まあ、こんな感じでたくさん出てくるわけだが、その中の/usr/lib/rpm/redhat/macrosを見てみると次のようになってた。

%_smp_mflags %([ -z "$RPM_BUILD_NCPUS" ] \\\
        && RPM_BUILD_NCPUS="`/usr/bin/getconf _NPROCESSORS_ONLN`"; \\\
        if [ "$RPM_BUILD_NCPUS" -gt 16 ]; then echo "-j16"; \\\
        elif [ "$RPM_BUILD_NCPUS" -gt 1 ]; then echo "-j$RPM_BUILD_NCPUS"; fi)

RPM_BUILD_NCPUSが0だったらgetconf _NPROCESSORS_ONLNの結果を代入する。 sysconf(3)のmanを見たら、_SC_NPROCESSORS_ONLNは現在オンラインのCPU数を返すとのことなので、getconfでも同じと思われ、_smp_mflagsには現在オンラインのCPU数が入るはず。これで_smp_mflagsの定義が分かりましたとさ。

このマクロファイルをいじってあげれば、好きに設定できると。今度は忘れないようにしましょう。

(追記) マクロの中身を確認するのに、なにも/usr/lib/rpm/以下をgrepしなくてもよいみたい。rpmコマンドで調べられる模様。

$ rpm --eval %macro

macroの部分に知りたいマクロを入れればOK。

参考:RPMマクロ - N.E.E.T―Never Ending Engineer’s Tragicomedy別館

*1:XはCPUの数

*2:前も調べたんだけど、すっかり忘れてた