Linux: pid numberからtask_structの取得

pid numberからtask_structの取得のめも

pid番号から直接task_structは取得できないので、pid構造体の取得 -> task_struct構造体の取得という流れになる。 pid名前空間を気しなくて良い場合は、find_get_pid()でpid構造体を取得し、pid_task()でtask_structを取得できる。pid名前空間が重要になる場合は、find_get_pid()ではなくてfind_ge_pid()を使ってpid構造体を取得する。

  static struct task_struct *find_task_by_pid(int nr) 
  {
          struct pid *pid = find_get_pid(nr);
          if (!pid) {
                  pr_warn("couldn't find pid %d's task\n", nr);
                  return NULL;
          }   
  
          return pid_task(pid, PIDTYPE_PID);
  }   

( ´ー`)フゥー...

エクストリームプログラミング

エクストリームプログラミング

Ansibleのget_urlでログインが必要なwebページからファイルをダウンロードする

Ansibleのplaybookを書いていて、ファイルをダウンロードする前にログインが必要な場合にどうすればよいのかを調べたのでめも。

使用してるAnsibleのバージョンは2系です。

curlで例えると、--userでIDとパスワードを↓のように渡すのをansibleでどうやるかってところです。

curl --user username:password -L https://www.example.com/foobar.zip -o foobar.zip

で、解決策は我らがstackoverflow.comにありました。

stackoverflow.com

必要なのはforce_basic_auth=yesでした。

- name: Download some app
  local_action:
    get_url force=yes
      url=https://www.example.com/foobar.zip
      dest=ダウンロード先は適当に
      url_password={{ lookup('env', 'WEB_PASSWD') }}
      url_username={{ lookup('env', 'WEB_USERNAME') }} 
      force_basic_auth=yes 

get_urlのドキュメントによるとforce_basic_authは2.0からサポートされたようですね。

( ´Д`)=3 フゥ

初めてのAnsible

初めてのAnsible

Linux netns: グローバルなNet Namespace

グローバルなnsproxyに設定するNet Namespaceの変数のinit_nsめも

これはコンパイル時にはリストしか初期化していないので、その他のデータはカーネルの起動時に初期化してます。

 35 struct net init_net = {
 36         .dev_base_head = LIST_HEAD_INIT(init_net.dev_base_head),
 37 };

グローバルなNet Namespaceの初期化はnet_ns_init()で行います。 net_ns_init()で行う主要な処理はこちらの記事で書いたものを使用します。

kernhack.hatenablog.com

たとえば、net_generic構造体の設定、register_pernet_subsys()でコンストラクタ・デストラクタの登録などです。 その他にsetup_net()によるリストや参照カウンタ等の設定もありますが、これはこちらの記事で調べています。

kernhack.hatenablog.com

というわけで、init_nsに関してはこれと言った特別なことはしていませんでした。

Linux net namespace: pernet_deviceめも

Linuxのnet namespaceにpernet_listというpernet_operation構造体のリストがあって、これがどのように設定されてるのかのめもです。 読んでるカーネルのバージョンは4.1です。

pernet_listを使ってるところは以前書いたこちらの記事にあります。 kernhack.hatenablog.com

で、このリストに要素を設定するのはどこかというとregister_pernet_device()で、kvmで動かしているarch linux(カーネルlinux 4.6.0-rc3)で確認したところ、この関数が呼ばれるのはブートのタイミングでは3回でした。カーネルのinit処理のタイミングなのでWARN_ON()を入れて確認してます。呼ばれたのは3回で、呼ばれ方としては2パターンでした。

最初と2回目はnet_dev_init()から呼ばれてます。

