メインスレッドをブロック詳細ハンドラ4-ファイルディスクリプタ、looper.loop

ポインタハンドルの違い

 C ++は、ハンドルとガイドを知っている、と私は多くの人が直接私たちのためにハンドルとポインタの間で同一視見つけ、アドレスの両方を学ぶ、私はなど、それはまた、多くの人が計画を処理し、導くために生じたと思います直接の原因番号。

  まず、それはポインタは述べています。ハンドルとポインタであるこれらの単語を覚えて、私たちの事業のそれぞれの物理的な位置が全く同じである人気のあるものは、彼は我々が同じアドレス限り、操作を介してメモリにそれを指示することができ、数字のメモリであり、されているアドレス主な違いはあります。

  私はそれが扱うとしましょう、一般的にシステムリソースの場所を指し、アドレスであると言うことができます。しかし、これらのリソースの場所は、我々はすべてのウィンドウをサポートする仮想メモリ技術、いくつかのリソースが復帰しているそのうちのいくつかは、同じ時間内にメモリからスワップアウトされるかもしれないことを知って、実際には同じです、それは彼の記憶では、異なる時間に同じシステムリソースを言うことです物理的な場所は、ウィンドウには、この問題のハンドルを変更することで、つまり、処理リソースの物理的な場所を、この問題を解決する方法である、不確実です。ウィンドウは、テーブルの記憶領域に固定物理的な位置に対応して、テーブルは、リアルタイムのアドレス内のリソースのすべてを記録し、実際には、直接リソースの物理アドレスを処理するために指していませんでしたが、テーブルに対応する一つのポイントなので、どんなリソーススワップインとアウト、ハンドルは彼のリアルタイム位置で見つけることができます。

  一般に、プログラミングは関係なくできるように、ハンドルによってシステム内の詳細を遮蔽することができるオペレーティング・システムの実装の詳細。あなたがハンドルとポインタの違いを理解することができない場合は、ポインタへのポインタを想像し、することができ、ポインタへのポインタとして扱う理解します。

ファイル記述子fd(ハンドル)

    通常のファイル、ディレクトリ、ファイル、リンク、ファイル、およびデバイスファイル:すべてのファイルとして見ることができるのLinuxシステムでは、ファイルを分割することができます。ファイル記述子(ファイルディスクリプタ)は、すべてのI / O操作を実行、効率的に開いているファイルを参照するために使用される非正の整数(通常は小さい整数)、で作成した開かれているファイルを、管理するために、カーネルのインデックスですシステムコールは、ファイルディスクリプタです。

    Linuxシステムでは、デバイスは、文書の形である機器を操作するために存在する最初のファイルを開き、ファイルを開く必要があり、ファイル記述子を取得します、それは非常に小さい正の整数です。各プロセスは、ファイルディスクリプタテーブルのコピーにPCB(プロセス制御ブロック)に格納され、ファイルディスクリプタテーブルのインデックスであり、各エントリは、ファイルポインタを開くためのリンクを有しています。ファイルディスクリプタの利点:標準POSIX互換性のある、多くのLinuxおよびUNIXシステムコールは、それに依存しています。Wenjianmiaoshufuの短所:アップUNIX以外のシステムに移行し、また直感的ではありません。

パイプ

Linuxでは、スレッドは通知を完了することができ、POSIX条件+ミューテックスを使用していますが、比較的少ない、とこのようにlinux 配管のが、広範な使用は、2つのFD(writeFD​​、readFD)を作成しますスレッドは、スレッド1 2を目覚めしようとしたとき、あなたはデータを書き込むことができwriteFD過去に、このスレッドはでブロックreadFD 2に戻ることができるようになります。

私はパイプの理由を使用することを理解していなかった前に、突然理解したいです。ブロッキング方法は、FDを待っているの選択、投票とのepoll、上でLinuxを使用することであるため、その後、このようにFDを使用するには、メソッドを呼び出す統一することができます。

