메모리 대응 입출력과 더불어 더 많은 정보와 예제를 담은 리눅스 교재를 배포했습니다. 아래의 페이지에서 리눅스 교재를 받아가세요.

https://reakwon.tistory.com/233

 

리눅스 프로그래밍 note 배포

티스토리에 리눅스에 관한 내용을 두서없이 여지껏 포스팅했었데요. 저도 제 포스팅을 찾기가 어렵기도 하고 티스토리에서 코드삽입을 하게 되면 이게 일자로 쭉 쓰여져있는 x같은 현상이 생겨

reakwon.tistory.com

 

메모리 대응 입출력(memory-mapped I/O)

메모리 대응 입출력 기법은 디스크의 파일을 메모리에 한 부분인 버퍼에 대응을 시켜 읽거나 쓰는 동작을 할 수 있는 기법입니다. 그래서 파일 입출력을 위한 read나 write를 사용할 필요가 없습니다. 메모리 대응 입출력을 사용하려면 커널에 파일을 메모리에 대응(mapping)시키겠다고 정보들을 알려줘야합니다. 그렇기 위해서 우리는 mmap함수를 비롯해 몇가지 함수를 사용합니다. 그래서 마지막에는 메모리 대응 입출력을 활용하여 파일을 copy하는 프로그램을 만들어보도록 하겠습니다.

1. mmap 함수

mmap은 아래와 같이 정의가 되어있습니다. 주어지는 정보들은 아래와 같습니다.

#include <sys/mman.h>

void *mmap(void *addr, size_t length, int prot, int flags,
                 int fd, off_t offset);

 

- 반환값 : 성공시 메모리의 맵핑된 주소를 반환합니다. void* 형으로 자료형에 따라 알맞게 변환할 수 있습니다. 실패시에는 MAP_FAILED를 반환합니다. MAP_FAILED는 (void*) -1을 의미합니다. 

- addr : 메모리 대응 영역의 시작주소를 뜻합니다. 보통 0을 넣어 적절한 주소를 반환시킵니다. 이때 addr은 시스템의 가상 메모리 페이지의 크기 배수로 설정이 되어야한다는 것을 주의하시기 바랍니다.

- length : 얼마만큼의 메모리를 초기화 할 것인지 크리를 정합니다. 이때 offset 이후부터라는 점을 기억하시기 바랍니다. offset은 아래의 설명이 있습니다.

- prot : 메모리를 보호하는 방식을 설정합니다. 아래의 표와 같이 정의가 되어있습니다. 인수의 이름은 꽤나 직관적이니 뭐 굳이 자세한 설명은 필요없을 것 같네요.

prot 인수 설명
PROT_READ 메모리 영역 읽기 가능
PROT_WRITE 메모리 영역 쓰기 가능
PROT_EXEC 메모리 영역 실행 가능
PROT_NONE 메모리 영역 접근 불가

 

- flags : 메모리 대응 영역에 특성을 설정합니다. 여기서는 세가지만 설명하는데 리눅스 혹은 다른 구현에서는 많은 옵션이 존재할 수 있습니다.

flag 이름 설명
MAP_FIXED 반환값이 정확히 전달받은 addr의 주소와 같은데, 이 flag는 이식성을 저하시키므로 사용하지 않는 것을 권합니다. 
MAP_SHARED 영역에 대응된 파일을 수정합니다. 그러므로 메모리의 수정이 일어나면 파일의 수정이 일어납니다. 아래의 MAP_PRIVATE나 MAP_SHARED 중 하나를 설정해야합니다.
MAP_PRIVATE 유추가 되죠? 영역에 대응된 파일을 수정하는 MAP_SHARED와는 달리 메모리 대응 영역만 수정이 일어날뿐 실제 파일에는 수정이 일어나지 않습니다. 이를 비공유 복사본이 생성된다고 합니다. 

 

- fd : 대응시킬 파일 서술자를 뜻합니다. fd는 어디서 얻어오죠? open에서 얻어올 수 있습니다. 네, mmap을 사용하기 전에 file을 열어야합니다. 

 

