2011-12-25 9 views
0

私はCでclone()関数を使用しようとしていますが、2番目の引数がどのように機能するかは不明です。 clone() manページあたり:this articleのコメントで提案に従った後Cでクローン関数を使用するのに、このポインタ演算が必要なのはなぜですか?

The child_stack argument specifies the location of the stack used by the 
    child process. Since the child and calling process may share memory, it 
    is not possible for the child process to execute in the same stack as the 
    calling process. The calling process must therefore set up memory space 
    for the child stack and pass a pointer to this space to clone(). Stacks 
    grow downwards on all processors that run Linux (except the HP PA proces‐ 
    sors), so child_stack usually points to the topmost address of the memory 
    space set up for the child stack. 

、私はこのCのプログラムを使用して作業を簡単な例を得ることができました:

#include <stdio.h> 
#include <sched.h> 
#include <stdlib.h> 
#include <assert.h> 

#define SIZE 65536 

int v1; 

int run(void *arg) { 
    v1 = 42; 
    return 0; 
} 

int main(int argc, char **argv) { 
    void **child_stack; 
    int pid, rc, status; 
    v1 = 10; 
    child_stack = (void **) malloc(SIZE); 
    assert(child_stack != NULL); 
    printf("v1 before: %d\n", v1); 

    pid = clone(run, child_stack + SIZE/sizeof(void **), CLONE_VM, NULL); 
    //pid = clone(run, child_stack + SIZE, CLONE_VM, NULL); 

    assert(pid != -1); 
    status = 0; 
    rc = waitpid(pid, &status, __WALL); 
    assert(rc != -1); 
    assert(WEXITSTATUS(status) == 0); 
    printf("v1 after: %d\n", v1); 
    return 0; 
} 

をしかし、私は混乱していますなぜclone行の中の特定のポインタ算術が必要であるかについて。与えられたcloneドキュメントによると、スタックは下向きに成長するはずですが、私はなぜそれを渡す前にmallocによって返されたポインタに値を追加すべきかを知っています。しかし、私はあなたがバイト数を追加すると思いますmalloc ' dを、その値を8で割った値(64ビットシステム上)ではなく、実際に動作するように見えます。上記のコードは、私がSIZEを定義したものとは関係なく正常に動作しているようですが、代わりにコメント付きのバージョンを使用すると、特定のしきい値を超えるすべてのSIZE値に対してセグメンテーション違反が発生します。

だから、与えられたclone行はなぜ機能しますが、コメントされた行はなぜですか? hereを説明するように、私が代わりにforkかのpthreadの、そもそもcloneを使用している理由については

は、私は、chroot監獄の抜け出しから信頼されていないプロセスを防ぐために、その高度なサンドボックス機能のいくつかを使用しようとしています。

答えて

1

、型のサイズがオフセット実際のメモリを決定するとき、例えば取る組み込まれる指さ:

int a[2] = {1, 2}; 
int* p = a; 

printf("%x: %x\n", &a[0], p); 
printf("%x: %x\n", &a[1], p + 1); 

この場合、pの値はpのアドレスだけではありません+ 1、それはp + 1 * sizeof(int)(指し示されるタイプのサイズ)の値です。これを考慮に入れて、いくつかのバイト数をオフセットする場合は、オフセットを変更するポインタタイプのサイズで除算する必要があります。お使いの場合には、言うことは、より正確かもしれので、あなたが指しているタイプは、void*です:

pid = clone(run, child_stack + SIZE/sizeof(void *), CLONE_VM, NULL); 

次のようなもので、この動作を可視化することができます。

int SIZE = 65536; 
void** child_stack = (void **) malloc(SIZE); 

void** child_stack_end = child_stack + SIZE; 
void** child_stack_end2 = child_stack + SIZE/sizeof(*child_stack); 

printf("%d\n", (intptr_t)child_stack_end - (intptr_t)child_stack); // "262144" 
printf("%d\n", (intptr_t)child_stack_end2 - (intptr_t)child_stack); // "65536" 
+2

'void **'ではなく 'void **'型の 'child_stack'はなぜですか? – SiegeX

+0

これは必須ではありません(任意のポインタ型を使用できます)。これは元の作者が使用していたタイプなので、ここで続けました。あなたが別のコメントで指摘したように、 'char *'はこのテストケースにとってより簡単な型のようです(sizeof()の必要条件を避けるため)。 – DRH

-1

child_stack + SIZEは、割り当てられたデータの終わりを過ぎて1を指しているため、その場所をスタックの開始点として使用するためのセグメンテーションフォールトは驚くべきことではありません。 child_stack + SIZE - 1を試しましたか?

+0

としてOKうまくいくことを

は注意してください。 – gsteff

2

T *型のポインタに整数値Vを追加すると、メモリアドレスがV * sizeof(T)だけインクリメントされます。コード内のchild_stackはタイプvoid**であるため、child_stack+SIZEは実際にはメモリアドレスがSIZE*sizeof(void*)バイト増加したことを意味します。ポインタ演算で

3

問題があることですあなたはchild_stackvoid **(voidポインタへのポインタ)として宣言しました。実際には、スタック(Cタイプを持たない)に使用される生のメモリへのポインタです。したがって、代わりにchar *またはintptr_tと宣言した方が意味があります。この場合、間違った型を修正するのではなく、直接ポインタ演算(child_stack + SIZEを渡す)を行うことができます。書かれたような補正は、(/ sizeof(void *)ではなく/ sizeof(void **)でなければなりません)間違っているが、それは、私はちょうどそれを試みたが、それはまだセグメンテーション違反ほとんどのマシン上sizeof(void **) == sizeof(void *)

+1

これ。なぜスタックが 'void ** 'として宣言されたのか不思議でした。私は 'void *'を理解することができますが、 'char *'は最良の選択のようです。 – SiegeX

関連する問題