[プロセスとスレッド] 学習ノート (1) - プロセスとスレッドの概要といくつかの API の仕上げ

プロセスとスレッド

スレッドを作成

ヘッドファイル#include <Windows.h>

CreateThread()これは、Windows システム API でスレッドを作成するために使用される関数であり、その定義は次のとおりです。

HANDLE CreateThread(
  LPSECURITY_ATTRIBUTES   lpThreadAttributes,
  SIZE_T                  dwStackSize,
  LPTHREAD_START_ROUTINE  lpStartAddress,
  LPVOID                  lpParameter,
  DWORD                   dwCreationFlags,
  LPDWORD                 lpThreadId
);

以下は、各パラメータの詳細な説明です。

  1. lpThreadAttributes: スレッド オブジェクトのセキュリティ属性を指定するために使用されるスレッド オブジェクトのセキュリティ記述子で、通常は NULL に設定されます。

  2. dwStackSize: スレッドのスタック サイズ (バイト単位)。0 の場合、デフォルトのスタック サイズが使用されます。

  3. lpStartAddress: スレッドのエントリ関数、つまり、スレッドの開始後に実行される関数ポインタ。この関数は、戻り値もパラメータも持たない関数か、DWORD の戻り値と LPVOID 型のパラメータを持つ関数である必要があります。

    DWORDあれはtypedef unsigned long

    PVOIDあれはtypedef void*

    LPVOIDあれはtypedef void far

  4. lpParameter: スレッド関数に渡されるパラメーターへのポインター。パラメータを渡す必要がない場合は、NULL に設定します。

  5. dwCreationFlags: 保留中のスレッドを作成するかどうか、親プロセスの優先度を引き継ぐかどうかなど、スレッドの作成フラグを指定します。

    再開スレッドがすぐに実行されず、CREATE_SUSPENDED としてマークされている場合、スレッドは呼び出しまで中断されます。

  6. lpThreadId: 新しいスレッドの識別子を受け取るために使用される変数を指します。スレッド識別子を取得する必要がない場合は、NULL に設定します。

CreateThread() 関数によって作成されたスレッドは同じプロセスで実行され、これらのスレッドはメモリ空間やグローバル変数などのプロセスのリソースを共有することに注意してください。異なるプロセス間でスレッドを作成する必要がある場合は、プロセス間通信テクノロジなどの他のテクノロジを使用する必要があります。

スレッドを終了する

1. スレッド関数は以下を返します。

これは、スレッドのすべてのリソースが適切にクリアされるようにする唯一の方法です。スレッド関数が戻ると、次のことが起こります。

スレッド関数で作成されたすべての C++ オブジェクトは、デストラクタを介して適切に元に戻されます

オペレーティング システムは、スレッドのスタックが使用するメモリを正しく解放します。

システムは、スレッド関数の戻り値としてスレッドの終了コードを設定します

システムは、スレッド カーネル オブジェクトの参照カウントをデクリメントします。

2.ExitTread 関数:

スレッド内で ExitThread 関数を呼び出すことで、自分のスレッドの実行を強制的に終了させることができます。プロトタイプは次のとおりです。

VOID ExitThread(DWORD dwExitCode);

この関数は、独自のスレッドの実行を終了し、オペレーティング システムがスレッドによって使用されているすべてのオペレーティング システム リソースをクリアします。ただし、C++ リソース (C++ オブジェクトなど) は適切に割り当て解除されません。

スレッドの中断と再開

スレッドへのハンドルがある限り、どのスレッドでも SuspendTread() を呼び出して別のスレッドの実行を一時停止できます。

プロトタイプは次のとおりです。

DWORD SuspendThread(HANDLE hThread);

戻り値は前回のサスペンド回数で、スレッドをサスペンドできる最大回数は MAXIMUM SUSPEND COUNT

パラメータ HANDLE hThread は、中断するスレッドを示します

ResumeThread を呼び出すと、中断されたスレッドを再開できます。プロトタイプは次のとおりです。

DWORD ResumeThread(HANDLE hThread);

戻り値は前回の中断回数で、パラメータ HANDLE hThread は再開するスレッドを示します

## スレッドの優先順位

BOOL SetThreadPrioroty //设置线程优先级
    (
    	HANDLE hThread,  //线程的句柄
    	int nPriority    //线程优先级级别
	);
スレッド優先度クラス 記号 優先値
1 Idle(最低) THREAD_PRIORITY_IDLE プロセスの優先度がリアルタイムの場合は 16 に調整し、
それ以外の場合は 1 にします。
2 最低最低 THREAD_PRIORITY_LOWEST -2 (元ベースで -2)
3 BELOW 基準以下 THREAD_PRIORITY_BELOW -1 (元のベースでは -1)
4 ノーマルスタンダード THREAD_PRIORITY_NORMAL 変更なし (プロセスの優先度値を取る)
5 ABOVE 基準より上 THREAD_PRIORITY_ABOVE +1 (元のベースで +1)
6 HIGHEST 高 THREAD_PRIORITY_HIGHEST +2 (元のベースで +2)
7 クリティカル最高 THREAD_PRIORITY_CRITICAL プロセスの優先度がリアルタイムの場合は 31 に調整し、
それ以外の場合は 15 に調整します。

スレッド間の同期

WaitForSingleObject(h, INFINITE);待機スレッド h INFINITE は無限待機を意味します

Windows プラットフォームでは、C++ でスレッド間同期を行う方法がいくつかあります。

  1. Mutex: Mutex は一般的に使用されるスレッド同期メカニズムであり、同時に 1 つのスレッドのみが共有リソースにアクセスすることを保証できます。Windows プラットフォームでは、CreateMutex()関数をミューテックスを作成し、WaitForSingleObject()関数とReleaseMutex()関数を使用してミューテックスをロックおよびロック解除できます。
  2. セマフォ (Semaphore): セマフォは、マルチスレッドの相互排除と同期を制御するためのメカニズムであり、共有リソースに同時にアクセスするスレッドの数を制御できます。Windows プラットフォームでは、CreateSemaphore()関数セマフォを作成し、WaitForSingleObject()関数とReleaseSemaphore()関数を使用してセマフォをロックおよびロック解除できます。
  3. イベント (イベント): イベントは、スレッド間通信および同期のメカニズムであり、1 つのスレッドが別のスレッドの操作の完了を待機できるようにします。Windows プラットフォームでは、CreateEvent()関数をイベントを作成し、WaitForSingleObject()関数とSetEvent()関数をイベントを待機および設定できます。
  4. クリティカル セクション: クリティカル セクションは、1 つのスレッドのみが同時に共有リソースにアクセスすることを保証する軽量の同期メカニズムです。Windows プラットフォームでは、InitializeCriticalSection()関数とEnterCriticalSection()関数とLeaveCriticalSection()関数を使用してクリティカル セクションをロックおよびロック解除できます。
  5. 条件変数 (Condition Variable): 条件変数は、1 つまたは複数のスレッドが特定の条件が真になるまで待機できるスレッド間同期のメカニズムです。Windows プラットフォームでは、CONDITION_VARIABLE型。

