銀色うつ時間

思い出すたび何か胸につっかえてるだけ

Happy Hacking Keyboard 無刻印モデル 清掃時のキー配置

Happy Hacking Keyboardを掃除しようと思ったとき、キートップを外した後無刻印モデルはキーがどこに配置されていたのか分からなくなるという問題がある。掃除そのものに関してはたくさんの記事が他に存在しているのでそちらに任せるとして、キー配置についてだけ記述しておく。

実は無刻印モデルでもキートップの裏に番号が記載されていて、それを基にどこにキーが配置されていたかが分かるのだが、全てメモを取っていくのは骨が折れる。今回骨を折ってキー配置をメモした図を作成したので、無刻印のHHKBを利用している人は参考にするとよい。

f:id:sisidovski:20171125124315p:plain

上記のマップは、HHKB US配列 無刻印、製造年は2011-06の製品を基に作成した。HHKB、無刻印キーボードでも裏に謎の番号ではなくてどのキーに対応しているか印字しておいてほしいですね。

Mockeryでモジュールをモックに差し替える

Mockeryを遣ってNode.jsのテストを書いている。requireしたモジュールをそのままモックに差し替えるとき、差し替えたいモジュールに渡されている文字列と全く同じものを渡す必要がある。差し替えるモジュールが標準ライブラリやnpm経由で取得してきたものであった場合はそのまま書けばよいが、自前のモジュールを相対パスなんかで呼び出しているときに気をつける。例えばこんな場面。

/lib/module.js

module.exports = {
  ouputMessage: () => {
    console.log('Hello from mod');
  }
};

/lib/foo.js

const module = require('./module.js');
module.outputMessage(); // Hello from mod

/lib/bar.js

const foo = require('./foo.js'); // Hello from mod

/test/index.js

const mockery = require('mockery');

describe('test', () => {

  before(() => {
    const mock = {
      outputMessage: () => {
        console.log('Hello from mock');
      }
    };
    mockery.registerMock('./module.js', mock);  // ここ
    mockery.enable({
      useCleanCache: true,
      warnOnUnregistered: false
    });
  });

  after(() => {
    mockery.deregisterMock('mod1');
    mockery.disable();
  });

  it('should say hello from mock', () => {
    const bar = require('../lib/bar.js') // Hello from mock
  });
});

テストコードからの相対パスではなく、モック化したいモジュールのパスをそのまま記述しないと動作しない。割と忘れて気づかないパターンが多い。

github.com

The arguments to registerMock are as follows:

module, the name or path of the module for which a mock is being registered. This must exactly match the argument to require; there is no “clever” matching.

はい😇

fetch APIのredirect関連メモ

HTTPS環境下で特定のURLからデータを取得するときに、リソースがHTTPで配信されていた場合、当たり前だけどMixed Content警告が発生する。

fetch('http://www.yahoo.co.jp/');
// こんなのが出力される
// Mixed Content: The page at 'https://example.com/' was loaded over HTTPS, but requested an insecure resource 'http://www.yahoo.co.jp/'. This request has been blocked; the content must be served over HTTPS.
// Fetch API cannot load http://www.yahoo.co.jp/. Failed to start loading.(anonymous function) 
// Promise {[[PromiseStatus]]: "rejected", [[PromiseValue]]: TypeError: Failed to fetch
// Uncaught (in promise) TypeError: Failed to fetch(…)

基本的にはHTTPS以外のものは呼ばない、CSPなどを利用してそもそもHTTPのリソースがブロックしてしまうといった解決策が存在するが、HTTPSのURLがHTTPのURLにリダイレクトする場合はどうすればよいのだろうか。

$ curl -I https://example.com
HTTP/1.1 302 Found
Content-Type: text/html; charset=UTF-8
Location: http://other-domain.com
Content-Length: 261

HTTPSからHTTPにリダイレクトなんてないだろ、と思ったりもするが、後方互換性を重視した開発の現場では稀にそういうことがある。IDの若い特定のユーザには古いページを表示する、古いページはHTTPSに対応していないといった状況などがそれにあたる。

fetch APIはredirectオプションによってリダイレクト時の振る舞いを制御できる。

developer.mozilla.org

redirect: The redirect mode to use: follow (automatically follow redirects), error (abort with an error if a redirect occurs), or manual (handle redirects manually). In Chrome the default was follow before Chrome 47 and manual starting with Chrome 47.

Fetch Standard

A request has an associated redirect mode, which is "follow", "error", or "manual". Unless stated otherwise, it is "follow".

