Notice
Recent Posts
Recent Comments
Link
«   2026/01   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
Tags
more
Archives
Today
Total
관리 메뉴

우당탕탕 개발일지

[C언어] IPC(1) (feat. 공유 메모리) 본문

Server/Linux, C

[C언어] IPC(1) (feat. 공유 메모리)

YUDENG 2025. 10. 19. 15:22

1. IPC

 

프로세스는 완전히 독립된 실행 객체로 다른 프로세스에 문제가 발생하여도 영향을 받지 않는다는 장점이 있다. 그러나 독립되어 있는 만큼 서로간에 통신이 어렵다는 문제가 있다. 이를 위해서 커널 영역에서는 IPC(Inter-Process Communication)라는 내부 프로세스간 통신을 제공하여 IPC 설비를 이용해서 프로세스 간 통신을 할 수 있다.

 

  • 정보 공유
    • 여러 사용자가 동일한 정보에 엑세스할 필요가 있을 수 있다.
  • 가속화
    • 특정 작업(task)을 여러 개의 서브 작업(sub-task)로 쪼개어 프로세스의 병렬성을 키움으로써 처리 속도를 높일 수 있다.
  • 모듈화
    • 특정한 시스템 기능을 별도의 프로세스(스레드)로 구분하여 모듈화된 시스템을 구성할 수 있다.
  • 편의성
    • 다수의 사용자가 동시에 여러가지 작업을 수행할 수 있다.
ipcs
 

 

ipcrm -h

 

 

IPC 종류

 

IPC의 종류는 크기 메세지 전달(Message Passing)과 공유 메모리(Shared Memory)로 나뉜다.

  • 메세지 전달(Message Passing)
    • 커널을 통해 메세지를 전달하는 방식으로 자원이나 데이터를 주고 받음
    • 커널을 이용하기 때문에 구현이 비교적 쉬움
    • 시스템 콜이 필요하며 이로 인해 오버헤드 발생
    • Pipe, Signal, Message Queueing, Socket 등
  • 공유 메모리(Shared Memory)
    • 공유 메모리 영역을 구축하고 공유 영역을 통해 자원이나 데이터를 주고 받음
    • 커널 의존도가 낮기 때문에 속도가 빠르고 통신이 자유로움
    • 자원과 데이터를 공유하기 때문에 동기화 이슈 발생
    • Semaphores 등

 

IPC 공통 구조체

 

IPC에서 공통적으로 사용하는 기본 요소는 키와 식별자이다.

struct ipc_perm {
    uid_t   uid;    // 구조체의 소유자 ID를 의미한다.
    gid_t   gid;    // 구조체의 소유 그룹 ID를 의미한다.
    uid_t   cuid;   // 구조체를 생성한 사용자 ID를 의미한다.
    gid_t   cgid;   // 구조체를 생성한 그룹 ID를 의미한다.
    mode_t  mode;   // 구조체에 대한 접근 권한을 의미한다.
    uint_t  seq;    // 슬롯의 일련번호를 의미한다.
    key_t   key;    // 키값을 의미한다.
    int     pad[4]; // 향후 사용을 위해 예약되어 있는 영역이다.
}
 

 

IPC 구조체는 사용을 마치면 반드시 프로그램 안에서 삭제해야 한다. IPC 구조체를 삭제하지 않을 경우 계속 남아 있게 되는데, 시스템에서 제공 가능한 IPC 구조체의 총 개수가 한정되어 있기 때문이다.

 

키 생성

IPC에서 사용하는 키는 다음과 같이 생성할 수 있다.

  • 키로 IPC_PRIVATE를 지정한다.
    • 식별자를 알아야 통신할 수 있으므로 IPC_PRIVATE를 키로 지정해 생성된 식별자를 서버와 클라이언트 모두 알 수 있게 해야 한다.
  • ftok() 함수로 키를 생성한다.
    • ftok() 함수는 경로명과 숫자값을 받아서 키를 생성한다.
    • 따라서 서버와 클라이언트가 같은 경로명과 숫자값을 지정하면 공통 식별자를 생성할 수 있다.

2. 공유 메모리

공유 메모리는 같은 메모리 공간을 두 개 이상의 프로세스가 공유하는 것이다.

