tonykang22 / study

0 stars 0 forks source link

[리눅스 개발환경] 15. IPC #86

Open tonykang22 opened 2 years ago

tonykang22 commented 2 years ago

15. IPC

IPC 개요



익명 파이프 (Pipe)

파이프란?





#include <unistd.h>

int pipe(int pipefd[2]);


#include<stdio.h> 
#include<stdlib.h> 
#include<unistd.h> 
#include<string.h> 
#include<sys/types.h> 
#include<sys/wait.h>

int main() {
    int fd1[2]; /* parnet write */ 
    int fd2[2]; /* child write */ 
    pid_t p;

    if (pipe(fd1)==-1) {
        fprintf(stderr, "pipe error: %m");
        return 1; 
    }

    if (pipe(fd2)==-1) {
        fprintf(stderr, "pipe error: %m");
        return 1;
    }

    p = fork(); 
    if (p < 0) {
        fprintf(stderr, "fork error : %m:);
        return 1;
    } else if (p > 0) { /* Parent */
        char writebuff[] = "Did you understand?"; 
        char readbuff[128] = {0};

        close(fd1[0]); /* close read of fd1 */ 
        write(fd1[1], writebuff, strlen(writebuff)+1); 
        close(fd1[1]);

        wait(NULL);

        close(fd2[1]); /* close write of fd2 */ 
        read(fd2[0], readbuff, sizeof(readbuff));     
        close(fd2[0]);

        printf("[Parent] read from Child: %s\n", readbuff);
    } else { /* Child */
        char readbuff[128] = {0};
        char writebuff[] = "OK!";

        close(fd1[1]); /* close write of fd1 */ 
        read(fd1[0], readbuff, sizeof(readbuff)); 
        close(fd1[0]);

        printf("[Child] read from Parent: %s\n", readbuff);

        close(fd2[0]); /* close read of fd2 */ 
        write(fd2[1], writebuff, strlen(writebuff)+1); 
        close(fd2[1]);

        exit(0); 
    }

    return 0;
}



#include <stdio.h>

FILE *popen(const char *command, const char *type);
int pclose(FILE *stream);


int main() { FILE *fp; int status; char buff[1024];

fp = popen("pwd", "r"); 
if (fp == NULL) {
    fprintf(stderr, "popen error: %m\n");
    exit(1); 
}

printf("Current Path is ");
while (fgets(buff, sizeof(buff), fp) != NULL) {
    printf("%s", buff);
}

status = pclose(fp); 
if (status == -1) {
    fprintf(stderr, "pclose error: %m\n");
    exit(1); 
}

return 0; 

}


<br><br>

## 지명 파이프 (FIFO)

- 명명된 파이프(named pipe) 혹은 지명 파이프는 일반 파이프를 확장한 것으로, 프로세스 간 통신 기법 중 하나이다.
<img width="540" alt="image" src="https://user-images.githubusercontent.com/86760744/182021293-36836911-7cac-4f92-8145-92a02f6a66de.png">

<img width="412" alt="image" src="https://user-images.githubusercontent.com/86760744/182021310-f89928da-dcf7-49cd-ab9a-b96e0ab2bfa3.png">

- 생성된 fifo 파일은 파이프 역할을 수행하며 해당 파일로 데이터를 보낼 수도 있고, (ex. cat file > fifo_pipe)
- 일반 파일처럼 삭제할 수도 있다. (ex. rm fifo_pipe)

<br>

``` c
#include <sys/types.h>
#include <sys/stat.h>

int mkfifo(const char *pathname, mode_t mode);
#include <fcntl.h> /* Definition of AT_* constants */ 
#include <sys/stat.h>

int mkfifoat(int dirfd, const char *pathname, mode_t mode);



int main() { int fd; char *fifofile = "/tmp/fifo_test"; char inputbuf[1024]; char readbuf[1024];

if (access(fifofile, F_OK) != 0) {
    if (mkfifo(fifofile, 0666) == -1) {
        fprintf(stderr,
        "mkfifo error: %m\n");
        exit(1); 
    }
    printf("FIFO file(%s) create OK.\n", fifofile);
}

while (1) {
    printf("input message: ");
    fgets(inputbuf, sizeof(inputbuf), stdin);

    /* Write to FIFO */
    fd = open(fifofile, O_WRONLY);
    write(fd, inputbuf, strlen*inputbuf)+1);
    close(fd);

    /* Read from FIFO */
    fd = open(fifofile, O_RDONLY);
    read(fd, readbuf, sizeof(readbuf));
    close(fd);

    print("read: %s", readbuf);
}

return 0;

}


<br>

- read_fifo.c의 코드이다.
``` c
#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <fcntl.h> 
#include <sys/stat.h> 
#include <sys/types.h> 
#include <unistd.h>

int main() {
    int fd;
    char *fifofile = "/tmp/fifo_test"; 
    char inputbuf[1024];
    char readbuf[1024];

    if (access(fifofile, F_OK) != 0) {
        if (mkfifo(fifofile, 0666) == -1) {
            fprintf(stderr,"mkfifo error: %m\n");
            exit(1); 
        }
        printf("FIFO file(%s) create OK.\n", fifofile);
    }

    while (1) {
        /* Read from FIFO */
        fd = open(fifofile, O_RDONLY); 
        read(fd, readbuf, sizeof(readbuf)); 
        close(fd);

        printf("read: %s", readbuf); 
        printf("input message: "); 
        fgets(inputbuf, sizeof(inputbuf), stdin);

        /* Write to FIFO */
        fd = open(fifofile, O_WRONLY); 
        write(fd, inputbuf, strlen(inputbuf)+1); 
        close(fd);
    }

    return 0;
}



공유메모리 개요

공유메모리란?

image



SysV 공유메모리



#include <sys/types.h>
#include <sys/ipc.h>

key_t ftok(const char *pathname, int proj_id);
key_t shmkey;

if ((shmkey = ftok("/tmp", 'a')) == (key_t) -1) {
    perror("IPC error: ftok");
    exit(1);
}


#include <sys/ipc.h>
#include <sys/shm.h>

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


#include <sys/types.h>
#include <sys/shm.h>

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


#include <sys/types.h>
#include <sys/shm.h>

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



예제

#include <stdio.h> 
#include <stdlib.h> 
#include <sys/ipc.h> 
#include <sys/types.h> 
#include <sys/shm.h>

int main() 
{
    key_t key; 
    int shmid; 
    char *str;

    key = ftok("shmtest", 1234);
    shmid = shmget(key, 1024, IPC_CREAT|0666); 
    str = (char *)shmat(shmid, (void *)0, 0);

    printf("Input data: ");
    if (fgets(str, 1024, stdin) == NULL) {
        perror("fgets erro: ");
        exit(1); 
    }

    printf("Written data: %s\n", str); 

   shmdt(str);

    return 0;
}


int main() { key_t key; int shmid; char *str;

key = ftok("shmtest", 1234);
shmid = shmget(key, 1024, IPC_CREAT|0666); 
str = (char *)shmat(shmid, (void *)0, 0);

printf("Read data: %s\n", str);

shmdt(str);
shmctl(shmid, IPC_RMID, NULL);

return 0; 

}


<br>

- 실행 결과
![image](https://user-images.githubusercontent.com/86760744/183564590-41cfcd83-ce18-42cf-8287-2bc6c22503a0.png)

![image](https://user-images.githubusercontent.com/86760744/183564599-a9cb52bc-a723-4437-a44b-94bf462eaf87.png)

<br><br>

## 세마포어
### 개요
- 개념 
    * 세마포어는 다익스트라가 고안한, 두 개의 원자적 함수로 조작되는 정수 변수로서, 멀티 프로그래밍 환경에서 공유 자원에 대한 접근을 제한하는 방법(상호배제)으로 사용된다.
- 원리 
    * 세마포어 는 정수값을 가지는 변수이며, 다음과 같이 P와 V라는 명령에 의해서만 접근할 수 있다.
        * P와 V는 각각 try와 increment를 뜻하는 네덜란드어의 첫글자를 딴 것이다.
    * P는 임계 영역에 들어가기 전에 수행되고, V는 임계 영역에서 나올 때 수행된다.
    * 이때 변수 값을 수정하는 연산은 모두 원자성을 만족해야 한다.
    * 즉, 한 프로세스에서 세마포어 값을 변경하는 동안 다른 프로세스가 동시에 이 값을 변경해서는 안된다.
- 종류
    * 계수 세마포어(Counting Semaphore) : 다수개의 자원에 대해 카운팅이 가능한 세마포어 (0 ~ n)
    * 이진 세마포어(Binary Semaphore) : 1개의 자원에 대해 카운팅, 즉 세마포어 값은 0 또는 1을 가진다.
- 설명
    * 계수 세마포어의 경우 3개의 자원이 있다고 가정할 때 (S = 3) 여러 프로세스가 이 자원에 접근을 시도할 것이다.
    * 자원에 접근할 때마다 세마포어를 하나씩 감소시키고(P), 세마포어 값이 0이 되면 자원에 접근을 시도하는 다른 프로세스는 대기하게 된다.
    * 자원에 접근했던 프로세스가 접근을 해제하면 세마포어를 다시 하나씩 증가시키고(V), 
    * 대기하던 프로세스는 다시 자원에 접근이 가능하게 된다.
    * 이진 세마포어는 계수 세마포어의 자원 값이 1개인 경우(S = 1)를 의미한다. (뮤텍스와 유사하다.)

<Br>

### SysV 세마포어
- SysV 세마포어의 주요 함수
    * semget : 세마포어의 IPC ID를 생성하거나 가져온다.
    * semctl : 세마포어 조작 (초기화, 정복획득, 제거)
    * semop : 세마포어 증가/감수
    * semtimedop : 타임아웃 기능이 추가된 semop
- 흐름
    * 1. semget()을 통해 ID 획득
    * 2. 새로 생성된 경우 semctl()을 통해 세마포어 초기화
    * 3. semop()를 이용해 세마포어 감소(P)
    * 4. 처리
    * 5. semop()를 이용해 세마포어 증가(V)
    * 6. semctl()을 통해 세마포어 정리

<br>

``` c
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

int semget(key_t key, int nsems, int semflg);



#include <sys/types.h> 
#include <sys/ipc.h> 
#include <sys/sem.h>

int semctl(int semid, int semnum, int cmd, ...);


#include <sys/types.h> 
#include <sys/ipc.h> 
#include <sys/sem.h>

int semop(int semid, struct sembuf *sops, size_t nsops);
int semtimedop(int semid, struct sembuf *sops, size_t nsops, const struct timespec *timeout);



예제

union semun { int val; struct semid_ds buf; unsigned short array; };

struct sembuf p = { 0, -1, SEM_UNDO }; struct sembuf v = { 0, +1, SEM_UNDO };

define SEM_P(ID) ({ \

if(semop(ID, &p, 1) < 0) { \
    perror("semop p"); \
    exit(1); \
} \

})

define SEM_V(ID) ({ \

if(semop(ID, &v, 1) < 0) { \
    perror("semop v"); \
    exit(1); \
} \

})

int sem_use; int semid; char shared_buf[] = "Hello, World!\n"; int shared_index = 0;

void print_one(void arg) { int tidx = (int )arg; bool b = true; char c;

while (b) {
    if (sem_use) SEM_P(semid);
    if (shared_index >= strlen(shared_buf)) {
        b = false; 
    } else {
        if (tidx)
            c = toupper(shared_buf[shared_index++]);
        else
            c = tolower(shared_buf[shared_index++]);
        sleep(rand()%2); 
        putchar(c); 
        fflush(stdout);
    }
    if (sem_use) SEM_V(semid); 
}

pthread_exit((void *)0); 

}

int main(int argc, char *argv[]) { key_t key; union semun u; int pid; pthread_t thread_t; int i, ret; int tidx[2] = {0, 1};

if (argc < 2) {
    printf("Usage: %s [0(OFF)/1(ON)]\n", argv[0]); 
    exit(1);
}

sem_use = atoi(argv[1]); 

key = ftok("shmtest", 1234);

semid = semget(key, 1, 0666 | IPC_CREAT); 
if(semid < 0) {
    perror("semget");
    exit(1); 
}

u.val = 1;
if(semctl(semid, 0, SETVAL, u) < 0) {
    perror("semctl");
    exit(1); 
}

for (i = 0; i <= 1; i++) {
    ret = pthread_create(&thread_t, NULL, print_one, (void *)&tidx[i]); 
    if (ret) {
        perror("pthread_create");
        exit(1); 
    }
}

pthread_join(thread_t, NULL);
return 0; 

}


- 실행 결과
![image](https://user-images.githubusercontent.com/86760744/183567721-a327f12e-2d49-41ec-82cc-b6d34ccfc940.png)

<br><br>

## POSIX 세마포어
- POSIX 세마포어를 사용하면 프로세스와 스레드가 작업을 동기화할 수 있다.
- POSIX 세마포어의 연산
    * sem_wait (3) : 세마포어 값을 1 감소 시킨다. (P), 세마포어의 값이 0이면 0보다 커질 때 까지 차단.
    * sem_post (3) : 세마포어 값을 1 증가 시킨다. (V)
- POSIX 세마포어는 두 가지 형태가 있다. 
    * 명명된 세마포어
    * 익명 세마포어

<br>

### 명명된 세마포어 (named semaphore)
-  /somename 형식의 이름으로 식별된다. 
- 즉, 초기 슬래시로 구성되는 최대 NAME_MAX-4 (즉, 251) 개의 null로 끝나는 문자열
- 동일한 이름을 sem_open (3)에 전달하여 두 개의 프로세스가 동일한 이름의 세마포어에서 작동할 수 있다.
- sem_open(3) 함수는 새로 명명된 새마포어를 생성하거나 기존의 명명된 세마포어를 연다.
- 세마포어를 연 후에는 sem_post (3) 및 sem_wait (3)을 사용하여 작동할 수 있다.
- 프로세스가 세마포어 사용을 마치면 sem_close (3)를 사용해 세마포어를 닫을 수 있다.
- 모든 프로세스가 세마포어 사용을 마치면 sem_unlink (3)를 사용해 시스템에서 제거할 수 있다.

<br>

### 익명 세마포어 (nameless semaphore)
- 이름이 없는 대신 여러 스레드 (스레드 공유 세마포어) 또는 프로세스 (프로세스 공유 세마포어)간에 공유되는 메모리 영역에 배치된다.
- 스레드 공유 세마포어는 프로세스의 스레드(예 : 전역 변수) 간에 공유되는 메모리 영역에 배치된다.
- 프로세스 공유 세마포어는 공유 메모리 영역(예 : shmget (2)을 사용해 작성된 System V 공유메모리 세그먼트 또는 shm_open (3)을 사용해 작성된 POSIX 공유메모리 오브젝트)에 배치해야 한다.
- 생성된 프로세스가 종료되면 세마포어가 없어지는 임시 세마포어이다.
- 사용하기 전에 sem_init (3)을 사용해 초기화해야 한다.
- 그런 다음 sem_post (3) 및 sem_wait (3)을 사용해 작동할 수 있다.
- 세마포어가 더 이상 필요하지 않은 경우, 세마포어가 있는 메모리를 할당 해제하기 전에 sem_destroy (3)를 사용해 세마포어를 제거해야 한다.

<br>

- POSIX 세마포어 **주요 함수**
    * sem_open : 명명된 세마포어를 생성하거나 오픈
    * sem_close : 명명된 세마포어 닫기
    * sem_unlink : 명명된 세마포어 제거
    * sem_init : 익명 세마포어 생성 및 초기화
    * sem_destroy : 익명 세마포어 제거
    * sem_wait : P 연산
    * sem_post : V 연산
    * 주의 : 컴파일 시 Link with -pthread

<br><br>

``` c
#include <fcntl.h>               /* For O_ * constants */
#include <sys/stat.h>         /* For mode constatns */
#include <semaphore.h>

sem_t *sem_open(const char *name, int oflag);
sem_t *sem_open(const char *name, int oflag, mode_t mode, unsigned int value);

int sem_close(sem_t *sem);

int sem_unlink(const char *name);


#include <semaphore.h>

int sem_init(sem_t *sem, int pshared, unsigned int value); 
int sem_destroy(sem_t *sem);

if (sem_init(&sem, 0, 1) == -1) { perror("sem_init"); exit(1); } ... sem_destroy(sem);


<Br>

``` c
#include <semaphore.h>

int sem_wait(sem_t *sem);
int sem_trywait(sem_t *sem);
int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);

int sem_post(sem_t *sem);



예제

sem_t sem;

void thread_t1(void arg) { int tidx = (int )arg; sem_wait(&sem); printf("[%d] Start...\n", tidx); / critical section / sleep(3); printf("[%d] End!\n\n", tidx); sem_post(&sem); }

void thread_t2(void arg) {

int tidx = *(int *)arg; 
int ret;

try: ret = sem_trywait(&sem); if (ret == -1) { if (errno == EAGAIN) { printf("[%d] retry get semaphoe...\n", tidx); sleep(1); goto try; } perror("sem_trywait"); exit(1); } else if (ret == 0) { printf("[%d] Start...\n", tidx); / critical section / sleep(3); printf("[%d] End!\n\n", tidx); sem_post(&sem); } }

int main() { int ret; pthread_t t1, t2, t3; int tidx[] = {0, 1, 2};

if (sem_init(&sem, 0, 1) == -1) { 
    perror("sem_init");
    exit(1);
}

ret = pthread_create(&t1, NULL, thread_t1, (void *)&tidx[0]); 
if (ret != 0) {
    fprintf(stderr, "pthread_create error: %m"); 
    exit(1);
}

ret = pthread_create(&t2, NULL, thread_t1, (void *)&tidx[1]); 
if (ret != 0) {
    fprintf(stderr, "pthread_create error: %m"); 
    exit(1);
}

ret = pthread_create(&t3, NULL, thread_t2, (void *)&tidx[2]); 
if (ret != 0) {
    fprintf(stderr, "pthread_create error: %m"); 
    exit(1);
}

pthread_join(t1, NULL); 
pthread_join(t2, NULL); 
pthread_join(t3, NULL);

sem_destroy(&sem);     
return 0;

}



<br>

- 실행 결과
![image](https://user-images.githubusercontent.com/86760744/183570497-7b657015-4ea2-45e9-b80d-f5d3a49c3315.png)