Linuxにおけるプロセス/スレッドの調査とか学習とか

概念的・理論的な意味におけるプロセスとスレッド

プロセスとはプログラムコードやメモリ空間などを含めた実行処理のインスタンスである
スレッドとはプロセス内でメモリを共有し、並行化するためのより細かい単位である

Linuxにおけるプロセスとスレッド

プロセスは概念的なプロセスをそのまま実装されたもの
プロセス内で並列・並行化するためのスレッドは2つの意味がある

  • 軽量プロセス(LWP)
    プロセス内でスレッドを実現するが、スレッドの実行スケジュールはOSのスケジューラが行う なので、OSスケジューラから見たら、プロセスとスレッド(LWP)は同じ単位になる
    OSのスケジューラを使うので、マルチコアにおけるCPU資源の割り当てが可能

  • ユーザースレッド(アプリケーションスレッド)
    ユーザ空間で 実装 されたスレッド機構
    実際は プロセス - LWP - ユーザースレッド という紐付けになる
    ユーザ空間で実装されたランタイムでスケジューリングされるので、プロセス内でのタスク切り替えになるのでシングルコアでしか動かない

なので、 Linux上でスレッドを使って並行・並列化するという文脈の時は

  • LWPを多重化すれば、並列化(CPUコアでスケールする)
  • ユーザースレッドを多重化すれば並行化(シングルコアでタイムスライス)

ユーザー空間上の実装で並列・並行化可能という意味で、この2つの方法を区分けせずに ユーザー(空間上の)スレッド と表記するケースがある

プロセススケジューラについて

プロセススケジューラを呼び出されるタイミングは

  • CPUによる200msごとの割り込みがあった場合
  • 実行中のスレッドが明示的にスケジューラを呼び出した場合
  • 他にもあるっぽい

これらのタイミングでプロセススケジューラが呼び出されて、その時に一番優先順にが高いプロセスにタイムスライスが割り当てられる
スケジューラはCPU割り当てのスイッチも行うことから、カーネルモードで実行される

カーネルモードとユーザーモードについて(CPUモード)

CPUのアーキテクチャ(x86とか)で実装されている実行権限コントロールの仕組み

カーネルモード = 特権モード = マスターモード = スーパーバイザーモード = システムモード
と状況によって呼ばれ方が違う

システムコールなどを通じてデバイスアクセスしたりする時はカーネルによってスレッドをカーネルモードに切り替える。
この切替が行われないままのユーザーモードだとデバイス操作などに必要なカーネルメモリ空間へのアクセスはできない(例外が発生する)

カーネルモードのユーザープロセス(スレッド) のことを カーネルスレッド を表記する場合があるが、状況によっては正解、、、?
カーネルスレッドはカーネルメモリ空間で実行される = カーネルモードのCPUで処理されているスレッドはカーネルメモリ空間へアクセスできる と同意っぽい

カーネル空間/ユーザー空間について

メモリ上においてカーネルモードのプロセスのみがアクセスできるメモリ空間

カーネルスレッドについて

PIDが0のinitや割り込み処理などを行うスレッド
ユーザープロセス(スレッド)と同じようにスケジューラによってCPU割り当てされる
カーネルスレッドはメモリ空間を固定で割り当てられていない
そのためスケジューラによって切り替わる直前のユーザープロセスのユーザーメモリ空間を使う(そのためカーネルスレッドのタイムスライスの時は毎回メモリ空間が異なる)

カーネルスレッドという言葉を使う時に OSカーネルの処理を専門で扱うスレッド を指す場合と カーネルモードのユーザープロセス(スレッド) を指す場合があるので注意(ここでは前者の意味)

プロセスと軽量プロセス(LWP)とスレッドと

プロセスはプログラムコードやスタック領域などを含めた実行状態のインスタンスみたいなもの
プロセス毎に違うメモリ空間が割り当てられているため、お互いのプロセスのメモリ空間には通常アクセスできない(そのため不具合があってもプロセス間で伝搬しない)

スケジューラーによってプロセスを切り替える時に実行中のコンテキスト(スタックなど)を一度保存したあと、切り替え後のプロセスも保存してあったコンテキストを復元して処理を続行する。
そのため切り替えるたびにこの処理が必要なため切り替えコストが高い(これをコンテキストスイッチが高い)と呼ぶ

