커널

-> 기존에 커널 = OS였지만 시대가 변하면서 점차 의미가 분리되었다.


커널 오브젝트

커널에 의해 관리되는 리소스 정보를 담고 있는 데이터 블록

-> 윈도우에 의해 생성되고 소멸되는 리소스를 관리하기 위한 데이터 블록을 커널 오브젝트라고 한다.


-> 커널과 커널 오브젝트는 다르다. 커널 오브젝트는 프로세스와 밀접한 관련이 있다.


또한 파이프 커널 오브젝트, 프로세스 커널 오브젝트, 쓰레드 커널 오브젝트는 각각 다르게 디자인되어있다.



사용자가 프로세스를 실행하면 프로세스만 실행되는 것이 아니라 os로 인해 프로세스에 대한 커널 오브젝트가 생성되고 커널 오브젝트는 프로세스 정보를 담고있고 관리하기 위한 커널 오브젝트가 생성된다.


또한 커널 오브젝트를 관리하기 위한 커널 오브젝트 관리 구조체도 생성된다. 커널 오브젝트 관리 구조체로 인해 커널 오브젝트가 생성된다.



예)

프로세스를 생성하면 프로세스 커널 오브젝트가 생성된다.


만약 사용자가 이 프로세스의 연산 우선순위를 높이고 싶다고 생각하고 실제로 우선순위를 높이기 위해서는 커널 오브젝트에 접근하여야 하는데, 커널 오브젝트는 임의의 접근이 불가능하다.


이 때 커널 오브젝트를 다루기 위해 필요한 것이 핸들로, 사용자가 os에 프로세스를 실행하도록 요청하면 os는 프로세스와, 프로세스 커널 오브젝트와, 프로세스 커널 오브젝트 핸들(프로세스 핸들)을 생성한다.


핸들은 os에 의해 특정 숫자로 넘버링된 이름을 가지고 있는데(실제로 int형 정수로 구성되어 있다.) 이를 이용하여 커널 오브젝트에 접근할 수 있게 된다.



Operation2.exe

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
#include <tchar.h>
#include <Windows.h>
 
int _tmain(int argc, TCHAR* argv[])
{
    //우선순위를 변경하는 함수
    //GetCurrentProcess() 현재 프로세스의 핸들 정보를 가져오는 함수
    //HIGH_PRIORITY_CLASS 우선순위를 높이는 매크로
    SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);
    while (1)
    {
        //Sleep()함수는 Blocked 상태로 변환된다. 우선순위를 포기하는 것
        //우선순위를 관찰하기 위해선 Ready 상태로 두어야 한다.
        for (DWORD i = 0; i < 10000; i++)
            for (DWORD i = 0; i < 10000; i++);    //Busy Waiting!!
        _fputts(_T("Operation2.exe \n"), stdout);
    }
    return 0;
}
cs


Operation1.exe

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <stdio.h>
#include <tchar.h>
#include <Windows.h>
 
int _tmain(int argc, TCHAR* argv[])
{
    STARTUPINFO si = { 0, };
    PROCESS_INFORMATION pi;
    si.cb = sizeof(si);
    TCHAR command[] = _T("branch.exe");
    CreateProcess(NULL, command, NULLNULL, TRUE, 0NULLNULL&si, &pi);
    while (1)
    {
        for (DWORD i = 0; i < 10000; i++)
            for (DWORD i = 0; i < 10000; i++);
        _fputts(_T("Operation1.exe \n"), stdout);
    }
    return 0;
}
cs


싱글코어 기반의 출력결과

않이


듀얼이상에선 순서대로 나온다.

프로세스란?


일반인 : 실행중인 프로그램


개발자 : 메모리 구조(OS에 의해 할당되는 메인 메모리의 리소스 뿐 아니라 가상 메모리의 리소스까지 포함), 실행중인 프로그램에 독립적인 레지스터 Set, etc


요즘 프로세스는 컨텍스트 스위칭을 피하기 위해 레지스터 Set을 여러개 만들어 스위칭에 의한 오버헤드를 피하고 있다.




스케줄러 : 운영체제에서 제공해주는 소프트웨어 장치(혹은 블럭)