w3cのspecにも未設定の場合はfollowが選択されるとあるし、実際の振る舞いもリダイレクトしてリソースを取得している。オプションの振る舞いをまとめると、2016年10月現在では以下のようになる。

  • follow
    • デフォルト. リダイレクト先まで追従してリソースの取得を行う
  • error
    • リダイレクトが発生した場合にネットワークエラーを投げる
  • manual
    • 手動でリダイレクトを制御する。このときResponseオブジェクトは opaqueredirect となる。opaqueredirectは、レスポンス内容が隠蔽された状態のオブジェクトで、実際のレスポンスはinternal responseにアクセスする必要がある(この辺りは検証不十分)

つまり、今回のようにHTTPS→HTTPへのリダイレクトが行われ得るリソースに関しては、redirectオプションにmanualを渡してあげれば一応mixed content警告は回避できそう。

fetch('https://redirect-to-http-resource.com', {redirect: 'manual'})
  .then(res => {
    // do something
  });

8月の現状

今日のこと。

七尾旅人の「兵士A」が各所で評判がよいので観たいと思うも、最終回17時半スタートという厳し過ぎる現実に突きつけられる。「兵士A」という名前からも、性質からもかの作品「911FANTISIA」を想起させるし、いずれお目にかかりたい。

代わりにちょうど公開初日だった「君の名は。」を観た。

f:id:sisidovski:20160827040419j:plain

まだ会ったことのない君を、探している

新海誠について、割と苦手意識があったのであまり期待していなかったのだけれど、大きく裏切られることになった。

新海誠作品に対して抱く名状し難い感情や身構えてしまうような感覚は、割と多くの人が共有できるところだと思う。肥大化した自意識と自己完結型の展開、それらに辟易としつつもある種の愛しさを湛えるようで、例えるなら村上春樹の影響をもろに受けた高校生の痛々しさ、そういう類の嫌悪、懐かしさであるように思う。今作はそういった「全力っぽさ」を上手に使えているようで、安心して観ることができた。今までの作品と比較すると、物語の骨子が強固なものになっていて、安定した土台があることが大きいようで、その土台があるからこそある種の白々しさを楽しめた、ということになるのだろうか。途中で挟まれるRADWIMPSの歌すら心地よく、新海誠的な思春期の男子が抱く陰湿な演出ではなく、ある種の爽やかさが駆け抜けるわけである。

新海作品の根底には常に「距離」が描かれていて、そのメタファ・舞台装置として空・線が登場する。今作も同様だが過去作品と比較してみると明らかに異質なのは、長編アニメーションとして成立するだけの奥行きを持ったストーリーが存在する点だ。そこから派生していくコメディ・SFに引き込まれ、夢中になっている内に、気がつけば「距離」の袋小路に入ってしまった。今までの作品は最初から「距離」を露骨に描きすぎいてどこか観ながら冷めてしまう自分がいたが、今作はよい意味で感情移入できて、最後は胸が締め付けられる思いだった。よい意味で新海誠らしからぬ普遍性あるストーリー・演出から来る面白さと、新海誠らしい言葉選び・距離感の描写・美しい映像が混じりあって、傑作だったように思う。セルフオマージュが散見しているところもよくて、秒速5センチメートルの、言の葉の庭の。ケモナーなってしまった細田作品と比較しても遜色がない程に面白かったのではと、思う。

youtu.be

夏の気持ち

この夏、蔵前や南千住で飲んだくれたり、隅田川をぼんやり眺めたり、シン・ゴジラを何度も観たり、サマソニでCreepを聴いたりと、割と楽しく過ごしている、たくさんサウナに行ったし、瞑想や筋トレもしてるし、夏の終わりにまた素晴らしい映画に出会えたし、やっぱ夏はエモくて最高っぽい。

観終わったあと、岐阜に住む祖父母に会いたいと思ったし、帰りに観た東京のビル群は、美しかった。何を書いてもネタバレになりそうだし、早く観た人と話したいし、早くもう一度観たい。

Progressive Web Appsについて

ドワンゴ Advent Calendar 2015 4日目の記事です。最近ちらほら耳にする、Progressive Web Appsというものについて。

Progressive Web Appsとは

Progressive Web Appsは、モバイルWebを開発する上での1つの標榜である、という認識でよさそう。Alex Russell氏がブログで提唱したのが始まりで、先日行われたChrome Dev Summitのkeynoteでも取り上げられている。

medium.com

www.youtube.com

