一、基礎概念
什麼是文件系統?
文件系統,即 File System,是指計算機使用的一類存儲媒介的物理組織和邏輯管理方式,它維護了對存儲媒介的訪問和管理。
那麼什麼是 Linux 文件系統?
Linux 文件系統是在 Linux 操作系統上,對存儲設備進行的一種邏輯組織形式。Linux 文件系統一般由三部分構成:超級塊、索引節點(inode)、數據塊。其中超級塊記錄著文件系統的元信息;索引節點記錄著文件和目錄的元數據;數據塊儲存著文件和目錄的實際內容。
Linux 文件系統中的 I/O 是什麼?
I/O 即 Input/Output,也就是 Linux 文件系統中的讀寫操作。讀操作是指從存儲設備中讀取數據到內存中,而寫操作則是將內存中的數據寫入到存儲設備中。I/O 操作是 Linux 文件系統中最基本的操作方式。
二、I/O 操作方式
Linux 文件系統中的 I/O 操作可以分為同步和非同步兩種方式。
1. 同步 I/O
同步 I/O 是指應用程序在執行 I/O 操作時,必須等待 I/O 操作結束後才能繼續執行後續的操作。同步 I/O 的特點是操作可靠,但一旦發生阻塞,就會嚴重影響應用程序的性能。
#include
#include
#include
#include
int main(void)
{
int fd;
char buf[1024];
fd = open("test.txt", O_RDONLY);
if (fd == -1) {
printf("File open error!\n");
return -1;
}
read(fd, buf, 1024);
printf("%s", buf);
close(fd);
return 0;
}
2. 非同步 I/O
非同步 I/O 是指應用程序在執行 I/O 操作時,無須等待 I/O 操作結束,可以先執行後續的操作,待 I/O 操作完成後再執行回調函數。非同步 I/O 的特點是操作效率高,但開發難度較大。
#include
#include
#include
#include
#include
void handle_read(int signum)
{
printf("Asynchronous I/O operation completed!\n");
}
int main(void)
{
int fd;
char buf[1024];
struct sigaction sa;
fd = open("test.txt", O_RDONLY);
if (fd == -1) {
printf("File open error!\n");
return -1;
}
sa.sa_flags = 0;
sa.sa_handler = handle_read;
sigemptyset(&sa.sa_mask);
if (sigaction(SIGIO, &sa, NULL) == -1) {
printf("Signal handler registration error!\n");
return -1;
}
fcntl(fd, F_SETOWN, getpid());
fcntl(fd, F_SETFL, O_ASYNC);
while (1) {
sleep(1);
}
close(fd);
return 0;
}
三、I/O 多路復用
I/O 多路復用是指讓一個或多個進程能夠同時監聽多個文件描述符的可讀可寫狀態,從而實現多路 I/O 服務。在 Linux 文件系統中,I/O 多路復用主要有 select、poll 和 epoll 三種機制。
1. select
select 是最早實現 I/O 多路復用的方法之一。通過 select 實現 I/O 多路復用需要將待監控的文件描述符加入到 fd_set 集合中,並設置超時時間。select 函數將會阻塞進程,直到有文件描述符發生變化或超時。select 函數會返回發生變化的文件描述符的個數。
#include
#include
#include
#include
#include
int main()
{
fd_set rfds;
struct timeval tv;
int retval;
FD_ZERO(&rfds);
FD_SET(0, &rfds);
tv.tv_sec = 5;
tv.tv_usec = 0;
retval = select(1, &rfds, NULL, NULL, &tv);
if (retval == -1) {
printf("select error!\n");
exit(EXIT_FAILURE);
} else if (retval) {
printf("Data is available now.\n");
} else {
printf("No data within five seconds.\n");
}
return 0;
}
2. poll
poll 是比 select 更加高效的 I/O 多路復用方法之一。poll 的實現方式與 select 類似,但其使用鏈表來存儲被監控的文件描述符集,解決了 select 方法中文件描述符數量的限制問題。
#include
#include
#include
#include
int main()
{
struct pollfd fds[1];
int retval;
fds[0].fd = STDIN_FILENO;
fds[0].events = POLLIN;
retval = poll(fds, 1, 5000);
if (retval == -1) {
printf("poll error!\n");
exit(EXIT_FAILURE);
} else if (retval) {
printf("Data is available now.\n");
} else {
printf("No data within five seconds.\n");
}
return 0;
}
3. epoll
epoll 是 Linux 的高級 I/O 多路復用方法。epoll 通過 epoll_create 函數創建 epoll 實例,然後使用 epoll_ctl 函數來向 epoll 中添加、刪除、修改被監控的文件描述符。epoll_wait 函數則是阻塞等待文件描述符狀態變化的發生,並返回變化的文件描述符集。
#include
#include
#include
#include
#define MAX_EVENTS 10
int main(int argc, char *argv[])
{
int sockfd;
struct epoll_event event;
struct epoll_event event_array[MAX_EVENTS];
int epoll_fd, nfds, i;
sockfd = create_and_bind("8080");
if (sockfd == -1) exit(EXIT_FAILURE);
if (make_socket_non_blocking(sockfd) == -1) exit(EXIT_FAILURE);
if (listen(sockfd, SOMAXCONN) == -1) exit(EXIT_FAILURE);
epoll_fd = epoll_create1(0);
if (epoll_fd == -1) exit(EXIT_FAILURE);
event.data.fd = sockfd;
event.events = EPOLLIN | EPOLLET;
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, sockfd, &event) == -1) exit(EXIT_FAILURE);
while (1) {
nfds = epoll_wait(epoll_fd, event_array, MAX_EVENTS, -1);
for (i = 0; i < nfds; i++) {
if (event_array[i].events & EPOLLIN) {
if (event_array[i].data.fd == sockfd) {
while (1) {
int conn_sock = accept(sockfd, NULL, NULL);
if (conn_sock == -1) {
if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) {
break;
} else {
perror("accept");
break;
}
}
make_socket_non_blocking(conn_sock);
event.data.fd = conn_sock;
event.events = EPOLLIN | EPOLLET;
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, conn_sock, &event) == -1) exit(EXIT_FAILURE);
}
} else {
do_use_fd(event_array[i].data.fd);
}
}
}
}
close(sockfd);
return 0;
}
四、文件映射技術
文件映射(Memory Mapping)是指操作系統將文件的一段內容直接映射到進程的地址空間,使得進程可以直接訪問文件內容,從而加快了文件 I/O 的速度。在 Linux 文件系統中,文件映射可以使用 mmap 函數來實現。
#include
#include
#include
#include
#include
#include
#include
int main(int argc, char *argv[])
{
int fd;
char *mapped;
struct stat file_stat;
fd = open(argv[1], O_RDONLY);
if (fd == -1) exit(EXIT_FAILURE);
if (fstat(fd, &file_stat) == -1) exit(EXIT_FAILURE);
mapped = mmap(NULL, file_stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
if (mapped == MAP_FAILED) exit(EXIT_FAILURE);
printf("%s", mapped);
if (munmap(mapped, file_stat.st_size) == -1) exit(EXIT_FAILURE);
close(fd);
return 0;
}
原創文章,作者:FABYO,如若轉載,請註明出處:https://www.506064.com/zh-tw/n/370137.html
微信掃一掃
支付寶掃一掃