수업/System Programming

[System Programming] Inter Process Communication(IPC)

hw-ani 2022. 11. 23. 18:16

앞서 배운 pipe/named pipe나 signal도 process간 통신 수단 중 하나이다. 이 글에선 다른 IPC 관련 명령어/함수들을 알아본다.

여기선 아래 두가지 IPC 수단을 추가로 배운다.
1. mesasge queue를 이용한 IPC
2. shared memory를 이용한 IPC
이외에도 file lock, semaphore, socket, 일반 file 등등.. IPC 관련 개념/system calls가 많긴한데 여기선 위 두가지만 배운다.
(pipe도 결국 위 두 개념에 속하는게 아닌가? → 말만 보면 그런 것 같긴한데, UNIX/LINUX에서 위 두가지를 전문적으로 해주는 기능이 존재함. pipe도 IPC는 맞지. pipe/message queue/shared memory 다 따로따로 지원된다는 말이다.)

아무래도 일반 file로 ipc를 수행하는건 disk까지 왔다갔다 해야돼서 소모도 크고, read/write 제한이 없어서 오류 가능성도 있고 하니 이렇게 OS에서 전문화된 방식을 지원해주는 것 같다.

 

 

> ipcs 명령어

ipcs 명령어 옵션 정리

$ ipcs
: 현재 OS에서 관리하는 IPC 수단 중, Message Queue, Shared Memory, Semaphore에 해당하는 자원의 정보를 보여준다.

$ ipcs -q
: Message Queue에 대한 정보만 보여준다.

$ ipcs -m
: Shared Memory에 대한 정보만 보여준다.

$ ipcs -s
: Semaphore에 대한 정보만 보여준다.(주의/ shared memory를 보려면 -m을 해야한다.)

$ ipcrm -q msqid
: id에 해당하는 message queue를 삭제한다. shared memory를 지울거라면 ipcrm에 `-m` 옵션을 줘야한다.
(key가 아니라 ipcs 했을때 나오는 id로 지운다는 점 주의하자. 각 옵션을 대문자로 주면 키로 지운다.)

 


Message Queue

큰 흐름은, msgget으로 특정 key를 가진 Message Queue의 id를 얻어오고, 그 id를 통해 해당 MQ로 send, receive하는 것이다.


> int msgget(key_t key, int msgflg);

특정 key_t값을 가지는 Message Queue의 qid를 가져오는 함수이다. (MQ마다 고유한 key_t값을 가진다.)
key는 message queue를 가져오기 위한 고유번호이다.(fopen이 이름 넘겨주는거랑 유사)
msgflg는 IPC_CREAT이나 IPC_EXCL이 권한과 함께 올 수 있다. ex) msgget(key, IPC_CREAT | 0666)
IPC_CREAT은 해당하는 key의 message queue가 없다면 그 key로 message queue를 만들지만, IPC_EXCL은 아예 message queue가 있으면 오류로 판단한다. 즉, msgget(key, IPC_CREAT | 0666)는 이미 해당 key의 MQ가 존재하면 그놈을 반환해주지만, msgget(key, IPC_CREAT | IPC_EXCL | 0666)은 이미 MQ가 존재하면 안된다, 완전히 새로 만들겠다는 말이다.
반환값은 해당하는 MQ의 qid이다. 오류면 -1을 반환한다.

> int msgsnd(int qid, const void* msgp, size_t msgsz, int msgflg);

특정 qid를 가진 Message Queue로 메시지를 보내는 함수이다.
첫번째 인자는 메시지를 보내고자하는 MQ의 qid이고, 두번째는 해당 보내고자하는 내용이 담겨있는 메모리의 주소값이다. 세번째는 메시지 size고.
마지막 인자는 message queue가 꽉찼을때 행동을 결정하는 인자다. 0을 넣으면 공간이 날때까지 대기하고, IPC_NOWAIT을 넣으면 공간이 없으면 그냥 -1 return하며 종료한다. 보통 0을 쓴다.
반환값이 0이면 정상이고, -1이면 에러이다.

> ssize_t msgrcv(int qid, void *msgp, size_t msgsz, long msgtyp, int msgflg);

특정 qid를 가진 Message Queue에서 메시지를 읽어오는 함수이다. 참고로 size를 크게 잡아도 message는 한번에 하나씩만 읽어온다.
세번째 인자까지는 msgsnd와 의미가 같다. 네번째도 다섯번재 인자랑 연관돼서 특정 동작을 지정해주긴하는데 우리 수준에선 일단 그냥 둘 다 0,0으로 쓰면 된다고 교수님께서 말씀하심.(필요하면 그때 더 찾아보라고... 한번 찾아보긴했는데 뭐 딱히 당장은 쓸데도 없고 따로 정리할 필요를 못느껴서 일단 생략, 배운거나 잘 쓰자.)
반환값은 버퍼로 receive한 데이터 크기이고, 에러일때는 -1이다.