원칙적으로 서로 다른 프로세스 A와 B는 각자 독립적인 주소 공간을 가지기 때문에 자신이 아닌 다른 프로세스의 메모리에 대한 접근을 할 수 없다. 그러나 운영체제는 공유 메모리를 사용하는 시스템 콜을 지원해서 서로 다른 프로세스들이 같은 메모리 공간을 사용해 데이터를 주고받을 수 있게 한다.

프로세스가 공유 메모리 할당을 시스템 콜을 통해 커널에 요청하면, 커널은 해당 프로세스에 메모리 공간을 할당해준다. 이후 어떤 프로세스건 해당 메모리 영역에 접근할 수 있게 된다.

 

 

✅ shmget ()

#include <sys/shm.h>

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

 

>> 공유 메모리 식별자를 생성한다.

  • key : IPC_PRIVATE 또는 ftok() 함수로 생성한 키
  • size : 공유할 메모리의 크기
    • 이미 공유된 메모리의 식별자를 읽어오는 것이라면 무시한다.
  • shmflg : 공유 메모리의 속성을 지정하는 플래그
    • IPC_CREAT(0001000) : O_CREAT 와 비슷한 기능으로, 새로운 키면 식별자를 새로 생성한다.
    • IPC_EXCL(0002000) : O_EXCL 과 비슷한 기능으로, 이미 존재하는 키면 오류가 발생한다.
    • 공유 메모리 식별자와 관련된 공유 메모리와 데이터 구조체가 새로 생성되는 경우는 다음 두가지 이다.
      • key가 IPC_PRIVATE이다.
      • key가 0이 아니며 다른 식별자와 관련되어 있지 않고, 플래그에 IPC_CREAT가 설정되어 있다.
      • 이 두 가지 경우가 아니면 기존 공유 메모리의 식별자를 리턴한다.
  • 할당된 메모리는 프로세스가 종료되어도 자동으로 해제되지 않으며, shmctl()을 통해 삭제하거나, OS가 재부팅되면 초기화된다.

 

✅ 반환값

  • 성공시, 공유 메모리 ID를 리턴한다.
  • 실패시, -1을 리턴하고 errno 값을 설정한다.
 
ERROR CODE
ERROR 설명
EACCES 프로세스의 user가 접근 권한이 없습니다.
EEXIST shmflag에 IPC_CREAT | IPC_EXCL option이 추가되었으며, key로 이미 생성된 shared memory segment가 존재합니다.
EINVAL size < SHMMIN 이거나 size > SHMMAX이거나 이미 만들어진 shared memory segment보다 size가 큰 경우에 발생합니다.
ENFILE open할 수 있는 file의 갯수가 시스템 한계에 도달한 경우.
ENOENT IPC_CREAT가 shmdflag에 설정되지 않았고, key로 생성된 shared memory segment가 없는 경우
ENOMEM 메모리가 부족합니다.
ENOSPC System에서 정의한 최대 segment 갯수(SHMMNI)에 도달했거나, 할당된 shared memory의 양이 시스템에서 정의한 최대(SHMALL)에 도달하여 Space가 없습니다.

 

 

✅ 공유메모리 구조체 (shmid_ds)

struct shmid_ds {
    struct ipc_perm shm_perm;    /* IPC 공통 구조체 */
    size_t          shm_segsz;   /* 공유 메모리 세그먼트의 크기 (bytes) */
    time_t          shm_atime;   /* 마지막으로 공유 메모리를 연결(shmat)한 시각 */
    time_t          shm_dtime;   /* 마지막으로 공유 메모리의 연결을 해제(shmdt)한 시각 */
    time_t          shm_ctime;   /* 마지막으로 공유 메모리의 접근 권한을 변경한 시각 */
    pid_t           shm_cpid;    /* 공유 메모리를 생성한 프로세스의 ID */
    pid_t           shm_lpid;    /* 마지막으로 shmop 동작을 한 프로세스의 ID */
    shmatt_t        shm_nattch;  /* No. of current attaches */
    ...
};

 

 

✅ shmat ()

#include <sys/shm.h>

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

 

