Windows 7上のVMware PlayerでLinuxのデバイスドライバをリモートデバッグする方法(1) - デバッグするカーネルモジュールを用意する

Pocket

この記事は、
Windows 7上のVMware Playerにgdbでリモート接続する方法
Windows 7上のVMware PlayerでLinuxのデバイスドライバをリモートデバッグする方法(1) - デバッグするカーネルモジュールを用意する
Windows 7上のVMware PlayerでLinuxのデバイスドライバをリモートデバッグする方法(2) - リモートデバッグする
Windows 7上のVMware PlayerでLinuxのデバイスドライバをリモートデバッグする方法(3) - 自動化する
の1つです。

まず、gdbがVMwareにリモート接続できることを確認してください。

1. デバッグに使うソースコードの説明

今回は3つのソースを用意しました。testmodule.cというモジュールを作りました。このモジュールは、メジャー番号88番でキャラクターデバイスを登録してioctlを処理するモジュールです。
また、Makefileは、そのモジュールをコンパイルするための物です。testdriver.cは、モジュールをテストするために使うユーザーモードのプログラムです。

testmodule.cは次の通りです。モジュールをロードするときに呼ばれるinit_module()とアンロードしたときに呼ばれるcleanup_module()があります。また、ユーザーモードのプログラムでioctl()を実行されたときに呼び出されるmy_ioctl()という関数があります。

file_operations構造体のunlocked_ioctlというメンバー変数にmy_ioctlを設定してregister_chardev()で登録することで、ユーザーモードのプログラムがioctl()を使ったときにカーネルモジュールのmy_ioctl()が実行されます。

init_module()内でregister_chardev()を呼び出す事でメジャー番号88番のキャラクターデバイスを登録しています。また、cleanup_module()のunregister_chardev()でデバイスの登録を解除しています。

testmodule.c -------------------------------------------------------

/*
  testmodule.c
  Linux test driver kernel module
  character device major=88 minor=0

  sudo insmod testmodule.ko
  sudo mknod /dev/testmodule c 88 0
  sudo chmod a+rw /dev/testmodule
*/

#include <linux/module.h>
#include <linux/fs.h>		// for struct file_operations

// modnameにモジュールの名前を設定します。また、my_majorの88番がデバイスの番号です。
char modname[] = "testmodule";
int my_major = 88;

long my_ioctl( struct file *, unsigned int, unsigned long );

// ユーザーモードのプログラムがioctl()を使ったときにmy_ioctl()を呼び出すよう設定します。
struct file_operations
my_fops =	{
		owner:		THIS_MODULE,
		unlocked_ioctl:		my_ioctl,
		};

// グローバル変数です。
int global_data;

// モジュールをロードしたときに呼び出されます。
int init_module(void)
{
	printk("\nInstalling %s \n", modname);
	printk( "(major=%d) \n", my_major );

	// 設定した名前と番号で、キャラクターデバイスを登録します。
	return register_chrdev( my_major, modname, &my_fops );
}

// モジュールをアンロードしたときに呼び出されます。
void cleanup_module(void)
{
	// キャラクターデバイスの登録を解除します。
	unregister_chrdev( my_major, modname );

	printk("\nRemovind %s \n", modname);
}

// ユーザーモードのプログラムで、ioctl()を使ったときに呼び出されます。
long my_ioctl( struct file *file, unsigned int count, unsigned long buf)
{
	int i;

	i = 1;
	global_data = 2;
	printk("\n%s is called! \n", modname);

	return 0;
}

MODULE_LICENSE("GPL");

-------------------------------------------------------------------

これをコンパイルするMakefileは、次の通りです。

Makefile ------------------------------------------------

ifneq	($(KERNELRELEASE),)
obj-m	:= testmodule.o
ccflags-y	:=-g -O0

else
KDIR	:= /lib/modules/$(shell uname -r)/build
PWD	:= $(shell pwd)
default:
	$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
	rm -r -f .tmp_versions *.mod.c .*.cmd *.o Module.symvers modules.order

endif