Apr 17 20:49:08 kerntest kernel:  [<ffffffff812e90f1>] dump_stack+0x63/0x82
Apr 17 20:49:08 kerntest kernel:  [<ffffffff8107a65b>] __warn+0xcb/0xf0
Apr 17 20:49:08 kerntest kernel:  [<ffffffff81b5f357>] ? netdev_boot_setup+0xef/0xef
Apr 17 20:49:08 kerntest kernel:  [<ffffffff8107a78d>] warn_slowpath_null+0x1d/0x20
Apr 17 20:49:08 kerntest kernel:  [<ffffffff814aa7ad>] register_pernet_device+0x6d/0x80
Apr 17 20:49:08 kerntest kernel:  [<ffffffff81b5f4b4>] net_dev_init+0x15d/0x1b1
Apr 17 20:49:08 kerntest kernel:  [<ffffffff81002123>] do_one_initcall+0xb3/0x200
Apr 17 20:49:08 kerntest kernel:  [<ffffffff81099055>] ? parse_args+0x295/0x4b0
Apr 17 20:49:08 kerntest kernel:  [<ffffffff81b0c194>] kernel_init_freeable+0x176/0x215
Apr 17 20:49:08 kerntest kernel:  [<ffffffff815b038e>] kernel_init+0xe/0x100
Apr 17 20:49:08 kerntest kernel:  [<ffffffff815bda42>] ret_from_fork+0x22/0x40
Apr 17 20:49:08 kerntest kernel:  [<ffffffff815b0380>] ? rest_init+0x90/0x90
Apr 17 20:49:08 kerntest kernel:  [<ffffffff812e90f1>] dump_stack+0x63/0x82
Apr 17 20:49:08 kerntest kernel:  [<ffffffff8107a65b>] __warn+0xcb/0xf0
Apr 17 20:49:08 kerntest kernel:  [<ffffffff81b5f357>] ? netdev_boot_setup+0xef/0xef
Apr 17 20:49:08 kerntest kernel:  [<ffffffff8107a78d>] warn_slowpath_null+0x1d/0x20
Apr 17 20:49:08 kerntest kernel:  [<ffffffff814aa7ad>] register_pernet_device+0x6d/0x80
Apr 17 20:49:08 kerntest kernel:  [<ffffffff81b5f4c8>] net_dev_init+0x171/0x1b1
Apr 17 20:49:08 kerntest kernel:  [<ffffffff81002123>] do_one_initcall+0xb3/0x200
Apr 17 20:49:08 kerntest kernel:  [<ffffffff81099055>] ? parse_args+0x295/0x4b0
Apr 17 20:49:08 kerntest kernel:  [<ffffffff81b0c194>] kernel_init_freeable+0x176/0x215
Apr 17 20:49:08 kerntest kernel:  [<ffffffff815b038e>] kernel_init+0xe/0x100
Apr 17 20:49:08 kerntest kernel:  [<ffffffff815bda42>] ret_from_fork+0x22/0x40
Apr 17 20:49:08 kerntest kernel:  [<ffffffff815b0380>] ? rest_init+0x90/0x90

3回目はwifiのcfg80211モジュールの初期化っぽいです。

Apr 17 20:49:08 kerntest kernel:  [<ffffffff812e90f1>] dump_stack+0x63/0x82
Apr 17 20:49:08 kerntest kernel:  [<ffffffff8107a65b>] __warn+0xcb/0xf0
Apr 17 20:49:08 kerntest kernel:  [<ffffffffa0579000>] ? 0xffffffffa0579000
Apr 17 20:49:08 kerntest kernel:  [<ffffffff8107a78d>] warn_slowpath_null+0x1d/0x20
Apr 17 20:49:08 kerntest kernel:  [<ffffffff814aa7ad>] register_pernet_device+0x6d/0x80
Apr 17 20:49:08 kerntest kernel:  [<ffffffffa0579011>] cfg80211_init+0x11/0xd6 [cfg80211]
Apr 17 20:49:08 kerntest kernel:  [<ffffffff81002123>] do_one_initcall+0xb3/0x200
Apr 17 20:49:08 kerntest kernel:  [<ffffffff811d642f>] ? kmem_cache_alloc_trace+0x1bf/0x230
Apr 17 20:49:08 kerntest kernel:  [<ffffffff8116fcd2>] do_init_module+0x5f/0x1ed
Apr 17 20:49:08 kerntest kernel:  [<ffffffff8110556c>] load_module+0x227c/0x2900
Apr 17 20:49:08 kerntest kernel:  [<ffffffff81102560>] ? symbol_put_addr+0x50/0x50
Apr 17 20:49:08 kerntest kernel:  [<ffffffff815b373e>] ? kmemleak_alloc+0x4e/0xb0
Apr 17 20:49:08 kerntest kernel:  [<ffffffff81105d43>] SyS_init_module+0x153/0x1a0
Apr 17 20:49:08 kerntest kernel:  [<ffffffff815bd832>] entry_SYSCALL_64_fastpath+0x1a/0xa4

