[윈도우즈 Socket함수]

- Winsock API는 개발자를 위한 헤더와 라이브러리 파일과 애플리케이션에서 사용하는 DLL파일로 구성됨.

- Winsock환경의 핵심은 소켓임....

 

[WSAStartup()]

- Winsock프로그램을 시작하기 위해서는 WSAStartup()함수를 호출해야함.

- 프로그램이 사용할 Winsock버전 정보를 운영체제에 전달하는 역할을 수행.

- int WSAStartup(WORD wVersion, LPWSDATA lpWSAData)

(1번째 파라미터: Winsock버전, 2번째 파라미터: Winsock라이브러리정보를 포함하는 구조체를 참조함.)

 

[WSACleanup()]

- Winsock라이브러리를 사용후 반드시 풀어주어야함.

- 프로그램에서 Winsock함수 사용을 종료한다는 신호만 전달함.

 

[Winsock함수]

           서버                                              클라이언트

    WSAStartup()                                   WSAStartup()

     WSASocket()                                    WSASocket()

          bind()

         listen()

     WSAAccept()    <------------------      WSAConnect()

       WSARecv()     <------------------         WSASend()

      WSASend()      ------------------>        WSARecv()

         close()          <---------------->            close()

    WSACleanup()                                  WSACleanup()

 

[Winsock 넌블로킹 소켓함수]

- 원하지 않는 블로킹 현상을 예방하는 아주 간단하고 원칙적인 솔루션은 입출력 함수가 호출 되었을때 블록하지 않도록 설계된 소켓을 사용하는 목적

- 넌블로킹 소켓을 사용함으로써, 데이터를 수집하거나 전송할 소켓을 찾는데 사용할수도 있슴.

ioctlsocket(SOCKET s, long cmd, u_long FAR* argp)

(s는 설정해야하는 소켓을, cmd는 소켓에서 수행할 동작을, argp는 커맨드 파라미터임.)

 

[WSAAsyncSelect()]

- 이벤트기반의 네트워크 소켓.

- 모니터링할 소켓과 소켓 이벤트가 발생할 때 해당창으로 전송할 원도우즈 메시지 값을 포함함.

- int WSAAsyncSelect(SOCKET s, HWND hWnd, unsigned int wMsg, long lEvent)

(s는 모니터링할 소켓을, hWnd는 이벤트 메시지를 수집할 부모창을 정의하며, wMsg는 전송할 실제 이벤트를 정의, lEvent는 소켓에서 모니터링할 이벤트를 정의함.)

 

* WSAAsyncSelect() 이벤트타입

   이벤트                                                                    설명

FD_ACCEPT                                     소켓에 새로운 연결이 확립됨.

FD_ADDRESS_LIST_CHANGE           소켓의 프로토콜 유형에 대한 로컬 주소 리스트가 수정됨.

FD_CLOSE                                       기존의 연결이 종료됨.

FD_CONNECT                                  소켓이 원격 호스트와 연결을 확립함.

FD_GROUP_QOS                             소켓 유형의 QoS값이 수정됨.

FD_OOB                                          소켓에서 대역 외(out-of-band) 데이터를 수집함.

FD_QOS                                          소켓의 QoS값이 수정됨.

FD_READ                                         소켓에 있는 데이터를 읽어들일 준비가 됨.

FD_ROUTING_INTERFACE_CHANGE 소켓의 라우팅 인터페이스가 특정목적에 대해서 수정됨.

FD_WRITE                                       소켓이 데이털르 출력할 준비가 됨.

 

[WSAEventSelect()]

- 윈도우즈 메시지를 통해서 소켓 통지를 처리하지 않고 대신 이벤트객체핸들을 통해 처리함.

- 여러 소켓이벤트를 처리하는데 각각에 대해서 별개의 윈도우즈 메서드를 생성하고 싶을때 사용..

- 먼저 WSACreateEvent()함수를 사용하여 고유의 이벤트를 정의해야함.

- WSASelect(SOCKET s, WSAEVENT hEvent, long lNetworkEvents)

(s는 모니터링할 소켓을, hEvent는 소켓 이벤트가 발생할 때 생성될 이벤트를 정의하며, lNetworkEvent는 모니터링할 모든 소켓이벤트의 비트와이즈 조합임.)

- 소켓 이벤트가 발생하면, WSACreateEvent()에서 지정한 이벤트 메서드가 실행됨.

 

[입출력오버랩(I/O)]

- 프로그램으로 하여금 독특한 데이터 구조(WSAOVERLAPPED)를 이용하여 한번에 한개 이상의 비동기 입출력 요청을 발생시킬 수 있도록 함.

- 소켓이 이벤트를 수집하면서 동시에 여러 개의 이벤트를 호출할 수 있기 때문에 이 이벤트들은 오버랩되는 것으로 간주됨.

- 이기법을 사용하기 위해서는 우선 오버랩이 가능하도록 설정된 플래그를 사용하여 WSASocket()함수로 소켓을 생성해야함.

-  64개의 이벤트로 한정하여 정의할 수 밖에 없기 때문에 대규모 네트워크 애플리케이션에는 적합하지 않음.

- 단일 스레드에서 처리됨...

 

