2017-02-13 7 views
0

私は関数ポインタの配列を通ってこれらの関数を順番に呼び出す関数を持っています。配列の内容を変更しながら配列内のポインタで関数を呼び出す

このプロセス中に関数ポインタの配列の内容が変更されるとどうなりますか?関数呼び出しは、予期せぬことが起こることがないように十分にアトミックであると考えることができるか、スタック上のプッシュが行われている間に関数ポインタを変更しないよう注意しなければならないか?

下記の例(擬似)コードです。 init()は起動時に1回実行され、callFunctions()は定期的に実行されます(たとえば、1秒に1回)。次にchangeFunctions()が来て、functionPtrArray []の内容を変更します。これは、コードがOSのような環境の異なるプロセスで実行されるため、いつでも発生する可能性があります。

void (*functionPtrArray[3]) (void); 

void init(void) 
{ 
    functionPtrArray[0] = function1; 
    functionPtrArray[1] = function2; 
    functionPtrArray[2] = function3; 
} 

void callFunctions(void) 
{ 
    for (i = 0; i < 3; i++) 
    { 
     *functionPtrArray[i](); 
    } 
} 

void changeFunctions(void) 
{ 
    functionPtrArray[0] = newFunction1; 
    functionPtrArray[1] = newFunction2; 
    functionPtrArray[2] = newFunction3; 
} 
+0

重要な質問は、一度f [0]が実行されると、f [1]とf [2]はf [0]と同じ集合からのものであるかどうかです。それ以外の場合、コードは正常です(関数ポインタを取得して格納することは基本的であり、基本的にはネイティブCPUとバスワードサイズであるため、1サイクルで読み書きサイクルがネイティブマシンワード全体をフェッチ/ストアします)。 –

+0

@PaulOgilvie何らかの汎用CPUが1サイクルで1ワード(xバイト)を読み込むことができると思いますか?たとえその前提が真実であったとしても、アーキテクチャの多くは、コードとデータの両方を格納するために使用されることを意図したマシンのワードサイズを超えたアドレスを拡張しています。 – Lundin

答えて

2

編集:私は長すぎたとランディンによって横取り:https://stackoverflow.com/a/42201493/5592711

重要なポイント: Cの関数を呼び出すと、ジャンプするまでジャンプするので、プログラムが関数を実行しているときには、関数ポインタを変更することによってプログラムフローに何も変更されません。

たとえば、このスニペットでは、some_function()は正常に実行され、ptrの変更の影響を受けません。

void(*ptr)(void) = &some_function; 
(*ptr)(); 
// ... in another thread, while 'some_function()' is executing 
ptr = &other_function; 

ただし、ここで重要な点を高くしている、メモリ操作がアトミックではありませんので、別のスレッドがそれを読んでいた一方で、いくつかのスレッドがfunctionPtrArray[0]要素を変更することができ、いくつかのゴミのアドレスにジャンプすることをリードすると、この仮説的な例のように、プログラムが失敗する原因になります。

pthreadライブラリを使用して、スレッド間の同期を確実にするには、mutexesを使用します(Googleには十分な情報があります)。

あなたの例では、このような同期を使用すると、次のようになります。

// You can use static mutex initialization 
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; 

void callFunctions(void) 
{ 
    pthread_mutex_lock(&mutex); 
    for (i = 0; i < 3; i++) 
    { 
     *functionPtrArray[i](); 
    } 
    pthread_mutex_unlock(&mutex); 
} 

void changeFunctions(void) 
{ 
    pthread_mutex_lock(&mutex); 
    functionPtrArray[0] = newFunction1; 
    functionPtrArray[1] = newFunction2; 
    functionPtrArray[2] = newFunction3; 
    pthread_mutex_unlock(&mutex); 
} 

この方法は、あなたの関数ポインタの配列を変更し、機能を実行することは、相互に排他的なタスクです。 あなたはこの例では、いくつかの制限があることを知っておく必要があります。

  1. ミューテックスが全体の機能の実行中にロックされている(それが必要とされていない、あなただけの、関数ポインタを読んでロックを解除して、それらを実行するためにそれをロックすることができます)
  2. ではなく、changeFunctionsと呼びます。それ以外の場合は、デッドロック状態になります。
+0

"他のスレッドがそれを読んでいる間に変更する:"関数アドレスがネイティブのマシンワードサイズであると仮定して、アセンブラのアトミックアクションを配列から関数アドレスを取得することを検討します。 –

+0

はい、いいえ、プラットフォームに依存します:http://stackoverflow.com/questions/1350994/is-it-safe-to-read-an-integer-variable-thats-beating-concurrently-modified-withou –

+0

Monti、ポインタのおかげで。私はかつて読んでいました: "あなたは間違いから学ぶ能力を持っています。あなたは今日多くのことを学ぶでしょう"( "あなた" "私"を意味する)。 –

2

callFunctions()が定期的に実行され、一度各秒と言います。次にchangeFunctions()が来て、functionPtrArray []の内容を変更します。これは、コードがOSのような環境の異なるプロセスで実行されるため、いつでも発生する可能性があります。あなたの説明から

は、functionPtrArrayアレイがdata raceあるunsychronized方法で複数のスレッド/プロセス(ES)4BY改変及びアクセスされることは明らかです。 それでは、一方向などの同期を提供する必要があります。

2

マルチスレッドシナリオの変数と同じです。いいえ、C11の_Atomic修飾子を使用しないと、アトミックであると見なすことはできません。

関数ポインタの読み込みがアトミックではない場合がよくあります。 16ビットアドレスの8ビットCPUがその一例です。これらのアーキテクチャの中には、安全で中断されない16ビットインデックスレジスタの処理を保証する命令がありますが、それ以外の命令はありません。別の例は、デフォルトのアドレスバス幅(バンキング、「farポインタ」)を超えて拡張メモリをサポートするアーキテクチャです。

コードが立っているので、関数ポインタの配列は別のスレッド/プロセス/ ISR /コールバックによって変更されてはならない、または競合状態のバグが発生する可能性があります。セマフォ/ミューテックス/クリティカルセクションでアレイへのアクセスを保護する必要があります。

実行時間がかかるため、実行中にほかのスレッドをすべてブロックする必要はありません。それは、この擬似コードのように、ローカル関数ポインタの下にコピーすることが最善である:

void callFunctions(void) 
{ 
    for (i = 0; i < 3; i++) 
    { 
    void(*local)(void); 

    grab_mutex(); 
     local = functionPtrArray[i]; 
    release_mutex(); 

    local(); // call is completely thread-safe 
    } 
} 
+0

呼び出しにパラメータがないため、呼び出しはアトミック(symbolicly 'mov ax、&f'の後に' call ax')されます。したがって、私はこの解決策が(配列全体がコピーされていない限り)何かを追加するとは思わない。 –

+0

@PaulOgilvie特定のシステムは述べられていません。原子アクセスがすべてのシステムで移植可能であることを保証するには、非常に素朴です。まず、16ビットのアドレスを持つ8ビットMCUの非常に一般的なケースを考えてみましょう。定義によって関数ポインタのアトミックなアクセスを持つことはできません。しかし確かに、世界中のすべてのコンピュータがPCであり、あなたが他のコンピュータのことを知らないので、私は投票していると仮定してください... – Lundin

+0

Lundin。私は確かに少し素朴かもしれません。解決策が編集されるまでunvoteできません。謝罪。 –

関連する問題