マルチプロセスとマルチスレッド(Linux)

目次

1.マルチプロセス
(1)概要
 プロセスはOSによってCPUに割り当てられたプログラムの実行単位で、プロセスには連続した仮想メモリ空間が用意されます。マルチプロセスは複数のプロセスが並列に実行できるように、CPUリソースを時間などで分割して共用します。
 マルチプロセス(マルチタスクともいう)では各プロセスにコンテキストが紐づいていて、CPUのレジスタの情報をコンテキストとして保持し、プログラムの実行に応じてコンテキストを切り替えることで処理を継続します。

(2)サンプル

 multi_process.c

#include	<stdio.h>
#include	<stdlib.h>
#include	<sys/types.h>	//getpid,fork
#include	<unistd.h>	//getpid,fork
#include	<sys/wait.h>	//wait

//プロトタイプ宣言
void counter();

int main(void)
{
	pid_t	p_pid,pid;	//int型
	int status1,status2;
	p_pid=getpid();	//プロセスID取得(※1)
	printf("[%d]start\n",p_pid);
	switch(pid=fork()){	//子プロセス生成(※2)
		case	0:	//子プロセス
			counter();
			exit(0);
		case    -1:	//エラー
			perror("fork");	//エラーメッセージの出力
			break;
		default:	//親プロセス
			printf("[%d]子プロセスID : %d\n",p_pid,pid);
			break;
	}

	switch(pid=fork()){	//子プロセス生成(※2)
		case 0:	//子プロセス
			counter();
			exit(0);
		case -1:	//エラー
			perror("fork");	//エラーメッセージの出力
			break;
		default:	//親プロセス
			printf("[%d]子プロセスID : %d\n",p_pid,pid);
			break;
	}

	pid=wait(&status1);	//子プロセスの状態変化を待つ(※3)
	printf("[%d]pid = %d end status:%d\n",p_pid,pid,status1);//	子プロセス終了
	pid=wait(&status2);	//子プロセスの状態変化を待つ(※3)
	printf("[%d]pid = %d end status:%d\n",p_pid,pid,status2);//	子プロセス終了
	printf("[%d]end\n",p_pid);	//親プロセス終了

	return 0;
}

void counter()
{
	int i;
	pid_t pid;
	pid=getpid();	//プロセスID取得
	for(i=0;i<10;i++){
		sleep(1);
		printf("[%d]%d\n",pid,i);
	}
return;
}

(※1)getpid()
getpid()は呼び出し元のプロセスのプロセスIDを返す。
getppid()は呼び出し元のプロセスの親プロセスのプロセスIDを返す。
#include <sys/types.h>
#include <unistd.h>
pid_t getpid(void);
pid_t getppid(void);  

(※2)fork()
呼び出し元プロセスを複製して新しいプロセスを生成する。
新しいプロセスは「子」プロセスと呼ばれ、呼び出し元プロセスは「親」プロセスと呼ばれる。
#include <sys/types.h>
#include <unistd.h>
pid_t fork(void);
戻り値:
成功した場合、親プロセスには子プロセスの PID が返され、
子プロセスには0が返される。
失敗した場合、親プロセスに-1が返され、子プロセスは生成されず、errno が適切に設定される。

(※3)wait()
子プロセスの状態変化を待ち、子プロセスのいずれかが終了するまで呼び出し元のスレッドの実行を一時停止する。
(書式) 
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *wstatus);
戻り値:
wait():成功すると、終了した子プロセスのプロセスIDを返す。エラーの場合 -1 を返す。

(3)実行結果

2.マルチスレッド
(1)概要
 スレッドはプロセス内で命令を実行する部分においてCPUコアを利用する単位で、1つのプロセスは1つ以上のスレッドから成ります。
 最近のCPUはコアが複数あり、1つのプロセスを複数のスレッドに分けることでCPUコアの利用率を上げて、処理の高速化が図れます。
 SMT (Simultaneous Multi-Threading)機能を備えたCPUでは1つのコアで複数のスレッドを同時に実行することができます(4コア8スレッドなど)。
 また、1つのプロセスの仮想メモリ空間をスレッドで共用するため、メモリの不整合が生じないようにスレッド間で同期をとる仕組みが必要となります。

(2)サンプル

multi_thread.c