アトミックロック

変数の足し算と引き算

LONG InterlockedIncrement(LONG volatile* IpAddend);//该函数提供多线程情况下,对一个变量以原子操作方式增加1
LONG InterlockedDecrement(LONG volatile* IpAddend);//该函数提供多线程情况下,对一个变量以原子操作方式减少1
LONG InterlockedExchange(LONG volatile* ipTarget,LONG IValue);//该函数提供多线程情况下,以原子操作方式用IValue给IpTarget指向的目标变量赋值,并返回赋值以前的IpTarget指向的值。
LONG InterlockedEXchangeAdd(LONG volatile* IpAddend,LONG IValue);//该函数提供多线程情况下,以原子操作方式将IpAddend指向的变量增加Value。并返回调用前的IpAddend指向的目标变量的值

クリティカル セクション

クリティカル セクションは、ポイントする前に一部の共有データへの排他的アクセスを必要とするコードの連続領域です。プロセス内のすべてのスレッドでこれらの共有データにアクセスするコードをクリティカル セクションに配置すると、共有データへの同期アクセスを実現できます。クリティカル セクションは、単一プロセス内のスレッドを同期するためにのみ使用できます。

CRITICAL_SECTION g_sec;			  //实例化临界区对象
initializeCriticalSection(&g_sec);//初始化临界区对象

EnterCriticalSection(&g_sec); //进入临界区
//临界区操作
LeaveCriticalSection(&g_sec); //离开临界区

DeleteCriticalSection(&g_sec); //释放临界区对象

クリティカル セクションに入ると、クリティカル セクション オブジェクトの参照カウントが 1 増加し、同じスレッドが複数回呼び出すことができますがEnterCriticalSection、n 回呼び出されると

EnterCriticalSection今後、LeaveCriticalSectionクリティカルセクションオブジェクトの参照回数を0にするためにn回呼び出さなければならない

他のスレッドのみがクリティカル セクションに入ることができますEnterCriticalSection(&g_sec);

クリティカルセクションを残すLeaveCriticalSection(&g_sec);

クリティカル セクション オブジェクトを解放するDeleteCriticalSection(&g_sec);

#include<iostream>
#include<windows.h>

using namespace std;
unsigned int g = 0;
int n = 1000000;

//创建临界区对象
CRITICAL_SECTION g_sec;

DWORD WINAPI ThreadProc(LPVOID lp)
{
	for (int i = 1; i <= n; i++)
	{
		EnterCriticalSection(&g_sec); //进入临界区
		g++;
		LeaveCriticalSection(&g_sec); //离开临界区
	}

	return 0;
}


int main() 
{
	//初始化临界区
	InitializeCriticalSection(&g_sec);

	HANDLE h = CreateThread(NULL,0,ThreadProc,0, 0,0);

	for (int i = 1; i <= n; i++)
	{
		EnterCriticalSection(&g_sec);
		g++;
		LeaveCriticalSection(&g_sec);
	}

	WaitForSingleObject(h, INFINITE);
	cout << "g=" << g << endl;

	CloseHandle(h);  //关闭句柄
    DeleteCriticalSection(&g_sec); //释放临界区对象

	return 0;
}

待機スレッド機能:

マルチスレッドでは、他の処理を続行する前に、特定のスレッドが完了するまで待機したい場合があります.これを実現するには、Windows API 関数、WaitForSingleObjectまたはWaitForMultipleObjects. どちらの関数も、オブジェクトがシグナル状態としてマークされるのを待ってから戻ります。これら 2 つの関数の利点は、待機プロセス中に非常に効率的なスリープ状態に入り、CPU タイム スライスをほとんど占有しないことです。

WaitForSingleObject()

WaitForSingleObject()

1.フォーマット

DWORD WaitForSingleObject(HANDLE hHandle,DWORD dwMilliseconds);THandle と Timeout (ミリ秒単位) という 2 つのパラメータがあります。

スレッドを待機する場合は、スレッドのハンドルと対応するタイムアウト時間を指定する必要があります。もちろん、無期限に待機する場合は、Timeout パラメータでシステム定数 INFINITE を指定できます。

2.オブジェクトを使用する

イベント、ミューテックス、セマフォ、プロセス、スレッド

3. リターンタイプ

WAIT OBJECT 0 は、待機中のオブジェクトにシグナルがあることを意味します (スレッドの場合、実行が終了したことを意味します); WAIT TIMEOUT は、待機時間中にオブジェクトにシグナルが送信されなかったことを意味します (スレッドは実行を終了していません)。

WAIT ABANDONED は、オブジェクトにシグナルがあるが、通常はロックが取得されていないか、その他の理由で、まだ実行できないことを示します。

WaitForMultipleObject()

DWORD WaitForMultipleObjects(DWORD nCount,CONST HANDLE *IpHandles,

BOOLfWaitAll,DWORDdwMilliseconds);

4 つのパラメータは次のとおりです。

1. nCount、DWORD タイプ、ハンドル配列の数を指定するために使用

2. IphObjects、ポインタ型; ハンドル配列のメモリ アドレスを指定するために使用されます

3. fWaitAll、ブール型、True は、指定されたハンドルを持つすべてのオブジェクトがシグナルを受信するまで関数が待機することを意味します。

4. 待機中のタイムアウト時間をミリ秒単位で指定するために使用される DWORD タイプの dwTimeout は、INFINITE にすることができます。

bWaitAll パラメーターが false に設定されている場合、WaitForMultipleObjects が複数のカーネル オブジェクトを待機する場合。その戻り値から WAIT OBJECT 0 を差し引いた値が、パラメーター IpHandles 配列のシリアル番号です。複数のカーネル オブジェクトが同時にトリガーされた場合、この関数によって返される知識は、シリアル番号が最も小さいものです。

TRUE の場合、続行する前にすべてのセマフォが有効になるまで待機します。(FALSE は、セマフォの 1 つが有効な場合に下向きに実行されます)

カーネル オブジェクト

クリティカル セクションは、同じプロセス内でシリアル化された方法で共有データにアクセスする場合に最適です。ただし、スレッドを他のスレッドによって実行される特定の操作と同期させたい場合は、カーネル オブジェクトを使用してスレッドを同期させる必要があります。

一般的に使用されるカーネル オブジェクトには、ミューテックス変数、セマフォ、およびイベントが含まれ、その他には、ファイル、コンソール入力、ファイル変更通知、および待機可能なタイマーが含まれます。