[완료포트]

- 이벤트를 복수의 스레드로 나누어서 처리하기 위해 사용됨.....

- 각 프로세서에 대해서 별개의 스레드를 생성하므로써, 각 프로세서에서 복수의 소켓을 동시에 모니터링하는 것이 가능함.


출처:http://byung2love.blog.me/140020497881

파일의 조작

저 수준 파일 입출력 (Low-Level File Access)


리눅스 혹은 윈도우즈 자체에서 제공해 주는 파일 입출력 함수를 사용하여 파일을 관리

(파일의 생성 및 삭제, 데이터 입력 및 출력) 하는것을 의미함.


리눅스에서는 모든 것을 파일로 관리한다.

-파일,소켓,표준 입력(키보드),표준 출력(모니터)

※흔히 표준 입출력 함수라 함은 ANSI 표준에서 제공하는 함수를 말한다.


파일에 파일 디스크립터를 할당해서 관리(파일 디스크립터는 정수)

※여기서 파일은 단순 파일 뿐아니라 소켓,표준 입출력을 포함한 의미이다.


 리눅스는 모든 것을 파일로 간주하기 때문에 파일 입.출력 함수로 소켓을 통한 데이터 전송 및 모니터나 키보드로 부터 데이터를 입.출력 또한 가능하다.

여기서 말하는 입출력함수는 표준입출력 함수가 아니라 리눅스에서 제공하는 입출력함수이다.



파일 디스크립터(File Descriptor)


파일을 관리하기 위해서 모든 파일(파일,소켓,표준 입력,표준 출력)에 파일 디스크립터를 할당 해 준다.


파일 디스크립터 

대상 

표준 입력 (Standard Input)

표준 출력 (Standard Output)

표준 에러 출력(Standard Error)


기본적으로  파일디스크립터는 정수형으로 이루어지면 0~2까지는 위 표처럼 기본적으로 할당 되어있다.


      Program                            Operating System

 




5





7






 <--------------
 





<--------------

 

파일 A


소켓 B


소켓 C


파일 D


만약 위처럼 요청에 따라 운영체제에서 소켓과 파일에 번호를 붙여 주면 프로그램에서 파일의 이름이아닌 넘버링된 숫자만으로 운영체제에 파일의 입력이나 출력등을 요청할수있다.

비유를 하자면 도서관에서 책에 번호를 붙여서 관리를 하는 것이라 보면된다.



File open 및 close


#include<fcntl.h>

#include<sys/types.h>

#include<sys/stat.h>


int open(const char *path, int flag);

open 함수의 const char *path 인자는 열려고하는 파일의 위치와 이름을  함께지니고 있는 문자열을 전달하고 

int flag는 파일을 열때의 모드를 설정하는것이다.

보면 함수의 리턴형이 int 형이다.

이것이 바로 파일 디스크립터 이다.

#include<unistd.h>


int close(int fildes);

닫고자하는 파일의 디스크립터를 인자로 전달하면 된다.


MODE 

의미 

O_CREAT 

 파일이 없으면 생성

O_TRUNC 

존재하던 데이터를 모두 삭제 ,다시 기록

O_RDONLY 

읽기 전용 모드로 파일을 오픈 

 O_WRONLY

쓰기 전용 모드로 파일을 오픈 

O_RDWR 

읽기와 쓰기 모두 가능한 모드로 파일을 오픈 

O_APPEND 

파일이 '추가모드'로 열린다. 파일 포인터가 열린 파일의 마지막 부분을 가리킨다.

 O_EXCL

 

O_NOCTTY 

 

 O_SYNC

 

 O_NOFOLLOW

 결로명이 심볼릭 링크라면,파일오픈이 안된다.

O_DIRECTORY 

경로명이 디렉토리가 아니면,파일오픈이 안된다. 



Data read & write


#include<unistd.h>


ssize_t write(int fildes, const void *buf, size_t nbytes);

ssize_t : signed int ,   size_t : unsigned int 

int fildes 는 데이터를 보낼 파일 디스크립터를 전달하면 된다.

void *buf 는 전달하고자 원하는 데이터의 배열정보

※배열 이름은 상수 포인터이다.

size_t nbytes 전달하고자 원하는 데이터의 크기

#include<unistd.h>


ssize_t read(int fildes, void *buf, size_t nbytes);

위와 같은데 size_t nbytes 는 읽어들일 데이터의 최대크기로 더 큰 데이터가 와도 여기서 지정한 크기까지만 읽어들인다.








서버 소켓 구현의 이해



Telephone

전화기 구입


전화번호 할당


케이블에 연결


수화기를 든다



<-------------------->


<-------------------->


<-------------------->


<-------------------->

 Server Socket


소켓 생성


IP주소 할당


연결 요청 대기 상태


연결 수락



서버를 전화받는 상황에 비유를 해보면 소켓을 전화기로 볼 수 있다.


일단 전화를 하려면 전화기가 필요하듯이 서버에서 데이터를 주고 받기위해서는 데이터를 주고받을 매개체인 소켓이 일단 필요하다. 


