[F-Lab 모각코 챌린지 21일차]

부추·2023년 6월 21일
0

F-Lab 모각코 챌린지

목록 보기
21/66

TIL

  1. telnet으로 HTTP 요청 날리기
  2. CPU 스케줄링 알고리즘
  3. 시저 암호화 간단 실습



1. Telnet으로 HTTP 요청 날리기

보통 telnet은 23번 포트로 접속하여 원격으로 컴퓨터를 조작하는데 쓰이지만, 대상 IP 컴퓨터의 특정 포트가 열려있는지 확인하는 용도로 쓰이기도 한다.

$ brew install telnet

telnet을 다운받고, HTTP 요청(80번)을 날려봤다.

$ telnet google.com 80

google.com에 대한 IP 주소에 80번 포트로 접근되었다! 이제 GET 요청을 날려보겠다.

응답 라인, 헤더가 쭉~ 펼쳐지고 그 아래 body 부분까지 나왔다.

동일한 포맷으로 naver.com에도 80포트 HTTP 요청을 날려봤다.
302 Moved Temporarily 응답이 떴다. Location 헤더부분의 URL은 https://www.naver.com/notfound.html 였고.. 접속해보면 네이버의 404 not found 페이지가 나온다.




2. CPU 스케줄링 알고리즘

프로세스 스케줄링을 위해 CPU는 PCB(Process Control Block)를 이용한다. PCB에는 운영체제가 프로세스를 관리하기 위한 기본적인 정보들이 들어있는데, CPU 스케줄링을 위한 정보 역시 여기에 포함된다. 대표적으로 프로세스의 우선순위, 현재 프로세스의 상태 등이 있다.

# 프로세스 상태

프로세스의 상태 다이어그램이다.

새로 생성된 프로세스 A(new)는 레디 큐에 들어간다(ready). 레디 큐에는 CPU에게 실행되기를 기다리는 프로세스들이 줄서있는 공간이다. 큐의 앞에 존재하는 프로세스들이 처리되고 A의 차례가 되면, 프로세스 A의 명령어들이 CPU에 의해 실행된다(running). 프로세스 진행을 위해 IO 작업이 필요하면 IO block이 일어나 IO 작업 대기 큐에 들어가게 되는데(waiting), IO 작업이 끝나면 다시 레디 큐로 돌아가서 CPU 처리를 기다린다. running중인 프로세스 A에게 할당된 시간이 끝나 interrupt가 발생하면 다시 레디 큐에 돌아가 자신의 차례가 되길 기다린다(ready). 프로세스의 전 과정이 끝나면 종료된다.(terminated)

레디 큐는 CPU의 작업을 대기하는 프로세스들이, 각 IO 장치의 대기 큐에는 IO장치의 입출력을 기다리는 프로세스들이 존재한다. OS에서 스케줄링의 대상이 되는 프로세스들은 이렇게 대부분 큐(queue)의 형태로 관리된다.


# 프로세스 우선순위

앞서 프로세스들은 보통 큐의 형태로 관리된다고 했지만, 모든 프로세스 스케줄링이 FIFO의 방식으로 동작하는 것은 아니다. 프로세스마다 우선순위가 다르다. 다시말해 우선순위가 높은 어떤 프로세스는 이미 레디 큐에 존재하는 프로세스보다 늦게 큐에 들어왔음에도 먼저 CPU에게 할당될 수 있다.

보통 IO bound 프로세스가 그렇다. IO bound 프로세스란 프로세스 작업의 대부분이 IO와 관련된 프로세스인데, 이 프로세스들은 실행시 CPU를 차지하는 시간이 길지 않기 때문에 CPU bound 프로세스보다 먼저 휙휙 처리해버리고 IO 작업에 집중할 수 있도록 하는 것이 낫기 때문이다. 만약 CPU bound 프로세스가 우선순위가 높다면 IO bound 프로세스는 별로 음료수 하나 사러 마트 왔는데 계산대 앞사람이 50만원어치 장을 보고 있다면 화가 나겠지 응응.