net_dev_init()から呼ばれるほうはこのようにループバックデバイスとデフォルトデバイスの設定をしてます。

7498         /* The loopback device is special if any other network devices
7499          * is present in a network namespace the loopback device must
7500          * be present. Since we now dynamically allocate and free the
7501          * loopback device ensure this invariant is maintained by
7502          * keeping the loopback device as the first device on the
7503          * list of network devices.  Ensuring the loopback devices
7504          * is the first device that appears and the last network device
7505          * that disappears.
7506          */
7507         if (register_pernet_device(&loopback_net_ops))
7508                 goto out;
7509 
7510         if (register_pernet_device(&default_device_ops))
7511                 goto out;

デフォルトデバイスのほうはコンストラクタはなくて終了時の処理のみのようです。

7438 static struct pernet_operations __net_initdata default_device_ops = {
7439         .exit = default_device_exit,
7440         .exit_batch = default_device_exit_batch,
7441 };

register_pernet_device()はこんな感じで、主だったことはregister_pernet_operations()がやってます。

889 int register_pernet_device(struct pernet_operations *ops)
890 {
891         int error;
892         mutex_lock(&net_mutex);
893         error = register_pernet_operations(&pernet_list, ops);
894         if (!error && (first_device == &pernet_list))
895                 first_device = &ops->list;
896         mutex_unlock(&net_mutex);
897         return error;
898 }

register_pernet_operations()はこうです。default_device_opsとかループバックデバイスもidは振ってないようなので、ops->idが設定されてないのでここは飛ばします。で、__register_pernet_operations()です。

788 static int register_pernet_operations(struct list_head *list,
789                                       struct pernet_operations *ops)
790 {
791         int error;
792 
793         if (ops->id) {
794 again:
795                 error = ida_get_new_above(&net_generic_ids, 1, ops->id);
796                 if (error < 0) {
797                         if (error == -EAGAIN) {
798                                 ida_pre_get(&net_generic_ids, GFP_KERNEL);
799                                 goto again;
800                         }
801                         return error;
802                 }
803                 max_gen_ptrs = max_t(unsigned int, max_gen_ptrs, *ops->id);
804         }
805         error = __register_pernet_operations(list, ops);
806         if (error) {
807                 rcu_barrier();
808                 if (ops->id)
809                         ida_remove(&net_generic_ids, *ops->id);
810         }
811 
812         return error;
813 }

pernet_operations構造体の登録は登録だけじゃなくて、必要に応じてinit()の実行もしています。まず最初にpernet_listの末尾に追加します。そして、init()がせて治されてops_init()がエラーを返した場合の後処理時に使用します。

730 static int __register_pernet_operations(struct list_head *list,
731                                         struct pernet_operations *ops)
732 {
733         struct net *net;
734         int error;
735         LIST_HEAD(net_exit_list);
736 
737         list_add_tail(&ops->list, list);
738         if (ops->init || (ops->id && ops->size)) {
739                 for_each_net(net) {
740                         error = ops_init(ops, net);
741                         if (error)
742                                 goto out_undo;
743                         list_add_tail(&net->exit_list, &net_exit_list);
744                 }
745         }
746         return 0;
747 
748 out_undo:
749         /* If I have an error cleanup all namespaces I initialized */
750         list_del(&ops->list);
751         ops_exit_list(ops, &net_exit_list);
752         ops_free_list(ops, &net_exit_list);
753         return error;
754 }