- offset : 파일의 시작에서 부터 얼마만큼 떨어진 영역을 메모리에 대응시킬것인가를 뜻합니다. 이떄 offset은 가상 메모리 페이지 크기의 정수배여야합니다.

 > 가상 메모리 페이지 크기

그렇다면 가상 메모리 파이지 크기는 어떻게 알 수 있을까요? 아래의 코드로 얻어올 수 있습니다.

page_size = sysconf(_SC_PAGESIZE);

 

2. munmap

메모리 대응 영역은 프로세스가 종료되면 자동으로 해제가 되는데, munmap을 사용하여 직접 해제시킬 수 있습니다.

#include <sys/mman.h>

int munmap(void *addr, size_t length);

 

- 반환값 : 성공시 0, 실패시 -1을 의미하며 errno에 에러 정보가 저장됩니다.

- addr : 해제할 메모리 영역의 주소를 지정합니다.

- length : 얼마만큼의 영역을 해제할 것인지 size를 지정합니다.

 

3. memcpy

#include <string.h>

void *memcpy(void *dest, const void *src, size_t n);

메모리를 copy하는 함수입니다. 단순히 설명하면 src의 내용을 dest로 n만큼을 복사합니다. 성공시 dest의 주소를 반환하여 줍니다.

 

4. msync

MAP_SHARED으로 메모리를 대응시켰을 경우 일정 시간이 지나면 파일로 내용을 동기시키긴 하지만,  msync 함수를 호출하면 변경된 내용이 바로 실제 파일로 적용이 됩니다. 단, MAP_PRIVATE는 파일에 변경사항이 저장되지 않는 다는 점을 기억하세요. 

#include <sys/mman.h>

int msync(void *addr, size_t length, int flags);

addr과 length는 mmap의 내용과 같습니다. flags의 내용은 대충 아래 표와 같습니다.

flag 설명
MY_ASYNC 페이지들이 호출 반환 후에 방출되게 하고 싶으면 이 인수를 사용합니다.
MY_SYNC 쓰기들이 실제로 완료된 후에 호출이 반환되게 하려면 이 flag를 사용합니다.
둘 중 하나는 설정해야합니다. 

 

5. 메모리 대응 입출력을 이용한 파일 복사 프로그램

cp 명령 아시죠? cp src dst 를 하게 되면 src의 내용이 dst라는 파일로 복사가 됩니다. 아래는 이를 메모리 대응 입출력을 통해서 구현해보는 소스코드입니다.

#include <stdio.h>
#include <sys/mman.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>

int main(int argc, char *argv[]){
        int srcfd, dstfd; //src 파일 서술자, dst 파일 서술자
        void *src, *dst;  //src 메모리 주소, dst 메모리 주소
        size_t copysz; //다음 copy할  메모리 내용 size
        struct stat sbuf;
        off_t fsz = 0; //다음 읽기, 쓰기를 기록할 위치(offset)
        long page_size; //시스템의 PAGE SIZE

        if((srcfd = open(argv[1], O_RDONLY)) < 0) {
                fprintf(stderr, "can't open %s for reading \n",argv[1]);
                exit(1);
        }

        if((dstfd = open(argv[2], O_RDWR | O_CREAT | O_TRUNC, 0777)) < 0){
                fprintf(stderr, "can't open %s for writing\n", argv[2]);
                exit(1);
        }


        //file 사이즈 얻기 위한 용도
        if(fstat(srcfd, &sbuf) < 0){
                fprintf(stderr, "fstat error\n");
                exit(1);
        }

        if(ftruncate(dstfd, sbuf.st_size) < 0){
                fprintf(stderr, "ftruncate error\n");
                exit(1);
        }

        page_size = sysconf(_SC_PAGESIZE);
        printf("page_size : %ld\n", page_size);

        while(fsz < sbuf.st_size){

                if((sbuf.st_size - fsz ) > page_size)
                        copysz = page_size;
                else
                        copysz = sbuf.st_size - fsz;

                //src 주소 설정
                if((src = mmap(0, copysz, PROT_READ, MAP_SHARED, srcfd, fsz))
                                == MAP_FAILED){
                        fprintf(stderr, "mmap error for input \n");
                        printf("error : %s\n",strerror(errno));
                        exit(1);
                }

                //dst 주소 설정 , 여기서 MAP_SHARED를 MAP_RPIVATE로 바꾸면? dst파일에 저장되지 않는다.
                if((dst = mmap(0, copysz, PROT_READ|PROT_WRITE, MAP_SHARED, dstfd, fsz)) == MAP_FAILED){
                        fprintf(stderr, "mmap error for output\n");
                        exit(1);
                }

                //src -> dst로 내용 복사
                memcpy(dst, src, copysz);

                //메모리 해제
                munmap(src, copysz);
                munmap(dst, copysz);
                //복사한 내용만큼 다음 메모리 위치를 이동시킬 offset 증가
                fsz += copysz;

        }

        exit(0);
}

 

