web-dev-qa-db-ja.com

プロセスが終了するのに時間がかかりすぎるのはなぜですか?

基本的に、私はPowerPC(Freescale e500mc)でLinux2.6.34を使用しています。約2.25GのmlockedVMを使用するプロセス(社内で開発された一種の仮想マシン)があります。殺すと、終了するのに2分以上かかることに気づきました。

少し調べてみました。最初に、開いているファイル記述子をすべて閉じましたが、違いは見られませんでした。次に、カーネルにprintkを追加しましたが、カーネルがVMAのロックを解除したためにすべての遅延が発生していることがわかりました。遅延はページ間で均一です。これは、/ proc/meminfoでロックされたページ数を繰り返しチェックすることで確認しました。私はそれだけのメモリを割り当てるプログラムをチェックしました、そして私がそれらに合図するとすぐにそれらはすべて死にます。

私は今何をチェックすべきだと思いますか?返信ありがとうございます。

編集:問題に関する詳細情報を共有する方法を見つけなければならなかったので、以下のプログラムを作成しました。

#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <sys/time.h>

#define MAP_PERM_1              (PROT_WRITE | PROT_READ | PROT_EXEC)
#define MAP_PERM_2              (PROT_WRITE | PROT_READ)

#define MAP_FLAGS               (MAP_ANONYMOUS | MAP_FIXED | MAP_PRIVATE)

#define PG_LEN                  4096
#define align_pg_32(addr)       (addr & 0xFFFFF000)
#define num_pg_in_range(start, end)     ((end - start + 1) >> 12)

inline void __force_pgtbl_alloc(unsigned int start)
{
        volatile int *s = (int *) start;
        *s = *s;
}

int __map_a_page_at(unsigned int start, int whichperm)
{
        int perm = whichperm ? MAP_PERM_1 : MAP_PERM_2;

        if(MAP_FAILED == mmap((void *)start, PG_LEN, perm, MAP_FLAGS, 0, 0)){
                fprintf(stderr,
                        "mmap failed at 0x%x: %s.\n",
                        start, strerror(errno));
                return 0;
        }

        return 1;
}

int __mlock_page(unsigned int addr)
{
        if (mlock((void *)addr, (size_t)PG_LEN) < 0){
                fprintf(stderr,
                        "mlock failed on page: 0x%x: %s.\n",
                        addr, strerror(errno));
                return 0;
        }

        return 1;
}

void sigint_handler(int p)
{
        struct timeval start = {0 ,0}, end = {0, 0}, diff = {0, 0};
        gettimeofday(&start, NULL);
        munlockall();
        gettimeofday(&end, NULL);
        timersub(&end, &start, &diff);

        printf("Munlock'd entire VM in %u secs %u usecs.\n",
                diff.tv_sec, diff.tv_usec);

        exit(0);
}

int make_vma_map(unsigned int start, unsigned int end)
{
        int num_pg = num_pg_in_range(start, end);

        if (end < start){
                fprintf(stderr,
                        "Bad range: start: 0x%x end: 0x%x.\n",
                        start, end);
                return 0;
        }

        for (; num_pg; num_pg --, start += PG_LEN){
                if (__map_a_page_at(start, num_pg % 2) && __mlock_page(start))
                        __force_pgtbl_alloc(start);
                else
                        return 0;
        }

        return 1;
}

void display_banner()
{
        printf("-----------------------------------------\n");
        printf("Virtual memory allocator. Ctrl+C to exit.\n");
        printf("-----------------------------------------\n");
}

int main()
{
        unsigned int vma_start, vma_end, input = 0;
        int start_end = 0; // 0: start; 1: end;

        display_banner();

        // Bind SIGINT handler.
        signal(SIGINT, sigint_handler);

        while (1){
                if (!start_end)
                        printf("start:\t");
                else
                        printf("end:\t");

                scanf("%i", &input);

                if (start_end){
                        vma_end   = align_pg_32(input);
                        make_vma_map(vma_start, vma_end);
                }
                else{
                        vma_start = align_pg_32(input);
                }
                start_end = !start_end;
        }

        return 0;
}

ご覧のとおり、プログラムは仮想アドレスの範囲を受け入れます。各範囲は開始と終了によって定義されます。次に、隣接するページに異なるアクセス許可を与えることにより、各範囲がページサイズのVMAにさらに細分化されます。プログラムを中断すると(SIGINTを使用して)、munlockall()の呼び出しがトリガーされ、上記の手順が完了するまでの時間が適切に記録されます。

これで、Linuxバージョンが2.6.34のフリースケールe500mcで0x30000000〜0x35000000の範囲で実行すると、合計munlockall()時間が約45秒になります。ただし、開始と終了の範囲をランダムな順序で小さくして(つまり、必ずしもアドレスを増やす必要はない)同じことを行って、ページ(およびロックされたVMA)の総数がほぼ同じになる場合は、munlockall()の合計時間を次のように観察します。 4秒以内にしてください。

Linux 2.6.34を使用してx86_64で同じことを試し、プログラムを-m32パラメーターに対してコンパイルしました。バリエーションは、ppcの場合ほど顕著ではありませんが、最初のケースでは8秒、2番目のケースでは1秒未満であるようです。場合。

Linux 2.6.10と3.19でプログラムを試しましたが、これらの大きな違いは存在しないようです。さらに、munlockall()は常に1秒未満で完了します。

したがって、問題は、それが何であれ、Linuxカーネルの2.6.34バージョン周辺にのみ存在するようです。

1
Anirban Ghoshal

メモリをオーバーコミットした場合、ディスク上に多くのtmpfsが存在する可能性があります。シャットダウンを処理するために、ページインが必要になる場合があります。 mlock()は、他の多くのメモリをディスクに強制する可能性があります。あなたがディスクレスであることを示すように、あなたはおそらくネットワークを介して読んでいます。

サーバーのシャットダウン中にsarを実行してすべての統計情報を収集します。 (sarはデフォルトではインストールされない場合があります。)オプションのキャプチャデータをファイルに使用し、5〜10秒の間隔を設定します。次に、余暇のボトルネックとなったリソースを調べます。

編集:コメントに基づいて、ページテーブルにロックの問題が発生した可能性があります。 mlockの呼び出しは何回行いますか?それらはすべて必要ですか?プロセスをシャットダウンする前に、ロックされたメモリセグメントのリストをダンプしてみてください。

2
BillThor