보통 마지막 인자를 0으로 주기때문에 MQ가 비어있다면 message가 들어올때까지 msgrcv는 대기한다.
(pipe랑 같네)

 

pipe와 message queue의 차이?
: pipe 내의 데이터들은 딱히 나눠지거나 하는게 없다. 무슨 말이냐면, pipe는 message를 밀어넣고 나면 반대편에서 읽는 양만큼 그대로 읽힌다. 하지만 message queue는 데이터들이 나눠져있기때문에, 한번 읽으면 하나의 message씩만 읽힌다.
그리고 message queue는 양방향 소통도 가능하지만, pipe는 단방향 소통만 된다.(MQ가 양항뱡 소통이 지원되더라도, 양방향 소통할때 그냥 두개 선언해서 쓰는게 보편적이지 않을까 싶긴하네)
pipe는 무조건 FIFO인 반면, MQ는 번호를 줘서 우선순위를 달리할 수도 있다.
pipe는 read/write시 양쪽 processes가 모두 동작해야하지만, MQ는 한 프로세스가 write을 하고 종료돼도 읽는데 지장없다.

 


Shared Memory

원래 기본적으로 processes의 각 메모리 영역은 OS에 의해 독립적으로 관리된다. OS가 서로 침범하는 것을 막는다.
하지만 우린 IPC를 해야하므로 OS에 공유되는 메모리를 잡아달라고 할 수 있다.(system call을 이용해 부탁하는 것)
큰 흐름은, shmget으로 특정 key를 가진 Shared Memory의 id를 얻어오고, 그 id를 통해 다시 해당 shared memory를 가상메모리에 attach하여 그 주소를 얻어온다. 그 다음부턴 기존 우리가 알던 읽기/쓰기 함수로 메모리에 접근하여 정보를 공유하면 된다.

shared memory는 message queue와 다르게 어떤 값을 읽었다고 해서 값이 사라지거나 하지 않는다. 그냥 메모리 상의 공간이기때문에 읽었다 썼다 지웠다 자유롭게 하며 소통하면 된다.

 

> int shmget(key_t key, size_t size, int shmflg);

특정 key값을 가진 Shared Memory의 id를 얻는 함수이다. (fopen/msgget과 유사)
이미 해당 key값으로 만들어진 shared memory가 있다면 그냥 그 놈의 id를 반환한다.
shmflg에 들어갈 수 있는 값과 그 의미는 msgget과 같다.(권한도 or 연산으로 같이 쓰임)
예를들어 (IPC_CREAT | 0666) 식으로 돼있다면, 해당 key의 shared memory가 만들어지는데, 새로 만들어지는 shared memory의 size는 size_t 인자에 해당한다.
반환값은 아무 이상없다면 shared memory의 id이다. 에러면 -1을 반환한다.

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

shmget으로 얻은 shared memory의 id를 이용해 해당 shared memory를 가상메모리에 attach해서 그 주소를 얻어오는 함수이다.(malloc과 유사)
두번째 인자가 NULL이면 system에서 알아서 적당한 주소를 반환해준다.(일단은 NULL만 쓰자..)
세번째 인자는 SHM_RDONLY나 0이 올 수 있다.(read 전용은 불가) 0이 오면 read/write 모두 허용하며 attach된다.(여기도 일단은 0만 쓰자..)
반환값은 attach된 가상메모리의 주소이다. 에러인 경우 (void*) -1 이다.
(shmget은 공유메모리를 관리하는 OS에 shared memory를 만들도록 요청하는 것이고, shmat는 그 공유메모리를 현재 프로세스에서 사용할 수 있게 가상메모리에 올리는? 붙이는(attach)? 것이다.

 

> int shmdt(const void *shmaddr);

shmat로 얻은 가상메모리 공간을 더 이상 접근할 수 없도록 detach한다.(free와 유사)
인자는 detach할 주소이다.
반환값은 0이면 성공, -1이면 에러이다.


Message Queue나 Shared Memory 같은 IPC를 사용하려면, 각 processes가 MQ/SM의 key를 알고 있어야한다.(공유해야한다.)
key를 공유하여 서로 같은 자원에 접근하여 소통한다.

책 p.500에 shared memory를 이용해 시간 정보를 server와 client가 주고받도록 간단하게 구현된게 있다.

shared memory도 일반 file처럼 권한이 존재하므로 client는 read만 하게 해줄 수도 있고 그렇다.
server가 데이터를 쓰고 있는데 읽으면 race condition이 발생해서 old와 new value가 섞일 수도 있다.
이를 방지하기위해 자원을 lock하는 특정 시스템이 필요할 수 있다.
이럴때 kernel이 inter process locking 매커니즘인 semaphores를 사용할 수 있다.

'수업 > System Programming' 카테고리의 다른 글

[System Programming] Semaphore  (0) 2022.12.03
[System Programming] Barrier  (0) 2022.11.26
[System Programming] pipe 공부  (0) 2022.11.09
[System Programming] Shell 공부  (0) 2022.11.05
[System Programming] who 공부  (0) 2022.11.02