전화기에 전화번호가 있듯이 당연하게 IP주소가 할당되어져야하고 연결 요청 대기 상태로 되있게끔 해야하는데 말하자면 전화기에 케이블을 연결하는 과정이라고 생각하면 쉽다.


그뒤로 전화가 왔을때 받으면 된다.

서버는 클라이언트가 연결을 요청했을때 이를 수락하면 된다.


일반적인 서버프로그래밍은 이러한 큰 틀을 가지고 있다.



일단 소켓통신을 위한 함수들은 간단하게 살펴보자.


소켓 생성


#include<sys/types.h>

#include<sys/socket.h>


int socket(int domain, int type, int protocol)


IP 주소, Port 정보


#include<sys/socket.h>


int bind(int socket, struct sockaddr *myaddr, int addrlen)


연결 요청 대기 상태 진입


#include<sys/socket.h>


int listen(int sockfd, int backlog)


연결 요청 수락 


#include<sys/socket.h>


int accept(int sockfd, struct sockaddr *addr, int *addrlen)




클라이언트 소켓 구현의 이해


전화를 거는 사람도 전화기와 전화번호가 필요한 것 처럼 

서버 뿐만아니라 클라이어트도 소켓이 필요하다.


소켓 생성


위와 동일


연결요청


#include<sys/socket.h>


int connet(int socket, struct sockaddr *serv_addr, int addrlen)













IPC(interprocessor communication)


하나의 프로그램에서 여러게의 프로세스를 형성 할 수있다.


프로세스간 통신

프로세스간 데이터 송 수신→메모리 공유 


A에서 B로 주고 받는다는 느낌보다는 메모리 공유 기법에서 이루어진다.

process A ------[memory]--------process B

각 프로세스의 메모리영역이 분리 되어있고 그 범위를 벗어날 수 없기때문에 프로세스들은 직접 서로 데이터를 주고 받을 수 없다. 

다른 프로세스의 메모리에 접근은 안전성을 위해서 막혀있다. 