#include	<stdio.h>	
#include	<stdlib.h>	
#include	<sys/types.h>	//getpid
#include	<pthread.h>	//pthread_create,pthread_join,pthread_self
#include	<unistd.h>	//sleep,getpid
#include	<string.h>	//strerror

//プロトタイプ宣言
void *counter(void *arg);

void main()
{
	pid_t	p_pid;
	pthread_t	thread_id1,thread_id2;
	int	status;
	void 	*result;

	p_pid=getpid();

	printf("[%d]start\n",p_pid);
	//1つ目のスレッド生成
	status=pthread_create(&thread_id1,NULL,counter,(void *)NULL);	//(※1)
	if(status!=0){
		fprintf(stderr,"pthread_create : %s",strerror(status));	//(※2)
	}
	else{
		printf("[%d]thread_id1=%d\n",p_pid,thread_id1);
	}
	//2つ目のスレッド生成
	status=pthread_create(&thread_id2,NULL,counter,(void *)NULL);	//(※1)
	if(status!=0){
		fprintf(stderr,"pthread_create : %s",strerror(status));	//(※2)
	}
	else{
		printf("[%d]thread_id2=%d\n",p_pid,thread_id2);
	}

	pthread_join(thread_id1,&result);	//スレッドの終了待ち(※3)
	printf("[%d]thread_id1 = %d end\n",p_pid,thread_id1);
	pthread_join(thread_id2,&result);	//スレッドの終了待ち(※3)
	printf("[%d]thread_id2 = %d end\n",p_pid,thread_id2);
	printf("[%d]end\n",p_pid);	//プロセス終了
}

void *counter(void *arg)
{
	int     i;
	pid_t   pid;
	pthread_t	thread_id;
	pid=getpid();	//プロセスID取得
	thread_id=pthread_self();	//呼び出したスレッドのIDを取得する(※4)

	for(i=0;i<10;i++){
		sleep(1);	//指定の秒数休止(※5)
		printf("[%d][%d]%d\n",pid,thread_id,i);
	}

	return(arg);
}

(※1)pthread_create()
新しいスレッドを生成する
(書式)
#include <pthread.h>
int pthread_create(pthread_t * thread,
pthread_attr_t * attr,
void * (*start_routine)(void *),
void * arg);
引数:
thread:スレッド管理用
attr:スレッド属性、NULLのときはデフォルト
(*start_routine)(void *):スレッドから呼び出される関数へのポインタ
arg:start_routine()の引数で渡すデータのポインタ
戻り値:
成功すると新しく作成したスレッドの識別子が引数threadの指す領域へ格納され、0 が返る。エラーの場合、非 0 のエラーコードが返る。

(※2)strerror()
エラー番号を説明する文字列を返す。
(書式)
#include <string.h>
char *strerror(int errnum);
const char *strerrorname_np(int errnum);
const char *strerrordesc_np(int errnum);
int strerror_r(int errnum, char *buf, size_t buflen);
            /* XSI-compliant */
char *strerror_r(int errnum, char *buf, size_t buflen);
            /* GNU-specific */
char *strerror_l(int errnum, locale_t locale);
関数 strerror(), strerror_l() と GNU 固有の関数 strerror_r()
はエラー内容を説明する文字列を返す。
エラー番号が未知の場合は “Unknown error nnn” という メッセージを返す。

(※3)pthread_join()
別のスレッドの終了を待つ
(書式)
#include <pthread.h>
int pthread_join(pthread_t th, void **thread_return);
引数:
th:待機するスレッドの指定
**thread_return:スレッドの戻り値を格納する領域
戻り値:
成功すると、thの返り値がthread_returnで指し示された領域に格納され、0が返る。エラーの場合、非 0 のエラーコードが返る。

(※4)pthread_self()
呼び出したスレッドのIDを取得する
(書式)
#include <pthread.h>
pthread_t pthread_self(void);

(※5)sleep()
指定の秒数の間だけ休止する
(書式)
#include <unistd.h>
unsigned int sleep(unsigned int seconds);
戻り値:
要求された時間が過ぎた場合はゼロを返す。
呼び出しがシグナルハンドラーに割り込まれた場合は、
休止の残り時間を返す。

(3)実行結果
 コンパイルはリンカにリンクするライブラリを伝えるためのオプション-lpthreadまたは-pthreadを指定する必要があります。

The end