在 Linux 系統下,IO(輸入/輸出)操作是程序與外部設備(如文件、網絡等)交互的重要方式。Linux 提供了豐富的系統調用和庫函數來支持各種 IO 操作。以下是對 Linux 下 IO 操作的詳細解析,包括文件 IO、網絡 IO 和緩沖機制等內容。
一、文件 IO 操作
1. 打開文件(open
?系統調用)
open
系統調用用于打開一個文件,返回一個文件描述符(file descriptor
),后續操作都通過這個文件描述符進行。
#include <fcntl.h>
#include <unistd.h>
#include <iostream>int main() {int fd = open("example.txt", O_RDONLY); // 打開文件用于只讀if (fd == -1) {perror("open failed");return 1;}std::cout << "File opened successfully, file descriptor: " << fd << std::endl;close(fd); // 關閉文件return 0;
}
-
O_RDONLY
:只讀模式。 -
O_WRONLY
:只寫模式。 -
O_RDWR
:讀寫模式。 -
O_CREAT
:如果文件不存在,則創建文件。 -
O_TRUNC
:如果文件已存在,將其長度截斷為 0。 -
O_APPEND
:寫入時將數據追加到文件末尾。
2. 讀取文件(read
?系統調用)
read
系統調用從文件描述符指定的文件中讀取數據。
#include <fcntl.h>
#include <unistd.h>
#include <iostream>int main() {int fd = open("example.txt", O_RDONLY);if (fd == -1) {perror("open failed");return 1;}char buffer[128];ssize_t bytesRead = read(fd, buffer, sizeof(buffer) - 1);if (bytesRead == -1) {perror("read failed");close(fd);return 1;}buffer[bytesRead] = '\0'; // 確保字符串以 null 結尾std::cout << "Read " << bytesRead << " bytes: " << buffer << std::endl;close(fd);return 0;
}
-
read
返回讀取的字節數,如果返回 0 表示已到達文件末尾,返回 -1 表示出錯。
3. 寫入文件(write
?系統調用)
write
系統調用將數據寫入到文件描述符指定的文件中。
#include <fcntl.h>
#include <unistd.h>
#include <iostream>int main() {int fd = open("output.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);if (fd == -1) {perror("open failed");return 1;}const char* msg = "Hello, world!";ssize_t bytesWritten = write(fd, msg, strlen(msg));if (bytesWritten == -1) {perror("write failed");close(fd);return 1;}std::cout << "Wrote " << bytesWritten << " bytes." << std::endl;close(fd);return 0;
}
-
O_CREAT
:如果文件不存在,則創建文件。 -
0644
:文件權限,表示所有者有讀寫權限,組用戶和其他用戶有讀權限。
4. 關閉文件(close
?系統調用)
close
系統調用用于關閉文件描述符,釋放資源。
close(fd);
二、網絡 IO 操作
1. 創建套接字(socket
?系統調用)
socket
系統調用用于創建一個套接字,用于網絡通信。
#include <sys/types.h>
#include <sys/socket.h>
#include <iostream>int main() {int sockfd = socket(AF_INET, SOCK_STREAM, 0); // 創建 TCP 套接字if (sockfd == -1) {perror("socket failed");return 1;}std::cout << "Socket created successfully, file descriptor: " << sockfd << std::endl;close(sockfd);return 0;
}
-
AF_INET
:IPv4 地址族。 -
SOCK_STREAM
:TCP 套接字。 -
SOCK_DGRAM
:UDP 套接字。
2. 綁定地址(bind
?系統調用)
bind
系統調用將套接字綁定到一個本地地址和端口。
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <iostream>int main() {int sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd == -1) {perror("socket failed");return 1;}struct sockaddr_in addr;addr.sin_family = AF_INET;addr.sin_port = htons(8080); // 端口號addr.sin_addr.s_addr = INADDR_ANY; // 綁定到所有可用地址if (bind(sockfd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {perror("bind failed");close(sockfd);return 1;}std::cout << "Socket bound to port 8080." << std::endl;close(sockfd);return 0;
}
3. 監聽連接(listen
?系統調用)
listen
系統調用將套接字設置為監聽狀態,等待客戶端連接。
if (listen(sockfd, 5) == -1) { // 最大連接隊列長度為 5perror("listen failed");close(sockfd);return 1;
}std::cout << "Server is listening on port 8080." << std::endl;
4. 接受連接(accept
?系統調用)
accept
系統調用接受一個客戶端連接,返回一個新的套接字用于與客戶端通信。
struct sockaddr_in client_addr;
socklen_t client_len = sizeof(client_addr);int clientfd = accept(sockfd, (struct sockaddr*)&client_addr, &client_len);
if (clientfd == -1) {perror("accept failed");close(sockfd);return 1;
}std::cout << "Client connected." << std::endl;
5. 讀寫網絡數據
使用 read
和 write
系統調用(或 recv
和 send
函數)進行網絡數據的讀寫。
char buffer[1024];
ssize_t bytesRead = read(clientfd, buffer, sizeof(buffer) - 1);
if (bytesRead == -1) {perror("read failed");close(clientfd);close(sockfd);return 1;
}buffer[bytesRead] = '\0';
std::cout << "Received message: " << buffer << std::endl;const char* response = "Hello, client!";
write(clientfd, response, strlen(response));
三、緩沖機制
1. 標準 IO 緩沖
C 標準庫提供了緩沖機制,通過 FILE*
指針操作文件。例如:
#include <cstdio>
#include <iostream>int main() {FILE* file = fopen("example.txt", "r");if (!file) {perror("fopen failed");return 1;}char buffer[128];while (fgets(buffer, sizeof(buffer), file)) {std::cout << buffer;}fclose(file);return 0;
}
-
fgets
從文件中讀取一行,自動處理緩沖。 -
fputs
將字符串寫入文件,也使用緩沖機制。
2. 系統調用與緩沖
系統調用(如 read
和 write
)通常不使用緩沖,直接與內核交互。如果需要緩沖,可以手動實現,例如:
char buffer[1024];
ssize_t bytesRead = read(fd, buffer, sizeof(buffer));
3. 非阻塞 IO 和異步 IO
-
非阻塞 IO:通過設置文件描述符為非阻塞模式,使
read
和write
在數據未準備好時立即返回int flags = fcntl(fd, F_GETFL, 0); fcntl(fd, F_SETFL, flags | O_NONBLOCK);
-
異步 IO:使用
aio
或io_uring
等機制,允許程序在 IO 操作完成之前繼續執行。