----------------------------------------------------------

Linuxのバージョン3.0になって、Makefileの作り方が少し変わったようです。KERNELRELEASEに値が設定されているかどうかで、2つの部分に分かれます。カーネルを作る時のkbuildというシステムは、KERNELRELEASEに値が設定されているときだけを見るようです。値がないときの設定は、ふつうのmakeが処理するのだそうです。モジュールをコンパイルするときのフラッグを設定するときに、以前はEXTRA_CFLAGSを使っていたと思いますが、ccflags-yに変わったようです。もしくは、モジュールの名前をつけてtestmodule-yとします。
ccflags-yに-gをつけることで、デバッグ情報をカーネルモジュールに埋め込みます。
また、-O0(オーとゼロ)をつけることで、gdb上でステップ実行したときに1行ごとに実行することができます。

このカーネルモジュールをテストするユーザーモードのプログラムが、次のtestdriver.cです。
/dev/testmoduleというデバイスを開いて、ioctl()でHello!という文字列を送り込みます。

testdriver.c -----------------------------------------------

/*
  testdriver.c
  Test testmodule.c
*/

#include  // for printf(), perror()
#include  // for open()
#include  // for exit()

char devname[] = "/dev/testmodule";

int main(void)
{
    int fd;
    int retval;
    char buf[100] = "Hello!";

    fd = open(devname, O_RDWR);
    if (fd < 0) {
        perror(devname);
        exit(1);
    }
    retval = ioctl(fd, sizeof(buf), &buf);

    return 0;
}

---------------------------------------------------------------

2. ゲストOSでLinuxカーネルモジュールを作る

ゲストOS上でLinuxカーネルモジュールをつくります。

3つのファイルを、Ubuntu 13.04内の同じディレクトリにおいてください。そしてmakeします。

$ make

これでtestmodule.koというファイルができたら成功です。

3. ユーザーモードのプログラムを作る

ゲストOS上で、ユーザーモードのプログラムを作ります。

$ gcc testdriver.c -o testdriver

とすることで、testdriverというプログラムができれば成功です。

4. 動作確認をする

まず、カーネルモジュールをロードします。モジュールのロードには、スーパーユーザーの権限が必要ですのでsudoを使います。insmodでロードできます。また、ロードされたかどうかlsmodで確認できます。

$ sudo insmod testmodule.ko
$ lsmod |grep testmodule
testmodule 12952 0

次に、ユーザーモードのプログラムからカーネルモジュールにアクセスするためのデバイスファイルを作ります。testmodule.cで登録したキャラクターデバイスで88番のデバイスファイルを作ります。ユーザーモードのプログラムがアクセスできるように読み書きの権限をつけておきます。

$ sudo mknod /dev/testmodule c 88 0
$ sudo chmod a+rw /dev/testmodule

これで、準備はできたので、testdriverをうごかします。

$ ./testdriver

なにもエラーが表示されなければ成功です。本当に実行されたかどうか、ログファイルで確認することができます。less /var/log/kern.logと打って出てきた画面で大文字のGを押してください。ログファイルの最後尾を見ることができます。

$ less /var/log/kern.log

Apr 11 20:51:22 Mars kernel: [ 1913.498362] Installing testmodule
Apr 11 20:51:22 Mars kernel: [ 1913.498366] (major=88)
Apr 11 20:56:28 Mars kernel: [ 2219.260167]
Apr 11 20:56:28 Mars kernel: [ 2219.260167] testmodule is called!

このように、testmodule is called!と表示されているはずです。

5. 後始末をする

ロードしたカーネルモジュールは、ゲストOSを再起動するとロードされなくなりますが、次のようにするとアンロードできます。

$ sudo rmmod testmodule

また、作ったデバイスファイルは削除することができます。

$ sudo rm /dev/testmodule

これで、リモートデバッグをするカーネルモジュールを用意することができました。

次に続きます。
Windows 7上のVMware PlayerでLinuxのデバイスドライバをリモートデバッグする方法(2) - リモートデバッグする

Pocket

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

*

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください