리눅스 프로그래밍 - 13주차

Lellow_Mellow·2022년 11월 27일
1
post-thumbnail

🔔 학교 강의를 바탕으로 개인적인 공부를 위해 정리한 글입니다. 혹여나 틀린 부분이 있다면 지적해주시면 감사드리겠습니다.

Advanced Inter-Process Communications: Andvanced IPC Facilities

Advanced IPC Facilities

Inter-Process Communication (IPC)

IPC는 동일한 system에서 process간에 정보를 공유하기 위한 것으로 System V IPCPOSIX IPC로 나눌 수 있다. 둘 다 message queue, semaphore, shared memory가 있으며, System V IPC가 상대적으로 POSIX IPC에 비해 오래되었다. 해당 강의에서는 System V IPC를 기준으로 진행하였다.

File & System V IPC

IPCFile과 다루는 방식이 유사하며, 이를 정리하면 아래와 같다.

FileMessage QueueSemaphoreShared Memory
File: filename"Key: key_tKey: key_tKey: key_t
File descriptor: intIdentifier: intIdentifier: intIdentifier: int
open()msgget()semget()shmget()
fcntl()msgctl()semctl()shmctl()
struct statstruct msqid_dsstruct semid_dsstruct shmid_ds
read()msgrcv()--
write()msgsnd()--
--semop()-
---shmat()
---shmdt()

Keys and Identifiers

Key

  • IPC를 지칭하는 값으로 외부적인 이름에 해당
  • IPC structure가 생성될 때마다 key가 정해져야 함

Identifier

  • process 안에서 사용하며, file descriptor처럼 IPC를 지칭하는 내부적인 이름에 해당
  • 음수가 아닌 정수로 지정되며, get을 사용해 얻을 수 있음
  • file descriptor와 달리 고유한 값이며, 다른 process에서도 이 값은 달라지지 않음

System Call : ftok

#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한다.

IPC get operations

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);

여기서 permflagsopen()에서의 flag와 유사하다. 하지만 CREAT의 경우 |로 permission을 뒤에 붙여준다는 차이점이 있다.

permflags

  • IPC_CREAT : O_CREAT
  • IPC_EXCL : O_EXCL

ipc ctl operations

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 operations concept summary

위에 대한 IPC의 흐름을 나타내면 아래와 같으며, 대부분 file의 경우와 대응되는 것을 확인할 수 있다.

Permission Structure

#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

Message Queue

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

message queue의 구조를 그림으로 나타내면 아래와 같이 linked list의 형태로 연결되어있는 것을 확인할 수 있다. 여기서 type은 우선순위 결정 시에 사용되며, length가 실질적인 data값의 길이에 해당되며, data에 message가 담기게 된다.

System Call : msgget

#include <sys/msg.h>

int msgget(ket_t key, int permflag);

msggetkey값을 전달받아 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_CREATIPC_EXCL이 지정됨
  • ENOENT : key에 대한 message queue가 존재하지 않는데 IPC_CREAT가 지정되지 않음
  • ENOMEM : 메모리 할당 불가능
  • ENOSPC : message queue 최대 개수 제한 초과

System Call : msgsnd

#include <sys/msg.h>

int msgsnd(int msqid, const void *message, size_t size, int flags);

msgsndmessage 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됨

System Call : msgrcv

#include <sys/msg.h>

int msgrcv(int msqid, void *message, size_t size, long msg_type, int flags);

msgrcvmessage queue로부터 message를 읽는다.

Return

  • 성공 시 읽은 message의 길이를 return
  • error 발생 시 return -1

Arguments

  • message : msgsnd와 같은 사용자 정의 message
  • size: mtext의 size
  • type : 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

System Call : msgctl

#include <sys/msg.h>

int msgctl(int msqid, int cmd, struct msqid_ds *msq_stat);

msgctlmsqid로 접근한 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 queue msqid를 제거하고, 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

Semaphore Concept

semaphoremutual exclusionsynchronization을 위해 사용된다. 2가지의 연산이 있는 정수 변수에 해당하며 이는 아래와 같다.

  • wait : p, lock
  • signal : v, unlock
    -> 여기서의 signal은 앞에서 학습한 signal과는 다른 개념이다.

일반적으로 아래와 같은 형식으로 사용한다.

p(sem);

operating something

v(sem);

✅ 실습 시간 추가 코드

ipcs command

system에서 사용중인 IPC 정보를 출력해준다.

option

-q : message queue 정보 출력
-s : semaphore 정보 출력
-m : shared memory 정보 출력
-> 내가 만든 IPC만 보기 : ipcs -q | grep 아이디

IPC_PRIVATE를 통해 만들어진 message queue의 경우 key = 0으로 나타나며, 특정 process에서 만든 유일한 key이기 때문에 외부에서 볼 수 없다.

ipcrm command

불필요한 IPC 객체를 삭제한다. ipcrm -q msqid의 형태로 사용한다.

profile
festina lenta

0개의 댓글