20200809のLinuxに関する記事は4件です。

mallocのパラメータが環境変数から設定される流れ

はじめに

malloc関連のパラメータはmallopt(3)で変更することができます。
例えば、下記を実行することで、malloc内部でmmapを使うしきい値を1MBに変更することができます。

mallopt(M_MMAP_THRESHOLD, 1024*1024);

malloptで設定可能なパラメータのうちいくつかは、環境変数でも変更できます。
例えば、先述のM_MMAP_THRESHOLDは、MALLOC_MMAP_THRESHOLD_環境変数でも設定できます。

$ export MALLOC_MMAP_THRESHOLD_=1048576
$ ./a.out

環境変数であればプログラムの変更なしに適用できるので、システムのデフォルトを変更するときなどに有用です。
この環境変数によるmalloptの変更は、glibcのTunablesという仕組みを使っています。

本記事ではglibcのtunablesが設定される流れを
MALLOC_MMAP_THRESHOLD_環境変数を例に備忘メモとして残しておきます。
誤りなどありましたら、ご指摘いただければ幸いです。

環境は以下のとおりです。

$ arch
x86_64
$ uname -r
5.4.0-42-generic
$ lsb_release -d
Description:    Ubuntu 18.04.4 LTS
$ dpkg -l | grep libc-bin
ii  libc-bin             2.27-3ubuntu1.2 amd64           GNU C Library: Binaries

MALLOC_MMAP_THRESHOLD_環境変数が反映される流れ

1. ローダ(ld-linux.so)でauxiliary vectorを確認する

auxiliary vectorとは、getauxval(3)で取得することができる、
kernelのELF loaderから渡される実行ファイルの補助情報です。

ローダで、auxiliary vectorからAT_SECURE情報を取得し、
AT_SECUREが真の場合、global変数の__libc_enable_secureが立ちます。

ここでAT_SECUREは、少し勘違いしやすい名前ですが、
今のプロセスがセキュアに実行される必要があるということを意味します。
より具体的に言えば、下記のいずれかの場合にAT_SECUREが立ちます。

  • set-user-ID もしくは set-group-ID が有効なプログラム
  • capabilitiesを付与されたプログラム
glibc-2.27/elf/dl-sysdep.c
ElfW(Addr)
_dl_sysdep_start (void **start_argptr,
          void (*dl_main) (const ElfW(Phdr) *phdr, ElfW(Word) phnum,
                   ElfW(Addr) *user_entry, ElfW(auxv_t) *auxv))
{
...

  for (av = GLRO(dl_auxv); av->a_type != AT_NULL; set_seen (av++))
    switch (av->a_type)
      {
...
      case AT_SECURE:
        __libc_enable_secure = av->a_un.a_val;
        break;
...
      }

  __tunables_init (_environ);

...
  (*dl_main) (phdr, phnum, &user_entry, GLRO(dl_auxv));
  return user_entry;
}

2. ローダで、環境変数の値からtunable_list[]を更新

__tunables_initでは、tunable_list[]の中にある項目が環境変数に含まれている場合、
tunable_initialize()で環境変数の値が反映されます。

ただし、__libc_enable_secureが立っている場合(=AT_SECUREが立っている場合)は、
以下の3種類のsecurity_levelに応じた処理が行われます。

  • SXID_ERASE: AT_SECUREの場合、子プロセスが読まないように環境変数から削除する
  • SXID_IGNORE: AT_SECUREの場合、無視する(環境変数には残る)
  • NONE: 常に反映する
glibc-2.27/elf/dl-tunables.c
void
__tunables_init (char **envp)
{
...
  while ((envp = get_next_env (envp, &envname, &len, &envval,
                               &prev_envp)) != NULL)
    {
...
      for (int i = 0; i < sizeof (tunable_list) / sizeof (tunable_t); i++)
        {
          tunable_t *cur = &tunable_list[i];
...
              if (__libc_enable_secure)
                {
                  if (cur->security_level == TUNABLE_SECLEVEL_SXID_ERASE)
                    {
                      /* Erase the environment variable.  */
                      ...
                    }

                  if (cur->security_level != TUNABLE_SECLEVEL_NONE)
                    continue;
                }

              tunable_initialize (cur, envval);
              break;
            }
        }
    }
}

tunableの各項目のsecurity_levelは、環境変数名や型と併せて、.listファイルで定義されています。

glibc-2.27/elf/dl-tunables.list
glibc {
  malloc {
    ...
    mmap_threshold {
      type: SIZE_T
      env_alias: MALLOC_MMAP_THRESHOLD_
      security_level: SXID_IGNORE
    }
    ...
  }
}

scripts/gen-tunables.awkによってdl-tunables.listからdl-tunable-list.hを自動生成し、
そこにtunable_list[]が定義されます。

