Computer Science/시스템 프로그래밍

[시스템 프로그래밍] Chapter 2

seungwon9201 2024. 4. 6. 20:16

프로그램이란  static 한 객체로 사용자들이 프로그램이 실행이 될 때 그 instruction squence를 나열한 것 

이 프로그램을 실행하면 프로세스가 된다.

 

Program counter : 다음에 실행할 instruction의 주소가 저장되어있음

스레드 실행 예시

ex) 프로세스 안에 245, 246, 247이 있다고 가정

결과 - 245, 246, 247

ex) 두 개의 프로세스이고 프로세스 1은  245, 246, 247이 다른 하나는 10, 11, 12, 13이 있다고 가정

만약 OS가 1번 프로세스를  먼저 실행하고 프로세스 1이 5개의 instruction을 하고 스위칭을 하고 프로세스 2는 4개의 instruction을 하고 스위칭한다고 가정하자. context switch(실행할 프로세스가 변경되는 과정)

결과 - 245, 246, 247. 245, 246, /context-switch instruction/ 10, 11, 12, 13,  /context-switch instruction/ 247, 245, 246........

 

Multiple processes vs multiple threads

하나의 프로세스 안에서 여러 개의 스레드를 사용하는 것이 context switch 오버헤드를 줄일 수 있다.

여러 개의 프로세스를 context switch 하는 오버헤드와 하나의 프로세스에 두 개의 스레드를 스위치 하는 오버헤드를 생각을 하면 스레드를 스위칭하는 오버헤드가 더 적기 때문 왜? 스레드는 프로세스의 일종의 subset 개념이라서 스레드가 관리하는 정보와 프로세스가 관리하는 정보의 양을 비교하면 스레드가 더 적음 그래서 context switch가 일어날 때 멈추는 프로세스는 프로세스의 상태를 저장해야 하는데 스레드가 보유한 정보의 양이 더 적기 때문에 전체적으로 퍼포먼스는 스레드를 스위칭하는 게 더 좋음

스레드들 간에는 공유하는 메모리가 존재할 수 있어서 그 공유하는 메모리를 이용해서 데이터를 더 쉽게 이용할 수 있음(OS의 도움 없이 효율적으로 데이터를 주고받을 수 있음)

프로세스들 간에 정보를 공유하려면 OS를 이용해야 해서 오버헤드가 큰 작업이 발생함

그러나 다중스레드는 스레드 간의 동기화하는 부분을 고려해야 함. 프로그래머에게 추가적인 부담이 있음


sample layout of a program image

automatic variables : ex) int a;

activation record : 동적인 객체, 함수 호출마다 그 함수를 실행하는데 필요한 정보를 담고 있는 데이터 구조, activation record는 그 해당 함수가 호출이 될 때 stack에 생성됨

main() 함수가 호출이되면 메인함수를 실행하는데 필요한 activation record가 stack에 생성됨, 그 activation record안에는 여러정보들이 들어가 있음 main()함수가 끝나면 activation record는 stack안에서 사라져서 동적인 객체라고 함(생겼다가 없어졌다 하기 때문)

메인함수가 실행되다가 또 다른 a()라는 함수를 호출을 하면 a함수를 실행하는데 필요한 activation record가 생성이 됨 이처럼 stack이 쌓였다가 줄어듦.

heap공간은 mallocfamily(malloc, alloc, calloc) 함수를 이용해서 동적으로 메모리를 할당한 경우에 사용, 이 함수를 통해서 할당된 메모리는 heap공간에 할당을 해줌. heap공간에 할당된 메모리는 프로세스가 살아있는 동안에 계속 유지가 됨; 이 메모리를 해지하기 위해서 free() 함수를 호출해서 수동으로 호출해야 됨

stack에 있는 정보는 따로 관리를 안 해도 해당함수가 리턴이 되고 나면 자동으로 activation record가 해제돼서 별도로 메모리 관리를 할 필요가 없지만 동적메모리를 할당을 할 때는 heap공간에 쌓여서 수동으로 free()를 호출하지 않으면 계속 남이 있음 

 

Library function calls