Progressive Web Apps自体は特に何か新しい技術を指すわけではなく、既存の技術の組み合わせに対して名前をつけただけのいわばマーケティングワードみたいなもので、つまるところ「◯◯と☓☓を組み合わせて作られた、よりアプリっぽい快適な動作をするWebアプリ」のことをProgressive Web Appsと呼びましょう、これからのWebはそうやって作っていきましょう、という宣言のようなもの。上記エントリでは次のように表現されている。

they’re just websites that took all the right vitamins.

こういった考え方が出てきた背景には、モバイルでの開発を取り巻くいくつかの課題が存在している。

とりあえずアプリ作ればいいと思ってませんか?

ネイティブアプリは確かに使いやすいしユーザとのエンゲージメントも高いけど、インストールされなくては意味が無い。ユーザが1ヶ月で利用する平均的なアプリの数は25個程度で、他のアプリとその枠を巡ってしのぎを削らなくちゃいけない。あなたのアプリは果たして、ストアに存在する膨大なアプリの中から検索され、インストールされ、実際に起動される、その25個の中に入れるだろうか。また、iOSAndroid、その他プラットフォームも含めてそれぞれで開発しなくてはならないし、保守コストに関しても馬鹿にならない。

ネイティブアプリの方が便利じゃないですか?

問題点として、モバイルにおけるWebアプリが根本的にイケてないということが挙げられる。ネイティブアプリよりレスポンスが遅い・タッチした後のフィードバックが作りこまれていない、オフラインで利用できない、プッシュ通知がない、ブラウザはタブが使いにくい等、考えればきりがない。

Progressive Web Appsが解決するモバイルWeb

上記問題に対して、TitaniumやXamarin、PhoneGapなど複数プラットフォームで利用できるハイブリッドアプリの開発が考えられるが、それはプラットフォームごとに開発するコストを減らすのみで、デプロイが楽になるわけでもないし、ストアからインストールされる可能性を上げてくれるわけではない。もっとも、開発コストを減らしてスピーディーに開発ができているかは、実際にハイブリッドアプリからネイティブに書きなおした事例なんかを考えてみると、有用な場面はかなり限定されると思う。

そこで提唱されているのが、HybridではなくProgressiveなWeb Apps。各プラットフォーム上で動くハイブリッドアプリを作るよりも、Web上で動作するアプリケーションを作っていく方が、よりネイティブアプリに近い動きを実現し、またユーザが利用する機会も増やせるのではないかという仮説に基づいている。WebをWebたらしめる性質なので当たり前だが、Web上にホストしてインストール不要で使えること、ハイパーリンクが利用可能であることなどを考えると、ユーザが利用する上でも、アプリケーションを提供する側としても扱いやすいし、プラットフォームごとに開発するコストやストアという仕組み上の制約(上記インストールまで到達する困難さ、課金システム、表現上の問題など)を考慮する必要がないことは大きなメリットとなり得るように思える。App Indexで最近アプリも載せられるようになってきたけど、検索インデックスを利用することもできる。

Webアプリが"appy"になるために必要なテクノロジー

下記のような機能を用いて、よりネイティブらしいアプリケーションを提供する。先に紹介したAlex氏のエントリや動画を見ると他にも列挙されているが、ここではより重要と思われるもののみ抜粋している。

  • Responsive Web Design
  • Service Worker(オフラインでの利用を想定)
  • ネイティブアプリのようなUIとインタラクション
  • Push Notification on the Open Web(Webアプリでのプッシュ通知)
  • App Install Banners(いわゆるブラウザが提供するホームに追加)

Responsive Web Design

割愛。

Service Worker

一応Service Workerについて軽く説明。Service Workerは通常のWebページのとは独立したサイクルでjsの実行コンテクストを提供する、イベント方式の機能。リクエストをフックしてキャッシュから応答したり、サーバのイベントを受けてそれをクライアントに通知するといった、クライアント用proxyとして利用できる。Service WorkerはHTTPSもしくはlocalhostでしか利用できないので、遊びで使うならgithub pagesなどを使うのが遊びではよさそう。デバッグしづらいので、作業はシークレットモードで行った方がよい。まあとにかく、Servie WorkerはProgressive Web Appsのコアとなる技術で、以下のような役割を果たす。

  • 静的ファイルをキャッシュすることで表示速度を改善する
  • サーバから取得したデータをキャッシュすることで、オフラインでの利用が可能になる
  • PUSH通知を受け取る
  • Service Workerを利用する = TLSでの通信が行われることなので、安全性を高められる

