단일,다중코어 시스템 프로그래밍

SangHoon Lee·2020년 6월 25일
0

*프로세스 생성
1. UNIX , LINUX 및 Windows 와 같은 대부분의 현대 운영체제들은 유일한 프로세스 식별자(pid)를 사용하 여 프로세스를 구분하는데 이 식별자는 보통 정수이다. pid는 각 프로세스에 고유의 값을 가지게 한다.
2. 프로세스 트리


3. 프로세스들의 목록 명령어 : ps -el
4. 일반적으로 프로세스가 자식 프로세스를 생성할 때, 그 자식 프로세스는 자신의 임무를 달성하기 위하여 어떤 자원(CPU 시간, 메모리, 파일, 입출력 장치)이 필요하다.
* 프로세스가 새로운 프로세스를 생성할 때, 두 프로세스를 실행시키는 데 두가지 가능 방법.
첫번째.
1. 부모는 자식과 병행하게 실행을 계속한다.
2. 부모는 일부 또는 모든 자식이 실행을 종료할 때까지 기다린다.
두번째.
1. 자식 프로세스는 부모 프로세스의 복사본이다. ( 자식 프로세스는 부모와 똑같은 프로 그램과 데이터를 가진다.)
2. 자식 프로세스가 자신에게 적재될 새로운 프로그램을 갖고 있다.

#include <sys/types.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
	int pid;
	/* new process create (fork) */
	pid = fork();
	if(pid < 0) {
		fprintf(stderr, “Fork Failed”);
		return 1;
	}
	else if(pid == 0) {
		execlp(“/bin/ls”, “ls”, NULL);
	}
	else {
		/* parent process waiting for child process */
		wait(NULL);
		printf(“Child Complete”);
	}
	return 0;
}

*부모 프로세스 에서 자식 프로세스들 중 하나의 실행을 다음과 같은 이유로 종료시킨다.
	1. 자식이 자신에게 할당된 자원을 초과하여 사용할 때, 이때는 부모가 자식들의 상태를 검사할 수 		있는 방편이 주어져야 한다.
	2. 자식에게 할당된 테스크(Task)가 더이상 필요 없을 때
	3. 부모가 exit를 하는데, 운영체제는 부모가 exit한 후에 자식이 실행을 계속는것을 허용하지 않는 		경우

*좀비 프로세스 : 프로세스 종료 상태가 저장되는 프로세스 테이블의 해당 항목은 부모 프로세스가 wait()를 	호출할 때까지 남아있게 되는데, 종료되었지만 부모프로세스가 아직 wait() 호출을 하지 않은 프로세스

*고아 프로세스 : 부모 프로세스가 wait()를 호출하는 대신 종료하고 남은 자식 프로세스

*공유 메모리 시스템
- 공유 메모리를 사용하는 프로세스간 통신에서는 통신하는 프로세스들이 공유 메모리 영역을 구축해야 하 고, 공유 메모리 영역은 공유 메모리 세그먼트를 생성하는 프로세스의 주소 공간에 위치한다.
※일반적으로 운영체제는 한 프로세스가 다른 프로세스의 메모리를 접근하는것을 금지해야 함!
공유 메모리는 둘 이상의 프로세스가 일반적으로 한 프로세스가 다른 프로세스의 메모리에 접근 금지 해야 한다는 이 제약을 제거하는 것에 동의하는것이 필요하다. 그런 후에 프로세스들은 공유 영역에 읽고 씀으로 써 정보를 교환할 수 있다. 데이터의 형식과 으ㅟ치는 이들 프로세스들에 의해 결정되며, 운영체제의 소관 이 아니다 또한 프로세스들은 동시에 동일한 위치에 쓰지 않도록 책임져야 한다.

*생성자 프로세스 : 프로세스 정보 생성
*소비자 프로세스 : 프로세스 정보 소비

*다중코어 프로그래밍
-코어가 여러 CPU 칩 형태를 따거나 칩안에 여러개가 존대하든지 이러한 시스템을 다중코어 또는 다중처리 기 시스템이라고 불린다.

Amdahl’s Law (암달의 법칙)

단일코어 시스템 병행실행 과 다중코어 시스템 병행실행

*다중코어 시스템 프로그래밍
1. 테스크 인식 (Identifying Task) : 응용을 분석하여 독립된 병행가능 테스크로 나눌 수 있는 영역을 찾는 작업이 필요하며, 테스크는 서로 독립적이고, 개별 코어에서 병렬 실행 될 수 있어야 한다.
2. 균형 (Balance) : 전체 작업에 균등한 기여도를 가지도록 테스크를 나누는 것은 매우 중요하다
3. 데이터 분리 (Data Spliting) : 테스크가 접근하고 조작하는 데이터는 개별 코어에서 사용 할 수 있도록 나누어져야 한다.
4. 데이터 종속성 (Data Dependency) : 테스크가 접근하는 데이터는 둘 이상의 테스크 사이에 종속성이 없 는지 검토되어야 한다.
5. 시험 및 디버깅 (Testing and Debugging) : 프로그램이 다중코어에서 병렬로 실행될 때 다양한 실행 경 로가 존재 할 수 있어야 한다.