공평한 스케줄링 알고리즘도 큰 이슈중 하나다



Running 상태 : CPU에 의해 실행중인 상태

Ready 상태 : 스케줄러에 의해 선택되길 기다리는 상태, 실행 준비는 다 끝난 상태

Blocked 상태 : 연산중에서 ALU(CPU)에 의존적인 연산이 있고, I/O연산처럼 의존적이지 않은 연산이 있다. Running상태는 CPU 연산이 진행되는 중이고, Blocked 상태는 현재 I/O 연산중인 프로세스를 뜻한다.

I/O연산이 끝나면 Blocked 상태가 끝나고 Ready 상태로 변한다.




컨텍스트 스위칭은 일반적으로 멀티태스킹 환경에서 1초에도 수십번씩 한다.


프로그래머 관점


컴퓨터 구조를 잘 아는 프로그래머도 컴퓨터 디자인에 참여

 -> 명령어를 짜거나 한다.


컴퓨터 디자인은 레지스터와 명령어 디자인


-> CPU, GPU를 짜기 위해서는 하드웨어 전문가가 ASIC 등을 이용해 로직을 짠다. 이용해 로직을 짠다. GPU같은 경우는 그래픽 프로세싱을 위한 알고리즘 전문가도 필요하며, 컴퓨터 기기와 조화를 이루게 하기 위해 인터페이스 전문가도 들어가게 된다. 여기에 프로그램 개발자 또한 포함되는데, 개발자들은 명령어 디자인과 같은 기기 실 사용에 관련이 있는 부분에 관여를 한다.


레지스터 디자인의 핵심


레지스터는 몇 비트로 구성할 것인가?

 -> 32비트 컴퓨터라면 레지스터 또한 32비트, 64비트 컴퓨터라면 64비트로 디자인 하는게 일반적이다. 성우횽은 16비트로 해보겠다고 한다.


몇 개 정도로 레지스터를 구성할 것인가?

-> 레지스터는 많으면 많을수록 좋다. 성우횽은 일단 8개로 해보겠다고 한다.


레지스터 각각을 무슨 용도로 사용할 것인가?

-> 레지스터는 범용적으로 사용하기보단 일반적으로 특정 용도를 가지고있다. 성우횽은 r4 ~ r7까지 용도를 주었다.

r4 : instruction register

r5 : stack pointer

r6 : link register

r7 : program counter


r0 ~ r3 레지스터는 연산을 위한 범용 레지스터다.



명령어 구조 및 명령어 디자인




명령어의 기본 모델


16비트 명령어

 -> 레지스터를 16비트로 만들자고 했기 때문에 명령어 또한 16비트가 되어야 한다.


사칙연산 명령어 구성


16비트 명령어 안에는 예약, 연산자, 저장소, 피연산자1, 피연산자2가 들어가 있다.


명령어 디자인이 되어 있어야 ALU가 명렁어를 해독할 수 있다. CPU디자인이 명령어 디자인과 병행되어야 하는 이유고, 하드웨어 전문가와 소프트웨어 전문가가 협업해야 하는 이유다.


이 경우 연산자에 3비트, 저장소에 3비트, 피연산자 각각 4비트로 구성했다.

-> 하지만 명령어에 따라 명령어 구성이 달라질 수 있다.(Store, Load 참고) 명령어 구성요소는 고정되어있지 않다.


연산 결과는 일단 레지스터에 저장한다. CPU 역시 마찬가지다. 여기서 레지스터가 8개이기때문에 저장소 3비트로 표현이 충분하다.


피연산자에는 레지스터 or 숫자가 올 수 있다. 이렇듯 조건이 붙게 되면 그만큼 비트를 조건 판독에 할애해야 한다. 첫번째 비트가 0이면 숫자, 1이면 레지스터라는 약속을 두거나 하는 등의 양보를 해야 한다는 것.


RISC와 CISC


명령어가 단순한 CPU 구조를 RISC(Reduced)라고 부른다. CISC(Complexed)는 복잡한 명령어를 뜻한다. 사용자가 원하는 명령을 다 적용시킬 수 있다.