http://www.w3.org/TR/service-workers/

Web App Manifest

アプリに関連するメタデータを記述したjsonファイルをサーバに置くことで、それらの情報をブラウザやクローラが解釈してくれる。Service Workerと併用することで、プッシュ通知やホームに追加が実現できる。

https://w3c.github.io/manifest/

Offline Use

Service Workerを使ってfetchイベントをキャプチャし、キャッシュが存在すればそれを返し、なければネットワークから取ってくる、といった動きが実現できる。これによって、1度閲覧したページはキャッシュされ、オフライン状態でもキャッシュから表示ができるようになる。

self.addEventListener('fetch', function(event) {
  event.respondWith(
    caches.match(event.request)
      .then(function(response) {
        if (response) {
          return response;
        }

        var fetchRequest = event.request.clone();
        return fetch(fetchRequest)
          .then(function(response) {
            if (!response || response.status !== 200 || response.type !== 'basic') {
              return response;
            }

            var responseToCache = response.clone();

            caches.open(CACHE_NAME)
              .then(function(cache) {
                cache.put(event.request, responseToCache);
              });

            return response;
          });
      })
  );
});

Push Notification

ブラウザからのPUSH通知。facebookなんかはもう実装されていた気がする。今回時間の問題で実装できなかったが、概ね以下のような手続きのはず。

  • GCMを使うためにGoogle Developer Consoleの利用登録してプロジェクトIDを取得する
  • manifest.jsonに書く
  • Service Worker登録
  • pushManagerを利用してsubscribe
  • Service Worker側でpushイベントをlistenする 

App Install Banners

バナーの追加UIを表示させる

  • manifest.jsonの配置
  • ServiceWorkerの登録

manifest.jsonに以下の値を設定する。

{
  "name": "a progressive web app for cats",
  "short_name": "PWA for cats",
  "icons": [{
        "src": "images/touch/ms-touch-icon-144x144-precomposed.png",
        "sizes": "144x144",
        "type": "image/png"
      }],
  "start_url": "/?homescreen=1",
  "display": "standalone",
}
  • iconは144*144サイズのものが必要
  • start_urlはホームから起動したときのURL
  • displayは起動時の表示方式。ネイティブアプリのようにブラウザとは別のタスクとして扱うことが可能
  • Service WorkerはregisterしていればOK

その他

他にも、ページ内のコンテンツとUIの部分を分けてUIのみ先に描画する工夫であったり、ブロッキングを行う読み込みをasyncなどを用いて改善するといった対応が必要。以下のページにこのあたりの改善方法についても書かれている。

Instant Loading Web Apps With An Application Shell Architecture | Web Updates - Google Developers

今回のサンプルは以下

sisidovski/progressive-web-app-sample · GitHub

事例

Flipkart.lite

以前ちょっと話題になっていた、インドのECサイトFlipkartのWebアプリケーション。 通信インフラが整っていない途上国はこういった軽いページに対する需要が高そう。

stories.flipkart.com

Pokedex.org

Progressive Web Appsとして作ったポケモン情報まとめ。パフォーマンス向上のためのテクニックがすごい。60fpsで動作させるための工夫が細かく説明されている。

www.pocketjavascript.com

まとめ

業界全体が「お陰さまで合計◯◯ダウンロード!」とか煽ったりしているしネイティブから脱却するとかは全く思わないけど、ネイティブアプリの市場が成熟しつつある中で、ネイティブ風というだけでなくWebらしさを活かしていくことがモバイルWebの流れとしては出てきたのかなと感じた。Progressive Web Appsに限らずiOS9のSearch API関連(Universal LinksやSpotlightによる検索)も含めると、アプリとWebの境界が曖昧になるというトレンドが浮かび上がってくる。この辺りの話は、先日のmosaic.fmがとても面白かったので未聴の方は是非聴くとよいと思う。

#20 Browser | mozaic.fm

SICP(計算機プログラムの構造と解釈)1.1

はじめに

『計算機プログラムの構造と解釈』を読む。動機は以下。

  • いわゆる情報系の勉強をしていないので、基礎を身につけたい
  • Lispインタープリタを実装してみたい
  • ストリーム、遅延評価、末尾再帰最適化、構文・字句解析器など、なんとなくしか知らないものを理解したい
  • すごいエンジニアがみんな読んでる

年単位でかかるかもしれないが、それでも終わらない可能性・挫折する可能性があるので、練習問題は無理に全部やらない。

資料