dl-tunable-list.h
static tunable_t tunable_list[] attribute_relro = {
  ...
  {TUNABLE_NAME_S(glibc, malloc, mmap_threshold), {TUNABLE_TYPE_SIZE_T, 0, SIZE_MAX}, {}, NULL, TUNABLE_SECLEVEL_SXID_IGNORE, "MALLOC_MMAP_THRESHOLD_"},
  ...
};

3. 初回malloc時に、tunable_list[]からmp_に反映

ここからは、tunableの話ではなく、mallocパラメータ固有の話になります。
また、実行タイミングもローダではなく、普通のmain関数に入ってから初めてmallocが呼び出されたときです。

プログラムで初めてメモリ確保関数を呼び出したときには、malloc_hook_iniが呼び出され、
そこからptmalloc_initを呼び出します。

glibc-2.27/malloc/hooks.c
static void *
malloc_hook_ini (size_t sz, const void *caller)
{
  __malloc_hook = NULL;
  ptmalloc_init ();
  return __libc_malloc (sz);
}

ptmalloc_initでは、2.で設定したtunable_listから、mmap_thresholdを取得し、
mallocパラメータをまとめて保持する構造体mp_に設定します。

glibc-2.27/malloc/arena.c
static void
ptmalloc_init (void)
{
...
  TUNABLE_GET (mmap_threshold, size_t, TUNABLE_CALLBACK (set_mmap_threshold));
...
}
glibc-2.27/elf/dl-tunables.c
void
__tunable_get_val (tunable_id_t id, void *valp, tunable_callback_t callback)
{
  tunable_t *cur = &tunable_list[id];

  switch (cur->type.type_code)
    {
...
    case TUNABLE_TYPE_SIZE_T:
    {
      *((size_t *) valp) = (size_t) cur->val.numval;
      break;
    }
...

  if (cur->initialized && callback != NULL)
    callback (&cur->val);
}

実際に設定するのはdo_set_mmap_threshold関数です。
ここを見ると実は設定値の上限があったり、
mmap_thresholdを設定するときには動的なしきい値制御を無効にしていることがわかります。

glibc-2.27/malloc/malloc.c
static inline int
__always_inline
do_set_mmap_threshold (size_t value)
{
  /* Forbid setting the threshold too high.  */
  if (value <= HEAP_MAX_SIZE / 2)
    {
      LIBC_PROBE (memory_mallopt_mmap_threshold, 3, value, mp_.mmap_threshold,
          mp_.no_dyn_threshold);
      mp_.mmap_threshold = value;
      mp_.no_dyn_threshold = 1;
      return 1;
    }
  return 0;
}

これで晴れて、mmapするかどうかの判断に、MALLOC_MMAP_THRESHOLD_環境変数の値が使われるようになります。
めでたしめでたし。

glibc-2.27/malloc/malloc.c
static void *
sysmalloc (INTERNAL_SIZE_T nb, mstate av)
{
...
  if (av == NULL
      || ((unsigned long) (nb) >= (unsigned long) (mp_.mmap_threshold)
      && (mp_.n_mmaps < mp_.n_mmaps_max)))
    {
...
          mm = (char *) (MMAP (0, size, PROT_READ | PROT_WRITE, 0));
    }
}

おわりに

mallocパラメータを環境変数で設定する場合、デバッガなりでちゃんと設定が反映されているか確認したほうが良いですね。

参考

mallocの動作を追いかける(環境変数編) - Qiita
mallopt(3) - Linux manual page
The GNU C Library
getauxval(3) - Linux manual page

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Ansible入門 その① 'Hello World!!'

Ansibleとは?

概要

構成管理ツールと呼ばれ、
リモートマシンやサーバをプログラムに従って、自動で設定することができます。
いちいち同じ作業を繰り返してサーバの設定をしなくても、
Ansibleにコードさえ渡してしまえば何台でも彼が設定してくれる便利な友達。
テスト環境の再現も可能!

メリット

  1. 冪等性
    同じプログラムからならどこで何回やっても同じ結果が保証されています。
  2. わかりやすい
    メインプログラムは、YAML上に構築され、本当にわかりやすく読みやすい。
  3. エージェントレス
    構成管理ツールといえば、他にもいろいろなサービスがあるが、
    多くがリモートホストにもそのソフトウェアを導入しなければ作動しない。
    しかし、AnsibleはそんなことしなくてもSSH接続だけで、勝手に動く!
  4. 任意のシェルコマンドがリモートサーバー上で実行可能
    シェルコマンドを簡単にプログラムに実装可能です。有難い。
    ちなみに、モジュールもたくさん用意されていて、色々便利。
  5. すぐに使えるようになる
    使えるようになるために必要な事前知識が少ないです。
    なんなら、playbookに命令書くんだな。
    そして、リモートホストはこう指定するんだな。で始められる気もする。

Ansibleインストールに関して

Macユーザの方々は本当に楽です。Ansible インストールで検索ください。
Windowsユーザの方々は少し大変です。
僕も苦戦しました。もはや苦戦しかしていない。
仮想環境あるかたは、特に困らないのかもしれませんが、僕はdockerのコンテナ上に立ち上げるところから始めました。ちなみに最近はec2上に立ち上げたものを使っています。
docker Ansibleaws Ansibleで検索すると色々出てくると思います。
【ちなみに私が参考にした記事様】
Docker編
【Ansible】DockerでAnsibleハンズオン
AWS編
【Ansible入門】EC2環境でAnsibleを実行してみよう

「Hello World」を出力してみる

今回は、localHost(自分自身)を使ってHelloWorldを出力するものを作ってみます。
Ansibleの基本はこれで分かるかなと思います。

必要なファイル

ファイル構成
helloworld
├── playbook.yml
└── inventory

playbook.yml
Ansibleでの設定管理のスクリプト。
基本的にAnsibleを使っている間の時間はこいつを書くことに費やします。
簡単に言えば、サーバーにやってほしい命令とかを書きます。
inventory
どのリモートホストを対象とするかを記述したファイル。INI形式。

inventoryの中身

inventory
[target]
localhost

playbookの中身

playbook.yml
---
- hosts: all
  tasks:
    - name: Hello World!
      debug:
        msg: "Hello World!"

YAML形式のファイルは字下げ(インデント)が大事ですので注意してください。

実行してみる

localhost
$ ansible-playbook -i inventory playbook.yml
PLAY [all] **********************************************************

TASK [Gathering Facts] **********************************************
ok: [localhost]

TASK [helloworld : Hello World!] ************************************
ok: [localhost] => {
    "msg": "Hello World!"
}

PLAY RECAP **********************************************************
localhost              : ok=2    changed=0    unreachable=0    failed=0   

こんな感じで、実行すると実行結果がバーッと出てくると思います。
これでとりあえずAnsibleが動きますね。簡単かつ便利です。

Ansible入門 その② '基本文法'

参考文献
初めてのAnsible(Lorin Hochstein著,玉川竜司訳,株式会社オライリー・ジャパン版)
Ansibleでできることを中の人が教えます

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Node.js の再インストール

Nodeがおかしくなった

$ node
Illegal instruction

環境

2020-02-13-raspbian-buster-lite

既存の Node.js

「Linux 環境に Node.js インストール」
https://qiita.com/nanbuwks/items/ed8adb2d4324c939a349
の通り、以下のようにインストール

1.Linux ディストリビューションの公式パッケージで Node.js と npm を入れる
2.npm install で最新の Node.js を入れる
3.古い Node.js を削除する

削除

node のインストール先を調べる
```

$ which node
/usr/local/bin/node
```

ファイル削除

$ sudo rm -rf /usr/local/bin/node
sudo rm -rf ~/.npm

削除できたかな?

$ npm
/usr/bin/env: ‘node’: No such file or directory

このあと、先の資料「Linux 環境に Node.js インストール」をもう一度実行して入れ直します。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ferenOSの導入2(インストール後の設定、日本語入力の設定)

インストール後の設定

インストール完了後、システムの再起動を行います。(インストール時に使用したメディアは取り出します。)
Feren OSセットアップへようこそ画面
setup1.png
セットアッププログラムが立ち上がりますので、「ようこそ」画面の言語を日本語に設定したあと、「ロケーション」、「キーボード」、「ユーザー情報」を適宜設定します。
設定後、「Welcom to Feren OS」が立ち上がります。

Welcom to Feren OS画面
setup5.png
好きなデスクトップレイアウトを選んでください。英語ですが、感覚的に選べると思います。
desktopLayout.png
テーマとカラーを選んでください。
theme.png
動画や音楽のサードパーティのコーデックをインストールできます。
installCodec.png
その他、KDEConnectやNightCoulorなどの設定ができます。

日本語入力の設定

インストール直後には、すでに日本語化されているferenOSですが、日本語入力はまだできない状態です。そこで、日本語入力できるように設定を行います。この作業もアプリケーション上で行えます。
installJa1.png
デスクトップ上の「Welcom Screen」アイコンから
[Main Menu]>[Getting started]>[Language & Input]へと進み設定を行います。
インプットメソッドのFcitxをインストールし、[Input Method]のボタンから設定を行います。
installJa2.png
画像の右側ウィンドウにあるように、言語サポートパッケージをインストールすることで日本語入力が可能になります。インストール後、再度ログインして入力方式フレームワークをFcitxに切り替えることで日本語入力可能です。変換システムはmozcになります。また、Ibusでも日本語入力は可能ですし、変換システムにanthyも導入可能です。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む