argv[1]은 복사할 파일 이름, argv[2]는 복사하여 나온 파일 이름입니다. ftruncate 함수로 출력 파일의 크기를 설정해줍니다. 

ftruncate는 아래와 같이 정의되어 있습니다. 간단히 설명하면 파일 서술자 fd의 길이를 length로 잘라버린다는 겁니다. 즉, 크기 지정한다고 보면 됩니다. 

#include <unistd.h>
#include <sys/types.h>

int ftruncate(int fd, off_t length);

 

이후 sysconf로 현재 나의 컴퓨터의 PAGE SIZE를 가져옵니다. 아까 말했듯이 addr과 offset은 PAGE SIZE의 배수여야한다고 했습니다. 

while 루프 안에서는 파일을 끝까지 복사할때까지 계속 반복합니다.

        while(fsz < sbuf.st_size){
        	//... 파일 복사 ...//
        }

while 루프 안의 로직은 어렵지 않습니다 .아까 배운 mmap을 통해서 src와 dst의 메모리를 대응 시킨 뒤에 memcpy 함수로 src의 내용을 dst의 내용으로 복사합니다.  그 이후 munmap으로 메모리를 해제하네요. 그 다음 page_size를 넘을만큼 그다면 다시 page_size 이후의 내용을 읽어야겠죠? fsz를 증가하는 이유가 그 이유입니다.

 

이상으로 메모리 대응 입출력에 대한 포스팅을 마치겠습니다.

반응형
블로그 이미지

REAKWON

와나진짜

,

MsgSend, MsgReceive, MsgReply를 이용한 client - server sample

MsgSend는 함수명만 보면 보내기만 할것 같지만, 실제로는 받는것도 하고 있다. Client - Server 모델에서 echo 서버 형태를 구현한다고 하면 client쪽에서는 send 이후 receive로 받아오는데, 이때 receive할때까지 client는 기다린다. MsgSend는 send, receive를 합한 형태로 보면 된다. 그렇기 때문에 MsgSend는 데이터를 받아올때까지 Blocking되는 특성이 있다. 함수 원형은 아래와 같다. 

#include <sys/neutrino.h>

int MsgSend (int coid,
             const void *smsg,
             int sbytes,
             void *rmsg,
             int rbytes);

 

coid : ConnectAttach()를 통해서 나온 값

smsg : 보내는 데이터

sbytes : 보내는 데이터의 길이

rmsg : 받는 데이터

rbytes : 받는 데이터의 길이 

 

MsgReceive는 데이터를 받는 함수이고, MsgReply는 응답을 하는 함수이다. sample을 보면 이해가 갈것이다.

//Server.c 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/neutrino.h>

