IPC는 프로세스간 데이터 이동이다.
ex) 웹브라우저가 웹서버에서 웹페이지를 요청할 때 HTML 데이터를 전송한다.
이 데이터 이동은 대부분 소켓을 이용한다.
ex) $ ls | lpr
셸은 | 기호로 표현되는 파이프로 두 개를 연결하는 ls 프로세스와 별도의 lpr 프로세스를 생성한다.파이프는 두 개의 관련 프로세스 사이의 단방향 통신을 허용힌다. ls 프로세스는 파이프에 데이터를 쓴다. lpr 프로세스는 파이프에서 데이터를 읽습니다.
이러한 유형의 IPC는 다음 기준에 따라 다름
가장 간단한 프로세스 간 통신 방법 중 하나는 공유 메모리를 사용하는 것입니다.
공유 메모리는 둘 이상의 프로세스들이 같은 메모리에 접근할 수 있게 한다.
한 프로세스가 메모리를 변경하면 다른 모든 프로세스에서도 수정 사항이 확인됩니다.
공유 메모리는 모든 프로세스가 동일한 메모리를 공유하기 때문에 프로세스 간 통신에서 가장 빠른 형태
커널은 공유 메모리에 대한 액세스를 동기화하지 않으므로 사용자가 직접 동기화해야함
ex) 프로세스는 데이터가 기록되기 전까지 메모리에서 읽으면 안 되며, 두 프로세스는 동시에 동일한 메모리 위치에 쓰면 안 된다.
이러한 경주 조건을 피하기 위한 일반적인 전략은 다음 섹션에서 논의하는 세마포어를 사용하는 것이다. 그러나 우리의 예시 프로그램은 공유 메모리 메커니즘에 초점을 맞추고 동기화 논리로 샘플 코드를 혼란스럽게 하지 않기 위해 메모리에 액세스하는 단일 프로세스만 보여준다.
Linux 메모리 모델을 이해하면 할당 및 첨부 프로세스를 설명하는 데 도움이 됩니다. 리눅스에서 각 프로세스의 가상 메모리는 여러 페이지로 분할됩니다. 각 프로세스는 메모리 주소에서 실제 데이터가 포함된 가상 메모리 페이지로의 매핑을 유지합니다. 각 프로세스가 자체 주소를 가지고 있더라도 여러 프로세스의 매핑은 메모리 공유를 허용하면서 동일한 페이지를 가리킬 수 있습니다. 메모리 페이지는 8장 "리눅스 시스템 호출"의 섹션 8.8 "mlock 제품군: 물리적 메모리 잠금"에서 더 자세히 논의된다.
새 공유 메모리 세그먼트를 할당하면 가상 메모리 페이지가 생성됩니다. 모든 프로세스가 동일한 공유 세그먼트에 액세스하기를 원하기 때문에 하나의 프로세스만 새 공유 세그먼트를 할당해야 합니다.기존 세그먼트를 할당하면 생성되지 않습니다.
새 페이지이지만 기존 페이지의 식별자를 반환합니다.프로세스가 공유 메모리 세그먼트를 사용하도록 허용하기 위해 프로세스가 이를 첨부하여 가상 메모리에서 세그먼트의 공유 페이지로 매핑되는 항목을 추가합니다.세그먼트가 완료되면 이러한 매핑 항목이 제거됩니다.더 이상 공유 메모리 세그먼트에 액세스하려는 프로세스가 없으면 정확히 한 프로세스가 가상 메모리 페이지의 할당을 취소해야 합니다.
세그멘테이션은 프로세스를 세그먼트의 집합으로 생각한다.
하나의 프로세스가 동작하려면 기본적으로 코드, 데이터, 스택 세 가지의 세그먼트는 항상 가지고 있다.
세그멘테이션은 물리적인 크기의 단위가 아닌 논리적 내용의 단위로 자르기 때문에 세그먼들의 크기는 일반적으로 같지 않다.
장점
- 페이징은 프로세스를 같은 단위로 자르게 되므로 중요한 부분과 중요하지 않은 부분이 같은 페이지 안으로 잘라질 수 있다
- 세그멘테이션 방법으로 자르게 되면 코드 영역은 코드 영역으로 잘리게 되고, 중요한 세그먼트, 중요하지 않은 세그먼트를 논리적인 내용 측면으로 자를 수 있다.
- 그렇기 때문에 보호와 공유의 기능을 수행하기 쉬워짐
단점- 세그먼트는 크기가 고정되어있지 않고 가변적
- 크기가 다른 각 세그먼트를 메모리에 두려면 동적 메모리 할당해야함
- 외부 단편화(메모리 낭비) 발생 가능
인자로 전달된 key의 값으로 공유메모리를 얻고 shared memory segment의 id를 반환
int shmget(key_t key, size_t size, int shmflg);
key: 공유메모리를 할당할때 사용하는 고유 key값
size: 메모리의 최소 size를 의미, 새로운 공유메모리를 할당받는다면 size를 명시하고 이미 존재하는 메모리면 0
shmflg:
IPC_CREAT: 새로운 메모리 세그먼트는 만듬, 이 flag를 사용하지 않는다면 shmget은 명시된 key와 연관된 것 찾고 접근할 수 있는 권한이 있는지 확인
IPC_EXCL: IPC_CREAT과 함께쓰는 플래그로 만약 메모리 세그먼트가 존재하면 shmget은 실패하게됨
IPC_EXCL을 사용하는 경우는 우선 공유메모리가 있는지 확인 후 없으면 IPC_CREAT을 통해 할당받으라는 뜻, 이렇게 공유메모리가 오염되는 것을 방지가능
Mode flags: ex) S_IWUSER : 부여된 권한을 나타내는 9비트
user, group 및 other를 사용하여 세그먼트에 대한 액세스를 제어, 실행 비트는 무시
S_IRUSR 및 S_IWUSR은 공유 메모리 세그먼트 소유자의 읽기 및 쓰기 권한
S_IROTH 및 S_IWOTH는 다른 사용자의 읽기 및 쓰기 권한
공유메모리를 얻었으면 메모리의 위치에 이 프로세스를 묶는(attach) 시스템 콜
void *shmat(int shmid, const void *shmaddr, int shmflg);
shmid: 공유메모리의 id를 의미합니다. shmget을 통해 얻어올 수 있습니다.
shmaddr:
-NULL(0)일 경우: 커널에서 적절한 주소를 반환하여 줌
-NULL이 아닐 경우: shmflg로 SHM_RND일때, 그 주소와 attach할 가장 가까운 주소를 반환
shmflg:
return: 성공시 적절한 포인터, 실패시 (void)-1(공유 메모리에 들어가있는 데이터가 정수형인지 구조체인지 모르니까 무엇이든 받을 수 있는 void 넘겨줄테니 알아서 반환해라)
공유메모리를 이 프로세스와 떼어냄, 이는 공유메모리를 제거하는 것이 아님에 주의
int shmdt(const void *shmaddr);
shmaddr : shmat에서 전달받은 그 포인터를 전달하면 됩니다.
return: 성공 시 0, 실패시 -1을 반환합니다.
공유메모리를 제어하기 위해 사용
ex) 공유메모리의 정보를 얻거나 어떤 값을 쓰거나 공유메모리를 삭제하는 등의 조작이 있다.
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
shmid: 공유메모리 id
cmd: 제어할 일종의 command
buf: shmid_ds라는 구조체
struct shmid_ds { struct ipc_perm shm_perm; // 소유권과 권한 size_t shm_segsz; // 세그먼트의 크기 (bytes) time_t shm_atime; // Last attach time time_t shm_dtime; // Last detach time time_t shm_ctime; // Last change time pid_t shm_cpid; // 최초로 만들어낸 PID pid_t shm_lpid; // 최근 shmat, shmdt를 수행한 PID shmatt_t shm_nattch; // No. of current attaches ... }; // //shmid_ds 구조체의 첫번째 구조체(ipc_perm) struct ipc_perm { key_t __key; // Key supplied to shmget(2) uid_t uid; // Effective UID of owner gid_t gid; // Effective GID of owner uid_t cuid; // Effective UID of creator gid_t cgid; // Effective GID of creator unsigned short mode; // Permissions + SHM_DEST and SHM_LOCKED flags unsigned short __seq; // Sequence number };
return: 성공시 -1이 아님, 실패시 -1/errno
각 공유 메모리 세그먼트는 시스템 전체 공유 메모리 세그먼트 수에 대한 제한을 위반하지 않도록 작업이 끝나면 shmctl을 사용하여 명시적으로 할당 해제해야 합니다. 종료 및 Exec을 호출하면 메모리 세그먼트가 분리되지만 할당 해제되지는 않습니다.
#include <stdio.h>
#include <sys/shm.h>
#include <sys/stat.h>
int main (){
int segment_id;
char* shared_memory;
struct shmid_ds shmbuffer;
int segment_size;
const int shared_segment_size = 0x6400;
/* Allocate a shared memory segment. */
segment_id = shmget(IPC_PRIVATE, shared_segment_size, IPC_CREAT | IPC_EXCL | S_IRUSR | S_IWUSR);
/* Attach the shared memory segment. */
//두번째 인자 0인 경우 적절한 주소 반환
shared_memory = (char*) shmat(segment_id, 0, 0);
printf("shared memory attached at address %p\n", shared_memory);
/* Determine the segment’s size. */
//공유 메모리 정보 조회하여 shmbuffer 에 저장
shmctl(segment_id, IPC_STAT, &shmbuffer);
segment_size = shmbuffer.shm_segsz;
printf("segment size: %d\n", segment_size);
/* Write a string to the shared memory segment. */
//sprintf()는 출력값을 문자열에 저장하는 함수
sprintf(shared_memory, "Hello, world.");
/* Detach the shared memory segment. */
shmdt(shared_memory);
/* Reattach the shared memory segment, at a different address. */
shared_memory = (char*) shmat(segment_id, (void*) 0x5000000, 0);
printf("shared memory reattached at address %p\n", shared_memory);
/* Print out the string from shared memory. */
printf("%s\n", shared_memory);
/* Detach the shared memory segment. */
shmdt(shared_memory);
/* Deallocate the shared memory segment. */
shmctl(segment_id, IPC_RMID, 0);
return 0;
}
shared memory attached at address 0x7fb65aec2000
segment size: 25600
shared memory reattached at address 0x5000000
Hello, world.
$ ipcs -m
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
0x00000000 1627649 user 640 25600 0
-m: 공유 메모리
:: 1627649 shmid인 공유 메모리 세그먼트가 사용중임을 나타냄
semaphores: shared data의 개수
두 개 이상의 프로세스가 동시에 공유 메모리와 같은 공유 자원을 접근할때 동기화를 걸어주는 것이 목표(한번에 여러 프로세스가 접근하여 데이터를 동시에 변경하는 것을 막기 위한 장치)
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
세마포어 식별자를 얻는데 쓰이는 시스템콜
세마포어는 집합이라 같은 집합에 속하여 index로 구분된다.
보통은 한 집합에 1개의 세마포어를 사용
int semget(key_t key, int nsems, int semflg);
key: 세마포어를 식별하는 키
nsems: 세마포어 자원의 갯수
semflg: 세마포어 동작옵션
return 성공시 세마포어 식별자
세마포어를 제어할 수 있는 시스템콜
int semctl(int semid, int semnum, int cmd, ...);
semid: 세마포어의 식별자
semnum: semaphore 집합에서 표현되는 일종의 인덱스
cmd: 세마포어를 제어할 수 있는 command(이것에 따라 semctl이 3개의 인자를 갖느냐, 4개의 인자를 갖느냐가 결정)
cmd 내용 GETVAL 세마포어의 현재 값을 구한다. GETPID 세마포어에 가장 최근에 접근했던 프로세스의 프로세스 ID를 구한다. GETNCNT 세마포어 값이 증가하기를 기다리는 프로세스의 개수 GETZCNT 세마포어 값이 0 이 되기를 기다리는 프로세스의 개수 GETALL 세마포어 집합의 모든 세마포어 값을 구한다. SETVAL 세마포어 값을 설정 SETALL 세마퍼어 집합의 모든 세마포어 값을 설정 IPC_STAT 세마포어의 정보를 구한다. IPC_SET 세마포어의 소유권과 접근 허가를 설정 IPC_RMID 세마포어 집합을 삭제
union semun: cmd에 의해 4번째 인자가 쓰일때 여러분이 작성하는 프로그램에서는 아래의 union을 정의해주어야함
union semun {
int val; // Value for SETVAL
struct semid_ds *buf; // Buffer for IPC_STAT, IPC_SET
unsigned short *array; // Array for GETALL, SETALL
struct seminfo *__buf; // Buffer for IPC_INFO (Linux-specific)
};
//
//semun에 멤버 semid_ds 구조체
struct semid_ds {
struct ipc_perm sem_perm; // Ownership and permissions
time_t sem_otime; // Last semop() time
time_t sem_ctime; // Last change time
unsigned long sem_nsems; // No. of semaphores in set
};
return 성공시 0, 실패시 -1
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/types.h>
#include <stdio.h>
/* We must define union semun ourselves. */
union semun {
int val;
struct semid_ds *buf;
unsigned short int *array;
struct seminfo *__buf;
};
/* Obtain a binary semaphore's ID, allocating if necessary. */
int binary_semaphore_allocation(key_t key, int sem_flags){
return semget(key, 1, sem_flags);
}
/* Deallocate a binary semaphore. All users must have finished their use. Returns -1 on failure. */
//명시적으로 할당 해제해야함
int binary_semaphore_deallocate(int semid){
union semun ignored_argument;
return semctl(semid, 1, IPC_RMID, ignored_argument);
}
int main(){
int semid;
semid = binary_semaphore_allocation(IPC_PRIVATE, IPC_CREAT|0660);
if(semid == -1)
printf("semget error\n");
int del = binary_semaphore_deallocate(semid);
if(del == -1)
printf("semctl error\n");
else if(del == 0)
printf("semctl success\n");
}
세마포어를 할당하고 초기화하는 것은 두 개의 별개의 작업이다.
바이너리 세마포어 초기화
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
// We must define union semun ourselves.
union semun {
int val;
struct semid_ds *buf;
unsigned short int *array;
struct seminfo *__buf;
};
// 값이 1인 이진 세마포를 초기화
int binary_semaphore_initialize (int semid){
union semun argument;
unsigned short values[1];
values[0] = 1;
argument.array = values;
return semctl(semid, 0, SETALL, argument);
//int semctl(int semid, int semnum, int cmd, ...);
}
//네 번째 인수의 경우 합집합 반달 객체를 작성하고 해당 배열 필드를 부호 없는 짧은 값의 배열로 지정
세마포어의 값을 증가, 감소시킴으로써 크리티컬 섹션 전 후에 사용
int semop(int semid, struct sembuf *spos, size_t nsops);
struct sembuf{
unsigned short sem_num; //sem_num은 작업이 수행되는 세마포어 집합의 세마포어 번호
short sem_op; // 세마포어 작업을 지정하는 정수
//양수이면 해당 숫자는 즉시 세마포어 값에 추가
//음수이면, 그 숫자의 절대값은 세마포어 값에서 차감
//만약 이것이 세마포어 값을 음의 값으로 만든다면, 세마포어 값이 세마포어의 절대값만큼 커질 때까지 호출은 차단
//0이면 세마포어 값이 0이 될 때까지 작업이 차단
short sem_flg; // operation flags
};
nsops: 변경하려는 세마포어 개수로 변경하려는 세마포어 개수가 여러 개일 때 사용
n개의 세마포어 옵션들
ex) Wait and Post Operations for a Binary Semaphore
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
// 세마포어 값이 양수가 될 때까지 차단한 다음 1만큼 줄임
int binary_semaphore_wait (int semid){
struct sembuf operations[1];
//0이면 세마포어 값이 0이 될 때까지 작업이 차단
operations[0].sem_num = 0;
//음수이면, 그 숫자의 절대값은 세마포어 값에서 차감
operations[0].sem_op = -1;
//프로세스가 종료될 때 리눅스는 자동으로 세마포어의 작업을 실행 취소
operations[0].sem_flg = SEM_UNDO;
return semop(semid, operations, 1);
}
//이진 세마포어의 값을 1씩 증가시킴, 즉시 반환
int binary_semaphore_post (int semid){
struct sembuf operations[1];
//0이면 세마포어 값이 0이 될 때까지 작업이 차단
operations[0].sem_num = 0;
//양수이면 해당 숫자는 즉시 세마포어 값에 추가
operations[0].sem_op = 1;
//프로세스가 종료될 때 리눅스는 자동으로 세마포어의 작업을 실행 취소
operations[0].sem_flg = SEM_UNDO;
return semop(semid, operations, 1);
}
$ ipcrm sem 5790517
식별자가 5790517인 세마포어 집합을 제거하려면 이 명령어 사용
매핑된 메모리는 파일과 프로세스의 메모리 사이의 연결을 형성합니다. 리눅스는 파일을 페이지 크기로 나눈 다음 가상 메모리 페이지로 복사하여 프로세스의 주소 공간에서 사용할 수 있도록 한다.따라서 프로세스는 일반적인 메모리 액세스로 파일의 내용을 읽을 수 있습니다. 메모리에 기록하여 파일 내용을 수정할 수도 있습니다.이렇게 하면 파일에 빠르게 액세스할 수 있습니다.
매핑된 메모리는 버퍼를 할당하여 파일의 전체 내용을 보관한 다음 파일을 버퍼로 읽어들이고 버퍼가 수정된 경우 버퍼를 나중에 파일에 다시 쓰는 것이라고 생각할 수 있습니다. Linux는 파일 읽기 및 쓰기를 처리합니다.
일반 파일을 프로세스의 메모리에 매핑하려면 mmap("메모리 MAPped", "em-map") 호출을 사용합니다.첫 번째 인수는 리눅스가 프로세스의 주소 공간에 파일을 매핑할 주소입니다. NULL 값을 사용하면 리눅스가 사용 가능한 시작 주소를 선택할 수 있습니다.두 번째 인수는 맵의 길이(바이트)입니다.세 번째 인수는 매핑된 주소 범위에 대한 보호를 지정합니다.보호는 각각 읽기, 쓰기 및 실행 권한에 해당하는 PROT_READ, PROT_WRITE 및 PROTECT_EXEC의 비트 "또는"로 구성됩니다.네 번째 논쟁은
추가 옵션을 지정하는 플래그 값입니다.다섯 번째 인수는 매핑될 파일에 열려 있는 파일 설명자이다.마지막 인수는 맵을 시작할 파일의 시작부터의 오프셋입니다.파일 전체 또는 일부를 메모리에 매핑할 수 있습니다.
시작 간격띄우기 및 길이를 적절하게 선택함으로써.
#include <sys/mmap.h>
void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
start: 파일을 매핑할 주소
length: 매핑할 길이
prot: 실행 권한, PROT_READ, PROT_WRITE, PROT_EXEC, PROT_NONE
flags:
mmap-write 프로그램은 파일을 열어 이전에 존재하지 않았던 파일을 만듭니다.그
열 세 번째 인수는 읽기 및 쓰기를 위해 파일이 열려 있음을 지정합니다. 파일의 길이를 모르기 때문에 lsek를 사용하여 파일이 정수를 저장할 수 있을 만큼 충분히 큰지 확인한 다음 파일 위치를 다시 시작 부분으로 이동합니다. 더 이상 필요하지 않기 때문에 프로그램은 파일을 매핑한 다음 파일 설명자를 닫습니다.프로그램은 매핑된 메모리에 임의의 정수를 쓰고, 따라서 파일을 쓰고, 메모리를 언매칭한다.프로그램이 종료될 때 리눅스가 자동으로 파일의 매핑을 해제하므로 문맵 호출은 불필요합니다.
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>
#define FILE_LENGTH 0x100
/* Return a uniformly random number in the range [low,high]. */
int random_range(unsigned const low, unsigned const high){
unsigned const range = high - low + 1;
return low + (int) (((double) range) * rand () / (RAND_MAX + 1.0));
}
int main(int argc, char* const argv[]){
int fd;
void* file_memory;
//void srand(unsigned int) 형태이다. 리턴타입은 없으며,
//전달인자로 주는 unsigned int 값을 seed라 부른다.
//즉 랜덤한 난수를 생성하려는 seed값을 결정해주면 된다.
//time에 null을 넣어주면 1970 ~ 부터의 경과시간을 돌려준다.
//따라서 항상 다른 값이 나옴
srand(time(NULL));
//이전에 존재하지 않던 파일 생성
//부호 없는 정수를 저장할 수 있을 정도의 크기인지 확인
fd = open(argv[1], O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
//lseek로 충분히 큰지 확인
lseek(fd, FILE_LENGTH+1, SEEK_SET);
write(fd, "", 1);
//파일 위치를 다시 시작부분으로 이동
lseek(fd, 0, SEEK_SET);
/* Create the memory mapping. */
file_memory = mmap(0, FILE_LENGTH, PROT_WRITE, MAP_SHARED, fd, 0);
close (fd);
//매핑된 메모리에 임의의 정수 write
sprintf((char*) file_memory, "%d\n", random_range (-100, 100));
//프로그램이 종료될 때 리눅스가 자동으로 파일의 매핑을 해제하므로 문맵 호출은 불필요
munmap (file_memory, FILE_LENGTH);
return 0;
}
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#define FILE_LENGTH 0x100
int main (int argc, char* const argv[]){
int fd;
void* file_memory;
int integer;
/* Open the file. */
fd = open(argv[1], O_RDWR, S_IRUSR | S_IWUSR);
/* Create the memory mapping. */
file_memory = mmap(0, FILE_LENGTH, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
close(fd);
//파일의 숫자를 읽은 다음 두배의 값을 파일에 작성
sscanf(file_memory, "%d", &integer);
printf("value: %d\n", integer);
sprintf((char*) file_memory, "%d\n", 2 * integer);
//프로그램이 종료될 때 리눅스가 자동으로 파일의 매핑을 해제하므로 문맵 호출은 불필요
munmap(file_memory, FILE_LENGTH);
return 0;
}
메모리 맵에 데이터를 갱신해도 바로 파일과 동기화가 이루어지는 것은 아니다. 커널이 여유있을 때 동기화 수행
그렇기 때문에 개발자가 직접 동기화를 보장하고 싶을 때 사용
int msync(void *start, size_t length, int flags);
start: mmap을 통해 리턴 받은 메모리 맵의 시작 주소
length: 동기화할 길이
flags:
공유 메모리 세그먼트와 마찬가지로 메모리 매핑 영역은 둘 이상의 프로세스가 한번에 매핑된 메모리에 접근하는 것을 방지하기 위해 세마포어 또는 fcntl 사용될 수 있다.
MAP_Private를 mmap로 지정하면 쓰기 시 복사 영역이 생성된다. 영역에 대한 모든 쓰기는 이 프로세스의 메모리에만 반영되며, 동일한 파일을 매핑하는 다른 프로세스에서는 변경 사항을 볼 수 없다. 모든 프로세스가 공유하는 페이지에 직접 쓰는 대신 이 페이지의 개인 복사본에 쓴다.이후 프로세스에 의한 모든 읽기 및 쓰기는 이 페이지를 사용
한 가지 일반적인 용도는 읽기와 쓰기를 대체하는 것
일부 프로그램의 경우 이 방법이 명시적 파일 I/O 작업보다 더 편리하고 빠르게 실행될 수 있음
메모리 매핑된 파일에 데이터 구조(예를 들어, 일반 구조 인스턴스)를 구축하는 것
이후 호출 시 프로그램은 파일을 메모리에 매핑하고 데이터 구조는 이전 상태로 복원됨
그러나 이러한 데이터 구조의 포인터들은 그들이 모두 메모리의 매핑된 동일한 영역 내에서 가리키고 파일이 원래 점유했던 동일한 주소 영역으로 다시 매핑되도록 주의를 기울이지 않는 한 유효하지 않다.
특수 /dev/zero 파일을 메모리에 매핑하는 것입니다. 0바이트의 소스가 필요한 프로그램은 /dev/zero 파일을 mmap할 수 있습니다.
int pipe_fds[2];
int read_fd;
int write_fd;
pipe(pipe_fds);
read_fd = pipe_fds[0];
write_fd = pipe_fds[1];
파이프 호출은 해당 프로세스와 하위 프로세스 내에서만 유효한 fd 만듬
프로세스의 fd는 관련 없는 프로세스로 전달할 수 없지만 fork를 호출할 때 fd는 child process에 복사됨
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
//각각 1초씩 중지
void writer(const char* message, int count, FILE* stream){
for(; count > 0; --count) {
//stream에 message 작성
fprintf(stream, "%s\n", message);
fflush(stream);
sleep(1);
}
}
/* Read random strings from the stream as long as possible. */
//stream에서 임의의 문자열을 가능한 오래 읽음
void reader (FILE* stream){
char buffer[1024];
//stream이 끝날 때까지 읽음, 줄바꿈이나 EOF까지
while (!feof (stream) && !ferror (stream) && fgets (buffer, sizeof (buffer), stream) != NULL)
fputs (buffer, stdout);
}
int main (){
int fds[2];
pid_t pid;
//pipe 생성
pipe(fds);
/* Fork a child process. */
pid = fork();
if (pid == (pid_t) 0) {
FILE* stream;
//write fd 닫음
close (fds[1]);
/* Convert the read file descriptor to a FILE object, and read from it. */
//read fd에서 읽음
stream = fdopen (fds[0], "r");
reader (stream);
close (fds[0]);
}
else {
/* This is the parent process. */
FILE* stream;
//read fd 닫음
close (fds[0]);
//fd에 write함
stream = fdopen(fds[1], "w");
writer("Hello, world.", 5, stream);
close (fds[1]);
}
return 0;
}
int dup(int fd);
int dup2(int fd1, int fd2);
fd를 복제하는 함수
dup() 호출 성공시 가장 낮은 fd 반환, 실패시 -1
dup2() 호출 성공시 fd2를 새 fd값으로 지정, 실패시 -1
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int main(){
int fds[2];
pid_t pid;
//creat pipe
pipe(fds);
// Fork a child process
pid = fork ();
if(pid == (pid_t) 0) {
//write fd close
close(fds[1]);
//read fd connect stdin
//dup2는 fds[0] 서술자 값을 두번째 인자로 설정
dup2(fds[0], STDIN_FILENO);
//파일 내부 텍스트 정렬함, default 알파벳 오름차순
execlp("sort", "sort", 0);
}
else {
/* This is the parent process. */
FILE* stream;
//read fd close
close(fds[0]);
/* Convert the write file descriptor to a FILE object, and write to it. */
//write fd 에 작성
stream = fdopen(fds[1], "w");
fprintf(stream, "This is a test.\n");
fprintf(stream, "Hello, world.\n");
fprintf(stream, "My dog has fleas.\n");
fprintf(stream, "This program is great.\n");
fprintf(stream, "One fish, two fish.\n");
fflush(stream);
close (fds[1]);
//pid인 child process 종료까지 대기
waitpid(pid, NULL, 0);
}
return 0;
}
파이프의 일반적인 용도는 하위 프로세스에서 실행 중인 프로그램으로 데이터를 보내거나 받는 것
popen 및 pclose 함수는 pipe(), fork(), dup2(), exec(), fdopen()을 호출할 필요가 없음
FILE *popen(const char *command, const char *type);
command: 실행할 명령어
type:
"r": 읽기용
"w": 쓰기용
return 성공시 FILE 포인터, 실패시 NULL
int pclose(FILE *stream);
return 성공시 -1아닌값, 실패시 -1
#include <stdio.h>
#include <unistd.h>
int main (){
FILE* stream = popen("sort", "w");
fprintf(stream, "This is a test.\n");
fprintf(stream, "Hello, world.\n");
fprintf(stream, "My dog has fleas.\n");
fprintf(stream, "This program is great.\n");
fprintf(stream, "One fish, two fish.\n");
return pclose (stream);
}