본격적으로 대표적인 CPU 스케줄링 알고리즘 몇 가지를 알아보자. 프로세스 우선순위를 설명할 때와 일맥상통하는 얘기로, CPU를 사용하는 시간이 적은 프로세스를 먼저 처리하는 것이 좋다. 그래야 전체 프로세스의 대기시간을 줄일 수 있기 때문이다. 정말 이런지는.. 스케줄링 알고리즘 각각의 예시를 들면서 설명하겠다. 처리에 각각 4초, 2초, 1초가 걸리는 프로세스 A,B,C가 있다고 가정하겠다.

2-1) FCFS : First Come First Serve

비선점형 FCFS는 레디 큐에 삽입된 프로세스 순서대로 처리하는 (설명하기 민망할 정도로) 단순한 알고리즘이다. A -> B -> C 순서대로 프로세스가 레디큐에 삽입되었다고 했을 때 실행 순서 다이어그램은 다음과 같다.
평균 대기시간은 B가 4초, C가 6초로 약 3.3초이다. 보면 알겠지만 C는 빡쳐있는 상태일 것이다. 내 실행시간은 1초밖에 안되는데 6초를 기다려야한다니 환장할 노릇이다.


2-2) SJF : Shortest Job First

그런 C의 마음을 달래주기 위해 고안한 알고리즘이다. 무식한 선착순이 아닌, 가장 짧은 실행시간을 가진 프로세스가 가장 먼저 실행된다.
이렇게 되면 프로세스들의 평균 대기시간이 획기적으로 줄어든다. 게다가 preemtive 방식일 경우, 이 알고리즘은 이론상 평균 대기시간의 측면에서 가장 완벽한 CPU 스케줄링 방식이 된다.

그러나 프로세스가 종료까지 얼마나 많은 시간이 걸릴진 아무도 예상하지 못한다. 전반적인 경향성을 살펴서 비슷하게 만들어볼 수는 있으나, SJF를 완전히 구현하는 것은 이론상의 얘기일 뿐이다.

2-3) Round Robin

FCFS에 타임 슬라이스의 개념이 추가된 스케줄링 알고리즘이다. 레디 큐의 삽입 순서대로 프로세스를 실행하되, 정해진 타임 슬라이스만큼의 시간이 지나면 해당 프로세스의 실행을 멈추고 프로세스를 레디 큐의 맨 뒤로 보내는 것이다. 다음은 같은 상황에서 타임슬라이스가 1일 때 라운드로빈의 동작 다이어그램이다.
타임 슬라이스에 대해 정해진 규약은 없다. 다만 너무 길면 FCFS와 다를게 없어지고, 너무 짧으면 Context Switching의 오버헤드가 커지게 된다. 현대 스케줄링 방식은 기본적으로 이같은 RR 방식을 베이스로 깔고간다.

더해서 타임 슬라이스가 끝난 후 프로세스가 완료되기까지 잔여시간을 계산에서 그 잔여시간이 가장 짧은 프로세스를 레디큐의 앞에 할당하는 SRT(Shortest Remaining Time)도 있는데, 역시 SJF과 마찬가지로 이론상의 스케줄링이다.


2-4) 다단계 피드백 큐 스케줄링

가장 먼저 우선순위 스케줄링이 존재한다. 간단하게 우선순위가 높은 프로세스를 먼저 실행하는 것이다. 그치만 모든 스케줄링을 이렇게 적용했을 경우, 우선순위가 낮은 프로세스는 영원히 실행되지 못하는 starvation 문제가 발생한다. 이를 방지하기 위해 프로세스에 aging을 적용한다. 일정 시간이 지나도 CPU에 할당되지 않은 프로세스의 우선순위를 한 단계씩 높이는 방법이다.

이를 발전시킨게 다단계 큐 스케줄링이다. 여러 개의 레디 큐를 두는 방식이다. IO bound 프로세스는 상위 우선순위 큐에, CPU bound 프로세스는 하위 우선순위 큐에 둔다. 같은 우선순위를 가진 프로세스들은 FCFS 방식으로 동작한다. 각 큐마다 타임 슬라이스나 스케줄링 알고리즘을 다르게 적용시킬 수 있다.

