2

execve 関数では、引数はポインター配列によって渡されます。これらのポインタが前のスタックのメモリを指している場合、これらのメモリは新しいプロセス イメージで引き続きアクセスできます。

#include <stdio.h>
#include <unistd.h>

int main(void)
{
    char filename[20] = "a.out";
    char str[20] = "hello\n";
    char *argv[3];

    argv[0] = filename;
    argv[1] = str;
    argv[2] = NULL;
    execve("/hel/a.out", argv, NULL);
    return 0;
}

/*   /hel/a.out code   */
#include <stdio.h>

int main(int argc, char *argv[], char *envp[])
{
    printf("%s\n", argv[1]);  /** Here, should the memory pointed by argv[1]
                               *  be freed after execve  has been called?
                               */ 
    return 0;
}
4

1 に答える 1

5

execve(2)のドキュメントを注意深く読んでください (より広い視野を得るためにAdvanced Linux Programmingも読んでください)。仮想メモリページングMMUプロセスについて読んでください。

execve システムコールは、プロセスに新しい仮想アドレス空間をインストールしています(そのため、正常に実行されているプログラムの古い仮想アドレス空間は新しい仮想アドレス空間execve によって上書きされて消えます)、以前のものとデータを共有しません(そして新しいプログラムが開始されるため、成功execveは返されません)。あなたの新しいプログラムは、例えばmmap(2) ...を使って、後で仮想アドレス空間を変更することができます。

新しい仮想アドレス空間内の文字列のアドレスargvは、 への引数のアドレスとは無関係execveです。文字列の内容は同じです。古い仮想アドレス空間と新しい仮想アドレス空間の間でデータ共有されませんが、新しいプログラム (およびプログラム環境) への引数はコピーされます。ASLRについても読む

の引数はexecve、開始関数 ( を呼び出すcrt0内)の新しい仮想アドレス空間の新しい呼び出しスタックにコピーされた (そのコピーがプッシュされた状態で)文字列です。もちろん、未定義の動作となるようなi-を行うべきではありません。_startmainfreeargv[]

したがって、 int a; argv[1]=(char*)&a;... execvewithは未定義の動作です。これは、 のアドレスにあるメモリ ゾーンが適切なヌル終了文字列argvであることを保証できないためです。エンディアンABIaについて読んでください。

そのexecve ため、適切な文字列(任意のポインターではない) の終了配列と、文字列の別のNULL終了 配列が必要であり、各文字列は 0 バイトで終了する必要があります。&を介して古いアドレス空間から新しいアドレス空間にコピーされる合計メモリ空間には、かなり小さい制限(通常は 128K バイト) があります。argvNULLenvARG_MAXargvenv

おそらく、共有メモリ ( shm_overview(7) を参照) を使用してさまざまなプロセス間でメモリを共有することができます (また、セマフォと同期します。sem_overview(7)を参照してください...)。しかし、多くの場合、他のプロセス間通信手法 (たとえば、 pipe(7) -s、fifo(7) -s、socket(7) -s など)を好むでしょう。

ところで、strace(1)も使用して、プログラムに関与するシステムコールを理解し、特に実行してproc(5)を使用し、仮想アドレス空間について詳しく理解してください。cat /proc/$$/mapscat /proc/$pidofyourprogram/maps

main両方の関数 (execve最初の関数の前return 0;、2 番目の関数の前) に次のようなものを入れることもできます。

 char cmd[64];
 snprintf(cmd, sizeof(cmd), "/bin/cat /proc/%d/maps", (int)getpid());
 printf("before running %s\n", cmd);
 fflush(NULL);
 int err = system(cmd);
 if (err) fprintf(stderr, "system failed err=%d\n", err);
 else printf("system %s done\n", cmd);

これにより、仮想アドレス空間のビューが表示されます。もちろん、より本格的なプログラムではfopen/proc/1234/mapsファイルを作成してループし、それfgetsまですべての行を読み取る必要があります。EOFfclose

辛抱強く、ここにあるすべての参考文献を読み、時間をかけて POSIX プログラミングについて詳しく学んでください。いくつかのフリー ソフトウェアのソース コード (たとえば、 http: //github.com/でいくつかの興味深いプロジェクトを選択できます...) を研究し、それらに貢献することは価値があるはずです。

于 2015-12-20T08:12:07.187 に答える