[sw정글-북] 📚CSAPP 8장 Exceptional Control Flow

Date:     Updated:

카테고리:

태그:

📚CSAPP 8장 Exceptional Control Flow

🚀노션에서 업데이트 된 내용으로 보기

📚CSAPP 8장 <== 클릭

📚CSAPP 8장 <== 클릭

📚CSAPP 8장 <== 클릭

📕start

  • 8.1 Exceptions
    • 예외적인 제외흐름
      • 컴퓨터에게 명령한다!!
      • Instruction을 쭉~ 따라 실행시키는데
        • PC카운터가 (Ak, Ak+1, Ak+2….)
        • 주우욱… 프로그램 카운터를 따라서, 실행됨
      • 하지만,
        • 컴퓨터가 명령어를 차례차례 수행하던 와중 어떤 문제가 생기거나, 특정 방향으로 제어하고 싶을 때
          • 제어흐름을 바꾸어야함
          • (= 예외적인 제외흐름) = ECF(Exceptional Control Flow)
    • 예외상황 처리하고

      • 프로그램을 종료할수도, 원래의 프로그램으로 돌아올 수도 있음
    • 예외테이블
      • 시스템을 부팅시 생성
      • 한 시스템 내에서, 가능한 예외상황들에 대해 예외 번호를 할당
      • 운영체제 커널에서 생성한것도 있음
      • OS서브루틴을 실행시킴(=간접프로시저 콜을 실행)
        • 핸들러 역할을 함
    • 예외상황과 프로시저콜의 차이점
      • 프로시저콜을 사용해서, 스택에 리턴주소를 푸시
      • 이게 예외상황 그 자체랑은 다름
      • 예외상황이 생기면, 프로시저콜이 리턴주소를 스택에 넣고 핸들러를 실행시킴으로써 알아서 잘 처리하고 다시 돌아오는 것이다
    • 운영체제는
      • 커널모드, 사용자모드 두가지가 있음
      • 커널모드는 운영체제의 핵심
        • 시스템콜을 통해 커널모드(컴터의 low level의 중요한부분 건드리는거)
        • 우리가 일반적으로 사용하는 프로그램들은, 사용자모드에서 돌아감
        • 예외처리 핸들러는 커널모드에서 돌아감
    • 8.1.2 예외의 종류
      • interrupt, trap, fault, abort 4가지가 있다
      • interrupt
        • 비동기적 예외사항
        • interrupt만 유일하게, instruction으로 인한 예외상황이 아님
        • 나머지 세개는 instruction으로 생긴 예외사항
        • instruction과 상관없이, (인터넷이 끊겼다던지, 누가 갑자기 이상한 버튼 눌렸다던지) 일어나는 사건들에 대한 예외
      • trap and system call
        • 일부러 만든 예외상황
        • systemcall
          • 커널루틴을 호출하는 예외핸들러로 가게 함
          • 커널에 접근할 수 있는 특권을 가진 instruction을 실행할 수 있도록 해줌

      • fault
        • fault(오류)와 error는 다르다
        • 핸들러가 처리할 수 있으면 fault
        • 오류를 수정할 수 없으면 프로그램 종료
      • abort
        • 복구할 수 없는 치명적인 에러
    • 8.1.3 리눅스 x86-64 시스템콜

  • 8.2 Processes
    • 실행 프로그램
    • 마치 운영프로그램을 독점하듯, 메모리를 독점하듯 보임
    • 8.2.1 논리적인 제어흐름
      • PC값들의 배열을 논리적인 제어흐름이라고 한다

      • 위그림을 보면, 여러 프로세스들이 있다. 이게 교대로 돌아가는 것임
    • 8.2.2 concurrent flow
      • 프로세서 A와 프로세서B가 실행시간이 겹침
      • 멀티태스킹
        • 다른 프로세스랑 교대로 실행됨(하나의 코어 내에서)
      • 병렬흐름
        • 멀티프로세싱같은거, 여러개의 코어에서 여러개의 작업 실행됨
    • 8.2.3 Private address space
      • 한 프로그램이 메모리독차지?? ⇒ nono, 사실 아님
      • 프로그램들은 자신만의 주소공간을 제공함(다쓰는거 아님, 난 여기까지만 쓰는거)

      • (위 그림)stack의 top부터 데이터를 넣어줌
        • 커널에서 사용할 수 있기 때문에, 맨 윗공간은 남겨둠
    • 8.2.4 user and kernel modes
      • 사용자들이 못쓰도록, 제한적으로 제공
      • 모드비트(mode bit)
        • 모드비트를 설정하면…사용자모드에서 커널모드로 동작
        • supervisor모드라고도 불림
      • user program은 syscall을 통해 커널에게 간접접근
    • 8.2.5 context switch
      • context switch를 통해 multitasking 사용

      • 그림보면, 유저영역이랑 커널 영역 와리가리 치면서 돌아다님
        • 터미널에 ㅇㅇㅇ읽어! 명령 하면 유저모드에서 커널모드로 전환됨
        • 다 읽고 나면, 다시 유저모드로 컴백
      • 스케줄링
        • 실행시키다가, context_switch가 필요하면 스케줄링 해줌
          1. 이전 컨텍스트 저장
          2. 일시저장된 컨텍스트 복원
          3. 새롭게 복원된 프로세스로 전달한다
        • 요약:
          • 걍 sleep시켜두고, 다른거하다가 다시 돌아옴
      • 예시
        • read실행시키면,
        • 메모리 접근해야되니까 잠깐 커널모드
        • 잠깐 커널모드 해둔 상황에서, 놀수는 없지 ⇒ 하던거 잠깐 좀더 하고 있음

  • 8.3 System Call Error Handling
    • 포크 함수

         if ((pid = fork()) < 0) {
         fprintf(stderr, "fork error: %s\n", strerror(errno));
         exit(0);
         }
      
    • strerror 함수는 errno의 특정값과 연계된 에러 string을 리턴.
    • 이 코드를 다음과 같이 단순화 가능

         void unix_error(char *msg) /* Unix-style error */
         {
         fprintf(stderr, "%s: %s\n", msg, strerror(errno));
         exit(0);
         }
      
    • 위처럼 함수로 쓰면???? 아래처럼 두줄로 소환

        if ((pid = fork()) < 0)
        unix_error("fork error");
      
    • 이거를 Stevens가 개발한 error handling wrappers 이용 단순화 가능

        pid_t Fork(void)
         {
         pid_t pid;
              
         if ((pid = fork()) < 0)
         unix_error("Fork error");
              
         return pid;
         }
      
    • 그렇다면! 다음과 같이 한줄 가능

        pid = Fork();
      
    • 대문자는 wrapper을, 소문자는 기본이름을 나타냄
    • 8.3장의 결론
      • 에러핸들링 중요하니까 좀 써라
      • 코드길다고? ⇒ 노노, stevens아저씨가 만든 wrapper쓰면 사용시 코드 1줄로 짧아지니까, 에러핸들러좀 써줘라
  • 8.4 Process Control
    • Unix는 C로부터 프로세스를 제어하기 위한 많은 시스템콜을 제공, 그중 중요한 함수들을 설명하고 어떻게 사용되는지 예제를 제공
    • 8.4.1 Obtaining Process IDs
      • PID(process ID)
      • getpid는 호출하는 함수의 PID를 리턴함
        #include <sys/types.h>
        #include <unistd.h>
        pid_t getpid(void);
        pid_t getppid(void);
      
      • 위의 함수들은 정수값 리턴(types.h에 정의되어있음)
    • 8.4.2 Creating and Terminating Processes
      • 프로그래머 관점 프로세스는 다음과 같은 세가지
        • Running
          • 프로세스는 CPU에서 실행중이겠지???
        • Stopped(별로 안중요)
          • SIGSTOP, SIGTSTP, SIGTTOU 등등 다양한 중지 시그널을 받게 됨
          • 위의 시그널 받으면 정지상태임
          • SIGCOUNT 시그널 받으면 다시 달리기 시작함
        • Terminated
          • 프로그램종료 3가지 경우
            • (1)종료하라고 하거나 (2)메인루틴 끝냈을때 (3)exit함수 호출될 때
      • Fork함수
        • 뭐하는 함수인가
          • 자식프로세스를 만들어냄
          • 자식프로세스는 엄마함수랑 거의 동일함
          • 엄마가 할 수 있는거 다 할수 있지만, 엄마함수랑 다른 PID를 가짐
        • 엄청 헷갈린다
          • 헷갈리는 이유: 한번호출되지만, 두번 리턴하기 때문
          • 첫번째 리턴
            • 부모에서 fork하면 자식의 PID를 리턴
          • 두번째 리턴
            • 자식에서의 fork는 0을 리턴한다
        • 사용법
          • 아래 코드 돌리면

              1 int main()
              2 {
              3 pid_t pid;
              4 int x = 1;
              5
              6 pid = Fork();
              7 if (pid == 0) { /* Child */
              8 printf("child : x=%d\n", ++x);
              9 exit(0);
              10 }
              11
              12 /* Parent */
              13 printf("parent: x=%d\n", --x);
              14 exit(0);
              15 }
            
          • 다음과 같은 결과

              //linux> ./fork
              parent: x=0 
              child : x=2
            
          • 아래와 같이 작동

          • 하나만 부르면 쉽지…….

            • 여러개 한번 불러보도록 하겠음

                1 int main()
                2 {
                3 Fork();
                4 Fork();
                5 printf("hello\n");
                6 exit(0);
                7 }
              
            • fork 2번하면… 4번 실행됨(😇…)

    • 8.4.3 Reaping Child Processes(프로세서 청소)
      • 좀비
        • 프로그램이 죽지않고 계속 실행되어서 주욱~메모리잡아먹고 앉아있음(좀비 프로세서)
        • 따라서, 잘 종료 해 주는것도 중요
      • waitpid로 프로그램 죽여벌임
      • 안중요해보이는 내용들(노란 영역)
        • WNOHANG, WUNTRACED, WCONTINUED 들로 waitpid 사용법 변경 가능
        • 그리고 WIFEXITED, WEXITSTATUS, WTERMSIG 등등으로, 종료상태 체크 가능
          • 정상적으로 종료되었는지
          • 시그널을 받지못해서 종료된건지
          • 종료시킨 시그널은 어쩐지 등등
      • waitpid는 사용법이 어렵다
        • 종료시킬때도, 자식 process들이 있다면 어떻게 종료될지 알 수 없다
        • 최종 결과는 같지만, 컴퓨터시스템 특성에 따라 예측할수 없는 순서로 청소(종료)됨
    • 8.4.4 Putting Processes to Sleep
      • sleep으로 재우면, 요청시간동안 프로그램 멈춰있음
      • pause으로 재우면, 해당 함수 다시 부를때까지 멈춰있음
    • 8.4.5 Loading and Running Programs
      • execve함수
        • 현재 프로그램 내에서 새로운 프로그램을 로드하고 실행함
        • 파일 이름을 찾을 수 없을때만, 다시 돌아감(리턴됨)
        • 따라서,
          • 한번 호출되고 절대로 리턴하지 않음
          • (참조: 아까 fork는 한번호출하고 두번 리턴함)
      • execve함수 작동
        • 작동시 필요한 세가지
          • executable object file의 파일명
          • 인자리스트 argv

          • 환경변수리스트 envp

        • 작동모습(아래사진)

          • 스택 맨위를 보면
            • libc_start_main이 있음
            • (7.9장 내용)해당함수가 시스템 초기화 시켜줌
            • “자~~ 다들 오늘 하루도 잘해봅시다 이제 프로그램 출발합니다~” 하고 초기화 해버리는 역할
          • 그다음
            • 인자리스트, 환경변수 읽어들이면서 프로그램 실행
    • 8.4.6 Using fork and execve to Run Programs
      • sh
        • 쉘의 역할
          • 사용자를 대신해, 다른 프로그램을 실행시켜주는 역할
        • 쉘의 후배들
          • csh, zsh, bash 등 나중에 만들어졌음
        • 쉘의 실행순서
          1. 읽기: 사용자가 알려준 명령줄 읽음
          2. 계산: 명령줄 분석해서 프로그램 실행시켜줌
      • 쉘프로그램 실행순서
        • 코드(클릭)

            1 #include "csapp.h"
            2 #define MAXARGS 128
            3
            4 /* Function prototypes */
            5 void eval(char *cmdline);
            6 int parseline(char *buf, char **argv);
            7 int builtin_command(char **argv);
            8
            9 int main()
            10 {
            11 char cmdline[MAXLINE]; /* Command line */
            12
            13 while (1) {
            14 /* Read */
            15 printf("> ");
            16 Fgets(cmdline, MAXLINE, stdin);
            17 if (feof(stdin))
            18 exit(0);
            19
            20 /* Evaluate */
            21 eval(cmdline);
            22 }
            23 }
          
        • 코드 해석
          • parseline함수 호출
            • (사용자가 입력한)스페이스로 구분된 명령(들) 분석
          • execve로 전달될 argv벡터를 만듬
            • 첫번째 인자
              • pip install 어쩌고 저쩌고
              • unzip A파일 B파일 -o 어쩌고저쩌고 압축해제
              • 위처럼, 첫인자는 즉시 해석가능한 내부 쉘 명령어
              • 혹은, 실행가능한 목적파일
            • &인자(앰퍼서드)
              • 백그라운드에서 계속 열어둬라
          • 파싱 끝나면
            • biltin_command함수 호출해서, 내장명령어인지 여부 체크
            • 내장명령어가 아니라면?
              • 쉘은 자식 프로세스를 만들고, 자식 프로그램을 실행
              • waitpid함수로 작업 종료될때까지 대기
        • 코드의 오류
          • 위의 코드는 쉘이 백그라운드를 전혀 청소하지 않음(zombie 제거 안함)
          • 8.5장의 시그널을 사용하여 좀비프로세스 해결해야함
  • 8.5 Signals
    • 하위수준의 하드웨어, 상위수준의 소프트웨어 두개의 시그널이 있음
    • 상위수준의 소프트웨어를 제어하기 위한게 signal
    • 가끔 하위수준 하드웨어를 관리하기도 함
    • 시그널을 제어할 때는 항상 작은 메시지가 같이 뜸
      • 어떤 이벤트가 발생했답니다!~ 라고 알려주는 메시지
    • 잘못된 메모리 참조, 0으로 나눌때 등 하위 수준에 관여함, kill함수로 종료할 때
    • 두가지 절차
      • signal 보내기
        • 하위수준 컨트롤 하기 위해
        • 의도적으로(상위수준에서) 요구할 때
      • signal 받기
        • signal handler가 처리해줌
        • 사용자 수준 함수를 내가 설정 할 수 있음
          • signal을 무시하는 방법
          • 프로그램을 종료하는 방법
          • signal대로 프로그램이 받아서 획득
      • pending
        • pending이 이루어지는 두가지 상황(ex, 수민이 찾아오는 상황)
          1. 블럭처리 해두었기 때문에pending
            • 진섭: “수민이 말은 앞으로 무시할게~”
          2. 다른 작업중이기때문에 pending
            • 진섭: “수민아 중선이랑 이야기중이니 바쁘다, 조금 이따 와라~”
        • 진섭에게 여러가지 기능이 있을텐데(대화기능, 공부기능, 식사기능, 이동 기능)
          • 같은종류(진섭-대화기능 사용중에, 수민이가 대화하려고 오면)가 오면, 한타입당 1개만 pending할 수 있음
        • 프로세스들을 그룹핑 해두면, 한번에 다 받을 수 있음
      • core dump
        • 특정 시점 상태에서의 메모리를 기록한다
        • 아 ㅈ 댔다… 에러나서 종료해야돼(비상🚨), 그래서 빨리 기록해야함
        • 심각한 오류가 있을 때, 강제로 프로그램이 꺼진다는 뜻으로 와전되어있긴 함
    • 8.5.1 signal Terminology
    • 8.5.2 시그널 보내기
      • 시그널을 보내는 방법은 여러가지가 있다
        1. 그룹
        • 한방에 모아서 보내면 효율적임
        • 다음 그림은 서로다른 pid를 group-pid(pgid)=20으로 묶어서 한방에 kill

      1. 키보드로 보내기
        • control + Z
        • control + C
      2. bin/kill/
      3. 알람함수
    • 8.5.3 시그널 수신
      • 핸들러 installing
        • 핸들러 넘겨준다는 말
      • 핸들러를 catching
        • 핸들러를 호출한다는 말
      • 핸들러 handling
        • 핸들러를 실행시킨다는 말
    • 8.5.4 시그널 블록, 블록 해제
      • 묵시적 블록방법
        • 내가 안해줘도, 법칙에 의해 블록
        • 한가지 타입 시그널 실행중이면, 내가 따로 설정 안해줘도 블록처리됨
      • 명시적 블록방법
        • 앞으로 ㅇㅇ말은 안들을꺼야!
        • 내가 명시적으로 블록처리해줌
      • 각 시그널마다 블록된거 1, 블록되지 않은거 0으로 됨
    • 8.5.5 시그널 핸들러 작성하기
      • 시그널이라는건 제어권을 가져오는 것
      • 동시성(concurrent)의 문제가 발생할 수 있음
        • 따라서, 시그널 핸들러는 아주 보수적으로 처리해야함
      • 문제점 3개
        • 같은전역변수로 뒤섞일 수 있음
        • 어떻게, 그리고 언제 시그널이 올 수 있는지 직관적이지 않음
        • 다른 시스템은 다른 시그널 처리방식을 가짐
      • 안전한 시그널 작성
        • 가장 작고, 가장 단순하게 시그널 핸들러를작성해야함
        • 핸들러 작성 진~~짜 어려움
        • malloc, exit, printf등등 안전하지 않음(또 웃긴게, _exit은 안전하다고 함)
        • 출력관련 유일한 안전한 방법은 write
        • 그냥 뭘 해도 다 안전하지 않다고함(그많큼 어렵다는 거지)
      • 캐시로부터 보호
        • volatile
          • volatile int c;
          • 야, int c저친구는, 캐시에다가 넣지 지마!! 캐시 금지!!
        • 캐시는 결국, 경향성을 따져서 자기가 캐싱해버리는거니까 위험함, 그래서 캐싱 금지시키는 것
      • 중단을 시킬 수 없도록 하는 방법
        • atomic 처리를 해줄 수 있음
        • 원자는 쪼갤 수 없으니까… 쪼개지 말고, 중단시키지도 말라는 뜻으로 작명된 듯(추측)
      • 정확한 시그널 처리
        • 좀비 프로세스 만들어지는 사례…
          • 시그널의 특성때문에, 값이 없는 자식이 만들어지는 상황 등
        • 시그널 이벤트를 일일히 카운트 할 수 없다!!!
          • 지멋대로 펜딩될때도 있고, 실행되기 때문에 그걸 카운팅하면 안됨
      • 호환성
        • 운영체제마다 signal의 의미가 다름
        • 어떤 시스템콜은 느림
          • pending에 들어갈수도, 안들어 갈 수도 있고, 중단될 수도 있음
          • 따라서 느린 시스템콜들에 대해서는 재시작 코드를 삽입해주어야 한다
    • 8.5.6 Synchronizing Flows to Avoid Nasty Concurrency Bugs
      • 프로그램은 몇개인데, 시그널은 엄청 많으니까 시그널이 기하급수적으로 늘어남
      • 따라서 버그가 발생하기 쉬움
      • 경주
        • 어떨때는 삭제가 더 빠르고, 어떨때는 자식들을 쉬게 하는 녀석들이 더 빠르기도 하고, 어떨때는 프로그램 제어(생성, 호출) 등이 빠름
        • 어떤 순서로 처리될지 알 수 없음
        • 따라서 때에 따라서는 좀비프로세서가 만들어 지기도 함
    • 8.5.7 Explicitly Waiting for Signals
      • pause 하려고 할 때 문제가 생길 수 있음
        • pause를 사용하려고 하면 경쟁조건이 발생할 수 있음
      • sleep 을 하려고 해도 문제가 생길 수 있음
        • 잠을 자야하는 길이를 결정할 수 있는 결정규칙이 없음
      • sigsuspend
        • 이걸로 멈춰두면 좋다
  • 8.6 Nonlocal Jumps
    • setjump longjump 두가지가 있음
      • 엄청 고급기술임
      • 원하는 위치로 이동
  • 8.7 Tools for Manipulating Processes
    • pass
  • 8.8 Summary
    • pass


😵배우면서 깨달은 내용을 정리해 보았습니다. 틀린 것 같은 개념을 아래 댓글에 달아주시면 감사합니다😵

🌜 Thank you for reading it. Please leave your comments below😄

맨 위로 이동하기

swjungle CSAPP 카테고리 내 다른 글 보러가기

댓글 남기기