[출일슬롯 방식의 IPC|작성자 F



메일슬롯(Mail Slot)


Receiver process<----- Receiver's Mail slot <------- Sender process


데이터를 받기 위해서 프로세스는 접근가능한 공간이 필요하다. 그렇기에 OS는 메일슬롯을 생성하고 받는프로세서는 함수호출을 통한 간접적 방법으로 이 영역에 접근이 가능하다. 그리고 이 영역에 주소를 할당하여 데이터를 전송하는 프로세스는 이 주소를 통하여 데이터를 전송한다.

우체통에 편지를 보내서 받는 개념이다. 

그렇게 때문에 양방향성의 통신이 아니라 단방향 통신이다.



Process A <--------  A Mail slot    <------------

Process B <--------  A Mail slot    <------------            Sender Process

Process C <--------  A Mail slot    <------------


현실에서는 우체통 마다 다른 주소를 같지만 윈도우즈에서는 메일슬롯의 주소가 같을 수 도 있고 또한 데이터를 보내는 프로세스가 한번의 보내기를 통하여 각각의 프로세스에 모두 전달 할 수 있다. 




Receiver 가 우체통을 생성하면 Sender 는 우체통에 접근하기위하여 연결을 하기 위해 어떻한 함수를 호출하는데 이런 연결과정을 거친후에 데이터를 전송 할 수 있다.

위에서 한번 나왔듯이 Receiver에서의 데이터 전송은 가능하지 않다.


잘 보면 여기서 사용하는 함수들이 파일 관련함수들이다. 

 A 가 파일을 생성하여 데이터 작성 --> B가 파일을 읽어서 데이터 받음

이런식의 파일을 이용한 단순하고 불안정한 통신이 가능한데 

실제로 메일슬롯 방식이 이러한 방식을 기반으로 만들어졌기 때문에  파일관련함수를 사용한다.



예제



메일슬롯 Receiver



#include <windows.h>

#include <tchar.h>

#include <stdio.h>


#define SLOT_NAME    _T("\\\\.\\mailslot\\mailbox")//이곳에 컴퓨터 이름이 들어간다.

/*이 주소를 이용하여 분리된 컴퓨터간에서 프로세스간 통신가능 하지만 실제로는 거의 이용되지 않는다*/

int _tmain(int argc, LPTSTR argv[])

{

HANDLE hMailSlot;  //mailslot 핸들

TCHAR messageBox[50];

  DWORD bytesRead;  // number of bytes read


/* mailslot 생성 */

hMailSlot=CreateMailslot(

SLOT_NAME, 

0, //메일슬롯 버퍼크기

MAILSLOT_WAIT_FOREVER,

NULL);

if(hMailSlot==INVALID_HANDLE_VALUE)

{

_fputts(_T("Unable to create mailslot!\n"), stdout);

return 1;

}


/* Message 수신 */

_fputts(_T("******** Message ********\n"), stdout);

while(1)

{

if(!ReadFile(hMailSlot, messageBox, sizeof(TCHAR)*50, &bytesRead, NULL))

{

_fputts(_T("Unable to read!"), stdout);

CloseHandle(hMailSlot);

return 1;

}


if(!_tcsncmp(messageBox, _T("exit"), 4))

{

_fputts(_T("Good Bye!"), stdout);

break;

}


messageBox[bytesRead/sizeof(TCHAR)]=0; //NULL 문자 삽입.

_fputts(messageBox, stdout);

}


CloseHandle(hMailSlot);

return 0;

}


-----------------------------------------------------------------------------------



 메일슬롯 Sender


#include <windows.h>

#include <tchar.h>

#include <stdio.h>


#define SLOT_NAME _T("\\\\.\\mailslot\\mailbox")


int _tmain(int argc, LPTSTR argv[])

{

HANDLE hMailSlot;  //mailslot 핸들

TCHAR message[50];

    DWORD bytesWritten;  // number of bytes write


hMailSlot=CreateFile(SLOT_NAME, GENERIC_WRITE, FILE_SHARE_READ, NULL,

OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

if(hMailSlot==INVALID_HANDLE_VALUE)

{

_fputts(_T("Unable to create mailslot!\n"), stdout);

return 1;

}


while(1)

{

_fputts(_T("MY CMD>"), stdout);

_fgetts(message, sizeof(message)/sizeof(TCHAR), stdin);


if(!WriteFile(hMailSlot, message, _tcslen(message)*sizeof(TCHAR), &bytesWritten, NULL))

{

_fputts(_T("Unable to write!"), stdout);

CloseHandle(hMailSlot);

return 1;

}


if(!_tcscmp(message, _T("exit")))

{

_fputts(_T("Good Bye!"), stdout);

break;

}


}


CloseHandle(hMailSlot);

return 0;

}










네트워크의 이해

네트워크란 End-System 들을 연결하는 하나의 System을 의미한다.

End-System : "호스트"라 한다.

ex)PC,프린터,핸드폰 등등등



End-System 는 종단 시스템으로 쉽게 말하면 네트워크에 연결된 단말기. 

호스트 라한다. 네트워크에 모바일로 연결된 핸드폰,PDA도 호스트가 된다. 


Internet의 이해

멀리 떨어진 둘 이상의 네트워크가 연결되어 이뤄지는 거대한 네트워크를 의미한다.

인터넷의 구축을 위해서는 서로 다른 네트워크를 연결하는 장비가 필요한다.

이를 두고 라우터(Router)라 한다.




Client / Server 모델

Server와 Client는 프로그램이다.(서버 장비를 말하는 것이 아니다)

Server는 Client의 연결요청을 기다린다.

Client는 Server에 요청을 하고 응답을 기다리는 호스트를 의미한다.



Client는 Server에 요청을 하면 Server는 요청에 대해 응답을 해주고 Client는 이 응답을 기다리는 모델이다. 일반적으로 Server가 먼저 요청하는 경우는 없다.


Server의 종류

Server는 일반적으로 Client에 비해 복잡하다.

두 가지 종류의 서버

Iterative Server : 한 순간에 하나의 클라이언트에게 응답한다.

Concurrent Server : 동시에 여러 클라이언트에게 응답한다.

 




 




네트워크 프로그래밍의 이해

네트워크로 연결되어 있는 두 호스트간의 데이터 송수신.

파일 입출력과의 차이점은 데이터를 주고 받는 대상에 있다.

소켓(soket)이라는 장치를 사용하여 프로그래밍 한다.

소켓이란 원격에 존재하는 두 호스트를 연결시켜 주는 매개체 역할을 한다.





커널 오브젝트와 핸들의 관계



프로세스가 생성될때 커널 오브젝트가 생성된다. 또한 그 프로세스의 핸들 테이블도 생성된다. 

핸들 테이블에 핸들 값이 있고 그 핸들값이 가리키는 대상 즉 커널 오브젝트가 저장된다. 

※핸들 테이블은 각 프로세스 영역에서만 의미를 지니기 때문에 가리키는대상이 같아도 핸들값이 다를 수 있다.


커널 오브젝트에는 그 커널오브젝트에 접근가능한 사용자의 수를 카운트하는 값이 존재한다. 만약 프로세스A가 생성된다면 그 값은 2가될겄이다. 하나는 자기자신일 것이도 다른 하나는 프로세스A를 실행시킨 그 무엇(마우스로 실행시키면 탐색기가 될수도있고 명령프롬프트가 될 수도 있다.)이 될 것이다.

자식 프로세스는 실행될때 부모 프로세스로 자신의 핸들값을 집어던진다.


프로세스가 소멸 될지라도 커널 오브젝트는 사라지지 않는다. 

다른 프로세스가 그 커널 프로세스를 참조 할 수도 있기때문이다.

하지만 이미 그 프로세스가 소멸 했는데 다른 프로세스가 참조한다해도 이미 할 수있는 일은 아무것도 없지않을까 ?

여기서 잠깐 C언어 프로그래밍을 보면 return 0,1,-1 또는 exit(-1),exit(1) 이런 식으로 종료되는 경우가 많다. 어차피 뭘 넣던 종료는 된다.

하지만 

뒤에 오는 값은 종료되는 프로세스의 부모프로세스에게 자신이 정상 종료인지 비정상 종료인지 알려주는 것이다.

부모 프로세스와 자식 프로세스가 완전 별도의 프로세스 일 수도 있지만 자식 프로세스의 결과에따라 부모 프로세스의 실행을 결정 할 수도 있기에 자식 프로세스가 정상적 종료인지 아닌지 궁금해 할 수도 있다.


프로세스가 종료될때 종료코드값를 리턴하는데 어디로 하느냐 바로 커널 오브젝트이다.  그래서 프로세스가 종료되어도 커널 오브젝트는 남아있는다.

그 커널 오브젝에 아무도 관심이 없어진 상태가 된야만 운영체제가 그 커널 오브젝트를 소멸 시킨다.



파일에 접근 할 때 ANSI 함수를 사용 하거나 시스템 함수를 사용 할 수 있다.

ANSI 함수를 사용하면 파일의 커널오브젝트가 생성되지 않는다고 알고있는 사람도 있다고하는데 ANSI 함수는 각 OS에 시스템 함수를 호출하는 방법으로 사용된다. 

즉 파일이 생성되면 반드시 커널 오브젝트는 생성된다.


프로세스가 자기 자신의 커널 오브젝트에 접근 할때는 핸들 테이블에 등록되어있는 값을 사용하는것이 아니라 자기자신을 의미하는 상수값을 반환받는다.



예제


#include <stdio.h>

#include <windows.h>

#include <tchar.h>


int _tmain(int argc, TCHAR* argv[])

{

STARTUPINFO si={0,};

PROCESS_INFORMATION pi;

si.cb=sizeof(si);


TCHAR command[]=_T("Operation2.exe");


CreateProcess(NULL,     // 프로세스 생성.

     command,

 NULL,

 NULL, 

 TRUE, 

 0, 

 NULL, 

 NULL, 

 &si, 

 &pi

);  //CreateProcess


while(1)

{

for(DWORD i=0; i<10000; i++)

for(DWORD i=0; i<10000; i++); //Busy Waiting!!

_fputts(_T("Operation1.exe \n"), stdout); 

timing+=1;

if(timing==2)

 SetPriorityClass(pi.hProcess,

 NORMAL_PRIORITY_CLASS);   //우선순위를 다시 낮춘다.


}


return 0;

}


--------------------------------------------------------------------------------

Operation2.exe


#include <stdio.h>

#include <windows.h>

#include <tchar.h>


int _tmain(int argc, TCHAR* argv[])

{

SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);


while(1)

{

for(DWORD i=0; i<10000; i++)

for(DWORD i=0; i<10000; i++); //Busy Waiting!!


_fputts(_T("Operation2.exe \n"), stdout); 

}


return 0;

}