Linux上での方法をブロック

まず、我々はフローの概念を定義し、フローは、ファイル、ソケット、パイプ、などのことができるカーネルオブジェクトI / O操作することができます。

    ファイル、またはソケット、またはパイプかどうか、我々はストリームとしてそれらを見ることができます。

    その後、我々は、読み取りによってI / O操作を議論し、我々はストリームからデータを読み取ることができ、書き込みを渡す、私たちは、ストリームにデータを書き込むことができます。今、私たちは、ストリームからデータを読み取る必要がありますが、何のデータ・ストリームが存在しない、(典型的な例は、そのようなソケットから読み出されたデータとしてクライアントのために、サーバーはバックパスするデータがない)行うにはどのようにこの時間を場合を想定?

  • 閉塞性:障害物が何のコンセプトですか?たとえば、あなたは宅配便を待っているが、宅配便が来たとき、あなたは知っていないと、何もあなたが行うことができます(または宅配便を待つ次のことはやって来なかった)とき、次にあなたがスリープ状態に入ることができ、ので、あなたは(あなたを覚ますことができるようにと仮定)の呼び出しに送られた品物を持っている場合、あなたが知っている与える表現。

 

  • 忙しいポーリングをノンブロッキング:ポーリングの方法がビジー状態の場合、宅配便は、その後、上記の例は、その後、あなたは宅配便の電話番号を知っている必要があり、その後、彼は毎分の電話を掛け:「あなたはそこにまだ?」

 

明らかに、ほとんどの人が大幅に無脳、廃棄物の法案はその時間も宅配便の多くを取るていないされていないだけでなく、第二のアプローチを使用することはありません。

まず、経済的でシンプルな、経済的な手段は非常に少ないCPU時間を消費するので、スレッドがスリープ状態にならばほとんどのプログラムは、彼らが自分たちの中で分割するつもりはない、ディスパッチキューシステムの外に落ち、、、第二のアプローチを使用することはありませんCPU貴重な時間は今残しました。

バッファ

    ブロッキングが行われている方法を理解するために、我々はバッファだけでなく、議論するカーネルバッファを説明するために、そして最終的にI / Oイベントを。

あなたは、ストリームを操作する際に、バッファの導入は、それは相対的で、より多くのユニットとして動作するためのバッファである、頻繁にシステムコール(あなたはそれが遅いです知っている)を引き起こし、頻繁なI / O操作を削減することですユーザ空間のため。カーネルの場合は、だけでなく、我々はバッファを必要としています。

管側導管があると、書き込みプロセス、B側がパイプから読み出されます。

 

  1. ブロックされたで始まるカーネルバッファが空であると仮定し、Bは、正方形として読み出されます。最初のパイプは、その後に書き込まれ、カーネルバッファにより、この時間は、非空状態に空の状態になり、カーネルは、ウェイクアップイベントを生成します「のイベントを呼び出してみましょう、Bを告げる バッファが空ではありません 。」

 

  1. しかし、「 バッファが空ではない 」イベント通知Bの後に、Bは、まだデータを読んでいない、と場合は、データパイプラインは、この時間を失う書くことができないカーネルは、データがA書かれたカーネルバッファに残ると約束しましたまた、バッファカーネルがBはまだ、カーネルバッファは、最終的にいっぱいになりますデータを読み取るために開始されていない、フルである、I / Oイベントが生成されます。この時、プロセスAに語った、とあなたは(ブロッキング)する必要があり、我々はこのイベントを置きます「と定義され 、バッファがいっぱいです 。」

 

  1. その後、カーネルバッファが空になるようBは最終的に、データを読み始めたと仮定し、カーネルAを伝えるために、この時間は、カーネルバッファに空きがあり、あなたは、長い眠りから目覚めたデータを書き続け、そして私たちは、このイベントを置くことができますY1は「と呼ばれる バッファがいっぱいではありません 。」

 

  1. おそらく、イベントY1のAは、カーネルバッファが空になるまでデータを読み続け通知されているが、何のデータがAとBに書き込まれません。今回は、カーネルは、あなたが詰まらせる必要がある、Bに語りました!「と私たちはこの時間を呼び出し 、空のバッファ 。」

 