#define EOK 1234
int main(void)
{
    int     rcvid;         // indicates who we should reply to
    int     chid;          // the channel ID
    char    message [512]; // big enough for our purposes

	printf("Server Start\n");

    // create a channel
    chid = ChannelCreate (0);
	printf("Wait in channel %d\n",chid);

    // this is typical of a server:  it runs forever
    while (1) {

        // get the message, and print it
        rcvid = MsgReceive (chid, message, sizeof (message),
                            NULL);
        printf ("Got a message, rcvid is %X\n", rcvid);
        printf ("Message was \"%s\".\n", message);

        // now, prepare the reply.  We reuse "message"
        strcpy (message, "This is the reply");
        MsgReply (rcvid, EOK, message, sizeof (message));
    }

	return 0;
}

서버쪽에서는 ChannelCreate로 chid를 가져오고, Client는 여기에 Attach한다. 

//client.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/neutrino.h>

int main(int argc, char *argv[]){
	char *smsg = "This is the outgoing buffer";
	char rmsg [200];
	int coid;
	int ret;
	pid_t pid;

	if(argc != 2){
		fprintf(stderr,"Usage : %s [server_pid] \n",argv[0]);
		exit(EXIT_FAILURE);
	}
	pid = atoi(argv[1]);
	// establish a connection
	coid = ConnectAttach (0, pid, 1, 0, 0);
	if (coid == -1) {
		fprintf (stderr, "Couldn't ConnectAttach to %d/%d/%d!\n", 0, pid, 1);
		perror (NULL);
		exit (EXIT_FAILURE);
	}

	// send the message
	if ((ret  = MsgSend (coid,
				smsg,
				strlen (smsg) + 1,
				rmsg,
				sizeof (rmsg))) == -1) {
		fprintf (stderr, "Error during MsgSend\n");
		perror (NULL);
		exit (EXIT_FAILURE);
	}

	if (strlen (rmsg) > 0) {
		printf ("Process ID %d returns code :%d, msg :\"%s\"\n", pid, ret, rmsg);
	}
}

 

여기서 ConnectAttach의 channel id는 1로 임의로 지정했는데, argv로 입력받는 것이 더 좋을듯하다. 난 귀찮아서...

반응형
블로그 이미지

REAKWON

와나진짜

,

나의 중이염 일대기

7살, 8살 쯔음 혼자서는 똥, 오줌도 못가릴 무렵, 귀 속이 찢어진 듯한 통증과 함께 진물이 나온 기억이 있는데, 이때 방치했던 것이 만성 중이염으로 이어졌다. 중이염이라는게 콧물 등에 포함된 세균이 유스타키오관을 통해 중이 안으로 침투해 염증을 일으키는 질환으로 진물이 고막 안에 고여있는데, 이때 느낌이 귀에 물이 찬 느낌이다. 그러다가 이게 마침내 터지면 귀에 진물이 나오게 된다. 고막에 구멍이 뚫리니 통증을 당연이 느끼게 되는것이다. 특히 이 유스타키오관이 짧은 어린애들이 잘걸리니 아이들이 아무이유없이 울고있다면 궁디팡하지말고 이비인후과로 데려가도록 하자. 

https://post.naver.com/viewer/postView.nhn?volumeNo=7842154&memberNo=24304009

 

암튼 12살까지 방치하다가 12살~13살까지 동네 이비인후과 병원을 다니기 시작했다. 몇년을 방치하니까 튜브를 삽관하는 수술을 했는데, 이때 삽관이 잘 되지 않아 대학병원에서 수술을 받았다. 14살이 되어서 아주대학교 병원에서 수술을 진행했고 귀 안에 튜브를 박고 1년 동안 통원 치료를 받았는데, 좀 처럼 청력을 돌아오지 않았다. 이렇게 튜브 삽관 수술을 18살때까지 진행하다가 더이상 진전되지 않은 것 같아 21살까지 방치하다가 군대를 가게 되었다.