저번 예제와 거의 같다. 달라진 점은 프로세스를 실행시키면 자식프로세스인 operation.exe는 자신의 우선순위를 높인다. 그후에 부모프로세스가 우선순위를 다시 일반적 우선순위로 바꾼다. 

프로세스를 실행시킬때 PROCESS_INFORMATION pi; 구조체를 전달해주는데 

pi 구조체의 pi.hProcess를 통해 해당 프로세스를 통해 접근하게 된다.





커널 오브젝트(Kernel Object)에 대한 이해


커널오브젝트(Kernel Object)

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



         Kernel Object                                                 Resource

 

파이프 커널 오브젝트


프로세스 커널 오브젝트


쓰레드 커널 오브젝트


<---------------->





<---------------->





<---------------->

 파이프 


프로세스


쓰레드




커널은 쉽게 말해서 OS의 핵심 이다.

파일의 생성,접근,삭제등을 할때 우리가집접하는 것이 아니라 우리가 OS에 명령을 내리면 OS가 이러저러한 잡업을 해준다.

이런 리소스를 컨트롤하기 위해서 필요한 정보를 담고있는 것이 커널오브젝트이다.


프로세스 기반 커널 오브젝트



CreateProcess 함수를 호출한다고 한다면 OS에 요청을 하면 OS는 프로세스만 생성하는 것이아니라 프로세스의 정보와 프로세스의관리에 필요한 정보들로 이루어진 커널 오브젝트를 생성한다.

OS에는 커널 오브젝트를 관리하기위한 구조체가 존재한다.




커널 오브젝트와 핸들의 관계


         Handle                                    Kernel Object                         Resource

 

파이프 핸들


프로세스 핸들


쓰레드 핸들




<----->





<----->




 

<----->


 

 

파이프 커널 오브젝트


프로세스 커널 오브젝트


쓰레드 커널 오브젝트





<--->





<--->




 

<--->


  

 

파이프


프로세스


쓰레드



커널 오브젝트에는 여러가지 정보가 담겨있지만 커널 오브젝트에는 직접접근이 허용되지 않는다.

그렇기에 커널 오브젝트에 정보를 바꿀 수 있게 하기위해 관련된 시스템 함수를 제공한다.

이러한 함수를 사용하기위해서 커널 오브젝트를 지정해야하는데 직접접근 할 수 있는 어떻한 정보도 얻지못한다  이러한 관계로 

OS는 커널 오브젝트를 만들때 각각 고유한 번호를 매긴다 이를 핸들(Handle)이라한다. 핸들은 우리가 반환받을 수있다.


단순하게 보면

커널 오브젝트는 구조체 변수이고 핸들은 숫자 일 뿐이다.