スレッドはプロセスと違い、一つのメモリ空間を使って生成される。(プロセスというメモリ領域に複数のスレッドを生成することが可能)
そのため、上記のようなメモリを退避が不要なためコンテキストスイッチのコストが低い

Linuxではプロセスを生成するときにLWPとして生成することができる。
これは実質スレッドだが、スケジューラはプロセスとして扱うのでタイムスライスのコントロールはOSのスケジューラが行う

言語ごとのプロセスとスレッドの扱いについて

go(goroutine)やpython

- プロセス
  ┃-LWP(ユーザー(空間上の)スレッド)
  ┃  └ユーザースレッド(アプリケーションスレッドと呼ぶ場合も): goroutine, python Thread
  ┃  └ユーザースレッド(アプリケーションスレッドと呼ぶ場合も): goroutine, python Thread
  ┃  └ユーザースレッド(アプリケーションスレッドと呼ぶ場合も): goroutine, python Thread

いわゆるグリーンスレッドを扱う言語
一つのプロセス(=LWP)上で、言語ランタイムによって擬似的にスレッドを生成する。 pythonや以前のRubyの場合はGIL(Global Interpreter Lock)という機構があるためパフォーマンスが出づらい(IO多重化には有効っぽい?)

pthreadやJVMのThreadの場合

- プロセス
  ┃-LWP(ユーザー(空間上の)スレッド)
  ┃  └アプリケーションスレッド(ユーザースレッドと呼ぶ場合も): pthread, JVM Thread
  ┃-LWP(ユーザー(空間上の)スレッド)
  ┃  └アプリケーションスレッド(ユーザースレッドと呼ぶ場合も): pthread, JVM Thread
  ┃-LWP(ユーザー(空間上の)スレッド)
  ┃  └アプリケーションスレッド(ユーザースレッドと呼ぶ場合も): pthread, JVM Thread

いわゆるネイティブスレッドを扱う言語
LWPとアプリケーションスレッドが1:1なので、タスクスケジュールはOSの機構を使う

参考

JavaにおけるOS上のスレッドの取り扱い

Threadのメモリモデルを色々調べていくうちにJavaのスレッドでOSスレッドとどうやって紐付いてるんだろうと思って調べた。
JavaにおけるTheradスケジューラはOSに依存するみたいな説明がされているが、要はJVMプロセス内でOSスレッドを生成してそれをwrapしている。
知識としては知っていたけど、改めて確認してみたかったメモ

public class Main {
    public static void main(String args[]) throws Exception {
        Thread.sleep(1000 * 60 * 60);  // プロセス終了しないように止めておく
    }
}


javacしてjava Mainでjavaプロセス起動してからPID確認

root@dc4d74fd6f08:/# jps
5684 Jps
5498 Main ←ここにいる

まずはjstackでjava threadを確認

root@dc4d74fd6f08:/# jstack 5498 | grep nid
"Attach Listener" #9 daemon prio=9 os_prio=0 tid=0x00007f29a0001000 nid=0x15ba waiting on condition [0x0000000000000000]
"Service Thread" #8 daemon prio=9 os_prio=0 tid=0x00007f29dc0c0000 nid=0x1587 runnable [0x0000000000000000]
"C1 CompilerThread2" #7 daemon prio=9 os_prio=0 tid=0x00007f29dc0b0800 nid=0x1586 waiting on condition [0x0000000000000000]
"C2 CompilerThread1" #6 daemon prio=9 os_prio=0 tid=0x00007f29dc0af000 nid=0x1585 waiting on condition [0x0000000000000000]
"C2 CompilerThread0" #5 daemon prio=9 os_prio=0 tid=0x00007f29dc0ac000 nid=0x1584 waiting on condition [0x0000000000000000]
"Signal Dispatcher" #4 daemon prio=9 os_prio=0 tid=0x00007f29dc0aa000 nid=0x1583 runnable [0x0000000000000000]
"Finalizer" #3 daemon prio=8 os_prio=0 tid=0x00007f29dc083800 nid=0x1582 in Object.wait() [0x00007f29c5a50000]
"Reference Handler" #2 daemon prio=10 os_prio=0 tid=0x00007f29dc07f000 nid=0x1581 in Object.wait() [0x00007f29c5b51000]
"main" #1 prio=5 os_prio=0 tid=0x00007f29dc009800 nid=0x157b waiting on condition [0x00007f29e36ea000]
"VM Thread" os_prio=0 tid=0x00007f29dc077000 nid=0x1580 runnable
"GC task thread#0 (ParallelGC)" os_prio=0 tid=0x00007f29dc01f000 nid=0x157c runnable
"GC task thread#1 (ParallelGC)" os_prio=0 tid=0x00007f29dc020800 nid=0x157d runnable
"GC task thread#2 (ParallelGC)" os_prio=0 tid=0x00007f29dc022000 nid=0x157e runnable
"GC task thread#3 (ParallelGC)" os_prio=0 tid=0x00007f29dc024000 nid=0x157f runnable
"VM Periodic Task Thread" os_prio=0 tid=0x00007f29dc0c2800 nid=0x1588 waiting on condition