RISC는 명령어 조합이 다양하지 않다. CISC로 한줄짜리 명령어를 RISC로 수십 줄 가량이 필요한 경우가 생긴다. 하지만 속도면에서는 RISC를 사용하는 것이 더 효율적이다.


RISC CPU의 연산 단계는 Fetch, Decode, Execution로 이루어지는데, 이 단계는 각각 1클락에 이루어진다고 보면 된다.


예를 들어 하나의 명령어를 실행하는데 드는 클락 비용은 3클락이다. 그 말은 5개 명령어를 수행하는데 드는 클락은 15클락이 되는 것이다.(3 * 5 = 15)


하지만 한 명령어를 3클락에 수행하지 못하는 경우를 주로 CISC라고 부르는데, 만약 3클락에 다 해결할 수 있으면 첫번째 명령어를 Fetch할때 다른 명령어를 Decode할 수 있고, 명령어를 Decode할 때 다른 명령어를 Execution 할 수 있다.

 -> 즉, Fetch와 Decode와 Execution을 한 클락에 동시 수행이 가능하다는 것이다.


-> 7클락에 5개의 명령어를 처리할 수 있다. n개의 명령어를 처리하는데 필요한 클락 수는 +2개가 된다는 것이다.


그렇기 때문에 CISC보다 적은 클락 수로 똑같은 일을 할 수 있는 것이다. -> 고성능 컴퓨터에서 RISC가 더 좋은 이유


반드시 클럭이 높다고 좋은 것이 아니라, 한 클럭 당 얼마나 효율적으로 연산을 수행하는가도 중요


클럭수가 낮으면 또한 열도 적게 발생한다.


결론 RISC 짱



LOAD와 STORE 명령어는 사칙연산 연산자와 명령어 구조가 다르다. 

'운영체제 > 윈도우 시스템' 카테고리의 다른 글

커널 오브젝트 오브젝트 핸들  (0) 2019.09.26
프로세스, 스케줄러  (0) 2019.09.25
64비트 기반 프로그래밍, GetLastError  (0) 2019.09.23
문자셋 SBCS, MBCS, WBCS  (0) 2019.09.19
Stored Program Concept  (0) 2019.09.18

성우형 목소리너무좋아요

운영체제 

모델 

char 

short 

int 

long 

ptr

windows 

LLP64 

1바이트 

2바이트 

4바이트 

4바이트 

8바이트 

UNIX 

LP64 

1바이트 

2바이트 

4바이트 

8바이트 

8바이트 



압도적감사


64비트와 32비트 공존의 문제점


절대 기본자료형으로 포인터를 캐스팅하지말자