이를 한단계 더! 발전시킨게 다단계 피드백 큐 스케줄링이다. 기본적 내용은 다단계 큐 스케줄링과 같으나, 여기선 프로세스들이 서로다른 우선순위의 레디 큐를 이동할 수 있다는 특징이 있다. 일단 그림을 보자.
기본적으로 우선순위가 높은 프로세스가 높은 우선순위 레디큐에 존재한다. CPU는 높은 우선순위 레디큐의 프로세스부터 실행한다. 상위 레디 큐가 비워져야 그 다음 레디 큐의 프로세스들을 처리할 수 있다.

우선순위는 높지만 CPU타임을 너무 많이 쓰는 프로세스에게 많은 CPU 타임을 할당하는 것은 비효율적이다. 주어진 타임 슬라이스동안 완료되지 못한 프로세스는 하위 레디큐로 쫓아낸다. 반대로, 하위 레디큐에 있으면서 계속 CPU에게 할당받지 못하는 프로세스는 starvation상태에 있다. 얼른 실행시켜줘야 하므로 상위 레디큐로 이동시켜준다.

구현이 복잡하고 생각해야 할 것도 많은 스케줄링 방식이지만, 현대 OS에서 가장 일반적으로 사용하는 CPU 스케줄링 알고리즘이다.




3. 시저 암호화 초간단 실습

# 시저 암호화

매우 간단한 치환 암호화이다. 암호화 하고자 하는 plain text를 알파벳 순서 기준으로 일정 거리만큼 밀어서 치환하는 알고리즘을 사용한다.

# 암호/복호화 코드

CaesarEncryption 클래스는 실제 암호화를 하는 encrypt(), decrypt() 메소드를 작성했다.

public final class CaesarEncryption {
    private final int distance;
    CaesarEncryption(int distance) {
        this.distance = distance;
    }

    public String encrypt(String plain) {
        StringBuilder cipher = new StringBuilder();
        for (Character c : plain.toCharArray()) {
            cipher.append((char) ((int) c + distance));
        }
        return cipher.toString();
    }

    public String decrypt(String cipher) {
        StringBuilder plain = new StringBuilder();
        for (Character c : cipher.toCharArray()) {
            plain.append((char) ((int) c - distance));
        }
        return plain.toString();
    }
}
  • 생성자로 이 CaesarEncryption를 이용해 암호화를 할 때, 알파벳 몇 칸을 미룰 것인지 설정할 수 있도록 했다. 해당 값은 "distance"라는 변수에 저장하도록 했는데, 필드와 클래스 자체를 final로 두어 외부에서 값의 접근이나 수정을 불가하게 만들었다.
  • encrypt()와 decrypt()는 모두 StringBuilder을 이용했다.
  • encrypt()에서는 암호화하고자 하는 문자열을 받아 아스키 코드 단위로 distance 거리만큼 "밀었다". decrypt()는 같은 과정을 정확히 반대로 진행했다.
  • Character 클래스 타입을 int로 형변환하면 아스키 코드로 변환됨을 이용했다. 따라서 이 메소드는 알파벳 범위 안의 완전한 카이사르 암호화를 구현했다기보단, 아스키 코드 기준의 치환 암호화를 했다고 보는 것이 더 맞겠다.

# 파일의 내용을 읽고 쓰는 기능

내부 구성으로 CaesarEncryption 클래스 객체를 두고 있다. IO를 위해 BufferedReader / BufferedWriter를 사용했다.

public class CaesarFile {
    private CaesarEncryption caesarEncryption;
    CaesarFile(CaesarEncryption caesarEncryption) {
        this.caesarEncryption = caesarEncryption;
    }

    void encrypt(String in, String out) {
        try (BufferedReader reader = new BufferedReader(new FileReader(in));
             BufferedWriter writer = new BufferedWriter(new FileWriter(out))) {
            String line = "";
            while ((line = reader.readLine()) != null) {
                writer.write(caesarEncryption.encrypt(line));
                writer.newLine();
            }
        }  catch (IOException e) {
            e.printStackTrace();
        }
    }

