PerlからC言語の関数を呼び出す

2015年3月30日

Perlで書いたCGIからc言語で書かれたライブラリの関数を呼び出してみた。
必要になる知識も多いので忘れないようにメモ

Perlからライブラリを呼び出すには?

環境など

使用する環境はLinux(debian)
ここでいうライブラリとは、
/usr/lib//usr/local/libなどの下に置かれてシステムから使用できるファイルで拡張子が".so“(※)のもの。

(※)
動的リンクで使われるライブラリが".so“、
静的リンクで使われるライブラリが".a“らしいが、
実際は同一のファイルの場合が多いみたい

やること

Perlからライブラリを呼び出す(以降『リンクする』と表記)手順は少々複雑だ。
なおライブラリの作成についてはここでは触れない。

ライブラリをリンクするには"XSLoader“モジュールを使う。
XSLoaderの概要によるとこのモジュールを読み込むためのモジュールを別途作り、
プログラムからは作ったモジュールを呼び出すのが吉みたい。
(たぶんプログラムから"require XSLoader;"しても行けそうだけど、ここは無難に従う)

################
# プログラム #
################
↓ "use MyLoader;"
################
# MyLoader.pm #
################
↓ "require XSLoader;"
################
# XSLoader.pm # ライブラリへのリンクを行う
################

というわけで、XSLoaderを呼び出すモジュールを作る必要がある。
h2xsコマンドを使うとモジュールを作成するための雛形が作れる。
なおh2xsはperlに同梱されているので、perlが動く環境なら使えるはず。

h2xs -A -n [作成するモジュール名]

これで指定したモジュール名のディレクトリが作成され、その中にモジュールを作成するための一式が用意される。

$ h2xs -A -n hoge
$ cd hoge
$ ls -l
-rw-r--r-- 1 *** *** 143 3月 1 07:08 Changes
-rw-r--r-- 1 *** *** 74 3月 1 07:08 MANIFEST
-rw-r--r-- 1 *** *** 803 3月 1 07:08 Makefile.PL
-rw-r--r-- 1 *** *** 1156 3月 1 07:08 README
-rw-r--r-- 1 *** *** 112 3月 1 07:08 hoge.xs
drwxr-xr-x 2 *** *** 4096 3月 1 07:08 lib
-rw-r--r-- 1 *** *** 174854 3月 1 07:08 ppport.h
drwxr-xr-x 2 *** *** 4096 3月 1 07:08 t

作成された一式の中で編集するのは***.xs
プログラムから呼び出すためのモジュール(lib/****.pm)、
そしてビルドの設定をするMakefile.PLだ。

なお、作成したモジュールをインストールするには以下のようにする。念のため

$ perl Makefile.PL # Makefileの作成
$ make # 一式のビルド
***(ビルド中〜〜
***(ビルド中〜〜
$ sudo make install # インストール

モジュール作成

XS (Perl extension)

h2xsで用意されたxsファイルを編集する。
xsファイルはファイルの前半は普通にc言語で記述でき、

"MODUE = ****** PACKAGE = *******"

の行以降が独自のマークアップ書式となっているようだ。
また、XS言語ならではの変数や関数も用意されている。
その辺りはここのサイトが参考になった。

なおxsファイルではリンクされたライブラリの関数呼び出しと、
Perlとc言語間の引数・戻り値の型変換を行っている。
makeする際にxsファイルは同名のc言語ファイルにコンバートされて、
そして最終的にはライブラリとして"blib/arch/auto/***/***.so"が生成される。

以下はライブラリ"libhoge"の"print_hoge"関数を呼び出すサンプル。

Makefile.PL

WriteMakefileの中を適宜編集する。リンクするライブラリはLIBSMYEXTLIBで指定。

use 5.014002;
use ExtUtils::MakeMaker;

WriteMakefile(
    NAME              => 'hoge',
    VERSION_FROM      => 'lib/hoge.pm',
    PREREQ_PM         => {},
    ($] >= 5.005 ?
      (ABSTRACT_FROM  => 'lib/hoge.pm',
       AUTHOR         => 'gm2bv ') : ()),
    LIBS              => ['-lhoge'],
    MYEXTLIB          => '〜パス〜/libhoge',
    DEFINE            => '', # e.g., '-DHAVE_SOMETHING'
    INC               => '-I.', # e.g., '-I. -I/usr/include/other'
);

hoge.xs

#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"

#include "ppport.h"

void print_hoge();  //関数宣言は必要。ヘッダをincludeでも良い

MODULE = hoge       PACKAGE = hoge

void
_print_hoge()
    CODE:
        print_hoge();

lib/hoge.pm

h2xsが自動生成した雛形を使用する。
このサンプルで編集したのは、@EXPORT配列の初期化部分とprint_hogeサブルーチンの箇所のみ

package hoge;

use 5.014002;
use strict;
use warnings;

require Exporter;

our @ISA = qw(Exporter);

our %EXPORT_TAGS = ( 'all' => [ qw(

) ] );

our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );

our @EXPORT = qw(
    print_hoge   # サブルーチンを外部で呼び出せるようにする
);

our $VERSION = '0.01';

require XSLoader;
XSLoader::load('hoge', $VERSION);

# サブルーチンを定義
sub print_hoge {
    return _print_hoge();
}

1;

gdbによるデバッグ

Makefile.PLWriteMakefileでデバッグオプションを設定する。

WriteMakefile(
   ******
   OPTIMIZE  => '-g -O2'
   ******
);

作ったモジュールを呼び出すテストスクリプトを書いて、gdbを起動する際にオプション"–args perl test.pl"を指定する。

おそらく環境や設定によると思うけど、gdb起動後に一度"run"して実行してからでないとブレークポイントが貼れなかったりした。
ここではデバッグさえこなせればいいので、よしなに。

参考サイト

メモperl,xs

Posted by gm2bv