>> 공유 메모리를 프로세스의 데이터 영역과 연결한다.

  • shmid : shmget() 함수로 생성한 공유 메모리 식별자
  • shmaddr : 공유 메모리를 연결할 주소
    • 특별한 경우가 아니면 0을 지정한다.
    • 값이 0이면 시스템이 알아서 적절한 주소에 공유 메모리를 연결한다.
  • shmflg : 공유 메모리에 대한 읽기/쓰기 권한
    • 0 : 공유 메모리에 대해 읽기와 쓰기가 가능하다.
    • SHM_RDONLY : 읽기만 가능하다.

shmat() 함수의 수행이 성공하면 shm_nattch 값은 1증가한다. shm_atime는 현재 시간으로, shm_lpid는 현재 프로세스 ID로 갱신된다.

 

✅ 반환값

  • 성공시, 연결된 공유 메모리의 시작 주소를 리턴한다.
  • 실패시, -1을 리턴하고 errno 값을 설정한다.
 
ERROR CODE
ERROR 설명
EACCES 접근권한이 없습니다. shmget()함수에서 설정한 권한이 shmat()함수를 호출한 process를 실행한 user에게 권한이 없습니다.
EIDRM shmid가 삭제된 id입니다.
EINVAL dshmid가 유효하지 않거나 shmaddr이 유효하지 않습니다. 또는 shmaddr에 segment를 attach할 수 없거나, shmflg가 SHM_REMAP으로 설정되었는 데, shmaddr값이 NULL입니다.
ENOMEM memory를 할당할 수 없습니다.

 

✅ shmdt()

#include <sys/shm.h>

int shmdt(const void *shmaddr);
 

>> 공유 메모리의 연결을 해제한다.

  • shmaddr : 연결을 해제할 공유 메모리의 시작 주소

 

✅ shmctl()

#include <sys/shm.h>

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

>> 공유 메모리의 정보를 조회/변경하거나, 할당된 공유 메모리를 삭제한다.

  • shmid : shmget() 함수로 생성한 공유 메모리 식별자
  • cmd : 수행할 제어 기능
    • IPC_RMID
      • shmid로 지정한 공유 메모리를 제거하고, 관련 데이터 구조체를 제거한다.
    • IPC_SET
      • 공유 메모리의 정보 내용 중 shm_perm.uid, shm_perm.gid, shm_perm.mode 값을 세 번째 인자로 지정한 값으로 바꾼다.
      • 이 명령은 root 권한이 있거나 유효 사용자 ID인 경우에만 사용할 수 있다.
    • IPC_STAT
      • 현재 공유 메모리의 정보를 buf로 지정한 메모리에 저장한다.
    • SHM_LOCK
      • 공유 메모리 세그먼트를 잠근다.
    • SHM_UNLOCK
      • 공유 메모리 세그먼트의 잠금을 해제한다.
  • buf : 제어 기능에 사용되는 공유 메모리 구조체의 주소
  • 반환값: 성공시 0을, 실패시 -1을 리턴한다.

 

🔷 주소록 프로그램 (공유 메모리)

  • 기존 주소록 프로그램에서 멀티 스레드 → 멀티 프로세스로 변경
  • 두 프로세스간 통신을 위해 Shared Memory 를 사용하도록 변경
더보기
  • 주소록 프로그램은 다음과 같은 형태로 실행을 한다.
    • 프로그램명 [display]
      • 프로그램명만 입력된 경우, 사용자로부터 주소록 정보를 받아서 파일에 저장하도록 함
      • display가 옵션으로 주어진 경우 주소록 파일 정보를 읽어서 화면에 출력하도록 해야 함
    • 주소록 프로그램이 주소록 정보를 저장하는 경우에는 사용자로 부터 주소록 정보를 입력받는 메인 스레드와 파일에 주소록 정보를 쓰는 파일 Write 스레드 2개로 구성되어야 하고 주소록 프그램이 주소록 정보를 출력하는 경우에는 파일에서 읽는 메인 스레드와 화면에 정보를 출력하는 Display 스레드 2개로 구성되어야 한다.
    • 두 스레드간에는 Queue 자료 구조를 통해 데이터를 주고 받는다. Queue 자료 구조는 직접 코딩한다.
      • Thread간 데이터를 송수신하기 위한 Queue는 세마포어와 mutex lock을 사용하여 구현

 