最後にops_init()です。先の流れだとops->id && ops->sizeは偽になります。そして、init()が設定されている場合は実行します。

 93 static int ops_init(const struct pernet_operations *ops, struct net *net)
 94 {
 95         int err = -ENOMEM;
 96         void *data = NULL;
 97 
 98         if (ops->id && ops->size) {
 99                 data = kzalloc(ops->size, GFP_KERNEL);
100                 if (!data)
101                         goto out;
102 
103                 err = net_assign_generic(net, *ops->id, data);
104                 if (err)
105                         goto cleanup;
106         }
107         err = 0;
108         if (ops->init)
109                 err = ops->init(net);
110         if (!err)
111                 return 0;
112 
113 cleanup:
114         kfree(data);
115 
116 out:
117         return err;
118 }

さて、これだとnet/ipv4/tcp_ipv4.cにあるtcp4_net_opsが登録されないため、tcp4_proc_net_init()が呼ばれません。では、これらはどうやって登録されているかというと、register_pernet_subsys()を使っています。

2327 int __init tcp4_proc_init(void)
2328 {
2329         return register_pernet_subsys(&tcp4_net_ops);
2330 }

register_pernet_subsys()はこのようになっていて、register_pernet_operations()よりシンプルになってます。

843 int register_pernet_subsys(struct pernet_operations *ops)
844 {
845         int error;
846         mutex_lock(&net_mutex);
847         error =  register_pernet_operations(first_device, ops);
848         mutex_unlock(&net_mutex);
849         return error;
850 }

register_pernet_operations()に渡すリストはfirst_deviceというリストでnet/net_namespce.cの冒頭で宣言されています。初期値はpernet_listを指しています。

29 static struct list_head *first_device = &pernet_list;

実際のところ、register_pernet_subsys()を使ってるところのほうが多いみたいです。

大体arch linuxの設定に近いはずのうちのカーネルで、ops_init()でops->init()により呼ばれた関数はこれらです。

ffffffff81272410 t proc_net_ns_init
ffffffff814a9ce0 t net_ns_net_init
ffffffff815abc20 t sysctl_net_init
ffffffff814ec0c0 t netfilter_net_init
ffffffff814ec600 t nf_log_net_init
ffffffff8149c010 t sock_inuse_init_net
ffffffff814e6610 t netlink_net_init
ffffffff814c81e0 t rtnetlink_net_init
ffffffff812ecef0 t uevent_net_init
ffffffff8149c0f0 t proto_init_net
ffffffff814d4140 t dev_proc_net_init
ffffffff814d40b0 t dev_mc_net_init
ffffffff814b0040 t netdev_init
ffffffff8144ab50 t loopback_net_init
ffffffff814d5d70 t fib_rules_net_init
ffffffff814e0040 t psched_net_init
ffffffff814ea8d0 t genl_pernet_init
ffffffff815a4b80 t wext_pernet_init
ffffffff814acc90 t sysctl_core_net_init
ffffffff815279a0 t arp_net_init
ffffffff8152c720 t devinet_init_net
ffffffff815356c0 t fib_net_init
ffffffff814eeae0 t ip_rt_do_proc_init
ffffffff8154f050 t xfrm_net_init
ffffffff8154a830 t xfrm4_net_init
ffffffff814ee490 t sysctl_route_net_init
ffffffff814edc20 t rt_genid_init
ffffffff814ee440 t ipv4_inetpeer_init
ffffffff815317c0 t igmp_net_init
ffffffff81518880 t tcp_sk_init
ffffffff8151d1d0 t tcp_net_metrics_init
ffffffff81526000 t udplite4_proc_init_net
ffffffff815294e0 t icmp_sk_init
ffffffff81545860 t ipmr_net_init
ffffffff8152fb50 t inet_init_net
ffffffff8152fc40 t ipv4_mib_init_net
ffffffff8151fd60 t raw_init_net
ffffffff815171d0 t tcp4_proc_init_net
ffffffff815232f0 t udp4_proc_init_net
ffffffff8153f630 t ping_v4_proc_init_net
ffffffff815418b0 t ip_proc_init_net
ffffffff814f4280 t ipv4_frags_init_net
ffffffff81557b30 t unix_net_init
ffffffff814cf230 t diag_net_init
ffffffff81540e40 t ipv4_sysctl_init_net
ffffffff8155d500 t inet6_net_init
ffffffff81581b50 t icmpv6_sk_init
ffffffff81594fb0 t ip6mr_net_init
ffffffff815795b0 t ndisc_net_init
ffffffff81584a00 t igmp6_net_init
ffffffff8157ff50 t raw6_init_net
ffffffff8157f5c0 t udplite6_proc_init_net
ffffffff81599810 t ipv6_proc_init_net
ffffffff81564bf0 t if6_proc_net_init
ffffffff8156fd20 t ipv6_inetpeer_init
ffffffff8156fdb0 t ip6_route_net_init
ffffffff815745d0 t fib6_net_init
ffffffff81597180 t xfrm6_net_init
ffffffff815991f0 t fib6_rules_net_init
ffffffff8156fc90 t ip6_route_net_init_late
ffffffff81590f20 t ip6_flowlabel_proc_init
ffffffff8156ce50 t ip6addrlbl_net_init
ffffffff81565d30 t addrconf_init_net
ffffffff81588540 t ipv6_frags_init_net
ffffffff8158bb80 t tcpv6_net_init
ffffffff8158d1b0 t ping_v6_proc_init_net
ffffffff815924c0 t ipv6_sysctl_net_init
ffffffff8159dcc0 t packet_net_init
ffffffffa014f130 t xt_net_init  [x_tables]
ffffffffa01788a0 t ip_tables_net_init   [ip_tables]

