🔔 학교 강의를 바탕으로 개인적인 공부를 위해 정리한 글입니다. 혹여나 틀린 부분이 있다면 지적해주시면 감사드리겠습니다.
IPC
는 동일한 system에서 process간에 정보를 공유하기 위한 것으로 System V IPC
와 POSIX IPC
로 나눌 수 있다. 둘 다 message queue
, semaphore
, shared memory
가 있으며, System V IPC
가 상대적으로 POSIX IPC
에 비해 오래되었다. 해당 강의에서는 System V IPC
를 기준으로 진행하였다.
IPC
는 File
과 다루는 방식이 유사하며, 이를 정리하면 아래와 같다.
File | Message Queue | Semaphore | Shared Memory |
---|---|---|---|
File: filename" | Key: key_t | Key: key_t | Key: key_t |
File descriptor: int | Identifier: int | Identifier: int | Identifier: int |
open() | msgget() | semget() | shmget() |
fcntl() | msgctl() | semctl() | shmctl() |
struct stat | struct msqid_ds | struct semid_ds | struct shmid_ds |
read() | msgrcv() | - | - |
write() | msgsnd() | - | - |
- | - | semop() | - |
- | - | - | shmat() |
- | - | - | shmdt() |
Key
- IPC를 지칭하는 값으로 외부적인 이름에 해당
- IPC structure가 생성될 때마다 key가 정해져야 함
Identifier
- process 안에서 사용하며, file descriptor처럼 IPC를 지칭하는 내부적인 이름에 해당
- 음수가 아닌 정수로 지정되며,
get
을 사용해 얻을 수 있음- file descriptor와 달리 고유한 값이며, 다른 process에서도 이 값은 달라지지 않음
#include <sys/ipc.h>
key_t ftok(const char *path, int id);
ftok
는 전달받은 경로와 id를 IPC key
로 변환한다.
Return
- 성공 시
return key_t
- error 발생 시
return -1
Arguments
path
: 존재하는 파일 경로id
: 정수 (하위 8bit만 사용)
동일한 path
를 사용하더라도 id
가 다른 경우에는 key
가 달라지며, 이는 유일하다. path
가 존재하지 않거나, 접근이 불가능한 경우에는 -1
을 return한다.
message queue
, semaphore
, shared memory
는 각각 자체적인 get
이 존재하며, key
를 이용하여 IPC를 생성하거나 연다. 각각 성공 시에 IPC identifier
를 return하고, error 발생 시에는 -1
을 return한다.
여기서 key
는 위의 ftok
를 이용하여 전달받거나, 임의로 지정하거나, IPC_PRIVATE
를 이용하여 임의로 random key
를 전달받을 수 있다. 마지막의 경우는 주로 parent와 child의 관계에서 사용한다.
#include <sys/msg.h>
#include <sys/sem.h>
#include <sys/shm.h>
int msgget(key_t key, int permflags);
int semget(key_t key, int nsems, int permflags);
int shmget(key_t key, size_t size, int permflags);
여기서 permflags
는 open()
에서의 flag
와 유사하다. 하지만 CREAT
의 경우 |
로 permission을 뒤에 붙여준다는 차이점이 있다.
permflags
IPC_CREAT
:O_CREAT
IPC_EXCL
:O_EXCL
get
과 마찬가지로 ctl
도 각각 존재한다.
#include <sys/msg.h>
#include <sys/sem.h>
#include <sys/shm.h>
int msgctl(int msqid, int command, struct msqid_ds *buf);
int semctl(int semid, int semnum, int command, ...);
int shmctl(int shmid, int command, struct shmid_ds *buf);
command
에 따라서 동작을 달리하며 이에 대해 간단하게 정리하면 아래와 같다.
command
IPC_STAT
: 정보 읽기IPC_SET
: 정보 설정IPC_RMID
: IPC 제거
위에 대한 IPC의 흐름을 나타내면 아래와 같으며, 대부분 file의 경우와 대응되는 것을 확인할 수 있다.
#include <sys/ipc.h>
struct ipc_perm {
uid_t uid; /* owner’s effective user id */
gid_t gid; /* owner’s effective group id */
uid_t cuid; /* creator’s effective user id */
gid_t cgid; /* creator’s effective group id */
mode_t mode; /* access modes – permission */
...
};
IPC 객체가 생성되면 위와 같은 ipc_perm
도 생성되며, permission
에 해당하는 정보도 포함하고 있다. 이는 creator나 super user가 ctl
을 call하여 수정이 가능하다. permission
을 정리하자면 아래와 같으며, read
, write
만 존재하며 execution
은 존재하지 않는다.
Permission Bit
----------------------------
user-read 0400
user-write 0200
group-read 0040
group-write 0020
other-read 0004
other-write 0002
message queue
는 process가 다른 process에게 message를 보내거나 받을 수 있도록 한다. 여기서 message는 string (char array)
이며 linked list
형태로 이루어져 kernel
메모리 공간에 저장된다.
/* <sys/msg.h> */
struct msqid_ds {
struct ipc_perm msg_perm; /* permission */
struct msg *msg_first; /* pointer to the first message on queue */
struct msg *msg_last; /* pointer to the last message on queue */
msglen_t msg_cbytes; /* current # bytes on queue */
msgqnum_t msg_qnum; /* # of messages on queue */
msglen_t msg_qbytes; /* max # of bytes on queue */
pid_t msg_lspid; /* pid of last msgsnd() */
pid_t msg_lrpid; /* pid of last msgrcv() */
time_t msg_stime; /* last msgsnd() time */
time_t msg_rtime; /* last msgrcv() time */
time_t msg_ctime; /* last change time */
};
message queue
의 구조를 그림으로 나타내면 아래와 같이 linked list
의 형태로 연결되어있는 것을 확인할 수 있다. 여기서 type
은 우선순위 결정 시에 사용되며, length
가 실질적인 data값의 길이에 해당되며, data
에 message가 담기게 된다.
#include <sys/msg.h>
int msgget(ket_t key, int permflag);
msgget
로 key
값을 전달받아 message queue identifier
를 get할 수 있다.
Return
- 성공 시
return msqid
- error 발생 시
return -1
Argument
permflag
-IPC_CREAT
:O_CREAT
-IPC_EXCL
:O_EXCL
이에 대한 간단한 사용 예시는 아래와 같으며, IPC_PRIVATE
의 경우는 이전에 없던 random key를 반환하기 때문에 항상 CREAT
와 같이 사용되어야 한다.
msqid = msgget(IPC_PRIVATE, 0644 | IPC_CREAT | IPC_EXCL);
발생할 수 있는 error는 아래와 같으며, 대부분 file과 유사하다.
Errors
EACCES
: key에 대한 message queue가 존재하지만 접근 권한 없음EEXIST
: key에 대한 message queue가 존재하는데IPC_CREAT
와IPC_EXCL
이 지정됨ENOENT
: key에 대한 message queue가 존재하지 않는데IPC_CREAT
가 지정되지 않음ENOMEM
: 메모리 할당 불가능ENOSPC
: message queue 최대 개수 제한 초과
#include <sys/msg.h>
int msgsnd(int msqid, const void *message, size_t size, int flags);
msgsnd
는 message queue
에 message를 삽입한다.
Return
- 성공 시
return 0
- error 발생 시
return -1
Arguments
message
: 사용자 정의 message이며, 아래와 같은 형식을 지켜야 함struct mymsg { long mtype; /* positive message type */ char mtext[SOMEVALUE]; /* message data */ };
size
:mtext
의 size이며,SOMEVALUE
보다는 작아야 함flags
:IPC_NOWAIT
를 지정할 수 있으며, 지정하지 않으면 공간이 부족할 경우에 공간이 생길 때까지 block됨
#include <sys/msg.h>
int msgrcv(int msqid, void *message, size_t size, long msg_type, int flags);
msgrcv
는 message queue
로부터 message를 읽는다.
Return
- 성공 시 읽은 message의 길이를 return
- error 발생 시
return -1
Arguments
message
:msgsnd
와 같은 사용자 정의 messagesize
:mtext
의 sizetype
: type에 의해 message를 읽는 우선순위가 정해지며,0
을 기준으로 구분하기 때문에 type은 일반적으로1
부터 사용함.0
도 사용은 가능하나,0
으로 설정한 message는 읽히지 않음
-==0
: 가장 앞부터 읽음
->0
: 해당 type의 가장 첫 msg부터 읽음
-<0
:<=|type|
에 해당되면서 가장 type이 작은 msg부터 읽음flag
:IPC_NOWAIT
,MSG_NOERROR
-MSG_NOERROR
은 msg의 크기보다 size가 작은 경우에 발생하는 error가 발생하지 않도록 하고, size만큼 읽고 나머지는 버리도록 하는 flag
#include <sys/msg.h>
int msgctl(int msqid, int cmd, struct msqid_ds *msq_stat);
msgctl
은 msqid
로 접근한 message queue
에 대한 권한 할당을 해제하거나 변경한다.
Return
- 성공 시
return 0
- error 발생 시
return -1
Arguments
cmd
-IPC_STAT
:msq_stat
에 현재 message queue에 대한 정보를 저장
-IPC_SET
:msq_stat
의 정보로 message queue를 설정
-IPC_RMID
: message queuemsqid
를 제거하고,msqid_ds
를 제거
-> message queue의 경우, kernel에 존재하기 때문에 재부팅 전까지는 사라지지 않음. 따라서 따로 제거가 필요msq_stat
:msqid_ds
구조체의 포인터/* <sys/msg.h> */ struct msqid_ds { struct ipc_perm msg_perm; /* permission */ struct msg *msg_first; /* pointer to the first message on queue */ struct msg *msg_last; /* pointer to the last message on queue */ msglen_t msg_cbytes; /* current # bytes on queue */ msgqnum_t msg_qnum; /* # of messages on queue */ msglen_t msg_qbytes; /* max # of bytes on queue */ pid_t msg_lspid; /* pid of last msgsnd() */ pid_t msg_lrpid; /* pid of last msgrcv() */ time_t msg_stime; /* last msgsnd() time */ time_t msg_rtime; /* last msgrcv() time */ time_t msg_ctime; /* last change time */ };
semaphore
은 mutual exclusion
과 synchronization
을 위해 사용된다. 2가지의 연산이 있는 정수 변수에 해당하며 이는 아래와 같다.
wait
: p, locksignal
: v, unlock
-> 여기서의signal
은 앞에서 학습한signal
과는 다른 개념이다.
일반적으로 아래와 같은 형식으로 사용한다.
p(sem);
operating something
v(sem);
system에서 사용중인 IPC
정보를 출력해준다.
option
-q
: message queue 정보 출력
-s
: semaphore 정보 출력
-m
:shared memory
정보 출력
-> 내가 만든 IPC만 보기 :ipcs -q | grep 아이디
IPC_PRIVATE
를 통해 만들어진 message queue
의 경우 key = 0
으로 나타나며, 특정 process에서 만든 유일한 key이기 때문에 외부에서 볼 수 없다.
불필요한 IPC
객체를 삭제한다. ipcrm -q msqid
의 형태로 사용한다.