各コアは常に、シグナル状態と非シグナル状態の 2 つの状態のいずれかになります。

スレッドが 1 つまたは複数のカーネル オブジェクトを待機しているときに、待機中のカーネル オブジェクトの 1 つまたは複数が非シグナル状態にある場合、待機中のカーネル オブジェクトがシグナル状態になるまで、スレッド自体がシステムによって中断されます。スレッドは実行を再開しません。一般的に使用される 2 つの待機関数があります。

ミューテックス

ミューテックスはクリティカル セクションに似ていますが、複数のプロセス間でデータ アクセスを同期できます。

CreateMutex (NULL,FALSE,"'MutexForSubThread');  //创建互斥变量

WaitForSingleObject (g_hMutex,INFINITE); //ミューテックスを待ち、ミューテックスがシグナル状態の場合、関数は戻り、同期ミューテックスは自動的に非シグナル状態になります

共有データ領域へのアクセス

ReleaseMutex(g hMutex); //ミューテックスを再シグナル化する

#include<iostream>
#include<windows.h>

using namespace std;
unsigned int g = 0;
int n = 100000;

//互斥量
HANDLE hmutex;


DWORD WINAPI ThreadProc(LPVOID lp)
{
	for (int i = 1; i <= n; i++)
	{
		WaitForSingleObject(hmutex, INFINITE); //等待互斥量变为有信号
		g++;
		ReleaseMutex(hmutex); //释放互斥量
	}

	return 0;
}


int main() 
{
	//创建互斥变量
	hmutex = CreateMutex(NULL, FALSE, NULL); //创建互斥变量

	HANDLE h = CreateThread(NULL,0,ThreadProc,0, 0,0);

	for (int i = 1; i <= n; i++)
	{
		WaitForSingleObject(hmutex, INFINITE);
		g++;
		ReleaseMutex(hmutex);
	}

	WaitForSingleObject(h, INFINITE);
	cout << "g=" << g << endl;

	CloseHandle(hmutex); //释放句柄

	return 0;
}

注: WaitForSingleObject();ReleaseMutex(); は時間がかかります. g を 1000000 レベルに上げると、永遠に待機することになります.

信号量

The semaphore kernel object is used for resource count. スレッドが WaitForSingleObject() 関数を呼び出してセマフォ オブジェクトのハンドルを渡すたびに、システムはセマフォのリソース カウントが 0 より大きいかどうかをチェックします。

If it is greater than 0, it means that there are a resources available. この時点で、システムはリソース カウントから 1 を減算し、スレッドを起動します。

If it is equal to 0, it means that no resources are available, and the system will suspend the thread until another thread release the object. セマフォの解放は、そのリソース カウントを増やすことを意味します。

クリティカル セクションやミューテックスとは異なり、セマフォはどのスレッドにも属しません。したがって、あるスレッドでセマフォ数を増やし、別のスレッドでセマフォ数を減らすことができます。

ただし、使用プロセスでは、セマフォの使用はミューテックスの使用と非常に似ており、ミューテックスはセマフォの特別なバージョンと見なすことができます。つまり、ミューテックスは最大リソース数が 1 のセマフォと見なすことができます。

セマフォは、M スレッドが N 個の共有リソースにアクセスする次の状況で一般的に使用されます (M > N)。

CreateSemaphore(NULL,iCount iCount, _T("Semaphore")); //创建信号量
WaitForSingleObject(m hSemaphore,INFINITE); //等待信号量
ReleaseSemaphore(m hSemaphore, 1, NULL); //释放信号量
イベント オブジェクト

共有データへのアクセスを制御するために使用されるミューテックスやセマフォとは異なり、イベントは操作が完了したことを通知します。

イベント オブジェクトには、手動リセット イベントと自動リセット イベントの 2 種類があります。

手動リセット イベントは複数のスレッドに同時に通知するために使用され、自動リセット イベントは 1 つのスレッドに通知するために使用されます。

複数のスレッドが WaitForSingleObjects() または WaitForMultipleObjects() を呼び出して自動リセット イベントを待機する場合、自動リセット イベントがシグナル状態になると、スレッドの 1 つが起動され、起動されたスレッドは引き続き実行されます。自動リセット イベントは再び非シグナル状態に設定され、他のスレッドはまだ中断状態のままです。この観点から、自動リセット イベントはミューテックスに多少似ています。

手動リセット イベントは、WaitForSingleObjects() または WaitForMultipleObjects() によって自動的に非シグナル状態にリセットされることはなく、手動リセット イベントを非シグナル状態にリセットするには、対応する関数を呼び出す必要があります。したがって、手動リセット イベントが通知されると、そのイベントを待機しているすべてのスレッドがアクティブになります。

たとえば、プロセスまたはスレッド オブジェクトの場合、待機が成功した場合、それはプロセスまたはスレッドの実行が終了したことを意味し、mutex オブジェクトの場合、このオブジェクトが現在他のスレッドによって所有されていないことを意味し、待機が一度終了したことを意味します。が成功した場合、現在のスレッドがミューテックスを所有し、他のスレッドがミューテックスを同時に所有することはできず、ReleaseMutex 関数を直接呼び出して積極的にミューテックスを解放します。

CreateEvent() 関数を使用して、イベント オブジェクトを作成します。

HANDLE CreateEvent(
  LPSECURITY_ATTRIBUTES lpEventAttributes,  // 事件对象的安全描述符
  BOOL                  bManualReset,       // 是否为手动重置事件
  BOOL                  bInitialState,      // 初始状态(有信号或无信号)
  LPCTSTR               lpName              // 事件对象的名称
);

以下は、各パラメータの詳細な説明です。

  1. lpEventAttributes: イベント オブジェクトのセキュリティ記述子を設定するために使用されるSECURITY_ATTRIBUTES構造体。yes の場合NULL、デフォルトのセキュリティ記述子が使用されます。
  2. bManualReset: イベント オブジェクトのリセット方法を指定します。である場合はTRUE、イベント オブジェクトが手動でリセットされることを意味し、イベントをリセットするにはResetEvent()関数FALSEそうである場合は、イベント オブジェクトが自動的にリセットされ、スレッドがイベントを待機するときに、イベントは自動的に無信号状態にリセットされます。
  3. bInitialState: イベント オブジェクトの初期状態を指定します。である場合はTRUE、イベント オブジェクトが最初にシグナル状態にあることを意味します。つまり、SetEvent()関数FALSEイベント オブジェクトはシグナル状態になります。シグナル状態。
  4. lpName: イベント オブジェクトの名前を指定します。これは、プロセスまたはシステム内でイベント オブジェクトを一意に識別するために使用できます。yes の場合NULL、イベント オブジェクトに名前がなく、プロセス内でのみ使用できることを意味します。パラメータ IpName は、イベント オブジェクトの名前を指定します。これにより、他のプロセスのスレッドが CreateEvent() または OpenEvent() 関数を呼び出して、イベント オブジェクトのハンドルを取得できます。

SetEvent() 関数でシグナル状態に設定します。

注: イベント オブジェクトが自動的にリセットされるか手動でリセットされるかに関係なく、シグナル状態に設定できます BOOL SetEvent(HANDLE hEvent);

ResetEvent 関数を使用して無信号状態に設定します。

注: イベント オブジェクトが自動または手動でリセットされるかに関係なく、BOOL ResetEvent(HANDLE hEvent); を実行できます。

ただし、自動リセット イベントに対して ResetEvent を実行する必要はないため、WaitForSingleObject() または WaitForMultipleObjects() が戻る前に、システムは自動的にイベント オブジェクトを非シグナル状態に設定します。

CloseHandle() 関数でイベントを閉じる

#include<iostream>
#include<windows.h>

using namespace std;
unsigned int g = 0;
int n = 100000;

//事件
HANDLE hEvent;

DWORD WINAPI ThreadProc(LPVOID lp)
{
	for (int i = 1; i <= n; i++)
	{
		WaitForSingleObject(hEvent, INFINITE);//判断事件是否已完成,如果已完成设置为未完成
		g++;
		SetEvent(hEvent);//重新设置为已完成
	}

	return 0;
}

int main() 
{
	//创建事件对象
	hEvent = CreateEvent(NULL, FALSE, TRUE, NULL);//第三为TRUE表示初始化为已完成

	HANDLE h = CreateThread(NULL,0,ThreadProc,0, 0,0);

	for (int i = 1; i <= n; i++)
	{
		WaitForSingleObject(hEvent, INFINITE);
		g++;
		SetEvent(hEvent);
	}

	WaitForSingleObject(h, INFINITE);
	cout << "g=" << g << endl;

	CloseHandle(hEvent); //关闭事件

	return 0;
}

スレッドのデッドロック

2 つのスレッドがある場合、1 つのスレッドが最初にオブジェクト 1 をロックし、次にオブジェクト 2 をロックしようとします。別のスレッドが最初にオブジェクト 2 をロックし、次にオブジェクト 1 をロックすることが起こります。このプロセスでは、スレッド 1 がオブジェクト 1 をロックすると、オブジェクト 2 をロックしようとしますが、残念ながら、スレッド 2 は既にオブジェクト 2 をロックしており、オブジェクト 1 をロックしようとしています。メソッド、スレッド 2 も両方のオブジェクトをロックしてメソッドを実行すると説明されていますが、誰もロックされたオブジェクトを手放そうとしないため、結果が得られず、この状況はスレッド デッドロックと呼ばれます。複数のスレッドが互いに所有するリソースを待機すると、デッドロックが発生する可能性があります。

スレッド間通信

1. 通信にグローバル変数を使用する

2. パラメータ転送方法

3. メッセージ配信方法

パラメーターの受け渡し:

#include<iostream>
#include<windows.h>

using namespace std;
unsigned int g = 0;
int n = 100000;

struct node
{
	int age;
	int id;
};

//事件
HANDLE hEvent;

DWORD WINAPI ThreadProc(LPVOID lp)
{
	node n = *(node*)lp;
	for (int i = 1; i <= n; i++)
	{
		WaitForSingleObject(hEvent, INFINITE);
		g++;
		SetEvent(hEvent);
	}

	return 0;
}

int main() 
{
	node n;
	n.id = 1, n.age = 10;
	//创建事件对象
	hEvent = CreateEvent(NULL, FALSE, TRUE, NULL);

	HANDLE h = CreateThread(NULL, 0, ThreadProc, &n, 0, 0);

	for (int i = 1; i <= n; i++)
	{
		WaitForSingleObject(hEvent, INFINITE);
		g++;
		SetEvent(hEvent);
	}

	WaitForSingleObject(h, INFINITE);
	cout << "g=" << g << endl;

	CloseHandle(hEvent); //关闭事件

	return 0;
}

プロセス

プロセス作成

bool CreateProcess() は bool を返し、作成が成功したか失敗したかを示します

bool CreateProcess(
LPCWSTR               lpApplicationName,     // 可执行文件的文件名或路径
LPWSTR                lpCommandLine,         // 命令行参数
LPSECURITY_ATTRIBUTES lpProcessAttributes,   // 进程的安全属性
LPSECURITY_ATTRIBUTES lpThreadAttributes,    // 线程的安全属性
BOOL                  bInheritHandles,       // 是否继承父进程的句柄
DWORD                 dwCreationFlags,       // 指定如何创建进程
LPVOID                lpEnvironment,         // 指定进程的环境变量
LPCWSTR               lpCurrentDirectory,    // 指定进程的当前工作目录
LPSTARTUPINFOW        lpStartupInfo,         // 指向STARTUPINFO结构体的指针
LPPROCESS_INFORMATION lpProcessInformation   // 指向PROCESS_INFORMATION结构体的指针
);
  1. lpApplicationName: 実行する実行可能ファイルのファイル名またはパス。NULL の場合、lpCommandLine の最初のパラメーターを実行可能ファイル名として使用します。
  2. lpCommandLine: コマンド ライン パラメーター。lpApplicationName が NULL の場合、このパラメーターには実行可能ファイル名とパラメーターが含まれている必要があります。lpApplicationName が NULL でない場合、このパラメーターには、実行可能ファイル名、コマンド ライン引数、およびその他のパラメーターを含めることができます。
  3. lpProcessAttributes: プロセスのセキュリティ属性。NULL の場合、プロセスはセキュリティ属性を継承しません。
  4. lpThreadAttributes: スレッド セキュリティ属性。NULL の場合、スレッドはセキュリティ属性を継承しません。
  5. bInheritHandles: 親プロセスのハンドルを継承するかどうかを指定します。TRUE の場合、子プロセスは親プロセスのハンドルを継承します。FALSE の場合、子プロセスは親プロセスからハンドルを継承しません。
  6. dwCreationFlags: プロセスの作成方法を指定します。たとえば、CREATE_NEW_CONSOLE を指定して、新しいコンソール ウィンドウを作成できます。
  7. lpEnvironment: プロセスの環境変数を指定します。NULL の場合、親プロセスの環境変数が使用されます。
  8. lpCurrentDirectory: プロセスの現在の作業ディレクトリを指定します。NULL の場合、親プロセスの現在の作業ディレクトリが使用されます。
  9. lpStartupInfo: コンソール ウィンドウのサイズや位置など、作成するプロセスに関する情報を含む STARTUPINFO 構造体へのポインター。
  10. lpProcessInformation: プロセス ハンドルやプロセス ID など、新しいプロセスに関する情報を含む PROCESS_INFORMATION 構造体へのポインター。

コンソール ウィンドウ STARTUPINFO:

typedef struct _STARTUPINFOW { 
    DWORD   cb;                 // 结构体的大小,以字节为单位 
    LPWSTR  lpReserved;         // 保留字段,必须设置为NULL 
    LPWSTR  lpDesktop;          // 指定要在其中启动新进程的窗口站(Window Station)和桌面(Desktop) 
    LPWSTR  lpTitle;            // 指定新进程的标题栏文本 
    DWORD   dwX;                // 指定新进程的初始x坐标 
    DWORD   dwY;                // 指定新进程的初始y坐标 
    DWORD   dwXSize;            // 指定新进程的初始宽度(以像素为单位) 
    DWORD   dwYSize;            // 指定新进程的初始高度(以像素为单位) 
    DWORD   dwXCountChars;      // 指定新进程的初始控制台屏幕缓冲区的宽度(以字符为单位) 
    DWORD   dwYCountChars;      // 指定新进程的初始控制台屏幕缓冲区的高度(以字符为单位) 
    DWORD   dwFillAttribute;    // 指定新进程的初始控制台屏幕缓冲区的填充字符和颜色 
    DWORD   dwFlags;            // 指定STARTUPINFO结构体的标志 
    WORD    wShowWindow;        // 指定新进程的初始显示状态 
    WORD    cbReserved2;        // 保留字段,必须设置为0 
    LPBYTE  lpReserved2;        // 保留字段,必须设置为NULL 
    HANDLE  hStdInput;          // 指定新进程的标准输入句柄 
    HANDLE  hStdOutput;         // 指定新进程的标准输出句柄 
    HANDLE  hStdError;          // 指定新进程的标准错误句柄 
} STARTUPINFOW, *LPSTARTUPINFOW;

ノート:

  1. 構造体のサイズ (バイト単位) は、sizeof(STARTUPINFOW) に設定する必要があります。
  2. lpReserved および lpReserved2 メンバーは NULL に設定する必要があります。
  3. lpDesktop メンバーは、新しいプロセスを開始するウィンドウ ステーション (Window Station) とデスクトップ (Desktop) を指定します。このメンバーが NULL の場合、新しいプロセスは現在のプロセスと同じウィンドウ ステーションとデスクトップで実行されます。
  4. dwX、dwY、dwXSize、および dwYSize メンバーは、新しいプロセスのウィンドウの位置とサイズを指定するために使用されます。
  5. dwXCountChars および dwYCountChars メンバーは、新しいプロセスのコンソール画面バッファーの幅と高さを指定するために使用されます。
  6. dwFillAttribute メンバーは、新しいプロセスのコンソール画面バッファーの塗りつぶし文字と色を指定するために使用されます。
  7. dwFlags メンバーは、STARTF_USEPOSITION、STARTF_USESIZE などの STARTUPINFO 構造体のフラグを指定するために使用されます。
  8. wShowWindow メンバーは、SW_HIDE、SW_SHOWNORMAL など、新しいプロセスの初期表示状態を指定するために使用されます。
  9. hStdInput、hStdOutput、および hStdError メンバーは、新しいプロセスの標準入力、出力、およびエラー ハンドルを指定するために使用されます。これらのメンバーが NULL の場合、新しいプロセスは親プロセスのハンドルを継承します。

プロセス情報構造: PROCESS_INFORMATION:

typedef struct _PROCESS_INFORMATION {
    HANDLE hProcess;	//表示进程句柄,用于标识一个进程对象。
    HANDLE hThread;		//表示线程句柄,用于标识一个线程对象。
    DWORD dwProcessId;	//表示进程ID,是一个唯一标识一个进程的数字。
    DWORD dwThreadId;	//表示线程ID,是一个唯一标识一个线程的数字。
} PROCESS_INFORMATION, *PPROCESS_INFORMATION, *LPPROCESS_INFORMATION;

プロセス コードの例を作成します。Firefox ブラウザーを開き、Bilibili を開きます。

#include<iostream>
#include<windows.h>

using namespace std;

int main() 
{
	TCHAR commandLine[] = TEXT("E:\\Mozilla Firefox\\firefox.exe https://www.bilibili.com/");
	_STARTUPINFOW startinfo = { sizeof(_STARTUPINFOW) };
	_PROCESS_INFORMATION processInfo;

	//创建进程
	bool ret = CreateProcess(
		NULL,				//不指定可执行文件的文件名
		commandLine,		//命令行参数
		NULL,				//默认进程安全性
		NULL,				//默认线程安全性
		FALSE,				//指定当前进程的句柄是否被子进程继承
		0,					//指定附加的、用来控制优先类和进程的创建的标志
		NULL,				//使用当前进程的环境变量
		NULL,				//使用当前进程的驱动和目录
		&startinfo,			//指向一个用于决定新进程的主窗体如何显示的STARTUPINFO结构体的指针
		&processInfo		//进程信息结构体
	);
	if (!ret)
	{
		cout << "创建失败" << endl;
		return 0;
	}
	else
	{
		WaitForSingleObject(processInfo.hProcess, INFINITE);
		cout << "新创建的进程ID:" << processInfo.dwProcessId << endl;
		cout << "新创建的线程ID:" << processInfo.dwThreadId << endl;
		CloseHandle(processInfo.hProcess);
		CloseHandle(processInfo.hThread);
	}

	return 0;
}

プロセス間通信

  • 名前のないパイプ (パイプ): パイプは半二重通信方式であり、データは一方向にしか流れず、関係のあるプロセス間でのみ使用できます。プロセス アフィニティは通常、プロセスの親子関係です。

  • 名前付きパイプ (名前付きパイプ): 名前付きパイプも半二重通信方式ですが、無関係なプロセス間の通信を可能にします。

  • メッセージ キュー (メッセージ キュー): メッセージ キューは、メッセージのリンクされたリストであり、カーネルに格納され、メッセージ キューによって識別されます。メッセージ キューは、信号送信情報が少ないという欠点を克服し、パイプラインはフォーマットされていないバイト ストリームしか伝送できず、バッファー サイズは制限されます。

  • ミューテックス

  • 共有メモリ (共有メモリ): 共有メモリは、他のプロセスがアクセスできるメモリのセクションをマップすることです. この共有メモリは 1 つのプロセスによって作成されますが、複数のプロセスがアクセスできます. 共有メモリは最速の IPC 方式であり、他のプロセス間通信方式を効率化するために特別に設計されています。多くの場合、プロセス間の同期と通信を実現するために、セマフォなどの他の通信メカニズムと組み合わせて使用​​されます。

方法 1: WM_COPYDATA メッセージを使用して、異なるプロセスのウィンドウ間でデータを転送する

// 声明一个接收数据的窗口句柄变量
HWND hReceiveDataWindow = FindWindow(NULL, ...);
// 声明COPYDATASTRUCT结构体变量
COPYDATASTRUCT data;
// 设置传输数据的长度为字符串pStr的长度
data.cbData = strlen(pStr);
// 向指定窗口发送WM_COPYDATA消息,其中WPARAM参数为当前拥有焦点的窗口的句柄,
// LPARAM参数为指向COPYDATASTRUCT结构体变量的指针
SendMessage(hReceiveDataWindow, WM_COPYDATA, (WPARAM)GetFocus(), (LPARAM)&data);

このコードのワークフローは次のとおりです。

  1. FindWindow 関数を呼び出して、指定されたクラスまたはウィンドウ名を持つ最上位ウィンドウを検索します。
  2. 送信するデータやデータのサイズなどの情報を含む COPYDATASTRUCT 構造体変数 data を初期化します。
  3. SendMessage 関数を使用して WM_COPYDATA メッセージを受信データ ウィンドウに送信し、データ構造のポインターをメッセージ パラメーターとして渡します。

以下は、各関数の関数とパラメーターの説明です。

  1. FindWindow 関数: 指定されたクラス名またはウィンドウ名を持つ最上位ウィンドウを検索します。この関数のパラメータは次のとおりです。

    HWND FindWindow(
      LPCWSTR lpClassName, // 窗口类名,如果为 NULL,则表示匹配所有窗口类
      LPCWSTR lpWindowName // 窗口名称,如果为 NULL,则表示匹配所有窗口名
    );
    
  2. SendMessage 関数: 指定されたメッセージを 1 つ以上のウィンドウに送信し、応答を待ちます。この関数のパラメータは次のとおりです。

    LRESULT SendMessage(
      HWND   hWnd,       // 接收消息的窗口句柄
      UINT   Msg,        // 消息类型,这里为 WM_COPYDATA
      WPARAM wParam,     // 消息附加参数,这里为 GetFocus() 的返回值
      LPARAM lParam      // 消息附加参数,这里为 COPYDATASTRUCT 结构体的地址
    );
    
  3. GetFocus 関数: 現在キーボード フォーカスがあるウィンドウ ハンドルを取得します。この関数にはパラメーターがなく、現在フォーカスされているウィンドウのハンドルを直接返します。

  4. COPYDATASTRUCT 構造体:送信するデータやデータのサイズなどの情報を示します。この構造体のメンバー変数は、次のように記述されています。

    typedef struct tagCOPYDATASTRUCT {
      ULONG_PTR dwData;  // 指定一些与数据相关的内容,可以为任何值
      DWORD cbData;      // 数据大小,即 pStr 的长度
      PVOID lpData;      // 指向实际数据的指针,即 pStr 的地址
    } COPYDATASTRUCT, *PCOPYDATASTRUCT;
    

方法 2: クリップボード

OpenClipboard(NULL)  //将OpenClipboard 函数的参数指定为NULL,表明为当前进程打开剪切板

HGLOBAL hGlobalClip;

hGlobalClip = GlobalAlloc (GHND, nCount); //给全局内存对象分配全局内存

pDataBuf = (char *GlobalLock (hGlobalClip));  //通过给全局内存对象加锁获得对全局内存块的引用

GlobalUnlock (hGlobalClip);   //使用全局内存块后需要对全局内存卡解锁

EmptyClipboard(); //清空剪贴板

SetClipboardData (CF TEXT, hGlobalClip);  //设置剪贴板数据,这里直接将数据放到了剪贴板中,而没有使用延迟提交技术

CloseClipboard();  //关闭剪贴板

IsClipboardFormatAvilable (CF_TEXT)  //判断剪贴板中的数据格式是否为CF_TEXT

hGlobalClip = GetClipboardData(CF_TEXT);  //从剪贴板中获取格式为CF_TEXT的数据

pDataBuf = (char *) GlobalLock(hGlobalClip);

このコードのワークフローは次のとおりです。

  1. OpenClipboard 関数を呼び出してクリップボードを開き、この関数のパラメーターに NULL を指定して、現在のプロセスのクリップボードを開きます。
  2. GlobalAlloc 関数を使用して、グローバル メモリ ブロックをグローバル メモリ オブジェクトに割り当てます。ここで、GHND パラメータは、割り当てられたメモリ ブロックの内容がゼロに初期化されることを示します。
  3. GlobalLock 関数を使用してグローバル メモリ オブジェクトをロックし、グローバル メモリ ブロックへの参照を取得して pDataBuf 変数に保存します。
  4. 完全なグローバル メモリ ブロックを使用した後、GlobalUnlock 関数を使用してグローバル メモリ ブロックのロックを解除します。
  5. EmptyClipboard 関数を使用して、クリップボードの内容を空にします。
  6. SetClipboardData 関数を使用してデータをクリップボードに設定します。CF_TEXT パラメーターはデータの形式の種類を指定し、hGlobalClip パラメーターはデータが配置されているメモリ ブロックのハンドルを指定します。
  7. クリップボードを閉じるには、CloseClipboard 関数を呼び出します。
  8. IsClipboardFormatAvailable 関数を使用して、指定されたデータ形式 CF_TEXT がクリップボードに存在するかどうかを判断します。
  9. GetClipboardData 関数を使用してクリップボードから指定された形式のデータを取得します。CF_TEXT パラメータは取得するデータ形式の種類を示し、戻り値はグローバル メモリ ブロック ハンドルです。
  10. 再度 GlobalLock 関数を使用して、取得したグローバル メモリ オブジェクトをロックし、グローバル メモリ ブロックへの参照を取得して、pDataBuf 変数に保存します。

以下は、各関数の関数とパラメーターの説明です。

  1. OpenClipboard 関数: クリップボードを開きます。この関数のパラメータは次のとおりです。

    BOOL OpenClipboard(
      HWND hWndNewOwner // 新的所有者窗口句柄,这里为 NULL 表示为当前进程打开剪贴板
    );
    
  2. GlobalAlloc 関数: グローバル メモリ ブロックを割り当てます。この関数のパラメータは次のとおりです。

    HGLOBAL GlobalAlloc(
      UINT uFlags,   // 分配内存的方式,这里使用 GHND,表示分配后的内存内容初始化为零
      SIZE_T dwBytes // 分配内存的大小,这里为 nCount
    );
    
  3. GlobalLock 関数: グローバル メモリ オブジェクトをロックし、グローバル メモリ ブロックへの参照を取得します。この関数のパラメータは次のとおりです。

    LPVOID GlobalLock(
      HGLOBAL hMem // 全局内存块句柄
    );
    
  4. GlobalUnlock 関数: ロックされたグローバル メモリ ブロックのロックを解除します。この関数のパラメータは次のとおりです。

    BOOL GlobalUnlock(
      HGLOBAL hMem // 全局内存块句柄
    );
    
  5. EmptyClipboard 関数: クリップボード内のすべてのデータを空にします。この関数にはパラメーターがありません。

  6. SetClipboardData 関数: データをクリップボードに設定します。この関数のパラメータは次のとおりです。

    HANDLE SetClipboardData(
      UINT   uFormat,   // 设置的数据格式类型,这里使用 CF_TEXT
      HANDLE hMem       // 数据所在的内存块句柄
    );
    
  7. CloseClipboard 関数: クリップボードを閉じます。この関数にはパラメーターがありません。

  8. IsClipboardFormatAvailable 関数: クリップボードに指定された形式のデータがあるかどうかを判断します。この関数のパラメータは次のとおりです。

    BOOL IsClipboardFormatAvailable(
      UINT format // 要检查的数据格式类型,这里使用 CF_TEXT
    );
    
  9. GetClipboardData 関数: クリップボードから指定された形式のデータを取得します。この関数のパラメータは次のとおりです。

    HANDLE GetClipboardData(
      UINT uFormat // 获取的数据格式类型,这里使用 CF_TEXT
    );
    

方法 3: 共有ファイルのマッピング

バッファを作成

HANDLE CreateFileMapping(
  HANDLE hFile,                    		// 文件句柄
  LPSECURITY_ATTRIBUTES lpAttributes, 	// 安全属性
  DWORD flProtect,                 		// 内存访问权限保护标志位
  DWORD dwMaximumSizeHigh,         		// 映射文件的最大尺寸(高位DWORD)
  DWORD dwMaximumSizeLow,          		// 映射文件的最大尺寸(低位DWORD)
  LPCTSTR lpName                   		// 共享内存对象名
);
  • hFile: 開いているファイル ハンドル。ディスク上のファイル、仮想ディスク デバイス、物理メモリなどの可能性があります。はいの場合INVALID_HANDLE_VALUEは、関連付けられたファイルを持たないマッピング オブジェクトを作成します。
  • lpAttributes: このメモリ領域のアクセス権と継承を制御するセキュリティ記述子へのポインタ。NULL の場合、既定のセキュリティ記述子を示します。
  • flProtect: 次の値の 1 つまたは複数を含む、メモリ アクセス保護フラグ ビット。
    • PAGE_READONLY: マッピング ファイルを読み取り専用モードで開きます。
    • PAGE_READWRITE: マッピング ファイルを読み取り/書き込みモードで開きます。
    • PAGE_WRITECOPY: マッピング ファイルをコピー オン ライト モードで開きます。
    • PAGE_EXECUTE: 実行権限;
    • PAGE_EXECUTE_READ: 読み取り権限と実行権限。
    • PAGE_EXECUTE_READWRITE: 読み取り、書き込み、および実行権限。
    • PAGE_EXECUTE_WRITECOPY: コピー オン ライト モード、読み取りおよび実行権限。
  • dwMaximumSizeHighand dwMaximumSizeLow: 作成されたマップ ファイルの最大サイズ (バイト単位)。最大値は 0 で、これはファイル マッピング オブジェクトを作成することを意味し、マッピング オブジェクトのサイズはファイル サイズと同じです。
  • lpName: 共有メモリ オブジェクト名、他のプロセスとの共有メモリを指定する必要があります。現在のプロセスでのみ使用する場合は、NULL に設定します。

ノート:

CreateFileMapping()関数を使用して、ファイルやその他のオブジェクトを呼び出しプロセスのアドレス空間にマップしたり、複数のプロセスがアクセスを共有する共有メモリ領域を作成したりできます。

マップされたファイル オブジェクトが作成されるとき、ファイルは既に存在している必要があります。そうでない場合、失敗します。マップ ファイルのサイズは、dwMaximumSizeHighおよびdwMaximumSizeLowパラメータによって指定されます。両方のパラメーターが 0 の場合、ファイルと同じサイズのマップされたファイル オブジェクトが作成されます。

マッピングファイルを作成するとき、flProtectパラメータは、メモリ領域のアクセス許可を記述するために使用される保護フラグビットを指定します。

複数のプロセスが同じ名前のマップされたファイルを開くとlpName、データの共有と同期を実現するために、共有メモリを介して相互に通信できます。

マッピングを作成する

MapViewOfFileこの関数は、作成済みのファイル マッピングまたは共有メモリ オブジェクトを呼び出しプロセスのアドレス空間にマップできるため、プロセスはマップされた領域を直接読み書きできます。

具体的な関数プロトタイプは次のとおりです。

LPVOID MapViewOfFile(
  HANDLE hFileMappingObject, // 映射对象句柄
  DWORD dwDesiredAccess,     // 访问权限
  DWORD dwFileOffsetHigh,    // 映射起始偏移(高位)
  DWORD dwFileOffsetLow,     // 映射起始偏移(低位)
  SIZE_T dwNumberOfBytesToMap // 映射大小
);

パラメータの説明:

  • hFileMappingObject: 作成されたマッピング オブジェクトのハンドル。

  • dwDesiredAccess: アクセス権。次の値の 1 つ以上が含まれます。

    • FILE_MAP_READ: マップされた領域を読み取ります。
    • FILE_MAP_WRITE: マッピング領域に書き込みます。
    • FILE_MAP_ALL_ACCESS: マップされた領域の読み取り、書き込み、およびコピー。
  • dwFileOffsetHighAnd dwFileOffsetLow: マッピング開始オフセット。ファイル内のマッピング領域の位置を指定します。ファイルの先頭からマッピングを開始するには、0 に設定します。

  • dwNumberOfBytesToMap: マップされた領域のサイズ (バイト単位)。0 に設定すると、ファイル全体または共有メモリ オブジェクトをマップすることを意味します。

バッファを閉じる

UnmapViewOfFileこの関数は、前の関数によってMapViewOfFile実行されたマッピング操作をキャンセルするために使用されます。これにより、マッピングされた領域によって占有されていたリソースが解放されます。

BOOL UnmapViewOfFile(LPCVOID lpBaseAddress); // 映射区域起始地址
//关闭内核对象的句柄
CloseHandle(HANDLE hObject); //内核对象句柄

コード例:

差出人:

#include<iostream>
#include<windows.h>

using namespace std;

int main()
{
	HANDLE hMapFile = CreateFileMapping(
		NULL,   //物理
		NULL,	//默认映射文件的安全级别
		PAGE_READWRITE,  //可读可写
		0,		//高位
		128,    //低位
		TEXT("fileMap")   //共享内存名
	);

	char* buf = (char*)MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0,128);

	cin.getline(buf,16);

	while (1);

	UnmapViewOfFile(buf);
	CloseHandle(hMapFile);

	return 0;
}