🔹 입력

 

1. 주소록 정보로 입력받는 데이터는 이름, 전화번호, 주소이다.

  • 이름은 한글로 최대 4글자
  • 전화번호는 xxx-xxxx-xxxx 형태로 입력받는다.
  • 주소는 한글로 최대 50자 입력받는다.

2.이름에 exit를 입력할 때까지 주소록 입력을 반복한다.

3.입력한 정보가 잘못된 경우, 에러 문구를 출력하고 이름부터 다시 입력받는다.

 

🔹 출력

  1. 주소 정보는 다음과 같이 출력하도록 한다.

——————————————————————————

1 - 김민

——————————————————————————-
전화번호: 010-8821-1234
주소: 서울 서초구 법원로3길 20-7

——————————————————————————-

2 - 이지민

——————————————————————————-

전화번호: 010-8821-1234
주소: 서울 서초구 법원로3길 20-7

——————————————————————————-

전체: 2명

——————————————————————————-

 

 

[C언어] 메모리 (feat. 주소록 프로그램)

리눅스 가상 메모리 구조 가상 메모리는 실제 각 프로세스마다 충분한 메모리를 할당하기에는 메모리 크기의 한계가 있어, 디스크(ROM)의 일부를 확장된 RAM처럼 사용하기 위한 개념이다.실제 물

uj0791.tistory.com

 

 

[ receiver_message.c ]

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>

#include <signal.h>
#include <semaphore.h>
#include <sys/shm.h>

#define MAX_NAME 13
#define MAX_PHONE_NUMBER 14
#define MAX_ADDRESS 151

#define PROJ_ID 4321
#define PROCESS_SIZE 5
#define KEY_FILE "sm_keyfile"
#define ADDRESS_DATA "address_data"

#define SEM_MUTEX "/sem_mutex"
#define SEM_EMPTY "/sem_empty"
#define SEM_FULL "/sem_full"

typedef struct address_info_t {
	char name[MAX_NAME];
	char phone_number[MAX_PHONE_NUMBER];
	char address[MAX_ADDRESS];
} address_info_t;

typedef struct shm_t {
	int front, rear;
	address_info_t data[PROCESS_SIZE];
} shm_t;

#define CLOSE_FILE(fp) \
	do { \
		if ((fp) != NULL) { \
			if (fclose(fp) == EOF) { \
				printf("[ERROR] fclose: %s\n", strerror(errno)); \
			} \
			(fp) = NULL; \
		} \
	} while (0)

#define SEM_CLOSE(SEM) \
	do { \
		if(sem_close(SEM) != 0) { \
			perror("[ERROR] sem_close"); \
		}\
	} while(0)

#define SEM_UNLINK(SEM) \
	do { \
		if(sem_unlink(SEM) != 0) { \
			perror("[ERROR] sem_unlink"); \
		} \
	} while(0)


static sem_t *sem_mutex, *sem_empty, *sem_full;
static shm_t *memory;

volatile int rcv_terminate_flag = 0;

int init_shm(int *shmid);
int receive_data(address_info_t *address_info);
int write_data(address_info_t *address_info);

void action(int sig, siginfo_t *info, void *context) {
	rcv_terminate_flag = 1;
}

void cleanup(int shmid) {

	if(shmdt(memory) == -1) {
		perror("[ERROR] shmctl");
	}

	if(shmctl(shmid, IPC_RMID, NULL) == -1) {
		perror("[ERROR] shmctl");
	}

	SEM_CLOSE(sem_full);
	SEM_UNLINK(SEM_FULL);
	SEM_CLOSE(sem_empty);
	SEM_UNLINK(SEM_EMPTY);
	SEM_CLOSE(sem_mutex);
	SEM_UNLINK(SEM_MUTEX);

}