mobiをkindleに送ってkindleから読んでいる。

html版

計算機プログラムの構造と解釈 第二版

訳にかなり癖があるので、意味を掴みにくい場合は、原著を確認するとよいかもしれない。また、コード集はこちらにしかないので、適宜見るとよい。

Welcome to the SICP Web Site

HTML版は、スタイルが適用されていないので、読みにくい。epub化を考えたけど、自分がやる前に既にepubおよびmobiで公開してくれている方がいたので、ありがたく使わせていただく。

github.com

環境

環境はOSXLisp/Scheme派生の言語Racketをバイナリからインストールして使っている。DrRacketというIDEが同梱されているので、そちらを利用するか、/Applications/Racket\ v6.2/binにPATHを通せば$ racketで対話型コンソールを起動できる。

racket-lang.org

Emacsの使用経験がないため、エディタは検討中。vimでやるか、これを期にemacsを覚えるか。。。

本文

1.1.7

平方根について。数学的な関数とコンピュータの記述について。 数学では平叙文的(何であるか)記述をするのに対して、コンピュータは命令文的(どうするか)記述をする。どう計算するかというアプローチに対して、通常は次々と近似をとるニュートン法を用いる。

> (define (sqrt-iter guess x)
    (if (good-enough? guess x)
        guess
        (sqrt-iter (improve guess x)
                   x)))
> (define (improve guess x)
    (average guess (/ x guess)))
> (define (average x y) (/ (+ x y) 2))
> (define (good-enough? guess x)
    (< (abs (- (square guess) x)) 0.001))
> (define (sqrt x)
    (sqrt-iter 1.0 x))

> (sqrt 2)
1.4142156862745097
> (sqrt 3)
1.7321428571428572
1.1.8

手続きを抽象化してブロック構造をとる方法、パラメータのスコープについて。外の入れ子にある束縛されたパラメータを内部で利用する(レキシカルスコープ)。

(define (sqrt x)
  (define (good-enough? guess)
    (< (abs (- (square guess) x)) 0.001))
  (define (improve guess)
    (average guess (/ x guess)))
  (define ( sqrt-iter guess)
    (if (good-enough? guess)
      guess
      (sqrt-iter (improve guess))))
  (sqrt-iter 1.0))

問題

EXSERCISE 1.3

三つの数を引数としてとり , 大きい二つの数の二乗の和を返す手続き

> (define (square a) (* a a))
EXERCISE 1.4

schemeの評価モデルは、演算子が合成式である組み合わせでも使える

> (define (a-plus-b a b)
  ((if (> b 0) + -) a b))
> (define (sum a b) (+ a b))
> (define (larger-square-sum a b c)
    (cond ((and (< a b) (< a c)) (sum (square b) (square c)))
          ((and (< b a) (< b c)) (sum (square a) (square c)))
          (else (sum (square a) (square b)))))
> (larger-square-sum 3 4 5)
41
EXERCISE 1.5

作用的順序の評価と正規順序の評価について

EXSERCISE 1.6

特殊形式として定義されているifを通常の手続きとして再実装して、1.1.7における平方根の手続きを行った場合、どうなるか。

> (define (new-if predicate then-clause else-clause)
    (cond (predicate then-clause)
          (else else-clause)))

> (define (sqrt-iter guess x)
    (new-if (good-enough? guess x)
            guess
            (sqrt-iter (improve guess x)
                       x)))

結果、無限ループする。これは、Schemeにおける通常の手続きが作用的順序で行われることに起因する。作用的順序での評価は、以下の通り。

  1. 組み合わせの部分式を評価する
  2. 最左部分式の値である手続き(演算子)を残りの部分式の値である引数に作用させる

つまり、一般的なSchemeの評価規則で定義されたnew-ifの場合だと、先に部分式が評価されるため、

(good-enough? guess x)

が真であったとしても