핸들을 얻는 방법은 리소스 별로 각각다르기 때문에 일일이 알아 두어야 한다.


※커널 오브젝트마다 고유 핸들 값이있는것은아니다.

자세한 내용은 다음장에 계속...





밑의 예제는 커널 오브젝트에 접근하여 프로세스의 우선순위를 변경하는 예제이다.


예제6-1]


#include <stdio.h>

#include <windows.h>

#include <tchar.h>


int _tmain(int argc, TCHAR* argv[])

{

STARTUPINFO si={0,};

PROCESS_INFORMATION pi;

si.cb=sizeof(si);


TCHAR command[]=_T("Operation2.exe");


CreateProcess(NULL,     // 프로세스 생성.

     command,

 NULL,

 NULL, 

 TRUE, 

 0, 

 NULL, 

 NULL, 

 &si, 

 &pi

);  //CreateProcess


while(1)

{

for(DWORD i=0; i<10000; i++)

for(DWORD i=0; i<10000; i++); //Busy Waiting!!

_fputts(_T("Operation1.exe \n"), stdout); 

}


return 0;

}


예제6-2]


#include <stdio.h>

#include <windows.h>

#include <tchar.h>


int _tmain(int argc, TCHAR* argv[])

{

SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);

            //GetCurrentProcess함수는 프로세스자신의 핸들을 얻어오는 함수이다.


while(1)

{

for(DWORD i=0; i<10000; i++)

for(DWORD i=0; i<10000; i++); //Busy Waiting!!


_fputts(_T("Operation2.exe \n"), stdout); 

}


return 0;

}


6-1과 6-2모두 단순 출력 프로그램이다.

예제6-1은 예제 6-2를 생성시키는데  6-2는 자신의 우선순위를 올리는 함수를 호출한다.

Busy Waiting은 출력결과가 너무빠르기때문에 출력을 늦추기위함인데 

sleep함수도 있긴하지만 sleep함수로 프로세스를 늦추면 Running상태에서 Blocked상태로 된다. 

하지만 Busy Waiting을 시키면 프로세스가 Blocked상태가아니라 스케줄러에 의해서 

Ready 상태로 된다.

예제가 우선순위에 의한 프로세스의 실행량을 보기 위함이기 때문에 sleep함수보다

Busy Waiting을 사용해야한다.


실행결과 

Operation2.exe

OpOperation2.exe

Operation2.exe

Operation2.exe

Operation2.exe

Operation2.exe

Opraeration2.exe

Operation2.exe

Operation2.exe

Operation2.exe

Operation2.exe

Opertion1.eation2.exe

Operation2.exe

중략.....................

결과를 보면 예제6-1보다 확실히 예제6-2가 많이 실행된것을 알 수있다.
또한 함수호출이 완료되기 이전에도 우선순위가 넘어갈 수 있다.

우선순위의 기준은 함수 단위가아닌 명령어 단위로 이루어 지기때문이다.




프로그래밍을 통한 프로세스의 생성


프로세스 생성 함수



BOOL CreateProcess(

LPCTSTR lpApplicationName ,   //실행파일 이름 지정

LPTSTR lpCommandLine ,          /*매개변수 전달  (실행파일이름과 매개변수                            

                                                     정보를 묶어서 하나로 이곳에 표현 가능)*/

LPSECURITY_ATTRIBUTES lpProcessAttributes ,

LPSECURITY_ATTRIBUTES lpTreadAttributes ,

BOOL binheritHandles ,

DWORD dwCreationFlags ,

LPVOID lpEnvironment ,

LPCTSTR lpCurrentDirectory ,

LPSTARTUPINFO lpstrartupInfo ,

LPPROCESS_INFORMATION lpProcessInformation

);




LPSTARTUPINFO 

 정보전달

CreateProess 

정보반환

  LPPROCESS_INFORMATION 


※CreateProcess함수를 호출하기 위해서는

 LPSTARTUPINFO, LPPROCESS_INFORMATION 구조체를 선언해야한다.


LPSTARTUPINFO

이구조체를 통하여 생성하고자하는 프로세스 특성정보를 설정하여 CreateProcess에 인자로 전달한다.

 

LPPROCESS_INFORMATION 

CreateProcess함수가 생성된 프로세스정보를 LPPROCESS_INFORMATION 에 반환한다.




LPSTARTUPINFO


typedef struct _STARTUPINFO{

DWORD cb;                //구조체 변수의 크기

LPTSTR lpReserved;

LPTSTR lpDesktop;

LPTSTR lpTitle;          //콘솔 윈도우의 타이틀 바 제목

DWORD dwX;            //프로세스 윈도우의 x좌표

DWORD dwY;            //프로세스 윈도우의 y좌표  

DWORD dwXSize;     //프로세스 윈도우의 가로 길이

DWORD dwYSize;     //프로세스 윈도우의 세로 길이

DWORD dwXCountChars;

DWORD dwYCountChars;

DWORD dwFileAttribute;

DWORD dwFlags;    //설정된 멤버의 정보

WORD wShowWindow;

WORD cbReserved2;

LPBYTE lpReserved2;

HANDLE hStdInput;

HANDLE hStdOutput;

HANDLE hStdError;

} STARTUPINFO, *LPSTARTUPINFO;


