11

サーバー上でJavaアプリケーションをブートストラップしているときに、LinuxカーネルによるCPU消費量が多いという問題があります。この問題は本番環境でのみ発生します。開発サーバーではすべてが光速です。

upd9:この問題について2つの質問がありました:

  1. それを修正する方法は?-名目上の動物はすべてを同期してドロップすることを提案しました、そしてそれは本当に役に立ちます。sudo sh -c 'sync ; echo 3 > /proc/sys/vm/drop_caches ;動作します。upd12:しかし確かsyncに十分です。

  2. なぜこれが起こっているのですか?-まだ開いています。耐久性のあるページをディスクにフラッシュすると、カーネルのCPUとIO時間が消費されることは理解していますが、これは正常な動作です。しかし、ストラジとは何ですか。「C」で記述されたシングルスレッドアプリケーションでさえ、カーネル空間ですべてのコアを100%ロードするのはなぜですか。

ref-upd10とref -upd11echo 3 > /proc/sys/vm/drop_cachesが原因で、メモリ割り当てが遅いという問題を解決するために必要ではないという考えがあります。メモリを消費するアプリケーションを開始する前に、`sync'を実行するだけで十分です。おそらく、この明日を本番環境で試し、結果をここに投稿します。

upd10: FSキャッシュページが失われた場合:

  1. 実行cat 10GB.fiel > /dev/nullした後
  2. sync確かに、耐久性のあるページはありません(cat /proc/meminfo |grep ^Dirty184kbを表示)。
  3. 私が得たチェックcat /proc/meminfo |grep ^Cached:4GBがキャッシュされました
  4. 実行int main(char**)すると、通常のパフォーマンスが得られました(32MBの割り当てられたデータを初期化するための50msなど)。
  5. キャッシュされたメモリが900MBに削減されました
  6. テストの概要:LinuxがFSキャッシュとして使用されているページを割り当てられたメモリに再利用することは問題ないと思います。

upd11:ダーティページがたくさんある場合。

  1. リストアイテム

  2. HowMongoDdWorksコメント部分を使用して例を実行し、readしばらくすると

  3. /proc/meminfo2.8GBはでDirty、3.6GBはCachedです。

  4. 停止HowMongoDdWorksして実行しましたint main(char**)

  5. 結果の一部は次のとおりです。

    init 15、時間0.00s x0[試行1/パート0]時間1.11sx1[試行2/パート0]時間0.04sx0[試行1/パート1]時間1.04sx1[試行2/パート1]時間0.05sx 0[試行1/パート2]時間0.42秒x1[試行2/パート2]時間0.04秒

  6. テストによる要約:耐久性のあるページが失われると、割り当てられたメモリへの最初のアクセスが大幅に遅くなります(公平を期すために、これは、アプリケーションメモリの合計がOSメモリ全体に匹敵し始めたときにのみ発生します。つまり、16GBのうち8GBが空いている場合は1GBを割り当てるのに問題はありません。3GB程度から最も遅くなります)。

これで、開発環境でこの状況を再現できたので、ここに新しい詳細を示します。

開発マシンの構成:

  1. Linux 2.6.32-220.13.1.el6.x86_64-Scientific Linuxリリース6.1(カーボン)
  2. RAM:15.55 GB
  3. CPU:1 X Intel(R)Core(TM)i5-2300 CPU @ 2.80GHz(4スレッド)(物理)

FSキャッシュ内の大量の耐久性のあるページが原因で発生する問題は99.9%です。ダーティページに多くを作成するアプリケーションは次のとおりです。

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.Random;

/**
 * @author dmitry.mamonov
 *         Created: 10/2/12 2:53 PM
 */
public class HowMongoDdWorks{
    public static void main(String[] args) throws IOException {
        final long length = 10L*1024L*1024L*1024L;
        final int pageSize = 4*1024;
        final int lengthPages = (int) (length/pageSize);
        final byte[] buffer = new byte[pageSize];
        final Random random = new Random();
        System.out.println("Init file");
        final RandomAccessFile raf = new RandomAccessFile("random.file","rw");
        raf.setLength(length);
        int written = 0;
        int readed = 0;
        System.out.println("Test started");
        while(true){
            { //write.
                random.nextBytes(buffer);
                final long randomPageLocation = (long)random.nextInt(lengthPages)*(long)pageSize;
                raf.seek(randomPageLocation);
                raf.write(buffer);
                written++;
            }
            { //read.
                random.nextBytes(buffer);
                final long randomPageLocation = (long)random.nextInt(lengthPages)*(long)pageSize;
                raf.seek(randomPageLocation);
                raf.read(buffer);
                readed++;
            }
            if (written % 1024==0 || readed%1024==0){
                System.out.printf("W %10d R %10d pages\n", written, readed);
            }

        }
    }
}

そして、これがテストアプリケーションです。これにより、カーネルスペースでHI(すべてのコアで最大100%)のCPU負荷が発生します(以下と同じですが、もう一度コピーします)。

#include<stdlib.h>
#include<stdio.h>
#include<time.h>

int main(char** argv){
   int last = clock(); //remember the time
   for(int i=0;i<16;i++){ //repeat test several times
      int size = 256 * 1024 * 1024;
      int size4=size/4;
      int* buffer = malloc(size); //allocate 256MB of memory
      for(int k=0;k<2;k++){ //initialize allocated memory twice
          for(int j=0;j<size4;j++){ 
              //memory initialization (if I skip this step my test ends in 
              buffer[j]=k; 0.000s
          }
          //printing 
          printf(x "[%d] %.2f\n",k+1, (clock()-last)/(double)CLOCKS_PER_SEC); stat
          last = clock();
      }
   }
   return 0;
}

前のHowMongoDdWorksプログラムの実行中、int main(char** argv)次のような結果が表示されます。

x [1] 0.23
x [2] 0.19
x [1] 0.24
x [2] 0.19
x [1] 1.30 -- first initialization takes significantly longer
x [2] 0.19 -- then seconds one (6x times slowew)
x [1] 10.94 -- and some times it is 50x slower!!!
x [2] 0.19
x [1] 1.10
x [2] 0.21
x [1] 1.52
x [2] 0.19
x [1] 0.94
x [2] 0.21
x [1] 2.36
x [2] 0.20
x [1] 3.20
x [2] 0.20 -- and the results is totally unstable
...

私は歴史的な目的のためだけにこの線より下にすべてを保持します。


upd1:開発システムと本番システムの両方がこのテストには十分な大きさです。 upd7:ページングではありません。少なくとも、問題が発生している間はストレージIOアクティビティは発生していません。

  1. dev〜4コア、16 GM RAM、〜8GB無料
  2. 本番環境〜12コア、24 GB RAM、〜16 GB空き容量(8〜10 GMはFSキャッシュの下にありますが、違いはありません。すべての16GMが完全に空き容量であっても同じ結果になります)、このマシンもCPUによってロードされますが、高すぎる〜10%。

upd8(ref):新しいテストケースと潜在的な説明は末尾を参照してください。

これが私のテストケースです(私はjavaとpythonもテストしましたが、「c」が最も明確なはずです):

#include<stdlib.h>
#include<stdio.h>
#include<time.h>

int main(char** argv){
   int last = clock(); //remember the time
   for(int i=0;i<16;i++){ //repeat test several times
      int size = 256 * 1024 * 1024;
      int size4=size/4;
      int* buffer = malloc(size); //allocate 256MB of memory
      for(int k=0;k<2;k++){ //initialize allocated memory twice
          for(int j=0;j<size4;j++){ 
              //memory initialization (if I skip this step my test ends in 
              buffer[j]=k; 0.000s
          }
          //printing 
          printf(x "[%d] %.2f\n",k+1, (clock()-last)/(double)CLOCKS_PER_SEC); stat
          last = clock();
      }
   }
   return 0;
}

開発マシンでの出力(部分的):

x [1] 0.13 --first initialization takes a bit longer
x [2] 0.12 --then second one, but the different is not significant.
x [1] 0.13
x [2] 0.12
x [1] 0.15
x [2] 0.11
x [1] 0.14
x [2] 0.12
x [1] 0.14
x [2] 0.12
x [1] 0.13
x [2] 0.12
x [1] 0.14
x [2] 0.11
x [1] 0.14
x [2] 0.12 -- and the results is quite stable
...

生産機械での出力(部分的):

x [1] 0.23
x [2] 0.19
x [1] 0.24
x [2] 0.19
x [1] 1.30 -- first initialization takes significantly longer
x [2] 0.19 -- then seconds one (6x times slowew)
x [1] 10.94 -- and some times it is 50x slower!!!
x [2] 0.19
x [1] 1.10
x [2] 0.21
x [1] 1.52
x [2] 0.19
x [1] 0.94
x [2] 0.21
x [1] 2.36
x [2] 0.20
x [1] 3.20
x [2] 0.20 -- and the results is totally unstable
...

開発マシンでこのテストを実行している間、すべてのコアがhtopで5%未満の使用率であるように、CPU使用率はグラウンドから上昇することさえありません。

しかし、本番マシンでこのテストを実行すると、すべてのコアで最大100%のCPU使用率が見られ(平均負荷は12コアマシンで最大50%上昇します)、それはすべてカーネル時間です。

upd2:すべてのマシンに同じcentos linux 2.6がインストールされています。私は、sshを使用してそれらを操作します。

upd3: A:スワップしている可能性は低く、テスト中にディスクアクティビティは確認されていません。また、十分なRAMも空いています。(また、descriptinが更新されます)。–ドミトリー9分前

upd4: htopは、カーネルによるHI CPU使用率、すべてのコアによる最大100%の使用率(製品上)を示します。

upd5:初期化が完了した後、CPU使用率は落ち着きますか?私の簡単なテストでは-はい。実際のアプリケーションの場合、新しいプログラムを開始するために他のすべてを停止するのに役立つだけです(これはナンセンスです)。

2つの質問があります:

  1. なぜこれが起こっているのですか?

  2. それを修正する方法は?

upd8:テストと説明を改善しました。

#include<stdlib.h>
#include<stdio.h>
#include<time.h>

int main(char** argv){
    const int partition = 8;
   int last = clock();
   for(int i=0;i<16;i++){
       int size = 256 * 1024 * 1024;
       int size4=size/4;
       int* buffer = malloc(size);
       buffer[0]=123;
       printf("init %d, time %.2fs\n",i, (clock()-last)/(double)CLOCKS_PER_SEC);
       last = clock();
       for(int p=0;p<partition;p++){
            for(int k=0;k<2;k++){
                for(int j=p*size4/partition;j<(p+1)*size4/partition;j++){
                    buffer[j]=k;
                }
                printf("x [try %d/part %d] time %.2fs\n",k+1, p, (clock()-last)/(double)CLOCKS_PER_SEC);
                last = clock();
            }
      }
   }
   return 0;
}

結果は次のようになります。

init 15, time 0.00s -- malloc call takes nothing.
x [try 1/part 0] time 0.07s -- usually first try to fill buffer part with values is fast enough.
x [try 2/part 0] time 0.04s -- second try to fill buffer part with values is always fast.
x [try 1/part 1] time 0.17s
x [try 2/part 1] time 0.05s -- second try...
x [try 1/part 2] time 0.07s
x [try 2/part 2] time 0.05s -- second try...
x [try 1/part 3] time 0.07s
x [try 2/part 3] time 0.04s -- second try...
x [try 1/part 4] time 0.08s
x [try 2/part 4] time 0.04s -- second try...
x [try 1/part 5] time 0.39s -- BUT some times it takes significantly longer then average to fill part of allocated buffer with values.
x [try 2/part 5] time 0.05s -- second try...
x [try 1/part 6] time 0.35s
x [try 2/part 6] time 0.05s -- second try...
x [try 1/part 7] time 0.16s
x [try 2/part 7] time 0.04s -- second try...

このテストから学んだ事実。

  1. メモリ割り当て自体は高速です。
  2. 割り当てられたメモリへの最初のアクセスは高速です(したがって、遅延バッファ割り当ての問題ではありません)。
  3. 割り当てられたバッファをパーツに分割しました(テストでは8)。
  4. そして、各バッファ部分に値0を入力し、次に値1を入力して、消費時間を印刷します。
  5. 2番目のバッファー部分の充填は常に高速です。
  6. しかし、最も遠いバッファ部分の塗りつぶしは、常に2番目の塗りつぶしよりも少し遅くなります(最初のページへのアクセス時にカーネルで追加の作業が行われると思います)。
  7. バッファ部分を最初に値で埋めるのにかなり長い時間がかかる場合があります。

提案されたanwserを試してみましたが、役に立ったようです。後で再確認して結果を投稿します。

Linuxは割り当てられたページをdurtyファイルシステムのキャッシュページにマップしているように見え、ページを1つずつディスクにフラッシュするのに長い時間がかかります。ただし、完全同期は高速に機能し、問題を排除します。

4

2 に答える 2

8

走る

sudo sh -c 'sync ; echo 3 > /proc/sys/vm/drop_caches ; sync'

開発マシンで。キャッシュが空であることを確認する安全で非破壊的な方法です。(上記のコマンドを実行しても、ディスクへの保存または書き込みがまったく同時に行われたとしても、データが失われることはありません。これは本当に安全です。)

次に、Java が実行されていないことを確認し、念のため上記のコマンドを再実行します。たとえば、Javaが実行されているかどうかを確認できます

ps axu | sed -ne '/ sed -ne /d; /java/p'

何も出力しないはずです。その場合は、最初に Java を閉じます。

ここで、アプリケーション テストを再実行します。開発マシンでも同じ速度低下が発生していますか?

ドミトリー、どちらの方法でもコメントを残したい場合は、問題をさらに調査させていただきます.

編集して追加:スローダウンが発生したと思われます。これは、Java 自体によって発生する大きな起動待ち時間が原因です。これは非常に一般的な問題であり、基本的に Java に組み込まれており、そのアーキテクチャの結果です。大規模なアプリケーションの場合、Java がクラスをロードして準備する必要があるため (ほとんどの場合もシリアルであるため、コアを追加しても役に立たないため)、マシンがどれほど高速であっても、起動の待ち時間はたいてい 1 秒のかなりの割合です。

言い換えれば、責任は Linux ではなく Java にあると私は信じています。Linux はカーネル レベルのキャッシングを通じて開発マシンのレイテンシを軽減するため、まったく逆です。これは、これらの Java コンポーネントを実質的に常に実行し続け、カーネルがそれらをキャッシュすることを認識しているからです。

編集 2: アプリケーションの起動時に Java 環境がアクセスするファイルを確認すると非常に便利です。あなたはこれを行うことができますstrace

strace -f -o trace.log -q -tt -T -e trace=open COMMAND...

によって開始されたプロセスのいずれかによって実行されたシステムコールtrace.logを含むファイルを作成します。開始するプロセスごとに出力を保存するには、次を使用しますopen()COMMAND...trace.PIDCOMMAND...

strace -f -o trace -ff -q -tt -T -e trace=open COMMAND...

dev インストールと prod インストールの出力を比較すると、それらが本当に同等かどうかがわかります。そのうちの 1 つに余分なライブラリまたは不足しているライブラリがあり、起動時間に影響を与える可能性があります。

インストールが古く、システム パーティションが適度にいっぱいになっている場合は、それらのファイルが断片化されている可能性があり、カーネルが I/O の完了を待つのにより多くの時間を費やしている可能性があります。(I/O の量は同じままであることに注意してください。ファイルが断片化されている場合、完了するのにかかる時間のみが増加します。) コマンドを使用できます。

LANG=C LC_ALL=C sed -ne 's|^[^"]* open("\(.*\)", O[^"]*$|\1|p' trace.* \
| LANG=C LC_ALL=C sed -ne 's|^[^"]* open("\(.*\)", O[^"]*$|\1|p' \
| LANG=C LC_ALL=C xargs -r -d '\n' filefrag \
| LANG=C LC_ALL=C awk '(NF > 3 && $NF == "found") { n[$(NF-2)]++ }
  END { for (i in n) printf "%d extents %d files\n", i, n[i] }' \
| sort -g

アプリケーションで使用されるファイルがどの程度断片化されているかを確認します。1 つまたは複数のエクステントを使用しているファイルの数が報告されます。COMMAND...元の実行可能ファイル ( ) は含まれておらず、アクセスするファイルのみが含まれていることに注意してください。

単一のコマンドでアクセスされたファイルの断片化統計を取得したいだけの場合は、次を使用できます

LANG=C LC_ALL=C strace -f -q -tt -T -e trace=open COMMAND... 2>&1 \
| LANG=C LC_ALL=C sed -ne 's|^[0-9:.]* open("\(.*\)", O[^"]*$|\1|p' \
| LANG=C LC_ALL=C xargs -r filefrag \
| LANG=C LC_ALL=C awk '(NF > 3 && $NF == "found") { n[$(NF-2)]++ }
  END { for (i in n) printf "%d extents %d files\n", i, n[i] }' \
| sort -g

問題がキャッシングによるものでない場合、2 つのインストールが真に同等ではない可能性が最も高いと思います。もしそうなら、私は断片化をチェックします。その後、-e trace=open両方の環境で完全なトレース ( を省略) を実行して、違いがどこにあるかを正確に確認します。


私は今、あなたの問題/状況を理解していると信じています.

prod 環境では、カーネル ページ キャッシュはほとんどダーティです。つまり、ほとんどのキャッシュはディスクに書き込まれるものです。

アプリケーションが新しいページを割り当てるとき、カーネルはページ マッピングを設定するだけで、実際には物理 RAM をすぐには割り当てません。これは、各ページへの最初のアクセス時にのみ発生します。

最初のアクセスで、カーネルは最初に空きページを見つけます。これは通常、「クリーンな」キャッシュ データ、つまり、ディスクから読み取られたが変更されていないデータを含むページです。次に、プロセス間の情報漏洩を避けるために、それをゼロにクリアします。(C ライブラリ割り当て機能malloc()などを使用する場合、関数の直接的なmmap()ファミリの代わりに、ライブラリはマッピングの一部を使用/再利用する場合があります。カーネルはページをゼロにクリアしますが、ライブラリはそれらを「ダーティ」にする場合があります。mmap()匿名のページを取得するには、それらをゼロにします。)

適切なクリーン ページが手元にない場合、カーネルは最初に最も古いダーティ ページの一部をディスクにフラッシュする必要があります。(カーネル内には、ページをディスクにフラッシュしてクリーンとマークするプロセスがありますが、ページが継続的にダーティになるようなサーバーの負荷がある場合は、ほとんどがクリーンなページではなく、ほとんどがダーティ ページであることが通常望ましいです。残念ながら、これは最初のページ アクセスの待ち時間が長くなることを意味し、現在遭遇している.)

各ページsysconf(_SC_PAGESIZE)の長さはバイト単位で、位置合わせされています。つまり、ポインターpがページの先頭を指すのは、 の場合のみです((long)p % sysconf(_SC_PAGESIZE)) == 0。ほとんどのカーネルは、ほとんどの場合、個々のページではなくページのグループに実際にデータを入力していると私は信じています。そのため、(ページの各グループへの) 最初のアクセスの待ち時間が長くなります。

最後に、ベンチマークに大混乱をもたらすコンパイラの最適化があるかもしれません。ベンチマーク用に別のソース ファイルを作成し、main()各反復で実行される実際の作業を別のファイルに記述することをお勧めします。それらを別々にコンパイルし、それらを一緒にリンクして、コンパイラーが時間関数を wrt に再配置しないようにします。実際に行った作業。基本的に、でbenchmark.c

#define _POSIX_C_SOURCE 200809L
#include <time.h>
#include <stdio.h>

/* in work.c, adjust as needed */
void work_init(void);      /* Optional, allocations etc. */
void work(long iteration); /* Completely up to you, including parameters */
void work_done(void);      /* Optional, deallocations etc. */

#define PRIMING    0
#define REPEATS  100

int main(void)
{
    double          wall_seconds[REPEATS];
    struct timespec wall_start, wall_stop;
    long            iteration;

    work_init();

    /* Priming: do you want caches hot? */
    for (iteration = 0L; iteration < PRIMING; iteration++)
        work(iteration);

    /* Timed iterations */
    for (iteration = 0L; iteration < REPEATS; iteration++) {
        clock_gettime(CLOCK_REALTIME, &wall_start);
        work(iteration);
        clock_gettime(CLOCK_REALTIME, &wall_stop);
        wall_seconds[iteration] = (double)(wall_stop.tv_sec - wall_start.tv_sec)
                                + (double)(wall_stop.tv_nsec - wall_start.tv_nsec) / 1000000000.0;
    }

    work_done();

    /* TODO: wall_seconds[0] is the first iteration.
     *       Comparing to successive iterations (assuming REPEATS > 0)
     *       tells you about the initial latency.
    */

    /* TODO: Sort wall_seconds, for easier statistics.
     *       Most reliable value is the median, with half of the
     *       values larger and half smaller.
     *       Personally, I like to discard first and last 15.85%
     *       of the results, to get "one-sigma confidence" interval.
    */

    return 0;
}

work()で定義された関数で行われる実際の配列の割り当て、割り当て解除、および充填 (繰り返しループごと) を使用しwork.cます。

于 2012-09-29T11:23:26.527 に答える
2

カーネルが利用可能なクリーンページを使い果たした場合、ダーティページをディスクにフラッシュする必要があります。多くのダーティページをディスクにフラッシュすることは、CPU負荷が高いように見えます。これは、ほとんどのカーネル側のものが機能するために(一時的に)1つ以上のページを必要とするためです。基本的に、ユーザースペースアプリケーションが非I / O関連のカーネル関数を呼び出した場合でも、カーネルはI/Oが完了するのを待機しています。

マイクロベンチマークを並行して実行する場合、たとえば、非常に大きなマッピングを何度も繰り返し__builtin_ia32_rdtsc()ダーティし、システムコールを呼び出さずにCPU時間を測定するプログラム(x86またはx86-64でGCCを使用している場合)を言うと、これが表示されます。カーネルがCPU時間を「すべて」消費しているように見える場合でも、十分なCPU時間を取得します。プロセスが内部的にメモリを必要とするカーネル関数(syscall)を呼び出す場合にのみ、そのプロセスは「ブロック」を呼び出し、新しいページを生成するためのページフラッシュをカーネルで待機し続けます。

ベンチマークを実行するときは、通常sudo sh -c 'sync ; echo 3 >/proc/sys/vm/drop_caches ; sync'、ベンチマークを実行する前に数回実行するだけで十分です。これにより、ベンチマーク中に過度のメモリプレッシャーが発生しないようにすることができます。実稼働環境では使用しません。(実行するのは安全ですが、つまりデータを失うことはありませんが、それはハンマーを使用して蚊を殺すようなものです。仕事には間違ったツールです。)

実稼働環境で、カーネルがダーティページをフラッシュするためにレイテンシが大きくなり始めた場合(これはデバイスの最大速度で行われ、アプリケーションのI / O速度も低下する可能性があります)、調整できます。カーネルのダーティページフラッシュメカニズム。基本的に、ダーティページをディスクにはるかに早くフラッシュするようにカーネルに指示し、(可能であれば)どの時点でもダーティページがそれほど多くないことを確認できます。

グレゴリー・スミスは、フラッシングメカニズムの理論と調整についてここに書いています。つまり、/proc/sys/vm/変更可能なカーネル調整可能ファイルが含まれています。echoこれらは起動時にデフォルトにリセットされますが、起動時にファイルの目的の値に簡単なinitスクリプトを簡単に記述できます。本番マシンで実行されているプロセスが大量のI/Oを実行する場合は、ファイルシステムの調整可能ファイルも確認できます。少なくとも、フラグ/etc/fstabを使用してファイルシステム(を参照)をマウントする必要がありますrelatime。これにより、ファイルが変更された後、またはそのステータスが変更された後の最初のアクセスでのみファイルアクセス時間が更新されます。

個人的には、マルチメディアワークステーション(および現在ある場合はマルチメディアサーバー)にも、1000Hzタイマーを備えた低遅延のプリエンプティブカーネルを使用しています。このようなカーネルは、ユーザープロセスをより短いスライスで実行し、通常、最大の計算能力はわずかに劣りますが、はるかに優れたレイテンシーを提供します。本番サービスがレイテンシーに敏感な場合は、本番サーバーをそのようなカーネルに切り替えることをお勧めします。

多くのディストリビューションはすでにそのようなカーネルを提供していますが、代わりにディストリビューションカーネルを再コンパイルするか、kernel.orgカーネルに切り替える方がはるかに簡単です。手順は簡単です。カーネル開発とツールをインストールする必要があります(Debianバリアントでmake-kpkgは、非常に便利です)。カーネルをアップグレードするには、再起動する前に、新しいソースを入手し、カーネルを構成し(通常、現在の構成をベースとして使用します- make oldconfig)、新しいカーネルをビルドし、パッケージをインストールします。ほとんどの人は、ハードウェアをアップグレードするだけでディストリビューションカーネルを再コンパイルするよりも費用効果が高いと感じていますが、カーネルを再コンパイルするのは非常に簡単です。とにかくカーネルのアップグレードのために自動的に再起動することはないので、再起動する前に簡単な手順(単一のスクリプトを実行することによってトリガーされる)を追加することは私にとってそれほど労力ではありません。

于 2012-10-03T17:33:58.170 に答える