Context Switching이란 커널 모드와 사용자 모드 간의 전환을 의미합니다.
현재 실행 중인 사용자 프로세스 (user mode) -> 커널 코드 (kernel mode)로 전환하거나 그 반대로 바뀌는 것을 말하는데 이 전환은 OS 레벨에서 이루어집니다.
예를 들어 read 호출 시 사용자 코드가 실행되며 시스템 콜이 발생하고, user mode에서 kernel mode로 전환되게 됩니다.
커널 에서 해당 fd를 확인하고 디스크로부터 실제 데이터를 읽고 커널 내부 버퍼에 복사를 수행합니다.
커널은 내부 함수를 사용하여 사용자 공간 버퍼로 데이터를 복사한뒤 작업이 끝나면 Kernel mode에서 User Mode로 돌아갑니다.
따라서 read, write는 2번의 context switch가 발생하여 대용량 데이터나, 빈도가 높은 I/O환경에서는 성능 병목이 발생할 수 있습니다.
반면, senfile()은 zero copy방식을 사용하여 커널 내부 버퍼에서 복사 대상으로 바로 파일을 전송하여 2개의 시스템 콜을 사용하는 read/write와는 달리 1개의 systemcall 만을 사용하여 동작합니다.
read()는 데이터를 사용자 공간으로 복사하는 구조이고, sendfile()은 사용자 공간으로 데이터를 복사하지 않고 커널 공간 내에서만 데이터를 이동시켜 사용자 공간으로의 복사가 0번 이루어 지기 떄문에 zero-copy 흐름이라고 부릅니다.
1. read/write 코드 예시
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#define BUF_SIZE 4096
int main() {
int fd_in = open("source_file", O_RDONLY);
int fd_out = open("dest_file", O_WRONLY | O_CREAT | O_TRUNC, 0644);
char buffer[BUF_SIZE];
ssize_t bytes;
if (fd_in < 0 || fd_out < 0) {
perror("open");
return 1;
}
while ((bytes = read(fd_in, buffer, BUF_SIZE)) > 0) {
if (write(fd_out, buffer, bytes) != bytes) {
perror("write");
return 1;
}
}
close(fd_in);
close(fd_out);
return 0;
}
2. sendfile() 코드 예시
#include <fcntl.h>
#include <unistd.h>
#include <sys/sendfile.h>
#include <sys/stat.h>
#include <stdio.h>
int main() {
int fd_in = open("source_file", O_RDONLY);
int fd_out = open("dest_file", O_WRONLY | O_CREAT | O_TRUNC, 0644);
struct stat stat_buf;
if (fd_in < 0 || fd_out < 0) {
perror("open");
return 1;
}
if (fstat(fd_in, &stat_buf) < 0) {
perror("fstat");
return 1;
}
off_t offset = 0;
ssize_t sent = sendfile(fd_out, fd_in, &offset, stat_buf.st_size);
if (sent < 0) {
perror("sendfile");
return 1;
}
close(fd_in);
close(fd_out);
return 0;
}
그 외 최신 방법
- splice 방식 (파일 - 파일 복사 (파이프 필요) )
: 파이프를 중간버퍼로 사용하여 커널 내부에서 데이터를 이동하는 방식으로 사용 버퍼의 크기의 한계가 있다는 단점이 있다.
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/stat.h>
#define BUF_SIZE 4096
int main() {
int fd_in = open("source_file", O_RDONLY);
int fd_out = open("dest_file", O_WRONLY | O_CREAT | O_TRUNC, 0644);
int pipefd[2];
if (fd_in < 0 || fd_out < 0) {
perror("open");
return 1;
}
if (pipe(pipefd) < 0) {
perror("pipe");
return 1;
}
while (1) {
ssize_t n = splice(fd_in, NULL, pipefd[1], NULL, BUF_SIZE, 0);
if (n == 0) break; // EOF
if (n < 0) {
perror("splice in");
return 1;
}
ssize_t m = splice(pipefd[0], NULL, fd_out, NULL, n, 0);
if (m < 0) {
perror("splice out");
return 1;
}
}
close(fd_in);
close(fd_out);
close(pipefd[0]);
close(pipefd[1]);
return 0;
}
- copy_file_range (파일 -> 파일)
: 커널 내에서 파일 간 직접 복사를 하는 방식으로 파일과 파일을 직접 복사하는 방식
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#define BUF_SIZE 4096
int main() {
int fd_in = open("source_file", O_RDONLY);
int fd_out = open("dest_file", O_WRONLY | O_CREAT | O_TRUNC, 0644);
off_t offset_in = 0, offset_out = 0;
ssize_t bytes;
if (fd_in < 0 || fd_out < 0) {
perror("open");
return 1;
}
while ((bytes = copy_file_range(fd_in, &offset_in, fd_out, &offset_out, BUF_SIZE, 0)) > 0) {
// loop continues
}
if (bytes < 0) {
perror("copy_file_range");
return 1;
}
close(fd_in);
close(fd_out);
return 0;
}
'C++' 카테고리의 다른 글
12. [C++] atomic/ mutex/ spin lock (0) | 2025.06.15 |
---|---|
11. [C++] 파티션 메모리 복사 (dd 명령어, 파일 입출력) (0) | 2025.06.03 |
10. [C++] 가중치가 없는 그래프에서 사이클 탐지 및 지름 구하기 (0) | 2025.06.03 |
09. [C++] 벡터와 리스트에서의 erase/remove 사용법 (0) | 2025.06.01 |
08. C++_string class와 관련 기능 (0) | 2024.06.16 |