LPPROCESS_INFORMATION


typedef struct _PROCESS_INFORMATION

{

HANDLE hProcess;        //프로세스의 핸들

HANDLE hThread;         //쓰레드 핸들

DWORD dwprocessId;    //프로세스의 ID

DWORD dwThreadId;      //쓰레드ID

} PROCESS_INFORMATION;

DWORD cb; 를 보면 구조체의 크기인데 왜 구조체가 정해져잇는데 구조체의 크기를 인자로 전달하는 걸까?

CreateProcess 함수를 보면 9번째 인자는 LPSTARTUPINFO로 확정되어있다.

그런데 나중에 다른인자를 올수있게 라이브러리를 개편한다면 전과의 호환성을 위해 구조체를 구별 할 수있게 전달하는 것이다.



예제 5-1]



AdderProcess 자식프로세스 단순덧셈프로세스

CreateProcess 부모프로세스 AdderProcess를 실행시키는 예제이다.


표준 검색경로

1 실행중인 프로세스의 실행파일이 존재하는 디렉터리

2 실행중인 프로세스의 현재 디렉터리(Current Directory)

3 Windows의 시스템디렉터리 (System Directory)

4 Windows 디렉터리 (Windows Directory)

5 환경변수 PATH에 의해 지정되어 있는 디렉터리




실행결과

 







프로세스와 스케줄러의 이해


프로세스의 이해

 

프로세스란?

메인 메모리로 이동하여 실행중인 프로그램 → 일반적인 정의

보통 프로세스라 하면 실행중인 프로그램 이다. 라고 표현을 한다.

틀린 표현은 아니다. 

그러나 메인메모리가 256메가 인데 실행파일의 크기가 1기가 라고 하면 과연 메모리에 올라가있는 코드만 프로세스인가? 

틀린건 아니지만 너무 애매한 표현이다.



프로세스의 구체적인 이해


프로세스의 범위

메모리 구조 + 레지스터 Set

프로세스 별 독립적인 대상은 프로세스의 범주에 포함시킬 수 있다.




 ↑

 Code 영역

명령어

(Instruction Code)

   

 ↑

Data 영역

 전역변수

static 변수

   

Heap 영역



Stack 영역

 프로그래머 할당

   

 



   

 지역변수

전달인자 정보

   
       

프로세스가 생성되면 위와같은 메모리가 할당 된다. 

프로그램이 실행된다라는 것은 레지스터에 프로그램 실행정보로 가득차게되는데

이런 레지스터 set과 메모리 구조 모두 프로세스 범위에 포함된다.


'프로세스를 구성하는 범주는 프로세스 별로 독립적으로 할당받는 리소스다.'

라고 할 수 있다.



프로세스 스케줄러


프로세스 스케줄러 기능

둘 이상의 프로세스가 적절히 실행되도록 컨트롤


스케줄링 방법

스케줄링 알고리즘에 따라 다양함.



스케줄러 라는 장치는 Windows 에서 지원해주는 소프트웨어적인 장치이다.

프로세스 스케줄러는 말그데로 프로세스를 스케줄링 해주는 것이다.

CPU는 하나인데 프로세스는 여러게를 실행 시키려하면 CPU는 하나의 프로세스만 실행시킬수 있기때문에 스케줄러가 프로세스의 순서를 정해주고  순서를 정하는 정책을 결정하고 관리해주는 것이다.


§스케줄러는 소프트웨어이기 때문에 스케줄러가 동작하는 순간에도 프로세스들은 동작하지 못한다. 넓은 관전에서 보면 스케줄러도 프로세스에 포함시킬 수 있다.






프로세스의 상태




프로세스는 Ready , Running, Blocked 같은 상태정보를 지닌다.

Running 상태는 CPU에 의해서 실행중인 상태로 CPU가 하나라면 Running 상태의 프로세스는 하나일 수 밖에 없다.

Blocked는 실행중이 아닌 상태.

Ready 상태는  Running 하고 싶은 상태로 C프로세스가  Running 상태이고

A와 B가 Ready 상태일때 C가 Blocked 상태로 간다고 하면 A와 B중 누가 Running

상태로 가느냐를 정해주는 것이 바로 스케줄러가 하는것이다.   

즉 Ready 상태에 있다는 것은 스케줄러가 선택해주기를 기다리는 상태이다.

그림에서 볼 수있듯이 모든 프로세스는 실행시키면 Ready 상태가 된다.

연산중에는 CPU에 의존적인 연산이 있고 그렇지 않은 연산있다.

그중에 I/O연산은 유독 CPU에 의존적이지 않기때문에 사칙연산과같은 기본적인 연산과 병행해 질수있다. 

만약에 Running 상태에 있는 C라는 프로세스가 I/O 연산시작 했을때 I/O 연산이 끝날때 까지 C는 쉬고있고 대신 다른 프로세스가  Running 상태가 되어도 된다.

