最近、インタビューでプロセスとスレッドの違いについて質問を受けました。本当に、私は答えを知りませんでした。ちょっと考えて、とても奇妙な答えをしました。
スレッドは同じメモリを共有しますが、プロセスは共有しません。これに答えた後、インタビュアーは私に邪悪な笑顔を与え、私に次の質問をした。
Q. プログラムが分割されるセグメントを知っていますか?
私の答え:うん(簡単だと思った)スタック、データ、コード、ヒープ
Q. つまり、教えてください:スレッドはどのセグメントを共有しますか?
私はこれに答えることができず、結局それらすべてを言うことになりました。
プロセスとスレッドの違いについて、誰でも正しい印象的な答えを提示できますか?
あなたはかなり正しいですが、スレッドはすべてのセグメントを共有します除くスタック。スレッドには独立した呼び出しスタックがありますが、他のスレッドスタックのメモリにはまだアクセス可能であり、理論的には他のスレッドのローカルスタックフレームにメモリへのポインタを保持できます(ただし、おそらく、そのメモリを配置するより良い場所を見つける必要があります!)。
Wikipedia (インタビュアーにとって本当に良い答えになると思う:P)
スレッドは、次の点で従来のマルチタスクオペレーティングシステムプロセスと異なります。
- 通常、プロセスは独立していますが、スレッドはプロセスのサブセットとして存在します
- プロセスはかなりの状態情報を保持しますが、プロセス内の複数のスレッドは状態やメモリやその他のリソースを共有します
- プロセスは個別のアドレス空間を持ちますが、スレッドはアドレス空間を共有します
- プロセスは、システムが提供するプロセス間通信メカニズムを介してのみ対話します。
- 同じプロセス内のスレッド間のコンテキスト切り替えは、通常、プロセス間のコンテキスト切り替えよりも高速です。
本当に指摘する必要があるのは、この質問には実際には2つの側面があるということです。理論的な側面と実装の側面です。
まず、理論的な側面を見てみましょう。プロセスとスレッドの違いとそれらの間で共有されるものを理解するには、プロセスが概念的に何であるかを理解する必要があります。
セクション2.2.2 モダンオペレーティングシステム3e のTanenbaumによるクラシカルスレッドモデルから、次のことがわかります。
プロセスモデルは、リソースのグループ化と実行という2つの独立した概念に基づいています。それらを分離することが有用な場合があります。ここがスレッドの出番です。
彼は続けます:
プロセスを見る1つの方法は、関連するリソースをグループ化する方法です。プロセスには、プログラムテキストとデータ、およびその他のリソースを含むアドレススペースがあります。これらのリソースには、開いているファイル、子プロセス、保留中のアラーム、シグナルハンドラ、アカウンティング情報などが含まれます。それらをプロセスの形でまとめることにより、より簡単に管理できます。プロセスが持つもう1つの概念は、実行のスレッドであり、通常は単にスレッドに短縮されます。スレッドには、次に実行する命令を追跡するプログラムカウンターがあります。現在の作業変数を保持するレジスタがあります。実行履歴を含むスタックがあり、呼び出されたがまだ返されていない手順ごとに1つのフレームがあります。スレッドは何らかのプロセスで実行する必要がありますが、スレッドとそのプロセスは異なる概念であり、別々に扱うことができます。プロセスは、リソースをグループ化するために使用されます。スレッドは、CPUでの実行がスケジュールされているエンティティです。
さらに下に、彼は次の表を提供します。
Per process items | Per thread items
------------------------------|-----------------
Address space | Program counter
Global variables | Registers
Open files | Stack
Child processes | State
Pending alarms |
Signals and signal handlers |
Accounting information |
上記は、スレッドが機能するために必要なものです。他の人が指摘したように、セグメントのようなものはOS依存の実装の詳細です。
OSの実装に完全に依存することをインタビュアーに伝えてください。
たとえば、Windows x86を取り上げます。 2セグメント[1]、コードおよびデータのみがあります。そして、どちらも2GB(リニア、ユーザー)アドレス空間全体にマッピングされます。 Base = 0、Limit = 2GB。それらは1つを作成しますが、x86ではセグメントを読み取り/書き込みと実行の両方にすることはできません。したがって、2つを作成し、CSがコード記述子を指すように設定し、残り(DS、ES、SSなど)がもう一方を指すようにします[2]。しかし、両方とも同じものを指し示しています!
あなたがインタビューした人は、彼/彼女が述べていないという隠された仮定をしていました、そして、それは引っ張る愚かなトリックです。
に関して
Q.セグメントスレッドの共有を教えてください。
少なくともWindowsでは、セグメントは質問とは無関係です。スレッドはアドレス空間全体を共有します。スタックセグメントSSは1つだけであり、DS、ES、およびCSが行うものとまったく同じものを指します[2]。つまり全血ユーザー空間。 0〜2GB。もちろん、それはスレッドが1つのスタックしかないことを意味しません。当然、それぞれに独自のスタックがありますが、x86セグメントはこの目的には使用されません。
多分* nixは何か違うことをします。知るか。質問の根拠となった前提は破られました。
ntsd notepad
から:cs=001b ss=0023 ds=0023 es=0023
一般に、スレッドは軽量プロセスと呼ばれます。メモリを3つのセクションに分割すると、コード、データ、スタックになります。すべてのプロセスには独自のコード、データ、およびスタックセクションがあり、このコンテキストにより、切り替え時間が少し長くなります。コンテキストの切り替え時間を短縮するために、人々はスレッドの概念を考え出しました。スレッドは他のスレッド/プロセスとデータおよびコードセグメントを共有し、独自のスタックセグメントを持っています。
プロセスには、コード、データ、ヒープ、およびスタックのセグメントがあります。現在、スレッドORスレッドの命令ポインター(IP)は、プロセスのコードセグメントを指します。データおよびヒープセグメントは、すべてのスレッドで共有されます。では、スタック領域についてはどうでしょうか?実際にスタック領域とは何ですか?スタックはヒープなどよりもはるかに高速に使用できるため、使用するスレッドのためだけにプロセスによって作成された領域です。プロセスのスタック領域はスレッド間で分割されます。つまり、3つのスレッドがある場合、プロセスのスタック領域は3つの部分に分割され、それぞれが3つのスレッドに割り当てられます。つまり、各スレッドが独自のスタックを持っていると言うと、そのスタックは実際には各スレッドに割り当てられたプロセススタック領域の一部です。スレッドの実行が終了すると、スレッドのスタックはプロセスによって回収されます。実際、プロセスのスタックがスレッド間で分割されるだけでなく、スレッドがSP、PC、状態レジスターのように使用するすべてのレジスターセットは、プロセスのレジスターです。したがって、共有に関しては、コード、データ、およびヒープ領域は共有されますが、スタック領域はスレッド間で分割されるだけです。
スレッドはコードとデータセグメント、およびヒープを共有しますが、スタックは共有しません。
スレッドはデータとコードを共有しますが、プロセスは共有しません。スタックは両方で共有されません。
プロセスは、たとえばFork()
の後など、メモリ、より正確にはコードを共有することもできますが、これは実装の詳細と(オペレーティングシステム)最適化です。複数のプロセスで共有されるコードは、コードへの最初の書き込みで(できれば)複製されます。これは copy-on-write として知られています。スレッドのコードの正確なセマンティクスについてはわかりませんが、共有コードを想定しています。
プロセススレッド スタックプライベートプライベート データプライベート共有 コードプライベート1 共有した2
1 コードはlogicallyprivateですが、パフォーマンス上の理由で共有される場合があります。 2 100%確信はありません。
スレッドはすべて [1]を共有します。プロセス全体に1つのアドレススペースがあります。
各スレッドには独自のスタックとレジスタがありますが、すべてのスレッドのスタックは共有アドレス空間に表示されます。
あるスレッドがスタックにオブジェクトを割り当て、アドレスを別のスレッドに送信すると、両方のスレッドがそのオブジェクトに同等にアクセスできます。
実際、私はより広範な問題に気付いただけです。Wordの2つの使用法segmentを混同していると思います。
実行可能ファイル(ELFなど)のファイル形式には、コンパイル済みコード(テキスト)、初期化データ、リンカーシンボル、デバッグ情報などを含むセグメントと呼ばれる個別のセクションがあります。ヒープまたはスタックセグメントはありませんここでは、これらはランタイム専用の構造であるためです。
これらのバイナリファイルセグメントは、異なるアクセス許可(たとえば、コード/テキストの読み取り専用実行可能ファイル、初期化されたデータのコピーオンライト非実行可能ファイル)を使用して、プロセスアドレス空間に個別にマッピングできます。
このアドレス空間の領域は、ヒープ割り当てやスレッドスタックなど、さまざまな目的で慣例により使用されます(言語ランタイムライブラリによって強制されます)。ただし、すべてメモリであり、仮想8086モードで実行している場合を除き、おそらくセグメント化されていません。各スレッドのスタックは、スレッド作成時に割り当てられたメモリの塊であり、現在のスタックトップアドレスがスタックポインターレジスタに格納され、各スレッドは他のレジスタとともに独自のスタックポインターを保持します。
[1] OK、わかっています:シグナルマスク、TSS/TSDなど。マップされたすべてのプログラムセグメントを含むアドレススペースは、まだ共有されています。
X86フレームワークでは、多くのセグメント(最大2 ^ 16-1)を分割できます。 ASMディレクティブSEGMENT/ENDSはこれを許可し、演算子SEGとOFFSETはセグメントレジスタの初期化を許可します。通常、CS:IPはローダーによって初期化されますが、DS、ES、SSの場合、アプリケーションは初期化を担当します。多くの環境では、.code、.data、.bss、.stackなどのいわゆる「単純化されたセグメント定義」を使用できます。また、「メモリモデル」(小、大、コンパクトなど)に応じて、ローダーはセグメントレジスタを初期化しますそれに応じて。通常、.data、.bss、.stack、およびその他の通常のセグメント(20年以来これを行っていないため、すべてを覚えていない)は、1つのグループにグループ化されます。これが、通常DS、ES、およびSSがtehを指す理由です同じ領域ですが、これは物事を単純化するためだけです。
一般に、すべてのセグメントレジスタは、実行時に異なる値を持つことができます。したがって、インタビューの質問は正しかった:CODE、DATA、およびSTACKのどれがスレッド間で共有されているか。ヒープ管理は別のものです-それは単にOSへの呼び出しのシーケンスです。しかし、組み込みシステムのようにOSがまったくない場合は、コードに新しい/削除を含めることができますか?
若者への私のアドバイス-良いアセンブリプログラミングの本を読んでください。大学のカリキュラムは、この点で非常に劣っているようです。
スレッドはヒープを共有します(スレッド固有のヒープに関する調査があります)が、現在の実装はヒープを共有します。 (そしてもちろんコード)
グローバルメモリに加えて、スレッドは他の多くの属性も共有します(つまり、これらの属性はスレッド固有ではなく、プロセスに対してグローバルです)。これらの属性には次のものが含まれます。
- プロセスIDと親プロセスID。
- プロセスグループIDとセッションID。
- 制御端末;
- プロセス資格情報(ユーザーおよびグループID);
- ファイル記述子を開く;
fcntl();
を使用して作成されたレコードロック- 信号の性質;
- ファイルシステム関連情報:umask、現在の作業ディレクトリ、およびルートディレクトリ。
- インターバルタイマー(
setitimer()
)およびPOSIXタイマー(timer_create()
);- System Vセマフォの取り消し(
semadj
)値(セクション47.8)。- リソース制限;
- 消費されるCPU時間(
times()
によって返される);- 消費されるリソース(
getrusage()
によって返される);そして- 素敵な値(
setpriority()
およびNice()
で設定)。スレッドごとに異なる属性には、次のものがあります。
- スレッドID(セクション29.5);
- シグナルマスク;
- スレッド固有のデータ(セクション31.3)。
- 代替信号スタック(
sigaltstack()
);- errno変数。
- 浮動小数点環境(
fenv(3)
を参照);- リアルタイムスケジューリングポリシーと優先度(セクション35.2および35.3)。
- CPUアフィニティ(Linux固有、セクション35.4で説明);
- 機能(Linux固有、第39章で説明);そして
- スタック(ローカル変数と関数呼び出しリンケージ情報)。
抜粋: Linuxプログラミングインターフェイス:LinuxおよびUNIXシステムプログラミングハンドブック、Michael Kerrisk 、619ページ
プロセスでは、すべてのスレッドがヒープメモリなどのシステムリソースを共有しますが、スレッドは独自のスタックを持ちます。
したがって、ansは、プロセスですべてのスレッドが共有するヒープメモリである必要があります。