動くメカニズムを図解&実験! Linux超入門 (My Linux・シリーズ)

動くメカニズムを図解&実験! Linux超入門 (My Linux・シリーズ)

exit(2)とpid namespaceめも

プロセスが終了するときにもpid namespaceが関係するので、その辺りのめもです。

流れとしてはこんな感じです。

do_exit()@kernel/exit.c
  -> exit_notify@kernel/exit.c
    -> forget_original_parent()@kernel/exit.c
      -> find_child_reaper()@kernel/exit.c
        -> zap_pid_ns_processes()@kernel/pid_namespace.c

今回読むのはzap_pid_ns_processes()です。 プロトタイプはこうです。

184 void zap_pid_ns_processes(struct pid_namespace *pid_ns)

この関数はPID Namespace(pid_ns)に属するプロセスに対してSIGKILLを投げていって、プロセスを終了させるのが仕事です。 最初にこの名前空間にこれ以上プロセスが作れないようにPID Namespaceをロックします。次はカレントプロセスに対してSIGCHLDを無視するようにします。そして、名前空間内の各プロセスに対してSIGKILLを投げていきます。このときはpid_task()を使ってpidからpidに該当するtask_struct構造体を取得します。

216         read_lock(&tasklist_lock);
217         nr = next_pidmap(pid_ns, 1);
218         while (nr > 0) {
219                 rcu_read_lock();
220 
221                 task = pid_task(find_vpid(nr), PIDTYPE_PID);
222                 if (task && !__fatal_signal_pending(task))
223                         send_sig_info(SIGKILL, SEND_SIG_FORCED, task);
224 
225                 rcu_read_unlock();
226 
227                 nr = next_pidmap(pid_ns, nr);
228         }
229         read_unlock(&tasklist_lock);

KILLしまくったら次はSIGCHLDを無視している間にゾンビ状態(EXIT_ZOMBIE)になってしまった子プロセスを回収します。使うのはsys_wait4()です。ただし、sys_wait4()だとEXIT_DEADになったプロセスは回収できないとのことですが、コメントによるとEXIT_DEADのプロセスは(゚ε゚)キニシナイ!!とのことです。これはinit_pid_nsのinitプロセスに任せるようです。まあ、名前空間はツリー構造なのでこの名前空間にいるプロセスはツリー構造の上にある名前空間から参照できますしね。 つぎにカレントプロセスをTASK_UNINTERRUPTIBLE状態にして、pid_ns->nr_hashed == init_pidsになるまでshcedule()を呼んで待ちます。