그럼 C는 Ready 상태가 되야될까? Blocked 상태가 되야할까?

 Ready 상태가 된다고 생각해보면 C는 다시  Running 상태가 될것이고 I/O연산이 끝나지 않았다면 다시  Ready 상태가 되는 반복을 하기때문에 Blocked 상태가 된다.

그리고 다시 I/O연산이 끝났다면 Ready 상태로 돌아가게 된다.

 Running 상태에서 바로 Ready 상태가 되는것은 다른 프로세스들고 실행을 시키여하기때문에 연산이 끝나지않았어도 Ready 상태로 내리는 경우이다.


1. 모든 프로세스는 실행시키면 Ready 상태가 된다.

2. Ready 상태이면 스케줄러가   Running 상태로 만들어준다.

3. Running 상태에서 다른 프로세스를 실행시키기위해 어쩔수 없이 양보하는 경우

    Ready 상태로 내려간다.

4. 일반적으로 I/O 연산을 하고있는 프로세스들은 다른프로세스들에게 양보하기위        

   해서 Blocked 상태로 간다.

5. I/O 연산이 끝나면 프로세스를 다시 진행시키기위해  Ready 상태가 된다.

6. 프로세스 종료.




컨텍스트 스위칭(context switching)




ALU가 연산을 하기위해서 레지스터에 의존적이다.

레지스터는 Running 상태의 프로세스 정보로 가득차있다.

이때 Ready상태의 프로세스를 실행 시키려 할때 프로세스를 실행시킬 수 있는 

첫 번째 조건은 지금 레지스터레 채워져있는 데이터를 다른 저장영역에 저장시키고

 Ready상태의 프로세스를 위한 데이터를 레지스터에 복원해야한다.

이런 작업을 컨텍스트 스위칭(context switching)이라 한다.













Direct 모드와 Indirect 모드


Direct 모드                              Indirect 모드          

  LOAD r1, 0x10 실행시                LOAD r1, [0x10] 실행시              

 r0

 

 r0

 
 r1

 ←10     0x10번지

 r1

 0x30                  ↓                 0x10번지

 r2   r2

   ↖                   ↓

 r3

  r3

       ↖   10                            0x30번지

 r4     ir

 

 r4     ir

 
 r5     sp  

 r5     sp

 
 r6     lr  

 r6     lr

 

 r7     pc

 

 r7     pc

 


Direct 모드는 말그데로 메모리에 있는 값을 레지스트로 가져와라 이고

Indirect 모드는  메모리에 있는 값을 다시 메모리로 참조에서 값을 가져오는것이다.  0x10[0x30] → 0x30[10] → r1

※  LOAD r1, [0x10] 여기서 []는 Indirect 모드 라는 표시로 아무거나 써도 상관은없다.



Direct 모드의 문제점


 

 <-  LOAD  ->

 <destination>

<-                    source                   ->

 0

 0

 1

1

0

 0

1

 1

 0

 0

 0

1

 1

 1

   

int a = 10;      //0x0010 번지할당

int b = 20;     //0x0100 번지 할당

int c = 0;       //0x0020 번지 할당

c = a + b;

                                                                                      

LOAD r1, 0x0010 (구성가능)

LOAD r2, 0x0100 (구성불가)


8비트로 표현가능한 수는 0~255이다.

LOAD 명령어를 살펴보면 source 부분은 8비트 밖에 되지않는다.

0x0010같은 경우는 8비트로 표현이 가능하지만 0x0100은 딱봐다 8비트로는 표현이가능하지않는다. 

그럼 명령어로 구성 할 수없는 메모리 주소는 어떻게 사용할 것이가??



문제해결


int a = 10;      //0x0010 번지할당

int b = 20;     //0x0100 번지 할당

int c = 0;       //0x0020 번지 할당

c = a + b ;

명령어로 표현해보면 메모리 주소값문제로 Direct 모드로 표현 할 수없다는 것을 알고있다.

그래서 Indirect 로 풀어보면 


LOAD r1 , 0x0010


MUL r0, 4, 4

MUL r2, 4, 4
MUL r3, r0, r2


STORE r3, 0x0030

LOAD  r2, [0x0030]


ADD r3, r1, r2

 일단 r1에 a값을 넣는것은 그냥 LOAD명령어를 넣으면 된다 

하지만 b값은 메모리주소 0x0100 에 할당되어있어서 명령어로 표현이 불가능하다 그래서 MUL 명령어를 이용해보자.

r0에 4 곱하기 4 연산값을 넣고 r2에도 반복하자.

그리고 r3에 r0와 r2의 값을 곱한값을 넣으면 16*16 즉 256 , 16진수로 0100 이다. r3에보면 0x100이 들어가 있는상태이다.

이제 r3에 있는 값을 0x0030 번지에 값을 넣고 Indirect로 0x0030을 불러와서 r2에 넣으면 r2에 원하던 값을 넣을 수 있게 된다 .


     레지스터                                 메모리

 r0         16

 

 r1         10

   10                                 0x0010(a)

 r2         16

   0                                  0x0020(c)

 r3     265(0x0100)

   
 r4   ir

  0x0100                               0x0030

 r5   sp

  20                                      0x0100

 r6   lr  
 r7   pc  


그림으로 만들어 보면 이런 모양일 것이다.



+ Recent posts