受信機:

#include<iostream>
#include<windows.h>

using namespace std;

int main()
{
	HANDLE hMapFile = CreateFileMapping(
		NULL,   //物理
		NULL,	//默认映射文件的安全级别
		PAGE_READWRITE,  //可读可写
		0,		//高位
		128,    //低位
		TEXT("fileMap")   //共享内存名
	);

	char* buf = (char*)MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0,128);

	for(int i = 0;i < 16;i++)
		cout<< *(buf + i); 

	while (1);

	UnmapViewOfFile(buf);
	CloseHandle(hMapFile);

	return 0;
}

メモリ管理

  • プロセス仮想アドレス空間

    32 ビット プロセスの場合、このアドレス空間は 4GB です。これは、32 ビット ポインターが 0x 00000 00 0 から 0xFFFFFFFF までの任意の値を持つことができるためです。これにより、ポインターは 4 294 967 296 個の値の 1 つを保持できるようになり、プロセスの 4 GB の仮想空間の範囲をカバーします。64 ビット プロセスの場合、64 ビット ポインターは 0x0 000 00 000 00 0 00 00 から OxFFFFFFFFFFFFFFFF までの任意の値を持つことができるため、このアドレス空間は 16EB (1TB = 1024GB、1 PB = 1024TB、1 EB = 1024PB) です。これにより、ポインターは 18446744073709 551616 のいずれかの値を持つことができ、プロセスの 16 エクサバイトの仮想空間の範囲をカバーします。これはかなりの範囲です。

    32 ビット CPU のアドレッシング空間は 4G であるため、仮想メモリの最大値は 4G であり、Windows オペレーティング システムはこの 4G を 2 つの部分、つまり 3G ユーザー空間と 1G システム空間に分割し、システム空間を共有します。プロセスごと はい, オペレーティングシステムといくつかのカーネルオブジェクトなどを保存し, ユーザー空間は使用のために各プロセスに割り当てられます. ユーザー空間には、プログラムコードとデータ、ヒープ、共有ライブラリ、およびスタックが含まれます.

  • ページング

    ページングの基本的な方法は、アドレス空間を多くのページに分割することです。各ページのサイズは CPU によって決定され、オペレーティング システムによってページ サイズが選択されます。現在、Inter シリーズの CPU は 4KB または 4MB のページ サイズをサポートしていますが、PC は現在 4KB の使用を選択しています。

  • カーネル空間:

    カーネル空間とは、プロセッサの最高レベルのスーパーバイザー モードで実行されるコードまたはデータを指します. カーネル空間は、0xC0000000 から 0xFFFFFFFF までの 1GB の線形アドレス空間を占有します. カーネルの線形アドレス空間は、すべてのプロセスによって共有されますが、実行中のプロセスのみが共有されますin カーネル状態のプロセスのみがアクセスできます. ユーザー プロセスはカーネル状態に切り替えて、システム コールを介してカーネル空間にアクセスできます. プロセスがカーネル状態で実行されるときに生成されるアドレスは、カーネル空間に属します.

  • ユーザー空間

    ユーザー空間は 0xBFFFFFFF から合計 3GB のリニア アドレス空間を占有します. 各プロセスは独立した 3GB のユーザー空間を持つため、ユーザー空間はプロセスごとに一意ですが、カーネル スレッドはユーザー空間アドレスを生成しないため、ユーザー空間はありません. さらに、子プロセスは親プロセスのユーザー空間を共有 (継承) しますが、親プロセスのユーザー空間を共有する代わりに、親プロセスと同じユーザー リニア アドレスから物理メモリ アドレスへのマッピング関係のみを使用します。ユーザー モードとカーネル モードの両方で実行されているプロセスは、ユーザー空間にアクセスできます。

    ユーザー空間では、メモリは次のように分割されます: 0x08048000 start