init_pidsは関数の最初のほうで、カレントプロセスがスレッドグループリーダーなら1、そうじゃなければ2と設定しています。

189         int init_pids = thread_group_leader(me) ? 1 : 2;

待ち状態を抜けたらカレントプロセスの状態をTASK_RUNNINGに戻します。そして、pid_ns構造体のreboot変数が0でない場合はcurrent->signal->group_exit_codeにその値を設定します。関数の最後にacct_exit_ns()を呼び、アカウンティング情報の後始末をします。

Net Namespace: copy_net_ns()めも

Net Namespaceのcopy_net_ns()の処理を読みます。カーネルのバージョンはv4.5。

まずはデータ構造で、Net Namespaceを表現するデータ構造はstruct net。定義はinlude/net/net_namespace.hにあります。この構造体にuser_namespace構造体とかns_common構造体みたいな名前空間の機能が使うデータもあれば、IPv4のnetns_ipv4構造体、パケットフィルタリングのnetns_nftables構造体などネットワーク関連のデータも諸々あります。

では、本題のcopy_net_ns()を読んでいきます。

351 struct net *copy_net_ns(unsigned long flags,
352                         struct user_namespace *user_ns, struct net *old_net)
353 {
354         struct net *net;
355         int rv;
356 

最初はフラグのチェックで、clone(2)などでflagにCLONE_NEWNETが設定されていなければ現在の(clone(2)なら親プロセスの)Net Namespaceの参照カウントを1つ増やして完了。

357         if (!(flags & CLONE_NEWNET))
358                 return get_net(old_net);
359 

net_alloc()は名前通りな関数で、net構造体用のメモリを確保する。 細かく見るとnet_alloc_generic()という関数も呼んでいて、これはnet構造体のメンバ変数、net_generic構造体genのメモリも確保してます。

360         net = net_alloc();
361         if (!net)
362                 return ERR_PTR(-ENOMEM);
363 

get_user_ns()はUser Namespaceの参照カウントをインクリメント。

364         get_user_ns(user_ns);
365 

Net構造体を弄るためにロックを取得。

366         mutex_lock(&net_mutex);

setup_net()でnet構造体の初期化。初期化に成功したら(rv == 0)、net構造体を双方向リストの最後に追加します。

367         rv = setup_net(net, user_ns);
368         if (rv == 0) {
369                 rtnl_lock();
370                 list_add_tail_rcu(&net->list, &net_namespace_list);
371                 rtnl_unlock();
372         }

net_namespace_listnet/core/net_namespace.cで宣言されています。

 32 LIST_HEAD(net_namespace_list);
 33 EXPORT_SYMBOL_GPL(net_namespace_list);

最後にロックを解放して、初期化に失敗していたらUser Namespaceの参照カウントを減らし、作ったnet構造体を破棄してエラーを返して終了です。

373         mutex_unlock(&net_mutex);
374         if (rv < 0) {
375                 put_user_ns(user_ns);
376                 net_drop_ns(net);
377                 return ERR_PTR(rv);
378         }

初期化に成功していたら作成したnet構造体を返します。

379         return net;
380 }

次にsetup_net()を見ときます。といっても、関数の半分ほどはエラー処理なので全部は見ませんけども。あと、これと言った処理は特にありません。

