Linuxの再入力可能関数と再入力不可関数


さいにゅうかのうかんすう
再入力可能な関数(すなわち、中断可能な関数)は、データ破壊を心配することなく、1つ以上のタスクによって呼び出され得る.リエントラント可能な関数は、いつでも中断され、一定時間後に実行が再開され、対応するデータが破壊されたり失われたりしません.
再入力可能な関数で使用される変数には、次の2つがあります.
1.ローカル変数を使用して、CPUレジスタまたはスタックに変数を保存する.
2.グローバル変数を使用しますが、この場合は、グローバル変数の保護(タスクが中断した後に他のタスクによって変数が変更されないように)に注意してください.
?
1
2
3
4
5 void   strcpy (*dest,*src)      while (* dest++ = *src ++){;}      *dest = NUL;
解析:上の関数は文字列レプリケーションに使用され、パラメータはスタックに格納されるため、各タスク呼び出し中に互いに相手のポインタを破壊することを心配することなく、変更関数をマルチタスクで呼び出すことができます.
 
基本的に次の関数は再入力できません.
    1.関数には静的なデータが使用されます.
    2.関数内にmalloc()またはfree()関数を使用したもの.
    3.関数内で標準のI/O関数が呼び出されます.
?
1
2
3
4
5
6
7 int   temp;   void   swap( int   *ex1, int   *ex2)      temp = *ex1; //(1)      *ex1 = *ex2;      *ex2 = temp;
解析:この関数のグローバル変数tempは、マルチタスクシステムでタスク1でswap関数を呼び出し、プログラムが(1)に実行されると中断され、さらに他のタスク2が実行され、タスク2でswap関数が呼び出されると、tempに格納されている値がタスク2によって変更されるため、再入力できない関数になります.したがって,タスク1が中断された箇所に戻って実行を続けると,tempに格納されている値が元のtemp値ではなくなり,エラーが発生する.
 
一般的な再入力可能な関数の方法は、次のとおりです.
1.グローバル変数を使用しないで、他のコードがこれらの変数の値を上書きしないようにします.
2.このような関数を呼び出す前に割り込みをオフにし、呼び出しが終わったらすぐに割り込みを開きます.関数の実行中に他のタスクの実行に中断されることを防止します.
3.使用信号量(反発条件).
とにかく、中断が安全であることを保証します.
再入力不可関数
マルチタスクシステムの下で、中断はタスクの実行の任意の時間に発生する可能性があります.1つの関数の実行期間が中断された後、ブレークポイントに再復元されて実行されるまで、関数に依存する環境が変更されていない場合、この関数は再入力可能であり、そうでなければ再入力できません.
中断の前後にコンテキストを保存して復元するのではないでしょうか.関数に依存する環境が変わったのではないでしょうか.
割り込み時にコンテキストが確実に保存されることは分かっているが,戻りアドレス,cpuレジスタなどの少量のコンテキストに限られ,関数内部で使用されるグローバル変数や静的変数,bufferなどは保護された列にないため,これらの値が関数が割り込まれている間に変化した場合,関数がブレークポイントに戻って実行されるとその結果は予想できない.
次のいずれかの条件を満たす多くは、再入力不可関数です.
(1)静的データ構造を用いた.
(2)mallocまたはfreeが呼び出された.
(3)標準I/O関数が呼び出された.
(4)浮動小数点演算を行う.
 
malloc/freeは再入力できません.グローバル変数を使用して空き領域を指します.標準I/Oライブラリの多くの実装では、グローバルデータ構造が使用されています.多くのプロセッサ/コンパイラでは、浮動小数点は一般的に再入力できません(浮動小数点演算の多くはコプロセッサまたはソフトウェアシミュレーションを使用して実現されます).
 
信号処理プログラムやマルチスレッドプログラミングでは、特に注意してください.
このような状況を考慮すると、
1)信号処理プログラムAの内外に同じ再入不可関数Bが呼び出されている.Bは実行中に信号によって中断され、A(AでBが呼び出された)に入り、用事が終わった後にBが中断された点に戻って実行を継続すると、B関数の環境が変化する可能性があり、その結果は予想できない.
2)マルチスレッドはプロセス内部の資源を共有し,2つのスレッドA,Bが同じ再入不可関数Fを呼び出し,AスレッドがFに入った後,スレッドスケジューリングをBに切り替え,BもFを実行すると,再びスレッドAに切り替えた場合,そのF呼び出しの結果も予想できない.
信号処理プログラムではリエント可能関数を呼び出しても問題があるので注意してください.一般的なルールとして、信号処理プログラムで呼び出されると、
関数を入力する場合は、errnoを前に保存し、その後errnoを復元する必要があります.(よく捉えられる信号はSIGCHLDであり、その信号処理プログラムは通常wait関数を呼び出し、様々なwait関数はerrnoを変更することができることを理解する.)