    void decrypt(String in, String out) {
        try (BufferedReader reader = new BufferedReader(new FileReader(in));
             BufferedWriter writer = new BufferedWriter(new FileWriter(out))) {
            String line = "";
            while ((line = reader.readLine()) != null) {
                writer.write(caesarEncryption.decrypt(line));
                writer.newLine();
            }
        }  catch (IOException e) {
            e.printStackTrace();
        }
    }
}
  • encrypt() : 암호화할 파일의 이름, 암호화해서 저장할 파일의 이름을 각각 인자로 받는다. readLine() 메소드를 통해 줄 단위로 파일 내용을 읽고, 해당 line 문자열을 그대로 CaesarEncryption.encrypt()에 보낸 결과를 저장했다.
  • decrypt() : 암호화된 파일의 이름, 복호화해서 저장할 파일의 이름을 각각 인자로 받는다. encrypt()와 정확히 같은 과정이지만 호출하는 메소드가 CaesarEncryption.decrypt()라는 차이가 있다.

# 실행해보기

클라이언트 main() 메소드를 작성했다.

public class Client {
    public static void main(String[] args) {
        CaesarEncryption caesarEncryption = new CaesarEncryption(3);
        CaesarFile caesarFile = new CaesarFile(caesarEncryption);

        caesarFile.encrypt("hello.txt","encrypt.txt");
        caesarFile.decrypt("encrypt.txt","decrypt.txt");
    }
}
  • 카이사르 암호화에 사용할 distance는 3이다. 아스키 코드 기준으로 3 거리만큼 떨어진 문자로 치환 암호화를 할 것이다.
  • 인풋 파일 이름은 "hello.txt"이다. 암호화 결과는 "encrypt.txt"에 저장된다.
  • 암호화된 파일 "encrypt.txt"를 복호화해서 "decrypt.txt"에 다시 저장할 것이다.

실행 결과의 "hello.txt"와 "decrypt.txt"에 저장된 파일 내용은 같아야 할 것이다! 실행해보자.


hello.txt :

안녕! 내 이름은 부추야.
나이는.. 비밀이야. 20대라는 것만 알려줄게.
지금 시저 ? 카이사르 ? 어떻게 읽어야할지 모르겠는데, 아무튼 그걸 이용해서 암호화하는 자바 코드를 작성했어.
그 실습을 위한 input 파일이야!
이 내용들은 암호화되어 저장될거야.
한 번 확인해보자! ^^

encrypt.txt :

앋녘$#낷#읷릇읃#붃춗앿1
낛읷늗11#빇밃읷앿1#53댃띿늗#겆맏#앏렧줇겏1
짃긋#싟젃#B#칷읷삯륷#B#얷떾겏#잀얷앿핣짃#몫륷겣늗덳/#앇묷튿#귻걻#읷욬핷섟#앗혻홗핛늗#잓밗#콗듟륿#잔섴햋얷1
귻#싧슸읇#윇핟#lqsxw#팏읿읷앿$
읷#낷욬듧읃#앗혻홗됛얷#젃잨됣걳앿1
핟#벋#환읻핷볷잓$#aa

decrypt.txt :

안녕! 내 이름은 부추야.
나이는.. 비밀이야. 20대라는 것만 알려줄게.
지금 시저 ? 카이사르 ? 어떻게 읽어야할지 모르겠는데, 아무튼 그걸 이용해서 암호화하는 자바 코드를 작성했어.
그 실습을 위한 input 파일이야!
이 내용들은 암호화되어 저장될거야.
한 번 확인해보자! ^^

실행은 잘 된 듯 하지만, distance 값이 3으로 짧은 탓 + 자모자 방식을 택하는 한글을 아스키코드로 할당했을 때 가까운 거리의 글자는 많이 닮아있다는 탓 덕분에 encrypt 파일을 봐도 원본 내용이 쉽게 노출되는듯 ^ㅆ^;;




REFERENCE

https://ko.wikipedia.org/wiki/%EC%B9%B4%EC%9D%B4%EC%82%AC%EB%A5%B4_%EC%95%94%ED%98%B8

https://jhnyang.tistory.com/7

혼자 공부하는 컴퓨터구조 + 운영체제 11장

profile
부추튀김인지 부추전일지 모를 정도로 빠싹한 부추전을 먹을래

0개의 댓글