272 static __net_init int setup_net(struct net *net, struct user_namespace *user_ns)
273 {
274         /* Must be called with net_mutex held */
275         const struct pernet_operations *ops, *saved_ops;
276         int error = 0;
277         LIST_HEAD(net_exit_list);
278 
279         atomic_set(&net->count, 1);
280         atomic_set(&net->passive, 1);
281         net->dev_base_seq = 1;
282         net->user_ns = user_ns;
283         idr_init(&net->netns_ids);
284         spin_lock_init(&net->nsid_lock);
285 

処理らしい処理ってこれくらいですね。

286         list_for_each_entry(ops, &pernet_list, list) {
287                 error = ops_init(ops, net);
288                 if (error < 0)
289                         goto out_undo;
290         }
291 out:
292         return error;

pernet_listはリストで、これもファイルの冒頭のほうで宣言されてます。

 24 /*
 25  *      Our network namespace constructor/destructor lists
 26  */
 27 
 28 static LIST_HEAD(pernet_list);
 29 static struct list_head *first_device = &pernet_list;

リストのデータはpernet_operations構造体です。

そして、ops_init()はこのような関数です。まず最初、98行目のif文で100行目に進む場合。これはnet_assign_generic()を呼んでますが、これは最初にnet_alloc()でメモリを確保したnet_generic構造体のgenのptrメンバ変数にdataを設定します。このときにgen->ptrに設定されているデータ数が多くて、配列を拡張する必要がある場合は配列の拡張処理が入ります。

 93 static int ops_init(const struct pernet_operations *ops, struct net *net)
 94 {
 95         int err = -ENOMEM;
 96         void *data = NULL;
 97 
 98         if (ops->id && ops->size) {
 99                 data = kzalloc(ops->size, GFP_KERNEL);
100                 if (!data)
101                         goto out;
102 
103                 err = net_assign_generic(net, *ops->id, data);
104                 if (err)
105                         goto cleanup;
106         }

次は見たままですね。pernet_operations構造体のinit()が設定されていたら、その関数を呼び出します。そして、エラーがなければ0を返して終了です。

107         err = 0;
108         if (ops->init)
109                 err = ops->init(net);
110         if (!err)
111                 return 0;

ここでpernet_operations構造体のinit()を設定しているところをいくつか適当に見てみます。これはIPv4のICMPのところです。net/ipv4/icmp.cでinit()とexit()を設定を設定しています。

1234 static struct pernet_operations __net_initdata icmp_sk_ops = {
1235        .init = icmp_sk_init,
1236        .exit = icmp_sk_exit,
1237 };

IPv4TCPnet/ipv4/tcp_ipv4.c)だとinit()、exit()、exit_batch()と3個の関数を設定しています。

2410 static struct pernet_operations __net_initdata tcp_sk_ops = {
2411        .init       = tcp_sk_init,
2412        .exit       = tcp_sk_exit,
2413        .exit_batch = tcp_sk_exit_batch,
2414 };

とまあ、こんな感じでプロトコル等に設定されているinit()を呼んでいきます。pernet_listの宣言のコメントに「Our network namespace constructor/destructor lists」とありますし、まさしくコンストラクタ・デストラクタですね。

改訂3版 サーバ/インフラエンジニア養成読本 (Software Design plus)

改訂3版 サーバ/インフラエンジニア養成読本 (Software Design plus)

lscpuコマンド

util-linuxパッケージにlscpu(1)なんてコマンドがあることを今さら知りました(;・∀・) gitのログから察するに2008年にリリースされたっぽいですね。

で、実際に実行するとこんな感じです。/proc/cpuinfoを見やすくした感じで良いですね。

masami@saga:~$ lscpu
Architecture:          x86_64
CPU op-mode(s):        32-bit, 64-bit
Byte Order:            Little Endian
CPU(s):                12
On-line CPU(s) list:   0-11
Thread(s) per core:    2
Core(s) per socket:    6
Socket(s):             1
NUMA node(s):          1
Vendor ID:             GenuineIntel
CPU family:            6
Model:                 63
Model name:            Intel(R) Core(TM) i7-5820K CPU @ 3.30GHz
Stepping:              2
CPU MHz:               2073.296
CPU max MHz:           3600.0000
CPU min MHz:           1200.0000
BogoMIPS:              6603.26
Virtualization:        VT-x
L1d cache:             32K
L1i cache:             32K
L2 cache:              256K
L3 cache:              15360K
NUMA node0 CPU(s):     0-11
Flags:                 fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc aperfmperf eagerfpu pni pclmulqdq dtes64 monitor ds_cpl vmx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid dca sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm epb tpr_shadow vnmi flexpriority ept vpid fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid cqm xsaveopt cqm_llc cqm_occup_llc dtherm ida arat pln pts

さらにkvmゲストで実行するとこうなります。さっきはなかったHypervisor vendorとVirtualization typeが増えてます。

masami@kerntest:~$ lscpu
Architecture:          x86_64
CPU op-mode(s):        32-bit, 64-bit
Byte Order:            Little Endian
CPU(s):                4
On-line CPU(s) list:   0-3
Thread(s) per core:    1
Core(s) per socket:    1
Socket(s):             4
NUMA node(s):          1
Vendor ID:             GenuineIntel
CPU family:            6
Model:                 60
Model name:            Intel Core Processor (Haswell, no TSX)
Stepping:              1
CPU MHz:               3299.896
BogoMIPS:              6602.78
Hypervisor vendor:     KVM
Virtualization type:   full
L1d cache:             32K
L1i cache:             32K
L2 cache:              4096K
NUMA node0 CPU(s):     0-3
Flags:                 fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 syscall nx rdtscp lm constant_tsc rep_good nopl eagerfpu pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm fsgsbase bmi1 avx2 smep bmi2 erms invpcid xsaveopt arat

この辺はlscpu.cのread_hypervisor()が処理してます。x86/x86_64の場合はread_hypervisor_cpuid()でcpuid命令を実行してハイパーバイザーの種類を調べてます。

 if (!strncmp("XenVMMXenVMM", hyper_vendor_id, 12))
        desc->hyper = HYPER_XEN;
    else if (!strncmp("KVMKVMKVM", hyper_vendor_id, 9))
        desc->hyper = HYPER_KVM;
    else if (!strncmp("Microsoft Hv", hyper_vendor_id, 12))
        desc->hyper = HYPER_MSHV;
    else if (!strncmp("VMwareVMware", hyper_vendor_id, 12))
        desc->hyper = HYPER_VMWARE;
    else if (!strncmp("UnisysSpar64", hyper_vendor_id, 12))
        desc->hyper = HYPER_SPAR;

認識できるハイパーバイザーはこんな感じのようです。

const char *hv_vendors[] = {
    [HYPER_NONE]    = NULL,
    [HYPER_XEN] = "Xen",
    [HYPER_KVM] = "KVM",
    [HYPER_MSHV]    = "Microsoft",
    [HYPER_VMWARE]  = "VMware",
    [HYPER_IBM] = "IBM",
    [HYPER_VSERVER] = "Linux-VServer",
    [HYPER_UML] = "User-mode Linux",
    [HYPER_INNOTEK] = "Innotek GmbH",
    [HYPER_HITACHI] = "Hitachi",
    [HYPER_PARALLELS] = "Parallels",
    [HYPER_VBOX]    = "Oracle",
    [HYPER_OS400]   = "OS/400",
    [HYPER_PHYP]    = "pHyp",
    [HYPER_SPAR]    = "Unisys s-Par"
};

仮想化の種類としてはこの4つがあって、paraとfullは良いとして、containerはどんな環境なのか気になります。

const char *virt_types[] = {
    [VIRT_NONE] = N_("none"),
    [VIRT_PARA] = N_("para"),
    [VIRT_FULL] = N_("full"),
    [VIRT_CONT] = N_("container"),
};

HYPER_PARALLELS(OpenVZ/Virtuozzo)のときと、

 /* OpenVZ/Virtuozzo - /proc/vz dir should exist
    *            /proc/bc should not */
    else if (path_exist(_PATH_PROC_VZ) && !path_exist(_PATH_PROC_BC)) {
        desc->hyper = HYPER_PARALLELS;
        desc->virtype = VIRT_CONT;

Linux-VServer(HYPER_VSERVER)の場合がcontainerになるようです。

         if (!*val) {
                desc->hyper = HYPER_VSERVER;
                desc->virtype = VIRT_CONT;
            }

ところで、util-linuxの2.28にはlsns(1)という名前空間の情報を見るツールが入りますよ。