2013-04-17 19 views
5

I/Oポートを介してハードウェアにデータを出力するための簡単なLinuxキャラクタデバイスドライバをプログラミングしています。私は浮動小数点演算を実行して、ハードウェアの正しい出力を計算する関数を持っています。残念ながら、これは、Linuxカーネルが浮動小数点演算を非常にきれいに処理しないので、私はこの関数をユーザ空間に保持する必要があることを意味します。Linuxカーネルモジュール内からuserspace関数を呼び出す

ここでは、セットアップの擬似的表現(それはちょうど私のコードの相対的なレイアウトを示し、このコードは、特定の何もしないことに注意してください)です。

ユーザ空間の機能

char calculate_output(char x){ 
    double y = 2.5*x; 
    double z = sqrt(y); 

    char output = 0xA3; 

    if(z > 35.67){ 
     output = 0xC0; 
    } 

    return output; 
} 

Kernelspaceコード

unsigned i; 
for(i = 0; i < 300; i++){ 
    if(inb(INPUT_PORT) & NEED_DATA){ 
     char seed = inb(SEED_PORT); 
     char output = calculate_output(seed); 
     outb(output, OUTPUT_PORT); 
    } 

    /* do some random stuff here */ 
} 

私は01を使用して考えましたはユーザー空間関数からデータを渡すが、関数呼び出しがループ内にあり、次の呼び出しがcalculate_outputになる前に多くのコードが実行されるという事実をどのように処理するかはわかりません。

私はこの作業を想定方法である:kernelspaceコードの

  1. メインユーザ空間のプログラムが(おそらくioctlを介して)kernelspaceコードを開始する
  2. ユーザ空間のプログラムブロック待機
    • kernelspaceプログラムは、出力データのためにユーザー空間プログラムを要求し、ブロックは、
    • ユーザ空間のプログラムのブロックを解除し、(ioctl?)データを算出し、送信し、次いでブロック再び
    • kernelspaceプログラムのブロックを解除し、
  3. kernelspaceプログラム終了を継続し、ユーザ空間
  4. ユーザ空間に通知しますはブロックをブロック解除し、次のタスクに進みます。

カーネル空間とユーザー空間の間の通信はどのようにしてブロックされるので、ユーザー空間でデバイスファイルをポーリングしてデータを送信する必要があるかどうかを確認する必要はありません。


警告:固定小数点演算は、私のコード例では、非常にうまく機能するだろうが、それは実際のコードではオプションではありません。私は浮動小数点が提供する広い範囲を必要とします。たとえそうでないとしても、固定小数点演算を使用するコードを書き直すことは、将来のメンテナのためのアルゴリズムを難読化することになります。

+0

あなたはほとんどすべてのものをユーザスペースに持つことができますし、カーネルのインターフェースイネーブラーを薄くできますか? –

+0

@ChrisStrattonそれは私が達成しようとしているものです。 forループ内で発生するものは、その他のハードウェア出力(単純なoutb呼び出しとそれ以外のもの)です。 –

+0

@ChrisStrattonああ、あなたはカーネルの外で 'for'ループを動かすことを意味しますか? –

答えて

4

私は、最も簡単な解決策は、仮想ファイルのための独自のファイル操作で、キャラクターデバイスをカーネルドライバーに作成することだと思います。その後、ユーザー空間でこのデバイスを開くことができますO_RDWR。あなたは、主に2つのファイル操作を実装する必要があります。

  • read - これはカーネルが戻ってユーザ空間までのデータを渡す方法です。この関数は、read()システムコールを呼び出すユーザー空間スレッドのコンテキストで実行されます。カーネルが出力を知るために必要な別のシード値を持つまでブロックする必要があります。

  • write - これは、ユーザー空間がデータをカーネルに渡す方法です。あなたのケースでは、カーネルは直前の読み取りに応答し、ハードウェアに渡します。

次にあなたがユーザ空間での単純なループで終わる:

while (1) { 
    read(fd, buf, sizeof buf); 
    calculate_output(buf, output); 
    write(fd, output, sizeof output); 
} 

とカーネル内まったくループ - すべてが物事を駆動しているユーザー空間プロセスのコンテキストで実行され、カーネルドライバはハードウェアとの間でデータを移動する責任があります。

カーネル側の "ここで何かランダムなことをやっている"ことが何であるかによって、それをかなり単純にすることはできないかもしれません。カーネルループが本当に必要な場合は、そのループを実行するためのカーネルスレッドを作成してから、input_datainput_readyoutput_dataoutput_readyの変数にいくつかの変数を設定してください。

カーネルスレッドがデータを読み込むときには、データをinput_readyに置き、input_readyフラグを設定し、入力待ちキューに信号を送り、wait_event(<output_ready is set>)を実行します。 readファイル操作では、wait_event(<input_ready is set>)が実行され、準備が整うとデータがユーザー空間に返されます。同様に、writeファイル操作は、ユーザー空間から取得したデータをoutput_dataに置き、output_readyに設定し、出力待ちキューに信号を送ります。

ioperm,ioplまたは/dev/portのようなものを使用して、低レベルのハードウェアアクセスを含めてすべてをユーザー空間で完全に行うことが、別の方法です(醜い、ポータブルではありません)。

+2

キャラクタデバイスノードではなく** sysfs **属性で 'read' /' write'を使用していましたが、基本的にここで説明したものを実装しました。 –

0

すべての「重い吊り上げ」を行うコードをユーザーモードに移行することをお勧めします。つまり、300個の値すべてを一度に計算し、それらをカーネルに渡すことです。

カーネルから任意のコードでユーザーモードを呼び出せるかどうかはわかりません。それは可能なことだと私は確信しています。それは例えば "信号"のようなものなのですが、私はあなたが「好きな方法で」できると確信しています(そして、ほとんど確かに、あなたはその機能で行うことができます)。確かに素晴らしいアイデアのようには思えないし、何度もusermodeを呼び戻すのはかなり遅いでしょう。

+0

残念ながら、実行順序を変更することはできません。これがループをユーザー空間に移動させるのを難しくしています。私は、私が直面している逐次的依存を反映するために私のサンプルコードを調整しました(すなわち、以前のハードウェア出力は関数に送られた入力に影響するかもしれません)。 –

+1

次に、カーネルで計算を実行する方法(固定小数点を使用する方法など)を提案します。usermodeへのすべてのバイトをコールバックするのは良い考えではありません。表示する計算より10〜100倍長くかかりますここに。 –

関連する問題