12. 프로세스와 스레드

2023. 9. 10. 20:25네트워크

프로세스 (Process)

프로그램이 돌아가고 있는 상태 즉, 컴퓨터에서 작업 중인 프로그램

 

 

모든 프로그램은 운영체제가 실행될 수 있도록 메모리 공간을 할당해줘야 실행될 수 있다.

그래서 프로그램을 실행하는 순간 파일은 컴퓨터 메모리에 올라가게 되고, 운영체제로 부터 시스템 자원(CPU)을 할당받아 프로그램이 실행되고 있는 상태가 프로세스이다.

 

 

 

* 프로세스의 자원 구조

 

프로세스가 만들어지면 4가지 메모리 영역으로 구성되어 할당받게 된다.

코드 영역(Code / Text) : 프로그래머가 작성한 코드가 CPU가 해석할 수 있는 기계어 형태로 저장되어있다.

데이터 영역(Data) : 코드가 실행되면서 사용되는 전역 변수나 데이터가 모여있다.

                                          프로그램 시작 시 할당되어 프로그램 종료 시 소멸된다.

스택 영역(Stack) : 함수 호출 시 할당되고 함수 호출이 완료되면 소멸한다.

힙 영역(Heap) : 생성자, 인스턴스와 같은 동적으로 할당되는 데이터가 저장된다.

 

코드 영역과 데이터 영역은 선언할 때 크기가 결정되는 정적 영역 
스택 영역과 힙 영역은 프로세스가 실행되는 동안 크기가 변하는 동적 영역

 

각각의 프로세스는 메모리 공간에서 독립적으로 존재하며 자신만의 메모리 공간을 가진다.

 

=> 프로세스 A,B,C가 있을 경우 각각 프로세스는 모두 같은 구조의 메모리 공간을 가진다.

독립적인만큼 다른 프로세스의 메모리 공간에 접근할 수 없다.

 

 

 

* IPC - 프로세스 간 통신

프로세스A에서 프로세스B에 직접 접근할 수 없기 때문에 프로세스 간의 통신을 하는 특별한 방식이 필요하다.

IPC는 크게 Message  passing 과 Shared memory 방법이 있다.

EX) 메일슬록, 파이프 등

 

프로세스는 독립적인 메모리 공간을 지니기 때문에 IPC를 통하지 않고 통신할 수 없다

여러개의 프로세스가 병렬적으로 실행되기 위해서는 필연적으로 *컨텍스트 스위칭이 발생할 수 밖에 없다.

 

 

* Context Switching

 

동시성 (Concurrency) 

프로세스들을 계속 번갈아가면서 조금씩 처리함으로써 마치 프로그램이 동시에 실행되는 것처럼 보이게 하는 것

 

이 때 작업들은 작은 단위로 조금씩만 수행하고 다음 작업으로 넘어가는 식으로 동작되는데,

이렇게 A → B → C → D 로 번갈아 바꾸는 것을 Context Switching이라고 부른다.

또한, 동작 중인 프로세스가 대기를 하면서 해당 프로세스의 상태를 보관하고 ,

대기하고 있던 다음 순서의 프로세스가 동작하면서 이전에 보관했던 프로세스 상태를 복구하는 작업을 말한다.

 

 

 

* 프로세스의 한계

과거에는 프로세스 하나만을 사용하기 때문에 한가지 작업이 완료될 때까지 기다려야했다.

그렇다고 동일한 프로그램을 여러 개의 프로세스로 만들면, 메모리를 차지하고 CPU에서 할당받는 자원이 중복될 것이다.

 

 


스레드 (Thread)

하나의 프로세스 내에서 동시에 진행되는 작업, 흐름의 단위

 

ex) 크롬 브라우저가 실행되면 프로세스 하나가 생성됨  →  파일 다운 + 온라인 쇼핑 + 게임 동시에 하기도 한다.

=> 하나의 프로세스 안에 여러가지 작업들이 동시에 진행되는 것인데

이러한 작업 흐름들을 스레드라고 하며 여러개라면 멀티 스레드라고 한다.

하나의 프로세스 안에 여러개의 스레드가 있다.

일반적으로

하나의 프로그램은 하나 이상의 프로세스를 가지고 있고, (프로세스를 생성하면 기본적으로 하나의 main 스레드가 생성됨)

하나의 프로세스는 반드시 하나 이상의 스레드를 갖는다.

 

 

스레드는 프로세스의 4가지 메모리 영역 중 stack만 할당받고,

나머지 Code, Data, Heap 영역은 프로세스 내 다른 스레드들과 공유한다.

=>스레드는 IPC 없이도 스레드의 통신이 가능하다.

각각의 스레드는 별도의 Stack을 가지고 있지만 heap 메모리는 고유하기 때문에 서로 다른 스레드에서 가져와 읽고 쓸 수 있다.

 

 

* 스레드 만드는 법

Thread 내에 run() 메소드를 상속받는 클래스에서 반드시 오버라이딩 해야함

 

1. Thread 클래스를 상속받아 쓰레드 작성하기

package com.example.thread;

// 스레드 규칙
// 1. Thread 클래스를 상속받는다.
public class MyThread extends Thread{

    private String str;
    public MyThread(String str){
        this.str = str;
    }
//    2. run()메소드를 오버라이딩한다.
//    3. 동시에 실행시키고 싶은 코드를 작성한다.
    @Override
    public void run() {

        for (int i =0; i < 10; i++){
            System.out.println(str);
            try {
                Thread.sleep(1000); //1초간 쉰다
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        } //for

    }
}
package com.example.thread;

public class MyThreadExam {

    public static void main(String[] args) {
        String name = Thread.currentThread().getName();
        System.out.println("thread name : " + name);
//      결과 : thread name : main

        MyThread myThread1 = new MyThread("*");
        MyThread myThread2 = new MyThread("+");

//        3. Thread는 start() 메소드로 실행한다.
        myThread1.start();
        myThread2.start();
    }
}

 

2. Runnable 인터페이스를 구현하여 스레드 작성하기

package com.example.thread;

// 1. Runnable 인터페이스를 구현한다.
public class MyRunnable implements Runnable {

    private String str;
    public MyRunnable(String str){
        this.str = str;
    }
    //  2. run()메소드를 오버라이딩한다.
    @Override
    public void run() {

        for (int i =0; i < 10; i++){
            System.out.println(str);
            try {
                Thread.sleep(1000); //1초간 쉰다
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        } //for

    }

}
package com.example.thread;

public class MyThreadExam2 {

    public static void main(String[] args) {
        String name = Thread.currentThread().getName();
        System.out.println("thread name : " + name);
        System.out.println("start!");

        MyRunnable myRunnable1 = new MyRunnable("*");
        MyRunnable myRunnable2 = new MyRunnable("+");

//        3. Thread 인스턴스를 생성하는데, 생성자에 Runnable 인스턴스를 넣어준다.
        Thread t1 = new Thread(myRunnable1);
        Thread t2 = new Thread(myRunnable2);

//        4. Thread가 가지고 있는 start() 메소드를 호출한다.
        t1.start();
        t2.start();

        System.out.println("end!");

    }
}

 

'네트워크' 카테고리의 다른 글

12. 네트워크 프로그래밍  (0) 2023.09.10