この場合は、空、バッファフルである4 4 I / Oイベントバッファをカバーして、バッファが空でない、バッファが(注いっぱいではありません。彼はカーネルバッファの言っているが、これは4期私の人生です作られた、のみ)原理を説明して作られています。4つのI / Oイベントは、基本的なブロッキングの同期です。

 

選択/世論調査

その後、我々は不利な点は、I / Oのをブロックしていると言います。ブロックI / Oモードでは、スレッドは、I / Oイベントストリーム処理することができます。あなたが複数のストリーム、またはマルチプロセス(フォーク)、またはマルチスレッド(pthread_createの)を処理したい場合は、残念ながら、これらの2つの方法の効率は高くありません。

 

    その後、再び考えるビジーポーリングにI / Oモードをノンブロッキング、我々は、複数のストリームを扱うことができることを見出した(1つのストリームは非ブロックモードにブロッキングモードから切り替えられた、本明細書でさらに議論されることはありません):

一方、  {
     ための Iストリームにおける[]。{
         場合 、私はデータを持っています
            使用不能になるまで読んで
    }
}

 

限り、我々は最初から最後まで流れのすべてを維持し、再び尋ねた、と最初からやり直して。あなたは、複数のストリームを処理することができますが、これは、すべてのストリームはデータがない場合ので、それからだけ無駄なCPU、明らかに良くありませんように。ここで私は、ブロッキングモードは、I / Oイベント処理のためのカーネルがブロックされていることを追加したり、目を覚ますと、他のオブジェクトへの非ブロックモードI / Oイベントを置く必要があります(選択し、以下に説明し、ファイルディスクリプタ)プロセス、あるいは直接無視。

 

    アイドルCPUを避けるために、プロキシを導入することができます(があった当初と呼ばれる選択薬を、そこと呼ばれる一つであるポール機関は、しかし、両方の本質は同じです)。代理店より強力な、それはI / Oイベント、彼の空き時間に、現在のスレッドがブロックアウトされます多くのストリームで同時に観察することができ、1つ以上のストリームは、I / Oイベントがある場合、ブロッキング状態から目覚めましたそう、私たちのプログラムは、再びすべてのストリームをポーリングします(ので、我々は「忙しい」という単語削除を置くことができます)。長い道のりをコード:

一方、  {
    (ストリーム[])を選択し
    以下のための ストリームにおけるIは、[] {
         場合 、私はデータを持っています
            使用不能になるまで読んで
    }
}

 

イベントが発生したI / Oが存在しないのであれば、私たちのプログラムは、選択の場所にブロックされます。まだ問題があるそこの選択から知っている。しかし、我々は、I / Oイベントが開催されましたが、知らなかったいくつかのストリーム(以上1、あるいはすべてがあるかもしれない)、我々は唯一の未分化ラウンドをできるということですデータを識別するために、すべてのストリームを行使し、それらが動作するためのデータのストリームに読み取りまたは書き込むことができます。

 

しかし、selectを使用して、我々はO(n)は、同時に処理大きな流れ、もはや無差別各ポーリング時間の有意差ポーリング複雑さを持っていません。ここでも適切に説明するのは、その最終的にできると述べたのepollをアップ。

epollを

epollイベント世論調査として理解することができ、ビジーポーリングと非差別の世論調査とは異なり、流れのepollは、I / Oイベントは、私たちが知っているどのように発生します。この時点で、私たちは、これらのストリームを操作する意味があります。(複雑さはO(1)に還元されます)

 

