Tuuna Computer Science

[ 공유 메모리 ] 본문

System programing

[ 공유 메모리 ]

GuTTe 2018. 6. 9. 21:02
[ 공유 메모리 ] 

프로세스에서 사용되는 메모리 영역은 해당 프로세스만이 사용할 수 있다. 
즉, 자신만의  메모리 영역을 의미한다. -> 커널에 요청한 프로세스만이 접근할 수 있다.  

하지만 여러개의 프로세스가 특정 메모리 영역을 사용했으면 하는 때가 있다. 

이를 공유 메모리라고 한다. 

공유 메모리는 IPC중에서 가장 빠른 수행속도를 기록한다. 

이 때 한번에 하나의 프로세스만이 메모[리에 접근가능을 보장해야 한다. 

IPC : 프로세스간의 통신 

[ 공유 메모리관련 함수 ] 

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

int shmget (key_t key, int size, int shmflg)
void* shmat (int shmid, const void* shmaddr, int shmflg)
int shmdt (const void* shmaddr)
int shmctl (int shmid, int cmd, struct shmid_ds * buf)


공유 메모리의 생성요청은 최초 공유메모리 영역을 만드는 프로세스가 커널에게 요청함으로써 이루어 지기때문에 커널에 의해 관리하게 된다. 

운영체제를 리부팅하거나, 직접 공유메모리 공간을 삭제하지 않는한 계속유지하게 된다. 

이러한 공유메모리공간은 shmid_ds구조체에 의해 관리된다. 

헤더파일 : "shm.h"

struct shmid_ds
{
    struct ipc_perm shm_perm;                //퍼미션(접근 권한) 
    int                      shm_segez;               //메모리 공간의 크기  
    time_t                shm_atime;               //마지막 attact 시간
    time_t                shm_dtime;               //마지막 detach 시간 
    time_t                shm_ctime;               //마지막 변경 시간  
    unsigned short  shm_cpid;                 //생성프로세스의 pid
    unsigned short  shm_lpid;                  //마지막으로 작동한 프로세스의 pid
    short                  shm_nattch;              //현재 접근한 프로세스의 수 
};

현재 접근중인 프로세스의 수 :: 공유메모리에 접근을 하기 위해서는 고유의 공유메모리 key를 통해서 접근가능해진다. 이 key값을 통해 다른 공유메모리들과 구분됨. 

[ shmget ]

:: 커널에 공유메모리 공간을 요청하기 위해 호출하는 시스템 호출 함수 

int shmget (key_t key, int size, int shmflg)

->key_t key :: 고유의 공유메모리임을 알려주기 위해 사용 
shmget함수를 통해 새로운 공유메모리를 생성 or 참조 

->int size :: 공유메모리의 크기 

->int shmflg :: 공유메모리의 접근 권한, 생성방식
 
IPC_CREAT : key를 이용하여 새로운 공유메모리 생성 
IPC_EXCL : 공간이 이미 있을 시 error 반환 
===========================
int shmid;
key_t keyval;

keyval = 1234;
shmid = shmget(keyval, 1024, IPC_CREAT | 0666)); //권한 설정  0666 : 파일의 기본 권한
if (shmid == -1)
{
    return -1;
}
============================
[ shmat ] 

:: 생성 후 공유메모리에 접근할 수 있는 int형의 "식별자" 얻게 됨. 
shmat를 이용해서 이용하고자 하는 프로세스가 공유메모리를 사용가능하도록 "덧붙임"작업을 해주어야 한다


void* shmat (int shmid, const void* shmaddr, int shmflg)

-> int shmid : shmget를 이용해서 얻어낸 식별자 번호 

-> const void* shmaddr : 메모리가 붙을 주소를 명시 
0을 사용시 커널이 메모리가 붙을 주소 명시 

-> int shmflg : 프로세스가 해당 공유메모리에 대한 "읽기 전용", "읽기/쓰기" 모드로 열 수 있다. 
SHM_RDONLY : 읽기 전용 
아무값도 지정 X  : 읽기/쓰기 가능 


[ shmdt ]

int shmdt (const void* shmaddr)

:: 프로세스가 더 이상 공유메모리를 사용할 필요가 없을 경우 프로세스와 공유메모리를 분리 하기 위해서 사용 

but : 공유메모리의 내용은 그대로 남는당!

shmdt가 성공적으로 수행되면 커널은 shmid_ds의 내용을 갱신 
즉, shm_dtime, shm_lpid, shm_nattch등의 내용 갱신 

 shm_mattch 의 값이 1씩 감소하며 0이 되면 프로세스가 X 

[ shmctl ]

:: 공유 메모리 제어

int shmctl (int shmid, int cmd, struct shmid_ds *buf)

-> int cmd :  공유 메모리 제어 

  • IPC_STAT :: 공유메모리 공간에 관한 정보를 가져오기 위해 사용 정보는 buf에 저장 
  • IPC_SET :: 공유메모리 공간에 대한 사용자권한 변경을 위해 사용 
  • IPC_RMID :: 공유메모리 공간을 삭제하기 위해서 사용, shm_nattch가 0이 될때까지 기다렸다가 삭제 