ユーザー空間のセグメンテーション:

  • テキストセグメント - コードセグメント

テキスト セグメントには、実行前に決定される (コンパイル時に決定される) プログラム コードが格納され、通常は読み取り専用です。

  • .rodata - 読み取り専用データ セグメント

const によって変更されたグローバル変数、define マクロによって定義された定数、読み取り専用の文字列定数など、いくつかの読み取り専用の定数データを保存します。

  • 。データ

コンパイル時に (実行時ではなく) 決定できる読み取り可能および書き込み可能なデータを保存します。それがいわゆる静的記憶領域です.初期値を代入したグローバル変数と初期値を代入した静的変数はこの領域に格納されます.通常はこの領域にも格納されます.静的宣言された変数は,それらがグローバル変数であるかどうかに関係なく.関数内で、初期値が割り当てられていない場合は .bass セクションに格納され、初期値が割り当てられている場合は .data セクションに格納されます

  • .bss

定義されているが初期値が割り当てられていないグローバル変数および静的変数は、この領域に配置されます

  • ヒープ

ヒープは、プロセスの実行中に動的に割り当てられるメモリ セグメントを格納するために使用されます. そのサイズは固定されておらず、動的に拡張または縮小できます. プロセスが malloc などの関数を呼び出してメモリを割り当てると、新しく割り当てられたメモリが動的にヒープに追加され (ヒープが拡張されます)、free などの関数によってメモリが解放されると、解放されたメモリがヒープから削除されます (ヒープが拡張されます)。ヒープが削減されます)。低から高へ。

  • 共有ライブラリーエリア

これは、ファイルの内容をメモリに直接マップするためにカーネルによって使用されます。すべてのアプリケーションは、Linux が提供する mmap() システム コールを使用するか、Windows で CreateFileMapping()/MapViewOfFile を使用して、このようなマッピングを実行できます。メモリ マッピングはファイル I/O の効率的な方法であるため、動的ライブラリのロードはこの方法で実装されます。もちろん、ファイルに関連付けられていないプログラム データの匿名メモリ マッピングも可能です。Linux では、malloc() を介して大容量メモリを申請すると、C ライブラリはヒープ スペースを使用する代わりに、メモリ マッピング セグメントに匿名のメモリ マッピングを作成します。ここでの「大きい」とは、MMAP_THRESHOLD バイトより大きいことを意味します。デフォルトは 128kb で、malopt() で調整できます。

  • スタック

スタック セグメントには、システムによって適用および解放されるパラメーター変数とローカル変数が格納され、静的メモリ割り当てに属します。高いところから低いところへ

おすすめ

転載: blog.csdn.net/weixin_50202509/article/details/129967533