イベントのepollをサポート4つのタイプがあるEPOLLIN(読みやすいハンドル)、EPOLLOUT(書くことができますハンドル)、EPOLLERR(ハンドルエラー)、EPOLLHUP(壊れハンドル)。

 

ファイルディスクリプタの実装の詳細を説明する前に、ファイルディスクリプタに関連するアクションが最初にリストされています:

  • epoll_create)(ファイルディスクリプタオブジェクト一般epollfd = epoll_createを作成
  • epoll_ctl(epoll_add / epoll_delフィット)、特定のイベントのストリームを追加/削除することがepollオブジェクト
    • epoll_ctl(epollfd、EPOLL_CTL_ADD、ソケット、EPOLLIN); //レジスタイベントバッファに流入するデータがあることを、空ではありません
    • epoll_ctl(epollfd、EPOLL_CTL_DEL、ソケット、EPOLLOUT); //登録バッファは、ストリームに書き込むことができるイベントの完全ではありません
  • 登録イベントが発生するまでのイベントがepoll_wait(epollfd、...)待ちます

(注:ときに読み出され、完全なストリームバッファへの書き込みまたは空バッファノンブロッキング発生、戻りを読み取る-1 /書き込み、およびエラー番号= EAGAINバッファだけでなく、ファイルディスクリプタバッファにも関するセットが空と満杯イベントではありません。) 。

 

おそらく、双方向のepollモデルであるコードは次のとおりです。

一方、  {
    [] active_stream  =  イベントがepoll_wait(epollfd)
      {[] active_stream Iに
        読み取りまたは書き込みまで
    }
}

 

    紙面の都合上、私は唯一のepollの詳細を使用すると、男とGoogleを参照してくださいどのような原則、明らかにすることを、ずっとこれを言う、実装の詳細に、Linuxカーネルのソースを参照してください。

 

なぜ1.Androidメインスレッドで死のサイクルで立ち往生)(Looper.loopないのだろうか?

これは、プロセス/スレッドについての最初の話は、言ったねじ切り伴う:プロセスの各アプリはアプリのアクティビティ/サービスおよび他のコンポーネントで実行されているさまざまなを運ぶための受精卵でフォークをプロセス、プロセスを作成する前に、最初に実行されています。上位アプリケーション・プロセスのためのプログラムは、アプリケーションのAndroidのランタイム上で実行されるように、Googleの意図的である、完全に透過的です。プロセス属性、またはネイティブコードのフォーク・プロセスによって:ほとんどの場合、アプリケーションはAndroidManifest.xmlをしてアンドロイドを設定しない限り、一つのプロセスで実行されます。

スレッド:アプリケーションのためのスレッドは、それぞれの新しいスレッド()新しいスレッドが作成され始めると、非常に一般的です。スレッドとの間でリソースの共有は、Linuxのプロセスとリソースを共有し、そして本質的な差がないかどうかに加えて、スレッドの観点からtask_struct構造であり、アプリケーションを処理し、実行可能ファイルの断片以上のものは、CPUプロセスまたはスレッドに表示されませんコード、CFSを使用して、CPUスケジューリングアルゴリズムは、各タスクのタイムスライスがCPUを楽しんだことを確実にするために、できるだけ公正で

その準備では、無限ループの問題と言います:

それは、実行可能コードの一部、実行可能なコードの実行が完了したとき以来のスレッドでは、スレッドのライフサイクルが終了し、スレッドは終了となります。メインスレッドのように、我々はいくつかの時間のために実行されたくない、と彼らは終了することを、どのようにそれを生き残ることができたことを確認するために?単純なアプローチダウン実行された可能性があり、実行可能なコードであり、死のサイクルが出口を確保することができなくなり、例えば、バインダー糸は、無限ループの方法で、異なる駆動バインダーを読み書きするループ、もちろん、単なる何のニュースがないとき無限ループ、寝ます。それは死のサイクルとどのように他の事項とそれを対処することですので、しかし、ここで、それはよく、別の問題につながる可能性が?新しいスレッドを作成することによって。

本当にあまりにも長い間、アプリケーションが立ち往生発生しないフレーム、発生してもANR、自身looper.loopを落としにつながるためのコールバックメソッドのonCreate / ONSTART / onResumeおよびその他の操作は、操作のメインスレッドであるはまります。

 

主な情報源をActivityThread:

    パブリック 静的 ボイドメイン(文字列[]引数){
        ......(省略)

        Looper.prepareMainLooper();

        ActivityThreadスレッド = 新しいActivityThread();
        thread.attach();

        もし(sMainThreadHandler == NULL ){
            sMainThreadHandler = thread.getHandler()。
        }

        もし){
            。Looper.myLooper()setMessageLogging(新しい
                    LogPrinter(Log.DEBUG、 "ActivityThread" ));
        }

        // イベントActivityThreadMainの終わり。
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER)。
        Looper.loop(); //は、メインスレッドが永遠に実行されるように、サイクルが死んでしまいます

        スロー 新しいのRuntimeException(「予期せず終了し、メインスレッドループを」);
    }

 

 

