단방항 통신 메커니즘으로 명령어 에서 사용하는 | 기호가 이에 해당
파이프는 부모프로세스와 자식프로세스간에만 사용이 가능하며, 다른 프로세스간에는 사용할 수 없다.
일반 파일 디스크립터를 사용하듯이 read(), write()를 이용하여 사용하면 된다.
#include<unistd.h>
int pipe(int pipefd[2]);
#define _GNU_SOURCE
#include <fcntl.h>
#include <unistd.h>
int pipe2(int pipefd[2], int flags);
pipefd[2]flagsO_CLOEXEC, O_DIRECT, O_NONBLOCK, O_NOTIFICATION_PIPE가 존재한다.O_DIRECTPIPE_BUF의 크기가 넘는 데이터를 작성하면 패킷을 분리하여 전송하고, 읽을 때도 패킷으로 처리한다.FIFO파일을 이용하여 부모-자식 프로세스간에만 동작하는 파이프를 다른 프로세스끼리 사용이 가능하게 한다.
#include<sys/types.h>
#include<sys/stat.h>
int mkfifo(const char *path, mode_t mode);
해당 시스템 콜은 FIFO파일을 만드는 것으로 반환 값으로 FIFO의 파일 디스크립터를 반환한다.
해당 디스크립터에 read(), write()를 이용하여 데이터를 주고 받는다.
파이프와 유사하지만 단방향이 아닌 양방향으로 프로세스간 통신하는 기법
하나의 메시지큐를 여러 프로세스가 이용하기 때문에 구분하기 위한 메시지 타입이 필요
이 때, 프로세스에서 메시지를 보내고 받는 연산은 커널에서 원자성을 보장해준다(오버헤드가 존재).
이 때, 메시지큐를 생성하는 msgget(), 전송하고 받는 msgsnd(), msgrcv(), 메시지큐를 제어하는 msgctl()이 존재한다.
msgget()#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t key, int msgflag)
매개변수
key
메시지 큐를 구분하는 인자로, 통신하려는 프로세스 끼리는 동일한 값을 지정해야 한다.
키 값은 ftok() 함수를 통해 설정하는 것이 추천되며, 한 프로세스 내에서만 사용하려면 IPC_PRIVATE를 지정한다.
msgflag
메시지 큐의 사용권한을 OR연산으로 지정한다.
IPC_CREAT, IPC_EXCL, 혹은 유저권한을 지정한다.
반환 값
성공하면 메시지큐의 id를, 실패하면 -1을 반환한다.
ftok(const char *pathname, int proj_id)경로(pathname)를 System V의 IPC 키로 변환해주는 함수
pathname은 반드시 존재하는 경로여야 하며,pathname과proj_id가 같으면 같은 값이 생성된다.성공하면
key_t타입의 수가, 실패하면 -1이 반환된다.
msgsnd()int msgsnd(int msgid, const void *msg_ptr, size_t msg_sz, int msg_flg);
매개변수
msgid
msgget()에서 반환받은 메시지 큐의 id
msg_ptr
메시지 큐에 전송하려는 메시지의 주소
메시지
메시지는 다양한 구조체가 사용될 수 있지만, 반드시 첫 시작은
long타입의 메시지 타입(msg_type)맴버 여야 한다.struct msg { long msg_type; char msg_text[1024]; };
msg_sz
메시지의 길이
msg_flg
0 (메시지큐가 꽉차있으면 해당 함수에서 블록되어 프로세스 수행이 멈춤) 또는
IPC_NOWAIT(큐가 꽉차있으면 -1을 반환) 플래그를 설정 할 수 있다.
msgrcv()int msgrcv(int msgid, void *msg_ptr, size_t msg_sz, long msgtype, int msgflg);
매개변수
msgtype
메세지 구조체에서 첫 맴버에 해당하는 long타입의 맴버에 해당한다.
이 타입으로 설정되어 있는 메시지만 선택적으로 수신한다.
그 밖에 매개변수는 msgsnd()와 동일하다.
msgctl()int msgctl(int msgid, int command, struct msgid_ds *buf);
매개변수
command
제어 명령으로 다음과 같다
IPC_STAT : 메시지 큐에 관련된 정보를 세번째 인수로 읽어온다.
IPC_SET : 메시지 큐의 정보를 세번째 인수로 설정한다.
IPC_RMID : 메시지 큐를 삭제한다.
*buf
struct msqid_ds
{
// msqid_ds 내부에 맴버로 msg_perm이 존재
uid_t msg_perm.uid;
uid_t msg_perm.gid;
mode_t msg_perm.mode;
};
각 프로세스마다 메모리 세그먼트가 할당되어 각기 다른 메모리 공간을 가지게 된다.
이 때, 공유 메모리는 프로세스의 가상 주소공간에 프로세스간 공유하는 메모리 주로를 매핑시켜 데이터를 공유 할 수 있게 해준다.
메시지큐나 파이프는 주고 받을 수 있는 메시지의 크기가 시스템에 의해 한정되어 있는데, 공유메모리는 할당 가능한 메모리만큼 크기를 할당 받을 수 있으므로 제한이 없다.
사용하는 함수는 공유 메모리를 생성하는 shmget(), 메모리를 매핑하고 분리하는 shmat(), shmdt(), 공유 메모리를 제어하는 shmctl()이 존재한다.
shmget()#include<sys/shm.h>
#include<sys/ipc.h>
int shmget(key_t key, size_t size, int shmflg);
매개변수
key
메시지큐의 key와 동일하다
size
공유할 메모리의 크기를 바이트 단위로 지정한다.
shmflg
메시지큐의 msgflag와 동일하다.
반환 값
성공하면 id 값을 반환하고, 실패하면 -1을 반환한다.
shmat()void *shmat(int shm_id, const void *shm_addr, int shmflg);
매개변수
shm_addr
할당된 공유 메모리를 프로세스에 매핑할 주소를 지정한다.
NULL을 지정하면 시스템에서 주소를 정해 반환해준다(보통 사용).
shmflg (하위 옵션은 모두 파일 권한이 있어야 한다.)
0 : 세그먼트의 읽기/쓰기가 모두 가능
SHM_RDONLY : 세그먼트를 읽기만 가능
SHM_EXEC : 세그먼트를 실행 가능하게 함
SHM_REMAP : 세그먼트가 다른 것으로 대체되어야 함
반환 값
성공하면 메모리의 주소를 반환하고, 실패하면 -1을 반환한다.
shmdt()int shmdt(const void *shmaddr);
shmctl()int shmctl(int shm_id, int command, struct shmid_ds *buf);
매개변수
command
제어명령은 다음과 같다.
IPC_STAT : 공유 메모리에 대한 정보를 세번째 변수로 읽어옴
IPC_SET : 세번째 변수의 값을 공유메모리의 정보로 지정
IPC_RMID : 공유 메모리 객체를 삭제
IPC_INFO : IPC 자원의 리눅스 설정 값을 읽어옴
shmid_ds
struct shmid_ds
{
struct ipc_perm shm_perm;
size_t shm_segsz;
// last attach time
size_t shm_attime;
// last detach time
size_t shm_dtime;
// last change time
size_t shm_ctime;
// pid of creator
size_t shm_cpid;
// pid of last at/dt
size_t shm_lpid;
shmatt_t shm_nattach;
};
struct ipc_perm
{
// key supplied shmget() or msgget()
key_t __key;
// Effective uid of onwer
uid_t uid;
// Effective gid of owner
gid_t gid;
// Effective uid of creator
uid_t cuid;
// Effective gid of creator
gid_t cgid;
// 권한
unsigned short mode;
// 시퀀스 번호
unsigned short __seq;
};
같은 공유 메모리에 접근하려는 경우 프로세스간에 같은 자원에 접근하는 문제가 발생한다.
이러한 동기화 문제를 해결하기 위해 다양한 해결책이 있는데 System V IPC 기능 중 하나인 세마포어 사용 할 수 있다.
세마포어는 연산기반의 동기화 방법이며, 세마포어 변수를 통해 구현한다.
세마포어는 두가지 연산으로 구성된다.
P(임계구역에 들어가는 연산)
세마포어 변수가 0보다 크면 감소
0이면 0보다 변수가 커질 때 까지 프로세스를 중지
V(임계구역에서 나오는 연산)
세마포어 변수를 증가시킨다.
세모포어에 의해 중지된 프로세스가 있으면 실행을 재개시킴
세마포어도 세마포어를 생성하는 semget(), 세마포어 연산을 하는 semop(), 세마포어를 제어하는 semctl()로 구성되어 있다
semget()#include<sys/types.h>
#include<sys/ipc.h>
#inlcude<sys/sem.h>
int semget(key_t key, int num_sems, int sem_flags);
매개변수
num_sems
세마포어의 개수를 지정
각각 관리해야하는 데이터의 개수에 따라 지정
sem_flags
msggget()과 동일
반환값
성공하면 id를 실패하면 -1을 반환
semop()int semop(int sem_id, struct sembuf *sem_ops, size_t num_sem_ops);
struct sembuf
{
short sem_num;
short sem_op;
short sem_flg;
};
매개변수
sem_ops
sembuf구조체를 이용하여 세마포어의 연산을 표현한다.
p연산의 경우 { 0, -1, SEM_UNDO};, v연산의 경우 { 0, 1, SEMUNDO}와 같이 사용하면 된다.
struct sembufsem_num : 연산을 진행할 세마포어의 번호
sem_op : 세머포어 연산
sem_flg : 세마포어의 플래그인데, 아래와 같은 종류가 존재
SEM_UNDO: 프로세스가 종료되면 자동으로 값을 이전으로 돌려둔다.
IPC_NOWAIT: 논 블로킹으로 동작
semctl()int semctl(int sem_id, int sem_num, int command, ... );
매개변수
sem_num : 제어하고자 하는 세마포어의 번호로 id단위가 아닌 개별 세마포어 단위로 제어가 가능함을 알 수 있다
command : 아래와 같은 제어 명령이 존재함
| command | 의미 |
|---|---|
GETVAL | 세머포어의 현재 값을 얻는다. |
GETPID | 세마포어의 접근했던 프로세스의 pid를 얻는다. |
GETNCNT | 세마포어가 증가 되기를 기다리는 프로세스의 수 |
GETZCNT | 세마포어가 0이 되기를 기다리는 프로세스의 수 |
GETALL | 세마포어 집합의 모든 세마포어의 값 |
SETVAL | 세마포어의 값을 설정 |
SETALL | 세마포어 집합의 모든 값에 대해 설정 |
IPC_STAT | 세마포어 오브젝트에 대한 정보 |
IPC_SET | 세마포어의 소유권과 사용권한을 설정 |
IPC_RMID | 세마포어 오브젝트를 삭제 |
command에 대한 매개변수로 아래 공용체를 사용
union semun
{
int val;
struct semid_ds *buf;
unsigned short *array;
struct seminfo *__buf;
};
struct semid_ds
{
struct ipc_perm sem_perm;
time_t sem_otime;
time_t sem_ctime;
unsigned long sem_nsems;
};
struct seminfo
{
int semmap;
int semmni;
int semmns;
int semmnu;
int semmsl;
int semopm;
int semume;
int semusz;
int semvmx;
int semaem;
};