root@dc4d74fd6f08:/# jstack 5498 | grep nid | wc -l
15

jstack的には15個のスレッドがあった


次にpsコマンドでOSスレッドを確認

root@dc4d74fd6f08:/# ps -feL | grep "java Main"
UID        PID  PPID   LWP  C NLWP STIME TTY          TIME CMD
root      5498     1  5498  0   16 11:36 ?        00:00:00 java Main
root      5498     1  5499  0   16 11:36 ?        00:00:00 java Main
root      5498     1  5500  0   16 11:36 ?        00:00:00 java Main
root      5498     1  5501  0   16 11:36 ?        00:00:00 java Main
root      5498     1  5502  0   16 11:36 ?        00:00:00 java Main
root      5498     1  5503  0   16 11:36 ?        00:00:00 java Main
root      5498     1  5504  0   16 11:36 ?        00:00:00 java Main
root      5498     1  5505  0   16 11:36 ?        00:00:00 java Main
root      5498     1  5506  0   16 11:36 ?        00:00:00 java Main
root      5498     1  5507  0   16 11:36 ?        00:00:00 java Main
root      5498     1  5508  0   16 11:36 ?        00:00:00 java Main
root      5498     1  5509  0   16 11:36 ?        00:00:00 java Main
root      5498     1  5510  0   16 11:36 ?        00:00:00 java Main
root      5498     1  5511  0   16 11:36 ?        00:00:00 java Main
root      5498     1  5512  0   16 11:36 ?        00:00:00 java Main
root      5498     1  5562  0   16 11:38 ?        00:00:00 java Main

root@dc4d74fd6f08:/# ps -feL | grep "java Main"| wc -l
16

ps的には16個あった


psコマンド的にはjstack比べてスレッドが一個多いのは
>root 5498 1 5498 0 16 11:36 ? 00:00:00 java Main
これが大元のjava親プロセスでメインスレッドを作るときにforkして子プロセスを作ってるっぽい(PIDとLWPが同じなので)

  • 1Processは1CPUで実行される
  • なので概念的にはProcessの中にいくらThreadを作成して並行化しようが、1CPUしか使わない
  • そこでLinuxとしてはLWP(Light Weight Process)としてメモリ共有したプロセス(軽量プロセス)を生成し、Threadとして扱う
  • 昔のLinuxはLWPがなかったので、単なるThreadだったのでマルチコアで多重スレッドにはできなかった
  • 実際、上記のps -feLの結果確認するとLWPのIDが全部違うのでOSスレッド = 軽量プロセスということになっている

スッキリした


プロセスとスレッド - Linux Kernel - FreeBSDいちゃらぶ日記

マネジメントの秘伝のタレ

今回は私が今までチームマネジメントやヒューマンマネジメントを通して学んだTIPSを整理してみたいと思います。
マネジメント(≒コミュニケーション)を支える技術について都度メモして、自分への戒めとして利用していたものを箇条書きにまとめました。
ある特定の状況だけでしか適用できないものが多いですが、応用はいろいろ効くと思っています。
マネジメントの立場にこれからチャレンジしていきたい人の一助になればと思ってます。