この死のサイクルに関連するコードを実行するために、新しいスレッドを準備する場所2.は表示されませんでしたか?

実際には、無限ループに入る前に、コードのActivityThread.mainに新しいバインダースレッドを作成します()内:

パブリック 静的 ボイドメイン(文字列[]引数){
        ....

        // メッセージキュールーパーやオブジェクト、メインスレッドのためのメッセージ処理作成
        )(Looper.prepareMainLooperします。

        // ActivityThreadオブジェクトが作成 
        スレッド= ActivityThread 新新)(ActivityThreadを。

        // (新しいスレッドを作成)バインダーチャネルを確立 
        thread.attach(偽の);

        Looper.loop(); // 実行中のメッセージループ
        スロー 新しい新しいのRuntimeExceptionを(「メインループが突然スレッドを出ました」);
    }

 

thread.attach(偽)(具体的に送信されるシステム・サービス・イベント・AMSを受信するためのサーバApplicationThread、バインダーを指す)バインダースレッドを作成し、バインダーメッセージハンドラによってスレッドはメインスレッドに送信される、特定のプロセス表示するSTARTSERVICEプロセス分析を、ここでは進まないようにC / Sアーキテクチャを使用して、プロセス間通信のための単純バインダー、と言います。:バインダー興味の友人について、あなたは私はほとんど答えを知っている別の質問見ることができる
バインダーとして使用されるのはなぜAndroidのIPCメカニズムを?- Gityuan答え

また、ActivityThreadが実際にスレッドではない、HandlerThreadクラスとは異なり、ActivityThreadは本当にThreadクラスを継承しませんが、多くの場合、メインスレッドで実行し、スレッドに印象を与え、実際にActivityThreadメインスレッドを運ぶのプロセスは、受精卵フォークによって作成されます。

3.死のサイクルが実行されているメインスレッドは、特にCPUリソースを消費していないされていますか?