1
2
int arr[10= { 0 };
int arrVal = (int)arr;
cs


물론 32비트에서도 좋은건 아니다


Windows 스타일 자료형


반드시 외울 필요는 읍다


Polymorphic 자료형


1
2
3
4
5
6
7
8
9
10
11
#if defined(_WIN64)
    typedef __int64 LONG_PTR;
    typedef unsigned __int64 ULONG_PTR;
    typedef __int64 INT_PTR;
    typedef unsigned __int64 UINT_PTR;
#else
    typedef long LONG_PTR;
    typedef unsigned long ULONG_PTR;
    typedef int INT_PTR;
    typedef unsigned int UINT_PTR;
#endif
cs


왜 UINT_PTR인데 *가 읍냐?

 -> 포인터 연산을 할 때, 포인터 값으로 무언가 연산을 해야할 때 사용하라고 _PTR을 붙였다.


예제


1
2
3
4
5
6
7
8
9
10
11
12
UINT CalDistance(UINT a, UINT b)
{
    return a - b;
}
int _tmain(void)
{
    INT val1 = 10;
    INT val2 = 20;
    _tprintf(_T("position &d, %d \n"), (UINT)&val1, (UINT)&val2);
    _tprintf(_T("distanc : &d \n"), CalDistance((UINT)&val1, (INT)&val2));
    return 0;
}
cs


1
2
3
4
5
#if defined(_WIN64)
    typedef unsigned __int64 UINT_PTR;
#else
    typedef unsigned int UINT_PTR;
#endif
cs



오류의 확인


GetLastError 함수와 에러코드


윈도우즈 프로그래밍에선 에러 로그를 저장하기 위에 전역 공간에(아마 데이터 공간?) 로그를 저장시키는데 이 에러 로그에 접근하는 함수가 바로 GetLastError 함수다.


 MSDN 홈페이지에 에러 코드별로 다 설명이 되어있으니 필요하면 참고


1
2
3
4
5
6
7
8
9
10
11
12
13
14
int _tmain(void)
{
    HANDLE hFile =
        CreateFile(        //Windows system 함수,
            _T("ABC.DAT"), GENERIC_READ, FILE_SHARE_READ,
            NULL, OPEN_EXISTING, FILE_ATRIBUTE_NORMAL,
            NULL);
    if(hFile == INVALID_HANDLE_VALUE)
    {
        _tprintf(_T("error code: %d \n"), GetLastError());
        return 0;
    }
    return 0;
};
cs


실행 결과

1 error code: 2


여기서 ABC.DAT이란 파일은 없다. 걍 없는 파일을 불러오려고 했을 때 어떤 오류가 나오는지 확인하는 것


INVALID_HANDLE_VALUE는 에러가 발생했는지를 체크하는 함수가 아니라, 에러가 있을때 그 에러가 있음을 알려주는 함수다.


또한, CreateFile 함수 호출 후 바로 GetLastError 함수가 호출되었는데, 이렇듯 GetLastError는 LastError이기때문에 특정 함수 호출 후 바로 GetLastError함수를 호출해야 그 기능을 온전히 할 수 있다.

'운영체제 > 윈도우 시스템' 카테고리의 다른 글

프로세스, 스케줄러  (0) 2019.09.25
컴퓨터를 디자인하자  (0) 2019.09.23
문자셋 SBCS, MBCS, WBCS  (0) 2019.09.19
Stored Program Concept  (0) 2019.09.18
힙 단편화, Windows Low Fragmentation Heap(LFH)  (0) 2019.09.18

문자셋의 종류와 특성


SBCS (Single Byte Character Set)

문자를 표현하는데 1바이트 사용

아스키 코드


MBCS (Multi Byte Character Set)

한글은 2바이트, 영문은 1바이트 사용


WBCS (Wide Byte Character Set)

문자를 표현하는데 2바이트 사용

유니코드


WBCS를 위한 두가지


- char를 대신하는 wchar_t

- "ABC"를 대신하는 L"ABC


main.cpp 유니코드 버전

1
2
3
4
5
6
7
8
9
10
int wmain(int argc, wchar_t* argv[])
{
    for(int i = 0; i < argc; ++i)
    {
        fputws(argv[i], stdout);
        fputws(L"\n", stdout);
    }
 
    return 0;
{
cs


windows.h MBCS & WBCS 동시 지원 매크로




나는 왜 windows 헤더 안에 매크로를 저렇게 덕지덕지 붙여놨나 했는데, 호환성 이유 때문이었구나 싶다.




Stored Program Concept (폰노이만 아키텍쳐)

아아.. 폰노이만


프로그램을 메모리에 저장하는 방식으로 컴퓨터가 디자인 되어야 한다.


Fetch

- CPU 내부로 명령어 이동

명령어는 메모리에 저장되어서 CPU에 의해 CPU 내부로 Fetch되어야 한다.

Bus를 타고...


Decode

- 명령어 해석

Fetch된 명령어를 해석

- 컨트롤 유닛


Execution

- 연산을 진행

해석한 명령어를 연산

- 보통은 ALU를 생각

ALU에 의해 연산된다고 생각해도 무방



메모리: Stored Program Concept의 근간을 이룸




Stored Program Concept를 바탕으로 컴퓨터구조에 접근하면 이러한 그림이 떠오르게 된다. 즉, 컴구와 폰노이만 아키텍쳐를 별개로 기억할 것이 아니라 둘이 엮어서 이해해야 한다는 것이다.


버스 시스템


데이터 버스

- 데이터 이동

어드레스 버스

- 주소 이동

컨트롤 버스

- 컨트롤 신호 이동


CPU

 데이터 버스

 메모리

 어드레스 버스

 컨트롤 버스


+ Recent posts