int main(void) {

	struct sigaction act;
	sigemptyset(&act.sa_mask);
	act.sa_flags = SA_SIGINFO;
	act.sa_sigaction = action;

	sigaction(SIGTERM, &act, NULL);

	int shmid;
	if(init_shm(&shmid) < 0){
		printf("[ERROR] failed init share memory\n");
		return -1;
	}

	while(!rcv_terminate_flag) {

		address_info_t address_info;
		printf("수신 대기중 . . .\n");

		if(receive_data(&address_info) < 0) {
			printf("[ERROR] failed send data\n");
			return -1;
		}

		printf("수신 메세지 처리중 . . .\n");

		if(strcmp(address_info.name, "exit") == 0) {
			printf("수신 종료중 . . .\n");
			break;
		}

		if(write_data(&address_info) < 0) {
			printf("[ERROR] failed write data\n");
			cleanup(shmid);
			return -1;
		}
	}

	cleanup(shmid);
	return 0;
}

int init_shm(int *shmid) {

	if(shmid == NULL) {
		printf("[ERROR] init_shm: shmid is NULL\n");
		return -1;
	}

	FILE *fp;
	if((fp = fopen(KEY_FILE, "a+")) == NULL) {
		perror("[ERROR] fopen");
		return -1;
	}
	CLOSE_FILE(fp);

	key_t key;
	if((key = ftok(KEY_FILE, PROJ_ID)) == -1) {
		perror("[ERROR] ftok");
		return -1;
	}

	int id;
	if((id = shmget(key, sizeof(shm_t), IPC_CREAT | 0666)) < 0) {
		perror("[ERROR] shmget");
		return -1;
	}

	void *temp;
	if((temp = shmat(id, (char *)NULL, 0)) == (void *)-1) {
		perror("[ERROR] shmat");
		return -1;
	}

	*shmid = id;
	memory = (shm_t *)temp;

	if((sem_mutex = sem_open(SEM_MUTEX, O_CREAT, 0666, 1)) == SEM_FAILED) {
		perror("[ERROR] sem open");
		return -1;
	}

	if((sem_empty = sem_open(SEM_EMPTY, O_CREAT, 0666, 0)) == SEM_FAILED) {
		perror("[ERROR] sem open");
		return -1;
	}

	if((sem_full = sem_open(SEM_FULL, O_CREAT, 0666, PROCESS_SIZE)) == SEM_FAILED) {
		perror("[ERROR] sem_full open");
		return -1;
	}

	return 0;
}

#define SEM_WAIT(SEM) \
	do { \
		if(sem_wait(SEM)) { \
			perror("[ERROR] sem_wait"); \
		} \
	} while(0)

#define SEM_POST(SEM) \
	do { \
		if(sem_post(SEM)) { \
			perror("[ERROR] sem_post"); \
		} \
	} while(0)


int receive_data(address_info_t *address_info) {

	if(address_info == NULL) {
		printf("[ERROR] receive_data: address info is NULL\n");
		return -1;
	}

	*address_info = memory->data[memory->front];

	SEM_WAIT(sem_empty);
	SEM_WAIT(sem_mutex);

	printf("메세지 수신. . . \n");
	memory->front = (memory->front + 1) % PROCESS_SIZE;

	SEM_POST(sem_mutex);
	SEM_POST(sem_full);

	return 0;
}

int write_data(address_info_t *address_info) {

	FILE *wfp;

	if((wfp = fopen(ADDRESS_DATA, "a")) == NULL) {
		printf("[ERROR] failed: %s\n");
		return -1;
	}

	if(strcmp(address_info->name, "exit") == 0) {
		return 0;
	}

	if(fwrite(address_info, sizeof(address_info_t), 1, wfp) != 1) {
		printf("[ERROR] %s\n", strerror(errno));
		CLOSE_FILE(wfp);
		return -1;
	}

	CLOSE_FILE(wfp);
	return 0;
}

 

[ sender_shared.c ]

include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <unistd.h>
#include <fcntl.h>
#include <error.h>
#include "line.h"
#include "is_korean.h"

#include <signal.h>
#include <semaphore.h>
#include <sys/shm.h>

#define MAX_NAME 13
#define MAX_PHONE_NUMBER 14
#define MAX_ADDRESS 151

#define PROJ_ID 4321
#define PROCESS_SIZE 5
#define KEY_FILE "sm_keyfile"
#define SEM_NAME "/shared_sem"

#define SEM_MUTEX "/sem_mutex"
#define SEM_EMPTY "/sem_empty"
#define SEM_FULL  "/sem_full"