実際には、ここではそれがに来るLinuxのパイプ/のepollメカニズム、それは単にメインスレッドがニュースではないときで詳述するように、それは、()メソッドで)(queue.nextループでnativePollOnceをブロックしていますメッセージキューであるAndroidのメッセージングの1-ハンドラ(Javaの層)は、データを書き込むためのパイプの書き込み最後まで配管作業にメインスレッドを覚ますために、次のメッセージが到着するか、すべてのトランザクションが発生するまで、メインスレッドが休止状態にCPUリソースを解放します。ここで使用されるファイルディスクリプタ機構は、記述子は、プログラムはすぐに適切な読み取りを通知したり、書込み動作(読み取りまたは書き込みする準備ができて)準備ができている場合ディスクリプタが複数、同時に監視することができ、機構IO多重化され、同期性質I / O、すなわち、書き込みがブロックされています。だから、メインスレッドは休止している時間のほとんどは、CPUリソースを大量に消費しません。

 

4.Activityのライフサイクルは、in vitroで無限ループを実現する方法であるまで実行することができるのですか?

ActivityThread内部クラスHは、通信の過程において、メッセージハンドラ機構を介して、ハンドラからのスレッド間用単にハンドラ機構を継承しました。

アクティビティは、異なる尺度を使用してメッセージを受信した場合、メインスレッドLooper.loopのライフサイクルに依存している:
H.handleMessage(MSG)の方法では、受信された異なるMSGによれば、対応するライフサイクルを行います。

そのようなMSG = H.LAUNCH_ACTIVITYの領収書として、アクティビティインスタンスを作成し、自分のActivity.onCreate()メソッドなどを実行するために、最終的にリフレクションを介しActivityThread.handleLaunchActivity()メソッドと呼ばれ、
別の例では、受信MSG = H.PAUSE_ACTIVITY、次いで呼び出しActivityThread.handlePauseActivity()メソッドは、最終的にActivity.onPause()メソッドを実行します。上記のプロセスは、私は話すのコアロジックを選ぶ、プロセスは、これよりも本当に複雑です。

メインメッセージのスレッドおよび場所はそれですか?

もちろん、プロセス内の他のスレッドは、メインスレッドへのAppハンドラを経由して送る、以下のページを参照してください。

最後に、アプリケーションを実行するプロセスのあなたに描くより深く理解を通じてプロセスとスレッド間の通信の視点から:

system_serverプロセスはシステムプロセスである、そのようなここでApplicationThreadProxyを提供するなどのシステムサービス、多数の実行コアキャリアフレームのJavaフレームワークは、(AMSと呼ぶ)、ActivityManagerService(ATPと呼ばれる)、2つのサービスはsystem_serverプロセスの異なるスレッドで実行されています、 ATP及びAMSが基づいているので、IBinderインターフェースはバインダースレッド、スレッド作成及び破壊バインダーは、決定されたバインダーによって駆動されます。

アプリは、我々は多くの場合、そのアプリケーションを言う過程で、メインスレッドがこのスレッドで実行されるコンポーネントのアクティビティ/サービスおよびUIやその他の関連事業のライフサイクルを担当し、また、各アプリの処理中ApplicationThread少なくとも二つのバインダーのスレッドがあるだろう(と呼ばれますAT)とActivityManagerProxy(AMPと称する)、図形描画されたスレッドに加えて、多くのスレッドがあるそのうち、スレッドおよび他のそのような信号の捕捉は、それらをここにリストしません。

異なるプロセス間の通信のためのバインダー、例えば図のように、クライアントプロセスによってサーババインダー別のプロセスにトランザクションを送る24は、スレッドにトランザクションスレッドを送信し、及び同じプロセスの異なるスレッドの通信ハンドラ例えば、図4のスレッドは、メインスレッドにメッセージを送信します。

:図に関連した活動は、次のようにプロセスがあり、そのような一時停止の活動として、ライフサイクルについて話します

  1. AMSスレッド2を呼び出すスレッド1 ATP;(による共有資源のプロセス内のスレッド間に、直接お互いを呼び出すが、マルチスレッドの同時実行の問題に注意を払う必要がありますすることができます)
  2. スレッド2は、バインダー糸アプリ4による処理に伝達されます。
  3. メインスレッドのアクティビティーにメッセージを一時停止するためのメッセージハンドラスレッド機構によって4。
  4. メッセージ、メッセージを受信した一時停止のアクティビティをループでlooper.loop()でメインスレッドは、ActivityThread.H.handleMessage()メソッドにメッセージを入れ、その後、メソッドを呼び出した後、最終的にそれがActivity.onPauseに呼び出します。 onPause()が扱われている場合()、サイクルは、ループを継続する継続します。

 

おすすめ

転載: www.cnblogs.com/muouren/p/11706457.html