[ 공유 메모리 제어 ]

ipcs -l  :: 자원의 제한 정보 

ipcs -m : 현재사용중인 ipc 자원 정보 

/proc/sys/kernel/shmmax : 프로세스가 생성할 수 있는 공유 메모리의 최대 크기 
/proc/sys/kernel/shmall : 현재 사용중인 메모리의 크기 

ex) echo 536870912 > /proc/sys/kernel/shmmax     


//attach 후에 fork를 호출하니 shmget이나 shmat가 필요하지 않다! 

#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
        int shmid;
        int pid;
        int *cal_num;
        void *shared_memory = (void *)0; //void형으로 선언
        // 공유메모리 공간을 만든다.
        shmid = shmget((key_t)1234, sizeof(int), 0666 | IPC_CREAT); //shmid를 반환한다 
        /*
        int shmget (key_t key, int size, int shmflg)
        shmget : 공유메모리 생성 함수
        -> 1_par : key 값
        -> 2_par : 크기
        -> 3_par : 파일 기본 권한 | IPC_CREAT : 공유메모리 생성
        return : 실패시 -1
        */
        if (shmid == -1)
        {
               perror("shmget failed : ");
               exit(0);
        }
        // 프로세스메모리를 공유메모리영역에 붙인다.
        shared_memory = shmat(shmid, (void *)0, 0);
        //shmget에서 shmflg은 복합적인 플래그로 처리되는데 0은 errorno값이 EINVAL로 리턴됩니다.
        /*
        void *shmat(int shmid, const void *shmaddr, int shmflg);
        shmat : 프로세스가 공유메모리를 사용할 수 있게 덧붙임
        -> 1_par : 식별자번호
        -> 2_par : 메모리가 붙을 주소 : 0 -> kernel이 지정
        -> 3_par : SHM_RDONLY : 읽기만 || 0 : 읽기 or 쓰기
        return : 실패시 -1 but 성공시 shared memory segment
        */
        if (shared_memory == (void *)-1)
        {
               perror("shmat failed : ");
               exit(0);
        }
        cal_num = (int *)shared_memory;
        //처음 선언시 void형이였으므로 int*형으로 캐스팅함 추론 : 단지 공유메모리의 주소를 줌
        pid = fork();
        //자식 프로세서 생성 || 생성시 공유메모리는 자식 프로세스에게 상속된다. 
        //그렇기에 attach후 자식프로세스 생성 후 shmget을 할필요가 없다 
        if (pid == 0) //성공시
        {
               shmid = shmget((key_t)1234, sizeof(int), 0); 
               //이건 그냥 한 번더 선언함. 안해도 됨. www.joinc.com예제에선 했길래
               if (shmid == -1) //shmget 실패시
               {
                       perror("shmget failed : ");
                       exit(0);
               }
               shared_memory = shmat(shmid, (void *)0, 0666 | IPC_CREAT);
                   //shmat에서도 0666 | IPC_CREAT -> 권한?
               if (shared_memory == (void *)-1) //shmat 실패시
               {
                       perror("shmat failed : ");
                       exit(0);
               }
               cal_num = (int *)shared_memory; //이건 공유메모리의 주소를 넘겨주기위해성
               *cal_num = 1; //주소를 넘겨받는 느낌인가 공유메모리의 주소?
               while (1)
               {
                       *cal_num = *cal_num + 1; //공유메모리의 주소에 저장된 값 연산 즉, 참조 
                       printf("child %d\n", *cal_num);
                       sleep(1);
               }
        }
        // 부모 프로세스로 공유메모리의 내용을 보여준다.
        // 이미 상속받은 세그먼트를 한 번 더 attach하니까
        //ipcs 상에 nattach가 3으로 나타나는 것으로 판단된다
        else if (pid > 0) //pid가 양수이면 부모 프로세서 자식 프로세서의 반환값이 양수
        {
               while (1)
               {
                       sleep(1);
                       printf("%d\n", *cal_num);
               }
        }

}

1.Manual에 따르면 fork시에 attach된 shared memory segment는 child 프로세스에 상속됩니다.
예제 코드는 attach 후에 fork를 호출하니 shmget이나 shmat가 필요하지 않습니다.

2.이미 상속받은 세그먼트를 한 번 더 attach하니까 ipcs 상에 nattach가 3으로 나타나는 것으로 판단됩니다.

3.shmget에서 shmflg은 복합적인 플래그로 처리되는데 0은 errorno값이 EINVAL로 리턴됩니다.

교수님한테 따로 물어본건데 암튼 그렇다넹 의문점 해결! 




'System programing' 카테고리의 다른 글

리눅스 메모리에 대해  (0) 2018.08.10
system call  (0) 2018.06.11
[ 좀비 프로세스 ]  (0) 2018.06.09
[리눅스] 프로세스에 대해서  (0) 2018.06.09
Comments