※自分向けのメモを整理しただけなので、一般的にこうあるべきという内容ではありません


会議編

-全員の参加を促そう

全員の発言機会が均等になっているか常に意識しよう
一言でも意見を言うことによって、その議題を決めたという意識を持てる

- 自分自身(チーム自身)で決めたという感覚に落としもう

「決められたこと」ではなく、「自分たちで決めたこと」という意識を促そう
その決定が実行されなかった時に他責にしずらくなる

- 話が長い人は最後に要約を返してあげよう

「つまり〜〜ということであってますか?」
まわりが飽きてしまうと会議からの離脱が増える
要約してあげることで離脱者を引き戻すことができる

- 視界から消えよう

マネージャーに向かって話すと、複数人なのに一対一のようになってしまう
マネージャーは目を合わせない、ホワイトボードに向かう、後ろに立つ、歩き回るなどして存在を消そう
そうすれば、自然と視線がメンバー同士になる

- 話が長い人はインターセプトしよう

ペンを落とす、くしゃみをする、携帯を鳴らす、トイレに行くなどして会話を途切れた瞬間に話題を変えよう
ただ、あからさまだと失礼なので注意

- わちゃわちゃし始めたときは議論の立ち位置を確認しよう

議論が広がりすぎたときはホワイトボードに簡易マインドマップなどの関連図を書いて、全員で位置関係の認識を合わせよう
空気がけっこう引き締まる

- 空気を占拠しよう

最終的にはマネージャーが責任を持つべきなので、その意思表示のためにも率先して場の空気を占拠しよう
最初にホワイトボード前に立つ、ホワイトボードマーカーを最初に手に取るなどをすればそんな空気になる

- 個人攻撃はすぐにやめさせよう

何も生まれないし、チーム内に禍根の残すきっかけになってしまう

- メンバー同士が衝突し始めたら目線を逃がすように誘導しよう

メンバー同士で正面から目が合った状態で、議論が白熱すると言葉が荒くなったり、喧嘩腰になったりする人もいる
そんな時は、ホワイトボードやノートに書き出したり、モニターを映してみたりして全員の目線を外に逃がそう

- 誰か喋るまで待とう

喋りすぎなマネージャーは議論の邪魔
静かな空気が苦手でも誰かが喋るまで待とう
それでも喋らなかったらそもそも会議自体の内容を見直そう

- よーく観察しよう

寝てる人はいないか、飽きていないか、明らかに他の作業をしていないか常に観察しよう

- 眠たそう、寝ている人がいたらあからさまに意見を求めよう

「寝ないでください」と直接言うのはスマートじゃない
あからさまに何度も意見を求めることによって本人が気づいてくれるのを促そう
それでもだめなら休憩をいれよう

- PCを開けないように机をなくそう

「PCを開くな」や「PCを持ち込むな」と注意するのはクールじゃない
仕組みで解決しよう

- 時間をコントロールしよう

(25分 + 5分) or (50分 + 10分)のワンセット一本勝負

- 議論の決定は必ず実行しよう

実行されないと、発言しても意味がないと思われるので参加意欲が失われる

面談編

- 注意・説教はやめよう

注意する時は、必ず信頼関係が築けてから
そうでない限りは注意・説教しても何も影響しない

- 期待は伝えずに聞いてみよう

「あなたに期待されていることってなんだと思いますか?」
先に質問することでメンバーの意識とマネージャーの意識のギャップを確認しよう

- 相手のテンションを下げないようにしよう

相手のテンションを下げても何も生まれない
テンションをあげるような言葉遣いを意識しよう

- 条件反射を徹底的になくそう

カチンとすることを言われても、反射的に反論しないようにしよう
3呼吸ぐらいは置こう(短気だと思う人は5呼吸ぐらい)
もしくは、スルーしてから面談の最後にどうしても言いたかったら言おう

- 権利と義務はセットではないことを意識しよう

権利は権利、義務は義務
納税していない人でも選挙権はある

- 期待をはっきりと伝えよう

どういう人材になってほしいか誠実にはっきりと伝えよう
実際に相手のやりたいこととマッチするかは別にして、まずは伝えよう