(sqrt-iter (improve guess x)
           x

が評価されるため、無限ループする

EXERCISE 1.7

曖昧。 平方根の手続きにおいて、入力が非常に小さい値もしくは大きい値にテストすっとが失敗する。大きい値の場合は、浮動小数点の比較における誤差によるところ。桁数の増大によって仮数が計算機に無視されるため、無限ループする。値が小さい場合、予測値が基準値より下回ると真を返すため、値にかなりのずれがあっても再帰が終了してしまう。改良版未着手。

EXERCISE 1.8

未着手。立方根の問題。ニュートン法の実装を改良する。

linuxにおけるメモリと関連コマンド(free, vmstat, top, sar)

linuxにおけるメモリの扱いを中心として、関連する統計情報の閲覧、監視ツールの見方についてまとめる。下に挙げた技評の2冊および学びの多かった記事を基にしています。

ページキャッシュ

freeとかtopとかの説明に入る前に、まず、OSのキャッシュの話から始める。大規模なデータを扱う際、Linuxはキャッシュの仕組みによって効率的にメモリ管理を行うことで、ディスクへのアクセスを減らし、データアクセスを高速化している。これは「ページキャッシュ」と呼ばれる仕組みで実現している。

Linuxには仮想アドレスと呼ばれる機能を提供している。論理的なアドレス(リニアアドレス)をOSが提供することによって、各プロセスはメモリ(あるいはスワップ)の物理的なアドレスを気にすることなく扱うことができるようになる。この際、Linuxはメモリ領域をアドレス単位ではなく1つのまとまったブロックとして提供しており、その単位が「ページ」と呼ばれる。サイズは4KB。このページをOSはメモリ上にずっと確保しており、別のプロセスが再び同じデータにアクセスしようとしたときに、ディスクへのアクセスを行うことなく高速に処理できる、という仕組みが、ページキャッシュの大まかな仕組み。Linuxはメモリが空いている限り全てページキャッシュに回そうとする。

基本的にはメモリに空きがあれば全てキャッシュに乗せるが、開いていない場合は古いキャッシュから破棄して新しいキャッシュと入れ替える。sar -rで確認すると、memusedの比率におけるkbcachedの値が確認できる。

06:00:01 AM kbmemfree kbmemused  %memused kbbuffers  kbcached kbswpfree kbswpused  %swpused  kbswpcad
06:10:01 AM    161280   3824360     95.95      6672   2008004   4174336     18620      0.44       380
06:20:01 AM    171652   3813988     95.69     18132   1995240   4174336     18620      0.44       380

メモリとディスク

メモリへのアクセスとディスクへのアクセスには、105から106(10万〜100万)倍程度の速度差が生じる。これは、メモリが電気的な部品であって、探索速度に物理的制約を受けることがないのに対して、ディスクの場合はヘッドの移動、ディスク回転という物理的な動作を伴う必要があることに起因する。を必要なデータがメモリ内に乗っていれば、多少アプリケーションが非効率であっても、ある程度パフォーマンスが担保できる。アプリケーションを構成する上で、iowaitを避けて極力全てのアクセスをメモリで行いたいのは、こうした理由からである。また、OSのキャッシュで捌ききれなくなったときに、分散の考え方が必要になる。

メモリの使用状況を確認する

メモリ使用量を計測する各種コマンドについて。環境はvagrantのubuntu14.04 LTS。

free

現在のメモリ使用量を確認できる。この出力は、/proc/meminfoファイルを元にしている。 オプションの-tTotal行の出力、-mはMB単位での出力を行う。

vagrant@vagrant-ubuntu-trusty-64:~$ free -tm
             total       used       free     shared    buffers     cached
Mem:           489        336        153          0         12        211
-/+ buffers/cache:        112        377
Swap:            0          0          0
Total:         489        336        153

Mem行は、物理的なメモリの総量。489MBのメモリが存在し、336MBが既に使用され、空きメモリは153MBということが分かる。bufferscachedはOSによって予約されている領域。buffersおよびcacheは、メモリが不足する場合に開放して利用できるようにする。よって、実質的なメモリの使用量および利用可能量を見るためには、2行目の-/+ buffers/cache行を見るのが正しい。2行目のusedMem行のusedからbuffers/cachedを引いた使用量、freeは足した量を表示している。ここではSwap行に出力はないが、メモリに載せるデータが増えてくると、スワップ領域がディスクに作成され、一部のデータがそちらに移される。

スワップ

ディスクを利用して使用可能なメモリ領域を増やす。メモリ容量が不足している場合に、現在使われていないデータを一時的にスワップ領域に退避させてメモリを開放する。 スワップ領域に退避させたデータの参照をプロセスが要求したときに、再度メモリ上に読み込まれる。ディスクへのアクセスがあるので、もちろん遅い。スワップ領域への退避・アクセスが頻発する条件は、要改善項目。メモリが不足しているため、以下のようなことを検討する必要がある。

  • 極端に大きなプロセス(メモリリークなどの異常含む)がないか
  • プログラム内に非効率にメモリを使う箇所がないか
  • メモリを増設できないか
  • 分散を検討できないか

ボトルネックの特定には、下で挙げるvmstatsarを使うとよい。

vmstat

メモリやCPUの情報だけでなく、スワップやプロセスの情報を出力できる。

vagrant@vagrant-ubuntu-trusty-64:~$ vmstat 1
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 3  0      0 144368  23220 200692    0    0    54    20   21  327  0  1 99  0  0
 0  0      0 144360  23220 200692    0    0     0     0   32   66  0  0 100  0  0
 0  0      0 144360  23220 200692    0    0     0     0   24   52  0  0 100  0  0

siスワップイン), soスワップアウト)が頻繁に動いている場合はスワップが発生している。他の項目としては、rは実行待ち状態のプロセス数、bは割り込み不可能なスリープ状態にあるプロセス数。memoryの項目はfreeと同様、ioはデバイスとのブロックの転送量、systemはコンテキストの毎秒の切り替えレート(割り込み回数・コンテキストスイッチの回数)、cpuCPU使用率である。