다중 스레드 모델
*다대일 모델 (Many-to-One Model)
- 많은 사용자 수준 스레드를 하나의 커널 스레드로 사상한다.

*일대일 모델 (One-to-One Model)
- 각 사용자 스레드를 각각 하나의 커널 스레드로 사상한다.

*다대다 모델 (Many-to-Many Model)
- 다대다 모델은 여러개의 사용자 수준 스레드를 그보다 작은 수 혹은 같은수 커널 스레드로 멀티 플렉스 한다.

  • 다대다 모델의 변형은 여전히 많은 사용자 스레드를 적거나 같은 수의 커널 스레드로 멀티플렉 스 시키지만 또한 한 사용자가 스레드가 하나의 커널 스레드에만 연관되는 것을 허용한다. 이를
    두 수준 모델 (Two-level Model) 이라 한다.

    *Pthreads
    • POSIX( IEEE 1003.1c)가 스레드 생성과 동기화를 위해 제정한 표준 API
#include <pthread.h>
#include <stdio.h>

int sum;
void * runner(void *param);

int main(int argc, char *argv[]) {
	pthread_t tid; /* the thread identifier */
	pthread_attr_t attr; /* set of attributes for the thread */
	if(argc != 2) {
		fprintf(stderr, “usage : a.out <interger value>\n”);
		return -1;
	}
	if(atoi(argv[1] < 0) {
		fprintf(stderr, “%d must be >= 0\n”, atoi(argv[1]));
		return -1;
	}
	/* get the default attributes */
	pthread_attr_init(&attr);
	/* create the thread */
	pthread_create(&tid, &attr, runner, argv[1]);
	
	/* now wait for the thread to exit */
	pthread_join(tid,NULL);

	printf(“ sum = %d\n”, sum);
}

void *runner(void *param) {
	int i, upper = atoi(param);
	sum = 0;
	for(i = 1; i<=upper; i++) {
		sum+=1;
	}
	pthread_exit(0);
}

Pthread를 이용한 멀티스레드 구현

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

void *t_function(void *data) {
        int id;
        int i = 0;
        id = *((int *)data);
        while(1) {
                printf("(%lu) %d : %d\n",pthread_self(), id, i);
                i++;
                if(i==5) {
                        return (void *)i;
                        sleep(1);
                }
        }
}

int main(void) {
        pthread_t p_thread[2];
        int thr_id;
        int status;
        int a = 1;
        int b = 2;
        thr_id = pthread_create(&p_thread[0],NULL,t_function,(void *)&a);
        if(thr_id <0) {
                perror("thread create error: ");
                exit(0);
        }

        thr_id = pthread_create(&p_thread[1], NULL, t_function, (void *)&b);
        if(thr_id < 0) {
                perror("thread create error : ");
                exit(0);
        }

        pthread_join(p_thread[0], (void **)&status);
        printf("return thread 0 %d\n",status);
        pthread_join(p_thread[1], (void **)&status);
        printf("return thread 1 %d\n",status);
        return 0;
}

*Window 스레드
1. 각 스레드를 유일하게 지목하는 스레드 ID
2. 처리기의 상태를 나타내는 레지스터 집합
3. 사용자 모드에서 실행될 때 필요한 사용자 스택, 커널 모드에서 실행될 때 필요한 커널 스택
4. 실행 시간 라이브러리와 동적 링크 라이브러리(DLL) 등이 사용하는 개별 데이터 저장영역

  • 레지스터 집합 , 스택 ,개별 데이터 저장 영역들은 그 스레드의 문맥으로 불린다. 스레드를 위해서는 아래 와 같은 자료구조를 가지고 있다.
    1. ETHREAD - 실행 스레드 블록 (executive thread block)
    - 스레드가 속한 프로세스를 가리키는 포인터와 그 스레드가 실행을 시작해야 할 루틴의 주소 , KTHREAD 에 대한 포인터도 가지고있다.
    2. KTHREAD - 커널 스레드 블록 (kernel thread block)
    - 스레드의 스케쥴링 및 동기화 정보를 가지고 있다. 또한 이 스레드가 커널 모드에서 실 행 될 때 사용되는 커널 스택과 TEB 에 대한 포인터를 가지고 있다.
    3. TEB - 스레드 환경 블록 (thread environment block)
    - 사용자 모드에서 실행될 때 접근되는 사용자 공간 자료구조로 , 스레드 식별자, 사용자 모드 스택 및 스레드 국지 저장소를 저장하기 위한 배열을 가지고 있다.

*리눅스 스레드
-프로세스를 복제하는 기능을 가진 fork() 시스템 호출을 제공하고, clone() 시스템 호출을 이용하여 스레드 를 생성할 수 있는 기능도 제공한다. 그러나 리눅스는 프로세스와 스레드를 구별하지 않는다. (프로세스 나 스레드 보단 테스크 라는 용어를 사용)

profile
C++ 공부하고있는 대학생입니다.

0개의 댓글