이때부터는 거의 자포자기다. 청력은 한쪽에만 의지하며 이후 +10년 추가. 합 20년 가까이 중이염을 앓고 지냈는데, 최근 이명과 함께 귀가 먹먹해지는 느낌이 들어 대학 병원이 아닌 귀 전문 병원으로 진료를 받으러갔다. 소리이비인후과가 유명하다고 해서  무려 청담으로 향했다. 1층을 제외한 건물 자체가 병원이다..

 

검사

가게 되면 우선 귀를 한번 째본다. 긴 뾰족한 꼬챙이로 귀의 고막을 한번 뚫는데, 이 이유는 안에 물이 찼는지, 액체형태로 빠져나오는지, 아니면 나오지 않는지 확인해보는 거라고 한다. 난 이 꼬챙이를 대학병원을 다니면서 몇 차례 당한적이 있어 보기만해도 몸에서 경기가 일어났다.

나 : "헑엉 ㅠㅠ 저 이거 안하면 안돼요? 이거 엄청 아픈데...ㅜㅜ "

의사 : "안아프게 할게요. 살짝만"

결국 살짝 뚫었는데, 그렇게 아프게 하지 않았다. 역시 다년간의 경험으로 처음부터 엄살을 떨어야만 의사샘이 살살해준다. 여러분도 일단 개거품부터 물어보자.

나는 진물이 나오지 않았다. 이것은 즉, 고름이 안에 진득진득하게 차있어서 그런것이다. 이제 청력검사를 시행한다. 청력검사는 별거없는데, 삐 소리가 나면 버튼을 누르는 식, 그리고 귀 안에 청신경?이 살아있나 보는 진단을 하게 된다. 별거없으니 패스

나는 고막이 안으로 말려들어간 유착성, 그리고 안에 고름이 차있는 진주종성 중이염 판정을 받고 수술을 하는 것으로 판정이 났다.  수술을 하기위한 피, 소변검사는 소리이비인후과 병원에서 해주는데, 나머지 X-ray 사진, 심전도 검사는 내과에서 하고 팩스로 전달해야한다.

 

입원은 총 아래와 같이 3일하게 된다. 무적권 1인실 밖에 없고, 청담이라 1박에 무려 24만원이다. 시간당 만원꼴.

 

수술은 총 3가지를 진행하는데 아래와 같다.

1. 유양동 절제술

2. 고막 성형술

3. 이소골 재건술

이 수술들을 진행하기 위해서는 귀구멍 안으로 수술하는게 아니라 귀 뒤 절개한 다음 진행해야하는 수술이라 좀 무섭...긴해도 전신마취라서 자고 일어나면 끝나있다... 라고 생각하는게 정신건강에 좋다. 스스로를 가스라이팅하길 바란다. 

이후 2차 수술도 할수도, 안할 수도 있다고 한다. 그건 1차 수술 결과를 보고 알 수 있나보다. 

 

입원(1일차)

드디어 그날이 되어 상큼하게 병원을 가서 접수를 한다. 뭔가 다리가 요염하게 나왔는데, 오줌을 참고 있었나보다. 여기 안내 데스크에 있는 간호사분들은 환자한테 먼저 인사 안하길래 내가 먼저했다. 날 못본거겠지? 그렇다고 내가 10곤대는 아니고... 

 

 

접수하고 나면 코로나 검사를 받고 여기서 대기좀 타야되는데 기다리는 시간동안 만성 중이염에 대해서 알아보자.

 

옼키, 대충 봤으면 이후 어지럼증 검사를 하고 입실하게 된다. 방안의 풍경은 이러하다.

 

 

 

8시까지는 환자가 돌아와야하고, 10시까지만 면회가 허용이 가능하다. 첫날은 저녁이 안나오니까 밖에서 먹고오거나, 싸오거나 해야한다. 오후 9시쯤 되어서 항생제 반응검사를 하고 항생제를 맞는다.

수술 당일(2일차)

나의 경우 수술은 전신마취를 통해서 진행했다. 오전 7시부터 수술실에 들어간것으로 기억하고, 마취전문의를 동반하여 수술을 진행한다. 그렇기 때문에 자고 일어나면 수술이 끝나있다. 아참! 수술 당일에 무조건 보호자가 한명이 필요하다. 