typedef struct address_info_t {
	char name[MAX_NAME];
	char phone_number[MAX_PHONE_NUMBER];
	char address[MAX_ADDRESS];
} address_info_t;

typedef struct shm_t {
	int front, rear;
	address_info_t data[PROCESS_SIZE];
} shm_t;

#define CLOSE_FILE(fp) \
	do { \
		if ((fp) != NULL) { \
			if (fclose(fp) == EOF) { \
				printf("[ERROR] fclose: %s\n", strerror(errno)); \
			} \
			(fp) = NULL; \
		} \
	} while (0)

#define SEM_CLOSE(SEM) \
	do { \
		if(sem_close(SEM) != 0) { \
			perror("[ERROR] sem_close"); \
		}\
	} while(0)

static sem_t *sem_mutex, *sem_empty, *sem_full;
static shm_t *memory;

volatile int send_terminate_flag = 0;

int init_shm(int *shmid);
int send_data(address_info_t *address_info);
int input_address(address_info_t *info);
int check_phone_number(char *number);
int reduce_space(char *address);

void action(int sig, siginfo_t *info, void *context) {
	send_terminate_flag = 1;
}

void cleanup() {

	if(shmdt(memory) == -1) {
		perror("[ERROR] shmctl");
	}

	SEM_CLOSE(sem_full);
	SEM_CLOSE(sem_empty);
	SEM_CLOSE(sem_mutex);

}	

int main() {

	struct sigaction act;
	sigemptyset(&act.sa_mask);
	act.sa_flags = SA_SIGINFO;
	act.sa_sigaction = action;

	sigaction(SIGTERM, &act, NULL);

	int shmid;
	if(init_shm(&shmid) < 0){
		printf("[ERROR] failed init share memory\n");
		return -1;
	}

	while(!send_terminate_flag) {

		address_info_t address_info;

		if(input_address(&address_info) < 0) {
			break;
		}

		if(strcmp(address_info.name, "exit") == 0) {
			if(send_data(&address_info) < 0) {
				cleanup();
				return -1;
			}
			break;
		}

		if(send_data(&address_info) < 0) {
			printf("[ERROR] failed send data\n");
			cleanup();
			return -1;
		}
	}

	cleanup();
	return 0;
}

#define SEM_UNLINK(SEM) \
	do { \
		if(sem_unlink(SEM)) { \
			perror("[ERROR] sem_unlink"); \
		} \
	} while(0)


int init_shm(int *shmid) {

	if(shmid == NULL) {
		printf("[ERROR] init_shm: shmid is NULL\n");
		return -1;
	}

	FILE *fp;
	if((fp = fopen(KEY_FILE, "a+")) == NULL) {
		perror("[ERROR] fopen");
		return -1;
	}

	key_t key;
	if((key = ftok(KEY_FILE, PROJ_ID)) == -1) {
		perror("[ERROR] ftok");
		return -1;
	}

	int id;
	if((id = shmget(key, sizeof(shm_t), IPC_CREAT | 0666)) < 0) {
		perror("[ERROR] shmget");
		return -1;
	}

	void *temp;
	if((temp = shmat(id, NULL, 0)) == (void *)-1) {
		perror("[ERROR] shmat");
		return -1;
	}

	*shmid = id;
	memory = (shm_t *)temp;

	memory->front = 0;
	memory->rear = 0;

	//SEM_UNLINK(SEM_MUTEX);
	//SEM_UNLINK(SEM_EMPTY);
	//SEM_UNLINK(SEM_FULL);

	if((sem_mutex = sem_open(SEM_MUTEX, O_CREAT, 0666, 1)) == SEM_FAILED) {
		perror("[ERROR] sem open");
		return -1;
	}

	if((sem_empty = sem_open(SEM_EMPTY, O_CREAT, 0666, 0)) == SEM_FAILED) {
		perror("[ERROR] sem open");
		return -1;
	}

	if((sem_full = sem_open(SEM_FULL, O_CREAT, 0666, PROCESS_SIZE)) == SEM_FAILED) {
		perror("[ERROR] sem_full open");
		return -1;
	}
	return 0;
}