SYNOPSIS box : 함수 스펙의 요약본

Error : 에러가 나는 경우 전통적인 UNIX는 -1을 리턴하고 errno라는 변수에 에러코드를 써줌. 새로 추가된 함수의 경우 -1을 리턴하는 게 아니라 에러코드를 바로 리턴하는 함수도 추가됨

Error handling functions

  • void perror(const char *s)

#include <stdio.h>

errno라는 에러코드에 해당하는 에러메시지를 사용자가 읽을 수 있는 형태의 에러메세지를 화면에 출력 

  • char* strerror(int errnum)

#include <stdio.h>

에러코드를 파라미터로 넘겨주게 되면 파라미터에 넘어온 에러코드에 해당하는 에러메시지를 스트링 값으로 리턴을 해줌

 

perror함수는 내가 보고 싶은 에러코드를 지정할 수 있는 게 아니라 현재 에러 변수에 설정된 에러코드에 해당하는 메시지를 출력하는 거고 strerror함수는 내가 보고 싶은 에러코드의 에러메시지를 출력해 준다.

 

어떤 함수에 에러가 발생해서 에러코드를 알기 위해 strerror함수를 호출을 했음, 근데 strerror함수조차 에러가 발생하면 새로운 에러코드가 기존에러코드를 덮어써버리는 문제가 발생하기 때문에 원래 알려고 하는 에러코드정보를 확인하고 싶으면 strerror함수를 호출하기 전에 에러변수에 적혀있는 값을 미리 다른 변수로 저장을 해놨다가 strerror함수가 사용이 끝나고 난 다음에 에러변수의 코드값을 다시 저장하는 방식으로 보존을 하면 안전함.


Good model of a function

  • 함수를 실행하다가 에러가 났다고 바로 종료하지 말고 에러값을 리턴하자
  • 불필요하게 array의 크기를 추정해서 사용하지 말 것
  • 불필요하게 임의의 값으로 상수값을 정하는 것보다는 이미 정의된 값으로 지정하자
  • 함수가 실행이 되면서 넘겨받은 파라미터(한도) 값을 가능한 수정하지 말자 
  • 정적변수나 malloc함수를 사용을 해서 동적으로 메모리를 할당하는 것을 피하자(불필요하게 메모리가 사용될 수 있음)
  •  malloc함수를 사용해서 동적메모리를 할당해서 사용하는 경우에는 free() 함수를 호출해서 해제하자.

Argument arrays 

argv는 캐릭터 포인터의 array 즉, 스트링들이 담긴 array

mine -c 10 2.0


Thread-safe strtok()

Thread-safe란 여러 개의 스레드가 동시에 함수를 호출했을 때 문제없이 동작이 되는 함수

strtok() 함수(스트링을 정해진 토큰 단위로 쪼개주는 역할을 하는 함수)는 여러 스레드가 동시에 호출을 해도 문제가 되고 하나의 스레드가 두 가지 이상의 작업을 동시에 진행을 하더라도 문제가 발생함.

char* strtok_r(char* s, const char* sep, char **lasts)로 이러한 문제들을 해결하자.(thread-safe 함)

strtok함수는 는 내부적으로 스태틱변수를 사용해서 문제가 발생했음

strtok_r함수는 마지막 파라미터를 통해서 이 함수가 저장할 인풋스트링의 스토리지를 마지막 파라미터로 별도로 지정

즉 여러 스레드가 서로 다른 파라미터를 제공하면 충돌문제가 발생하지 않아서 thread-safe 함

 

use of static variables 

함수를 여러 번 호출하면서 저장되는 값을 사용하려고 하는 경우 스태틱변수를 사용.

스태틱함수가 저장된 파일 안에서만 액세스할수있고 외부에서는 엑세스 할 수 없음

내부적으로 스태틱변수를 사용하는 경우 strtok처럼 thread-safe 하지 않은 경우가 있을 수 있기에 조심

 

Process termination

정상적으로 종료되는 경

main함수에서 리턴

exit() 함수 호출

int atexit(void (*func)(void)에 등록된 함수가 종료될 경우