수술이 끝나고  난 후의 모습인데, 미간에 야무지게 인상을 쓰고 있는거보니, 다행히 살아있다. 사진은 내 사랑스러운 여자친구가 찍어주심.. 

 

음.. 이때부터는 이제 통증과의 싸움이긴 한데, 진통제를 맞으면 조금은 괜찮아지긴한다. 근데 아프면 너무 참지말고 진통제를 맞거나 먹는것이 더 좋다. 통증의 정도는 귀를 송곳으로 뚫은 정도의 통증이라고 보면 될것 같다.

수술이 끝난 직후 나의 증상은 몇가지 있었는데 나열하면 아래와 같다.

● 혀가 얼얼하다. 약간 미각이 안느껴진다. 수술후 하루가 지나도 그렇다.

● 목이 아프다. 전신 마취중에 기관지가 손상이 될 수도 있다고하는데, 그런건가보다. 목이 좀 아프다. 

● 어지러움은 없었다. 마취깨고 1시간 지나고 나서야 제정신이 들었다.

● 귀에 물흐는 소리와 물이 차있는 소리, 마치 고막이 움직이는 느낌이 든다. 고막 성형했으니 그런 소리가 나는것은 매우 정상이라한다. 

 

병원밥은 못참지

이 와중에 밥은 레알 맛있는데, 씹기가 어려운게 문제다. 

사진에 보이는거 파란색 머리띠 저게 진짜 답답하긴 한데, 그 안에 거즈로 피를 막아놓고 고정해서 차고 있어야한다. 수술이 7시부터 9시 살짝 넘어서 끝났고, 이 거즈는 세시 정도에 의사샘이 갈아준다. 이때 본인 피 보고 놀라지 마시길...

나같은 경우에는 시간이 지날수록 통증이 좀 심해졌다. 그렇지만 저녁먹고 진통제 맞고, 새벽에 아플때 진통제 먹고 그러니 참을만은 했다.

오늘 하루 종일 나 챙겨주고 간호해준 여자친구, 너한테 평생잘해주께 영원히. 사랑한다 베이비♥♥♥♥

 

퇴원(3일차)

오후 6시에 상처부의를 소독해주고, 거즈대신 살색 의료용 테이프같은거로 갈아준다. 9시에 의사샘이 다시 오시는데, 별건 없다. 어디 불편한지, 아픈지 묻고 퇴원하면 된다. 입원비.. 188만원 나왔다. 18

이제부터 7주일동안은 상처를 잘 아물게 최대한 노력을 해야하는데, 항생제는 식후 30분 3끼 다먹고 소화제도 같이 먹는다. 아플때는 진통제를 먹으면 된다. 나같은 경우에는 하루에 두번 잘 챙겨먹는다. 

사진에서 보면 가장 오른쪽 위에는 귀 외이도에 넣어주는 지혈솜? 같은건데, 새로로 꽂아넣고 살색 테이프로 귀에 붙여줘야 나중에 갈때 편하다. 

 

수술후 주의사항 중에서도 술, 담배는 안되는 것은 다 알고 있겠지만 물을 특히 조심해야한다. 오히려 안씨는것을 추천해줄 정도로 물을 닿게해서는 안된다. 특히 상처부위말고 귀구멍안에 물은 진짜 들어가면 안된다고한다. 조심해야지~~~

여기까지 나의 중이염 수술 과정과 후기를 적었는데, 지금 이 시점은 퇴원하고 막 글을 쓰는 시점이라서 수술 결과는 모른다. 근데 의사샘이 수슬은 잘됐다고 알려주셨는데, 이제 잘 관리하고 2차 후기를 남길 수 있도록 해야지. 이 글을 끝까지 본 사람이면 중이염 수술을 생각하고 있는 분일텐데, 너무 무섭게 생각치 마시고 용기 내서 완치하세요.

반응형
블로그 이미지

REAKWON

와나진짜

,