- 徹底的に相手の立場を想像しよう

どのように伝えられたらテンションがあがるか?
どのような言葉を使われたら気分が悪いか?

- うわべの会話はやめよう

うわべで話していると相手は必ず気づく
本心から興味を持っていないことは必ず伝わる
うわべで褒めても必ず見抜かれる
そのためには日頃から観察しよう

- 指示ではなく、提案をしよう

条件、目標、作業など相手が決めきれない時は複数提案してあげよう
ちょっとの違いでもいいから複数だそう
"選択した" という行為によって、自分で決めたことになる

- 相手の心を揺さぶろう

扇動的な言葉を使って、相手の心を揺さぶろう
そこにモチベーションを上げるヒントがある

- 評価しないように意識しよう

マネージャーがその人の全てを評価できると思うことはおこがましい
評価はさまざまなプロセスで最終的に決まる
面談しただけの印象で固定観念に落とさないようにしよう

- 直接プレッシャーをかけないようしよう

直接的なプレッシャーは人を潰すだけ
期待をする、期待をされることでプレッシャーは相手の中で勝手に醸成される






マネジメントは技術です。
本もたくさん出ていますし、バイブルと言われているような有名な本もあります。
知識をつけて、実践して、失敗して、工夫して、身につけていくものです。

プログラミングと全く同じですね!

wordpressのdockerコンテナからRDSに接続する

waysaku.hatenablog.com
の続き

今度はRDSに接続してみる。
RDSはとりあえずdevでぽちぽち立ち上げ。

mysql -h ${RDS ENDPOINT} -P 3306 -u ${RDS USER} -p

で接続できることを確認。
接続できない時は立ち上げたVPC上のSecurity Groupの設定で接続元(この時はローカルのMacから接続してたので自宅のグローバルIP)とポートを許可する



RDSに接続できることを確認できたらwordpressのコンテナ起動オプションに
https://hub.docker.com/_/wordpress/
に従い -e オプションで接続先のDB設定を追加する

docker run --name wordpress -e WORDPRESS_DB_HOST=${RDS ENDPOINT} -e WORDPRESS_DB_USER=${RDS USER} -e WORDPRESS_DB_PASSWORD=${RDS PASSWORD} -p 80:80 -d wordpress

起動できたら前回同様にブラウザでアクセスしてみる
#dockerホストのIP確認

$ docker-machine ip wordpress
>192.168.99.100


http://192.168.99.100にアクセス -> OK

同一dockerホスト上でwordpressとmysqlをそれぞれのdockerコンテナで起動して接続してみるメモ

※環境はMacOS
virtualBoxとdocker-machineはセットアップ済み


#docker-machineでwordpress起動用のdockerHostを起動

$ docker-machine create --driver virtualbox wordpress

# 環境変数を設定

$ eval "$(docker-machine env wordpress)"

#mysqlのdockerコンテナを起動

$ docker pull mysql
$ docker run --name mysql -e MYSQL_ROOT_PASSWORD=mysql -d -p 3306:3306 mysql

#接続の確認

mysql -h $(docker-machine ip wordpress) -uroot -p

dockerでwordpressを起動

$ docker pull wordpress
$ docker run --name wordpress --link mysql -p 80:80 -d wordpress

ブラウザでアクセスしてみる
#dockerホストのIP確認

$ docker-machine ip wordpress
>192.168.99.100


http://192.168.99.100にアクセス -> OK

Raspberry Pi2 Model Bでmdadmを使ったraid1構築

  1. apt-get install mdadm でmdadmをインストール
  2. sdaとsdbをfdiskで初期化してから、それぞれsda1とsdb1のパーテーションを作成
  3. mdadm --create /dev/md0 --level=raid1 --raid-devices=2 missing /dev/sda1 でまずはsda1だけの片肺状態で/dev/md0を作成
  4. mkfs -t ext4 -c /dev/md0 で/dev/md0にext4のファイスシステムを作成する
  5. /etc/fstabに /dev/md0 /data ext4 defaults 0 0を追記する
  6. 試しに mount /dev/md0でマウントしてみる→OK
  7. mdadm --add /dev/md0 /dev/sdb1 で残りのsdb1をraidに参加させる