위의 사진은 리눅스 커널 구조 그림이다. 위의 사진을 보면 프로세스의 특징은 아래와 같다.
위와 같은 이유로 별도의 설비가 없다면 통신이 어렵다.
이렇게 프로세스간 통신을 위해서 커널 영역에서는 다양한 IPC(Inter Process Communication) 을 제공한다.
개념
PIPE는 우리가 생각하는 파이프와 비슷하게 작동한다.
익명 파이프라고도 부른다.
보통 관련이 있는 프로세스끼리 사용한다(e.g 부모, 자식 프로세스)
작동 원리
장점
단점
사진
코드
<unistd.h> 에 있는 pipe 함수를 통하여 2개의 Int 원소가 있는 배열을 pipe로 만들어 볼 것이다.
파이프는 실제로는 커널 영역에 생성되며 파이프를 생성한 프로세스는 file descriptor만을 가지고 pipe를 이용한다 이때 데이터를 [1]번 원소에 쓰게 되면 0번으로 그 데이터를 읽을 수 있다.
fork를 하게된다면 자식 프로세스는 File descriptor를 그대로 사용할 수 있기 때문에 이를 통해 자식 프로세스와 부모 프로세스가 pipe로 통신할 수 있다.
아래의 코드는 부모 프로세스가 보내는 메시지를 자식 프로세스가 받아 출력하는 코드이다.
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#define MAXBUF 1024
#define READ 0
#define WRITE 1
int main(){
// file discriptor
int fd[2];
char buf[MAXBUF];
// pipe 에 실패할 경우 에러를 출력 후 종료
if (pipe(fd) < 0){
fprintf(stderr, "pipe error: %s\n", strerror(errno));
return -1;
}
// fork 에 실패할 경우
pid_t pid = fork();
if (pid == -1){
fprintf(stderr, "fork error: %s\n", strerror(errno));
return 1;
}
printf("\n");
// 부모 프로세스
if (pid > 0) {
// 부모 프로세스는 쓰기만 할 것이므로 PIPE의 READ 하는 부분을 닫는다
close(fd[READ]);
strcpy(buf, "message from parent\n");
write(fd[WRITE], buf, strlen(buf));
}else{
// 자식 프로세스는 읽기만 할 것이므로 PIPE의 WRITE 하는 부분을 닫는다.
close(fd[WRITE]);
read(fd[READ], buf, MAXBUF);
printf("child got message : %s\n", buf);
}
return 0;
개념
코드
아래의 코드는 별개의 프로세스가 이름을 가지고 named pipe를 이용해 통신하는 모습을 알 수 있다.
client.c
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#define MAXBUF 1024
#define NAME "./named_pipe"
int main(void) {
char buf[MAXBUF];
int fd;
int nread, i;
// write 전용 파이프 열기
if ((fd = open(NAME, O_WRONLY)) < 0) {
printf("fail to open named pipe\n");
return -1;
}
// 데이터 전송
for (i = 0; i < 3; i++) {
snprintf(buf, sizeof(buf), "Send Message[%i]", i);
if ((nread = write(fd, buf, sizeof(buf))) < 0 ) {
printf("fail to call write()\n");
return 0;
}
}
return 0;
}
Server.c
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#define MAXBUF 1024
#define NAME "./named_pipe"
int main(void) {
char buf[MAXBUF];
int fd;
int nread, rc;
// named pipe 가 있다면 삭제해주자
if (access(NAME,F_OK) == 0) {
unlink(NAME);
}
// named pipe 생성
if ((rc = mkfifo(NAME,0666)) < 0) {
printf("fail to make named pipe\n");
return 0;
}
// named pipe 열기
if ((fd = open(NAME, O_RDWR)) < 0) {
printf("fail to open named pipe\n");
return 0;
}
while (1) {
if ((nread = read(fd, buf, sizeof(buf))) < 0 ) {
printf("fail to call read()\n");
return 0;
}
printf("recv: %s\n", buf);
}
return 0;
}
결과창
개념
코드
message queue 사용함수
나이와 이름을 메시지 큐에 올리면 그 큐에 있는 데이터를 받아오는 코드이다
data.h
struct human{
short age;
char name[20];
};
struct message{
long msg_type;
struct human data;
};
2. sender.c
``` c
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/types.h>
#include <sys/msg.h>
#include <stdlib.h>
#include <string.h>
#include "data.h"
int main(){
key_t key=12345;
int msqid;
struct message msg;
// msg type은 무조건 0보다 커야한다.
msg.msg_type=1;
msg.data.age=80;
strcpy(msg.data.name,"ghkim");
//msqid를 가져오기.
//없다면 큐를 생성한다.
if((msqid=msgget(key,IPC_CREAT|0666))==-1){
printf("msgget failed\n");
exit(0);
}
//메시지보내기
if(msgsnd(msqid,&msg,sizeof(struct human),0)==-1){
printf("msgsnd failed\n");
exit(0);
}
printf("message sent\n");
}
```
3. Receiver.c
``` c
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/types.h>
#include <sys/msg.h>
#include <stdlib.h>
#include <string.h>
#include "data.h"
int main(){
key_t key=12345;
int msqid;
struct message msg;
//sender 쪽의 msqid얻어오기
if((msqid=msgget(key,IPC_CREAT|0666))==-1){
printf("msgget failed\n");
exit(0);
}
//메시지를 받기
if(msgrcv(msqid,&msg,sizeof(struct human),0,0)==-1){
printf("msgrcv failed\n");
exit(0);
}
printf("name : %s, age :%d\n",msg.data.name,msg.data.age);
//이후 메시지 큐 지우기.
if(msgctl(msqid,IPC_RMID,NULL)==-1){
printf("msgctl failed\n");
exit(0);
}
}
```
개념
코드
사용함수
shmget : 인자로 전달된 Key 값으로 공유메모리를 얻고 shared memory segmentf를 돌려준다.
shmat : 메모리의 위치에 이 프로세스를 묶는 시스템콜
shmdt : 공유 메모리를 이 프로세스와 떼어내는 것
shmctl : 공유메모리를 제어하기 위하여 사용
아래의 코드는 shared memory 가 없다면 새로운 메모리 세그먼트를 만들고 메모리 주소를 얻어와 값을 변경하는 코드이다. Remove_shm은 값을 변경하고 마지막엔 공유메모리를 종료시킨다
shm.c
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
int main(){
int shmid;
int *num;
key_t key=12345;
void *memory_segment=NULL;
// key 값을 전달하여 공유메모리를 얻고 공유메모리 조각의 Id 를 받아 shmid에 저장
if((shmid=shmget(key,sizeof(int),IPC_CREAT|0666))==-1){
printf("shmget failed\n");
exit(0);
}
// 공유메모리를 얻었다면 메모리의 위치에 이 프로세스를 묶는다
if((memory_segment=shmat(shmid,NULL,0))==(void*)-1){
printf("shmat failed\n");
exit(0);
}
// num에 공유메모리에서 얻어온 포인터를 이용해 값 증
num=(int*)memory_segment;
(*num)++;
printf("shared memory value :%d\n",(*num));
return 0;
}
remove_shm.c
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
int main(){
int shmid;
int *num;
key_t key=12345;
void *memory_segment=NULL;
if((shmid=shmget(key,sizeof(int),IPC_CREAT|0666))==-1){
printf("shmget failed\n");
exit(0);
}
if((memory_segment=shmat(shmid,NULL,0))==(void*)-1){
printf("shmat failed\n");
exit(0);
}
num=(int*)memory_segment;
(*num)++;
printf("shared memory value :%d\n",(*num));
// shared memory 를 제어하기위해서 사용한다
if(shmctl(shmid,IPC_RMID,NULL)==-1){
printf("shmctl failed\n");
}
return 0;
}
개념
코드
사용 함수
main.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
int main(int argc, char **argv) {
int fd;
char *file = NULL;
struct stat sb;
int flag = PROT_WRITE | PROT_READ;
if (argc < 2) {
fprintf(stderr, "Usage: input\n");
return 0;
}
if ((fd = open(argv[1], O_RDWR|O_CREAT)) < 0) {
perror("File Open Error");
return 0;
}
if (fstat(fd, &sb) < 0) {
perror("fstat error");
return 0;
}
file = (char *)malloc(40);
// mmap를 이용해서 열린 파일을 메모리에 대응시킨다.
// file은 대응된 주소를 가리키고, file을 이용해서 필요한 작업을
// 하면 된다.
if ((file = (char *) mmap(0, 40, flag, MAP_SHARED, fd, 0)) == NULL) {
perror("mmap error");
return 0;
}
printf("%s\n", file);
memset(file, 0x00, 40);
munmap(file, 40);
close(fd);
return 0;
}
개념
코드
사용법
main.c
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <semaphore.h>
#define THREAD_NUM 4
sem_t semaphore;
void* routine(void* args) {
// 세마포어를 얻을 때 까지 기다린다.
sem_wait(&semaphore);
sleep(1);
printf("Hello from thread %d\n", *(int*)args);
// 세마포어를 되돌려준다.
sem_post(&semaphore);
free(args);
}
int main(int argc, char *argv[]) {
pthread_t th[THREAD_NUM];
// 세마포어 만들기 초기값 1
sem_init(&semaphore, 0, 1);
int i;
for (i = 0; i < THREAD_NUM; i++) {
int* a = malloc(sizeof(int));
*a = i;
if (pthread_create(&th[i], NULL, &routine, a) != 0) {
perror("Failed to create thread");
}
}
for (i = 0; i < THREAD_NUM; i++) {
if (pthread_join(th[i], NULL) != 0) {
perror("Failed to join thread");
}
}
sem_destroy(&semaphore);
return 0;
}
개념
코드
본 예제에서는 Unix Domain Socket을 통하여 내부 프로세스간에 통신을 한다.
client.c
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <unistd.h>
#include <sys/un.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char **argv) {
int client_len;
int client_sockfd;
char buf_in[255];
char buf_get[255];
struct sockaddr_un clientaddr;
if (argc != 2) {
printf("Usage: %s [UDS path]\n", argv[0]);
exit(0);
}
// socket 생성
client_sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
if (client_sockfd == -1) {
perror("Failed to create socket: ");
exit(0);
}
//
memset(&clientaddr, 0, sizeof(clientaddr));
clientaddr.sun_family = AF_UNIX;
strcpy(clientaddr.sun_path, argv[1]);
client_len = sizeof(clientaddr);
// connect
if (connect(client_sockfd, (struct sockaddr *)&clientaddr, client_len) < 0) {
perror("Failed to connect: ");
exit(0);
}
while(1) {
memset(buf_in, 0, sizeof(buf_in));
printf("입력 : ");
fgets(buf_in, 255, stdin);
write(client_sockfd, buf_in, strlen(buf_in));
if (strncmp(buf_in, "quit", strlen("quit")) == 0) {
close(client_sockfd);
exit(0);
}
while(1) {
read(client_sockfd, buf_get, 255);
if (strncmp(buf_get, "end", strlen("end")) == 0)
break;
printf("%s\n", buf_get);
}
}
close(client_sockfd);
exit(0);
}
server.c
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[]) {
int server_sockfd, client_sockfd;
int state;
struct sockaddr_un clientaddr, serveraddr;
int client_len = sizeof(client_len);
char buf[255];
state = 0;
if (argc != 2) {
printf("Usage: %s [UDS path]\n", argv[0]);
exit(0);
}
if (access(argv[1], F_OK) == 0) {
unlink(argv[1]);
}
if ((server_sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
perror("Failed to create socket: ");
exit(0);
}
memset(&serveraddr, 0, sizeof(serveraddr));
serveraddr.sun_family = AF_UNIX;
strcpy(serveraddr.sun_path, argv[1]);
state = bind(server_sockfd , (struct sockaddr *)&serveraddr,
sizeof(serveraddr));
if (state == -1) {
perror("Failed to bind: ");
exit(0);
}
state = listen(server_sockfd, 5);
if (state == -1) {
perror("Failed to listen: ");
exit(0);
}
while(1) {
client_sockfd = accept(server_sockfd, (struct sockaddr *)&clientaddr,
(socklen_t*)&client_len);
if (client_sockfd == -1) {
perror("Failed to accept: ");
exit(0);
}
while(1) {
memset(buf, 0, 255);
if (read(client_sockfd, buf, 255) <= 0) {
close(client_sockfd);
break;
}
printf("%s", buf);
if (strncmp(buf, "quit", strlen("quit")) == 0) {
write(client_sockfd, "Connection Closed\n", strlen("Connection Closed\n"));
close(client_sockfd);
break;
}
write(client_sockfd, "IPC SUCCESS", strlen("IPC SUCCESS"));
}
}
close(client_sockfd);
}