crashコマンドの使い方(file_systems編)

以前super_blockからNFS情報を取得する方法を書きました。
Linuxではさまざまな種類のファイルシステムを扱うことができ、それぞれのオブジェクトが必要となります。このオブジェクトがfile_system_typeという構造体であり、file_systemsというグローバル変数です。
そこで今回は、file_systemsからどんな情報が取れるかcrashコマンドを使って説明したいと思います。

初めに、file_systemsから、file_system_typeの一覧を表示してみます。手順は、listコマンドを使ってfile_system_type.nextを順番に辿っていきます。

crash> p file_systems
file_systems = $1 = (struct file_system_type *) 0xffffffff819f95c0 <sysfs_fs_type>
crash> struct -o file_system_type
struct file_system_type {
   [0x0] const char *name;
   [0x8] int fs_flags;
         struct dentry *(*mount)(struct file_system_type *, int, const char *, void *);
  [0x18] void (*kill_sb)(struct super_block *);
  [0x20] struct module *owner;
  [0x28] struct file_system_type *next;
...
crash> list file_system_type.next 0xffffffff819f95c0 -s file_system_type.name
ffffffff819f95c0
  name = 0xffffffff818b3596 "sysfs"
ffffffff819fa000
  name = 0xffffffff81897bf5 "rootfs"
...
  name = 0xffffffffa026a1c4 "xfs"
ffffffffa0331b80
  name = 0xffffffffa0328be0 "rpc_pipefs"
ffffffffa03839c0
  name = 0xffffffffa037fae0 "nfsd"
ffffffffa05ea1e0
  name = 0xffffffffa05e4e47 "nfs"
ffffffffa05ea160
  name = 0xffffffffa05e4e42 "nfs4"
crash> 

ここでは、nfs4を例にして、file_system_typeの値を表示してみます。

crash> file_system_type 0xffffffffa05ea160
struct file_system_type {
  name = 0xffffffffa05e4e42 "nfs4", 
  fs_flags = 0x8002, 
  mount = 0xffffffffa05ce030 <nfs_fs_mount>, 
  kill_sb = 0xffffffffa05cd870 <nfs_kill_super>, 
  owner = 0xffffffffa05edbe0 <__this_module>, 
  next = 0x0, 
  fs_supers = {
    first = 0xffff880035a83138
  }, 
 ...
}

file_system_typeの構造体を確認すると、fs_supersは次のように定義されています。

include/linux/fs.h:
...
struct file_system_type {
        const char *name;
        int fs_flags;
...
        struct dentry *(*mount) (struct file_system_type *, int,
                       const char *, void *);
        void (*kill_sb) (struct super_block *);
        struct module *owner;
        struct file_system_type * next;
        struct hlist_head fs_supers;

また、hlist_headが何かを確認し、hlist_nodeの値も取得してみます。

crash> whatis hlist_head
struct hlist_head {
    struct hlist_node *first;
}
SIZE: 0x8
crash> whatis hlist_node
struct hlist_node {
    struct hlist_node *next;
    struct hlist_node **pprev;
}
SIZE: 0x10
crash> hlist_node 0xffff880035a83138
struct hlist_node {
  next = 0x0, 
  pprev = 0xffffffffa05ea190 <nfs4_fs_type+48>
}

このfs_supersは、super_blockのs_instancesに登録されているので、super_blockを参照しても確認できます。

fs/super.c:
...
struct super_block *sget(struct file_system_type *type,
                        int (*test)(struct super_block *,void *),
                        int (*set)(struct super_block *,void *),
                        int flags,
                        void *data)
{
        struct super_block *s = NULL;
...
        s->s_type = type;
        strlcpy(s->s_id, type->name, sizeof(s->s_id));
        list_add_tail(&s->s_list, &super_blocks);
        hlist_add_head(&s->s_instances, &type->fs_supers);

では、マウントされている状況で確認してみます。

crash>  mount
     MOUNT           SUPERBLK     TYPE   DEVNAME   DIRNAME
ffff88007d12e100 ffff88007c771000 rootfs rootfs    /         
ffff88007ac57700 ffff88007c220800 sysfs  sysfs     /sys      
...
ffff88007b817200 ffff880035a83000 nfs4   192.168.122.135:/tmp /mnt      
crash> 

crash> super_block ffff880035a83000
struct super_block {
  s_list = {
    next = 0xffffffff819ea610 <super_blocks>, 
    prev = 0xffff8800799d1000
  }, 
...
  s_mtd = 0x0, 
  s_instances = {
    next = 0x0, 
    pprev = 0xffffffffa05ea190
  }, 

file_system_typeのfs_supersでもsuper_blockのs_instancesでも「0xffffffffa05ea190」が確認できたので、繋がりがあることがわかります。
図にすると、このような関係性になります。
f:id:shinji629:20181213143352p:plain

PayPay 「403 Forbidden」エラー

いま話題の新しいスマホ決済サービス「PayPay(ペイペイ)」です。ソフトバンクグループのもと、「100億円あげちゃうキャンペーン」として、PayPayで支払ったら20%を還元するみたいです。
早速YahooIDで新規登録しようとしたら、エラーに遭遇しました。
f:id:shinji629:20181211115930j:plain

この問題は、Chromeアプリを無効にすることで回避できました。
手順は、次のとおりです。

  1. 『設定』→『アプリと通知』→『アプリ』と進んでアプリ一覧より『Chrome』のページを開きます。そして『無効にする』をタップします。
  2. 「(Chrome)このアプリを出荷時の状態に戻しますか? データがすべて削除されます。」と警告がでるので、ここは「キャンセル」します。
  3. あとは、PayPayアプリから普通に登録ができます。
  4. 最後に、Chromeを有効に戻しておきます。

無事登録が完了すると、自動的に500円がチャージされました。
f:id:shinji629:20181211121709j:plain

NFSとsuper_blockについて(Apple ペンシルで描いた図解)

前回さらっとsuper_blockからnfsの情報を取得する方法を記述しました。
もしかしたら、「え???」と思う方がいるかもしれないので、
図を使って説明したいと思います。 せっかくApple ペンシルを買ったので、練習したいというのが、ホントの理由です。

まずは、ざっくり3つの領域を説明したいと思います。
1. ユーザー空間
アプリケーション(OS上で動作するソフトウェア)が使用するメモリ領域

2. カーネル空間
カーネルが使用するメモリ領域

3. ハードウェア
ハードディスクなど

図にするとこのような感じになります。
f:id:shinji629:20181210113816j:plain
ここでポイントとなるのは、VFSNFSの関係性です。Virtual FileSystem(VFS)は、ファイルシステムExt4NFSなど)の上位に位置する抽象化層です。なぜ抽象化するかといえば、ファイルシステムの違いを意識することなくアクセスが可能になるからです。

VFSを構成する主な4つのオブジェクトがこちらです。

  • superblock: マウントされたファイルシステムに関する情報
  • inode: ファイルに関する情報
  • file: プロセスによってオープンされたファイルに関する情報
  • dentry: ディレクトリエントリ(パス)に関する情報

マウントされたローカルファイルシステムに対する情報は、super_blockに登録されています。では、早速super_blockのデータ構造を見ていきましょう。

include/linux/fs.h
...
struct super_block {
        struct list_head        s_list;         /* Keep this first */
        dev_t                   s_dev;          /* search index; _not_ kdev_t */
        unsigned char           s_blocksize_bits;
        unsigned long           s_blocksize;
        loff_t                  s_maxbytes;     /* Max file size */
        struct file_system_type *s_type;
        const struct super_operations   *s_op;
        const struct dquot_operations   *dq_op;
       ...

s_typeをチェックして、ファイルシステムNFSかどうか確認できます。例えば、グローバル変数のsuper_blocksのリンクリストからnfsのsuper_blockを探すこともできます。

crash> cat ~/macro/print-sb
define print-superblocks
        set $n = super_blocks.next
        while ($n != &super_blocks)
                set $off = (unsigned long)&((struct super_block *)0)->s_list
                set $s = (struct super_block *)((unsigned long)$n - (unsigned long)$off)
                if ($s->s_type == &nfs_fs_type)
                        printf "(struct super_block *) %p, type=nfs_fs_type\n", $s                         
                end
                if ($s->s_type == &nfs4_fs_type)
                        printf "(struct super_block *) %p, type=nfs4_fs_type\n", $s
                end
                set $n = $n->next
        end
end
crash> source ~/macro/print-sb
Redefine command "print-superblocks"? (y or n) [answered Y; input not from terminal]
crash> print-superblocks
(struct super_block *) 0xffff880035a83000, type=nfs4_fs_type

また、super_blockのメンバーのs_fs_infoにファイルシステムのプライベート情報が登録されます。NFSのサーバやクライアントの情報は、こちらで登録されます。

fs/nfs/super.c:
...
static int nfs_compare_mount_options(const struct super_block *s, const struct nfs_server *b, int flags)
{
        const struct nfs_server *a = s->s_fs_info;
        const struct rpc_clnt *clnt_a = a->client;
        const struct rpc_clnt *clnt_b = b->client;
...

この辺りの関係性が理解できると、vmcoreのsuper_blockからnfsの情報を取得することができます。

居心地のいいカフェ

そんなに頻繁ではありませんが、たまにカフェにふらっと立ち寄ることがあります。都会にいるとどこにでもカフェが点在するので、見つけるのに困ることはありません。

しかし、今回紹介したいカフェは、見つけるのに困りそうなカフェです。ただし、非常に雰囲気がよく、居心地のいいカフェです。長野県の「高原カフェこもれび」です。

すぐ近くには、小川が流れていて、非常にリラックスできそうな別荘エリアです。また、マスターは、人のよさそうな方でした。

残念ながら、冬の間は休みなので、春になったらまた立ち寄ってみようかと思います。

f:id:shinji629:20181208144812j:plain

 

ピアノ練習用アプリ

独学で楽器を練習する場合、間違いを指摘してもらうことがほとんどないでしょう。自分で気付いて、繰り返し練習するというパターンかと思います。

最近では、アプリも充実していて、ipadのマイクでピアノの音をしっかり聞き分け、間違いを指摘してくれるアプリがあります。今回試したアプリがSimply Pianoというアプリです。ちょっと試しただけなので、中級や上級編がどうなのかわかりませんが、かなり懇切丁寧に解説してくれます。はじめに、Cコードから練習しますが、おそらく文字の読めないお子様でもスタートできそうです。

f:id:shinji629:20181206204347p:plain

タイミングや音がづれていた場合、色でお知らせしてくれます。また、各ステップの最後に、BGMにあわせて、練習するモードがあります。間違えると、スローな訓練モードに切り替わったりして、ゲーム感覚で進められるのも特徴です。

vmcoreの解析メモ (NFSv4編)

vmcoreの解析に関するメモは、あまり公開されていないので、今回NFS関連で役に立つかもしれない方法を紹介していきます。

まず、モジュールをロードしておきます。

crash> mod -s nfs
crash> mod -s nfsv4

これをしないと、nfs_server構造体をチェックしたいときなど、次のようなエラーが発生します。
No struct type named nfs_server.

次にsuper_blockからnfsのポインタを探していきます。ここでは、nfs_serverとnfs_clientのアドレスをチェックしてみます。
マクロに設定しておくと、使い回しができるのでちょっと便利です。

crash> mount
MOUNT SUPERBLK TYPE DEVNAME DIRNAME
...
ffff88007b817200 ffff880035a83000 nfs4 192.168.122.135:/tmp /mnt

crash> cat ~/macro/nfs-pointers
define superblk-to-nfs-pointers
set $sb = (struct super_block *)$arg0
set $nfs_server = (struct nfs_server *)($sb->s_fs_info)
set $nfs_client = $nfs_server->nfs_client
print $sb
print $nfs_server
print $nfs_client
end
crash> source /root/macro/nfs-pointers
Redefine command "superblk-to-nfs-pointers"? (y or n) [answered Y; input not from terminal]
crash> superblk-to-nfs-pointers 0xffff880035a83000
$16 = (struct super_block *) 0xffff880035a83000
$17 = (struct nfs_server *) 0xffff880035a84800
$18 = (struct nfs_client *) 0xffff88007a313c00

 nfs_clientのアドレスがわかったので、実際にどんな値が入っているか確認することができます。

crash> struct nfs_client 0xffff88007a313c00
struct nfs_client {
cl_count = {
counter = 0x1
},
... (長いので省略します。)
cl_hostname = 0xffff88007a3076d0 "192.168.122.135",
cl_rpcclient = 0xffff880035db5400,
rpc_ops = 0xffffffffa0636160 <nfs_v4_clientops>,
cl_proto = 0x6,
cl_nfs_mod = 0xffffffffa065fdc0 <nfs_v4>,
...
cl_serverowner = 0xffff880035a87000,
cl_serverscope = 0xffff880035a85000,
cl_implid = 0xffff880035e96000,
cl_sp4_flags = 0x0,
cl_ipaddr = "192.168.122.225\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
fscache = 0xffff880035a09058,
cl_net = 0xffffffff81a44000 <init_net>
}

cl_rpcclientのアドレスがわかったので、rpc_clntの値も確認できます。

crash> struct rpc_clnt 0xffff880035db5400
struct rpc_clnt {
cl_count = {
counter = 0x2
},
cl_clid = 0x1,
cl_clients = {
next = 0xffff88007bd93208,
...
cl_xprt = 0xffff880035a81800,

...

また、cl_xprtのアドレスがわかったので、rpc_xprtの値も確認できます。「どうして?」って思われる方もいるかもしれないので、関連するソースコードを紹介します。

 include/linux/sunrpc/clnt.h:
...
struct rpc_clnt {
atomic_t cl_count; /* Number of references */
unsigned int cl_clid; /* client id */
struct list_head cl_clients; /* Global list of clients */
struct list_head cl_tasks; /* List of tasks */
spinlock_t cl_lock; /* spinlock */
struct rpc_xprt __rcu * cl_xprt; /* transport */

crash> struct rpc_xprt 0xffff880035a81800
struct rpc_xprt {
count = {
counter = 0x2
},
ops = 0xffffffffa0330300,
timeout = 0xffffffffa0325fa0,
...
state = 0x12,
...
xprt_net = 0xffffffff81a44000 <init_net>,
servername = 0xffff88007a3071a0 "192.168.122.135",
address_strings = {0xffff88007a3079b0 "192.168.122.135", 0xffff88007a0fde60 "2049", 0xffffffffa0327fe8 "tcp", 0xffff88007a3076f0 "c0a87a87", 0xffff88007a0fde58 " 801", 0xffffffffa0327fe8 "tcp"},
debugfs = 0xffff880069800f00
}
 

例えば、 stateのビットを確認すると接続されているかどうかチェックすることもできます。

crash> eval -b 0x12|grep bits
bits set: 4 1

該当するソースコードは、こちらになります。

include/linux/sunrpc/xprt.h:
...
#define XPRT_LOCKED (0)
#define XPRT_CONNECTED (1)
#define XPRT_CONNECTING (2)
#define XPRT_CLOSE_WAIT (3)
#define XPRT_BOUND (4)
#define XPRT_BINDING (5)
#define XPRT_CLOSING (6)
#define XPRT_CONNECTION_ABORT (7)
#define XPRT_CONNECTION_CLOSE (8)
#define XPRT_CONGESTED (9)
#define XPRT_CONNECTION_REUSE (10)

static inline void xprt_set_connected(struct rpc_xprt *xprt)
{
set_bit(XPRT_CONNECTED, &xprt->state);
}

つまり、bit 1がセットされているので、接続されていることがわかります。

米国の銀行の進化について

久しぶりに米国の某銀行の1-800-xxxに電話してみたところ、驚くべき進化していました。ちなみに、Skypeからだと無料で1-800にスマホから電話できます。ほとんどの作業は、ウェブで完了しますが、一部システムが海外のスマホ番号の桁に対応していないため、codeの受信ができないことがあります。

そこで直接オペレータと話すために、初めに機械式の本人確認があります。口座番号、Card 番号、SSNなどどれかを入力すると顧客情報がオペレーターに伝わるようになっています。その後、オペレーターに代わると、簡単な本人確認と、登録されたメールに確認コードがメールされるので、確認してほしいと依頼されました。その番号で確認をとり、要件を伝え、再度記録ように内容の録音をしていました。

5分程度で要件が終わり電話を切りました。その直後、要件のサマリーメールが銀行から送信されてきました。

あまりの手際の良さに、正直びっくりといった印象でした。