top

topコマンドは、現在のシステム全体の負荷情報を閲覧するのに利用する。プロセス・CPU・メモリ・スワップが確認できる。主なオプションは、以下の通り。

vagrant@vagrant-ubuntu-trusty-64:~$ top          # CPU使用率順にソート
vagrant@vagrant-ubuntu-trusty-64:~$ top -o %MEM  # メモリ使用率順にソート. -o fieldnameでソート順を切り替えられる。centosでは-a?
vagrant@vagrant-ubuntu-trusty-64:~$ top -p [PID] # 特定のプロセスを監視
vagrant@vagrant-ubuntu-trusty-64:~$ top -d3      # 3秒ごとに更新

topではshiftでソート順の切り替えができる。

shift + m // メモリ使用率順
shift + p // CPU使用率順
ヘッダについて
vagrant@vagrant-ubuntu-trusty-64:~$ top
top - 19:56:06 up  2:12,  2 users,  load average: 0.00, 0.01, 0.05
Tasks:  75 total,   1 running,  74 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.0 us,  0.0 sy,  0.0 ni,100.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem:    501748 total,   140316 used,   361432 free,     5252 buffers
KiB Swap:        0 total,        0 used,        0 free.    24128 cached Mem

1行目は、uptimeコマンドと同じ出力。現在時刻、システム稼働時間、ログインユーザ数、LoadAverageと並ぶ。LAは左から、1分、5分、15分を単位時間としている。2行目は、タスクの統計。上の例では、75のタスクがあり、稼働中のものは1、待機中のものが74となっている。3行目はCPU。usがユーザ、syがシステム、niがnice、idがidle、waがiowait、hiはhardware interrupt、siはsoftware interrupt、stがsteal。 3行目、4行目はメモリ統計。これはfreeで解説したものと同じ。

プロセス一覧
 PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND
 1789 vagrant   20   0  107696   1352    348 S  0.3  0.3   0:01.28 sshd
 8523 vagrant   20   0   23536   1560   1116 R  0.3  0.3   0:00.02 top
    1 root      20   0   33608   1716    276 S  0.0  0.3   0:00.86 init

ここでのCPUは1コアを100%とした値が出力されるため、仮に何かのバッチスクリプトのプロセスが100%になっていたからといって焦る必要はない(効率はよくないだろうけど)。

PID // プロセスid
USER // 実行ユーザ
PR // 優先度
NI // ナイス値
VIRT // 割り当てられた仮想メモリのサイズ
RES // スワップのない物理メモリのサイズ
SHR // 割り当てられた共有メモリのサイズ
S // プロセスのステータス。R: 実行可能,S: 停止,D: 割り込み不可の停止,T: 停止またはトレース中,Z: ゾンビ・プロセス,W: スワップ・アウトしたプロセス, N: ナイス値が正
%CPU // CPU使用率
%MEM // メモリ使用率
TIME // プロセスの経過時間
COMMAND // タスクのコマンド名
LoadAverage

1CPUにおける単位時間あたりの実行待ち、ディスクI/Oの完了待ちをしているタスク。これが高ければ高いほどCPUかIO起因で高負荷といえる。雑な例えをすると、手元のPCでchromefirefoxxcodeを立ち上げて更にでかいExcelファイル読み込もうとすると、マルチタスクとはいえちょっと重いよねって話。

sar

sarコマンドは、CPU使用率およびディスクI/Oを出力できる。他のコマンドと異なる点としては、過去に遡れることが挙げられる。また、topやfreeと異なり定期的に1行ずつ出力されるので、負荷試験時などに計測を行いやすい。sysstatパッケージをインストールする必要があるので、sarコマンドがない場合はインストールする。