#define SEM_WAIT(SEM) \
	do { \
		if(sem_wait(SEM)) { \
			perror("[ERROR] sem_wait"); \
		} \
	} while(0)

#define SEM_POST(SEM) \
	do { \
		if(sem_post(SEM)) { \
			perror("[ERROR] sem_post"); \
		} \
	} while(0)

int send_data(address_info_t *address_info) {

	if(address_info == NULL) {
		printf("[ERROR] send_data: address info is NULL\n");
		return -1;
	}

	SEM_WAIT(sem_full);
	SEM_WAIT(sem_mutex);

	memory->data[memory->rear] = *address_info;
	memory->rear = (memory->rear + 1) % PROCESS_SIZE;

	SEM_POST(sem_mutex);
	SEM_POST(sem_empty);

	return 0;
}

#define CHECK_DASH(buf, idx) \
	if(buf[idx] != '-') { \
		printf("[%d]%c is not '-'\n", (idx)+1, buf[(idx)]) ; \
		return -1; \
	}

#define CHECK_NUM(buf, idx) \
	if ( (buf[(idx)]) < '0' || (buf[(idx)]) > '9' ) { \
		printf("[%d]%c is not number\n", (idx)+1, buf[(idx)]) ; \
		return -1 ;\
	}

int input_address(address_info_t *info) {

	while(!send_terminate_flag) {
		printf("Name: ");
		char name[MAX_NAME];

		if(line(name, sizeof(name)) == -1) {
			printf("[ERROR] failed input name\n");
			continue;
		}

		if(strcmp(name, "exit") == 0) {
			strcpy(info->name, name);
			return 0;
		}

		if(is_korean(name) < 0) {
			printf("[ERROR] failed check if name is korean\n");
			continue;
		}

		printf("Phone Num: ");
		char number[MAX_PHONE_NUMBER];

		if(line(number, sizeof(number)) == -1) {
			printf("[ERROR] failed input phone number\n");
			continue;
		}

		if(check_phone_number(number) < 0) {
			printf("[ERROR] failed check phone number format\n");
			continue;
		}

		printf("Address: ");
		char address[MAX_ADDRESS];

		if(line(address, sizeof(address)) == -1) {
			printf("[ERROR] failed input address\n");
			continue;
		}

		if(reduce_space(address) < 0) {
			printf("[ERROR] failed reduce address space\n");
			continue;
		}

		strcpy(info->name, name);
		strcpy(info->phone_number, number);
		strcpy(info->address, address);

		return 0;
	}

	return 0;

}

int check_phone_number(char *number) {

	if(number == NULL) {
		printf("[ERROR] phone number must not be NULL\n");
		return -1;
	}

	if(strncmp(number, "010", 3) != 0) {
		printf("[ERROR] phone number must start with 010\n");
		return -1;
	}

	int idx = 3;
	CHECK_DASH(number, idx);

	for(idx += 1; idx < 8; idx++) {
		CHECK_NUM(number, idx);
	}

	CHECK_DASH(number, idx);

	for(idx += 1; idx < 13; idx++) {
		CHECK_NUM(number, idx);
	}

	return 0;
}



int reduce_space(char *address) {

	if(address == NULL) {
		printf("[ERROR] address must not be NULL\n");
		return -1;
	}

	int space = 0, i, idx = 0;
	int len = strlen(address);

	char temp[len];

	for(i = 0; i < len; i++) {

		if(address[i] == ' ') {
			if(!space) {
				temp[idx] = address[i];
				space = 1;
			}
		}

		else {
			if(space) space = 0;
			temp[idx++] = address[i];
		}
	}

	temp[idx] = 0;
	strcpy(address, temp);

	return 0;
}

 

is_korean.cline.c는 이전 글에서 구현한 함수로, 다음 글에 포함시켜 두었다.

 

[C언어] IPC(2) (feat. 메세지)

[C언어] IPC(1) (feat. 공유 메모리)1. IPC 프로세스는 완전히 독립된 실행 객체로 다른 프로세스에 문제가 발생하여도 영향을 받지 않는다는 장점이 있다. 그러나 독립되어 있는 만큼 서로간에 통신

uj0791.tistory.com

 

728x90