vagrant@vagrant-ubuntu-trusty-64:~$ sudo apt-get install sysstat

デフォルトでは10分間隔でログ出力されている。必要に応じて変更する場合は、以下のファイルを編集する。また、ログの保存期間も必要に応じて伸ばしておく。centosであれば/etc/sysconfig/sysstatあたり。ubuntuであれば/etc/default/sysstat?(未検証)

vagrant@vagrant-ubuntu-trusty-64:~$ cat /etc/cron.d/sysstat
~
# Activity reports every 10 minutes everyday
5-55/10 * * * * root command -v debian-sa1 > /dev/null && debian-sa1 1 1
オプション
vagrant@vagrant-ubuntu-trusty-64:~$ sar -q   loadaverage
vagrant@vagrant-ubuntu-trusty-64:~$ sar -u  CPU使用率
vagrant@vagrant-ubuntu-trusty-64:~$ sar -b  I/O
vagrant@vagrant-ubuntu-trusty-64:~$ sar -r  メモリとスワップ使用率
vagrant@vagrant-ubuntu-trusty-64:~$ sar -s time 時間以降のデータ
vagrant@vagrant-ubuntu-trusty-64:~$ sar -e time 時間までのデータ

オプションなしでsar 1 5等の指定も可能。この場合は、1秒を単位時間として5秒間CPUの使用率を出力する。

vagrant@vagrant-ubuntu-trusty-64:~$ sar 1 5
Linux 3.13.0-55-generic (vagrant-ubuntu-trusty-64)    07/06/2015   _x86_64_    (1 CPU)

08:47:12 PM     CPU     %user     %nice   %system   %iowait    %steal     %idle
08:47:13 PM     all      0.99      0.00      0.00      0.00      0.00     99.01
08:47:14 PM     all      0.00      0.00      0.00      0.00      0.00    100.00
08:47:15 PM     all      0.00      0.00      1.00      0.00      0.00     99.00
08:47:16 PM     all      0.00      0.00      0.00      0.00      0.00    100.00
08:47:17 PM     all      0.00      0.00      0.00      0.00      0.00    100.00
Average:        all      0.20      0.00      0.20      0.00      0.00     99.60

sarは過去に遡って出力できるため、ボトルネックの特定に重宝する。-qでLoadAverageが高かった場合は、-u -eで該当時間帯のCPU使用率をチェック、userが高ければCPU負荷、iowaitが高ければディスクI/O負荷と切り分けることができる。

vagrant@vagrant-ubuntu-trusty-64:~$ sar -u -s 04:00:00

04:00:01 AM       CPU     %user     %nice   %system   %iowait    %steal     %idle
04:10:01 AM       all      4.78      0.01      1.38      2.84      0.00     90.99
04:20:01 AM       all      3.03      0.00      1.10      0.02      0.00     95.85
04:30:01 AM       all      2.99      0.00      1.06      0.02      0.00     95.93

監視ツールの見方

まず、監視ツールを導入する。ここではXymon(旧hobbit)を利用して、メモリに関する部分のみ見ていくことにする。Xymonオープンソースのネットワーク・サーバ監視ツール。読み方はサイモンorシモン。恐らくサイモンだろう。シモンとガーファンクルとは言わないし。。。GPL2ライセンス。

これが監視ツールで満たときのメモリ使用率。

f:id:sisidovski:20150707060345p:plain

freeコマンドを元にグラフを出力している。

actualは、バッファとキャッシュ分を差し引いた値。freeでいうところの-/+ buffers/cache行のusedのことである。realは、Mem行、つまりバッファとキャッシュを含めた使用率の値である。そのため、realよりもactualの方が低い値となっている。ページキャッシュがあまり乗っていない起動直後は、actualrealに大きな変化が見られない。swapはその名の通りスワップです。

言い回しが違うだけで確信が掴めなかったけど、以下のMLに書いてあった。

Re: [hobbit] Memory check

インストール手順は、こちらの記事が参考になります。

karia.hatenablog.jp

おわり

Linuxにおけるメモリの扱いを中心としてざっと見てきたが(CPUとかディスクとかが中途半端に入ったけど)、メモリ管理の仕組みそのものや、メモリ管理を意識したスケールアウト時の考え方、各コマンドの詳細、実践に則した使い方、監視ツールの多項目等、もっと掘り下げたいところ。間違い等あれば指摘ください。

参考資料