Bài 41.128 phút đọc
File descriptor và open, read, write
Sau bài này bạn sẽ làm được
- Giải thích file descriptor là gì
- Dùng open với đúng cờ và quyền
- Viết vòng lặp đọc và ghi đầy đủ xử lý partial
- Kiểm tra giá trị trả về của close
Trong Unix, mọi thứ là tệp: tệp thường, thư mục, ống, socket, thiết bị, terminal. Và mọi tệp được thao tác qua bốn lời gọi tầng thấp: open, read, write, close. Chương này chạy được trên bản MinGW, nên các bản ghi là kết quả thật.
#File descriptor
File descriptor
Một số nguyên nhỏ, chỉ mục vào bảng tệp mở của tiến trình. Bạn
open một tệp và nhận về một số; mọi thao tác sau đó dùng số đó. Ba số đầu tiên có sẵn khi chương trình khởi động.#define STDIN_FILENO 0 /* dau vao chuan */
#define STDOUT_FILENO 1 /* dau ra chuan */
#define STDERR_FILENO 2 /* dau ra loi chuan */
/* Khi chuong trinh khoi dong, ba fd nay da mo san, thuong deu
tro toi terminal. open() tra ve fd nho nhat con TRONG, nen
file dau tien ban mo thuong la fd 3. */#open
open và các cờ
#include <fcntl.h>
int open(const char *path, int flags, ... /* mode_t mode */);
int fd = open("data.txt", O_RDONLY); /* chi doc */
int fd = open("out.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644); /* ghi, tao, xoa cu */
int fd = open("log.txt", O_WRONLY | O_CREAT | O_APPEND, 0644); /* ghi noi cuoi */
if (fd < 0) { perror("open"); return 1; }| Cờ | Ý nghĩa |
|---|---|
| O_RDONLY / O_WRONLY / O_RDWR | Chế độ truy cập, chọn đúng một |
| O_CREAT | Tạo nếu chưa có, cần tham số mode |
| O_TRUNC | Xoá sạch nội dung cũ |
| O_APPEND | Luôn ghi vào cuối, nguyên tử khi nhiều tiến trình ghi |
| O_EXCL | Với O_CREAT: lỗi nếu tệp đã có, chống đua tạo |
| O_NONBLOCK | Không chặn |
| O_CLOEXEC | Tự đóng fd khi exec, chống rò rỉ sang tiến trình con |
#read và write
Chữ ký và giá trị trả về
#include <unistd.h>
ssize_t read (int fd, void *buf, size_t n);
/* tra ve so byte DOC DUOC, 0 = het tep (EOF), -1 = loi */
ssize_t write(int fd, const void *buf, size_t n);
/* tra ve so byte GHI DUOC */#Vòng lặp đầy đủ
Hai mẫu phải thuộc
#include <unistd.h>
#include <errno.h>
/* Ghi DAY DU n byte */
ssize_t ghi_day_du(int fd, const void *buf, size_t n) {
const char *p = buf;
size_t con_lai = n;
while (con_lai > 0) {
ssize_t w = write(fd, p, con_lai);
if (w < 0) {
if (errno == EINTR) continue; /* bi tin hieu ngat -> thu lai */
return -1; /* loi that */
}
p += w;
con_lai -= (size_t)w;
}
return (ssize_t)n;
}
/* Doc DAY DU n byte (hoac toi EOF) */
ssize_t doc_day_du(int fd, void *buf, size_t n) {
char *p = buf;
size_t con_lai = n;
while (con_lai > 0) {
ssize_t r = read(fd, p, con_lai);
if (r < 0) { if (errno == EINTR) continue; return -1; }
if (r == 0) break; /* EOF */
p += r;
con_lai -= (size_t)r;
}
return (ssize_t)(n - con_lai);
}mycat.c, tự viết cat bằng open/read/write
int main(int argc, char **argv) {
if (argc < 2) { fprintf(stderr, "dung: mycat <tep>\n"); return 1; }
int fd = open(argv[1], O_RDONLY);
if (fd < 0) { perror("open"); return 1; }
char buf[4096];
ssize_t n;
while ((n = read(fd, buf, sizeof buf)) > 0)
if (ghi_day_du(STDOUT_FILENO, buf, (size_t)n) < 0) {
perror("write");
return 1;
}
if (n < 0) perror("read");
close(fd);
return 0;
}terminal
gcc -O2 -Wall -Wextra mycat.c -o mycat.exe
printf 'dong mot\ndong hai\ndong ba\n' > thu.txt && ./mycat.exe thu.txt
dong mot dong hai dong ba
#close
Kiểm tra close, và rò rỉ mô tả tệp
if (close(fd) < 0)
perror("close"); /* loi ghi dia co the lo ra o day, khong phai o write */
/* Vi sao kiem tra close: voi ghi co dem, du lieu co the con nam
trong bo dem cua nhan luc write tra ve. Loi ghi dia (het cho,
dia hong) co the chi lo ra khi nhan XA du lieu do, tuc luc close.
Nen bo qua ket qua close la co the mat du lieu ma khong biet. */Tự làm thử
- Tự viết
catbằngopen,read,writevà chạy nó trên một tệp. - Tự viết
cpsao chép tệp, xử lý đầy đủ đọc và ghi từng phần. - Bỏ vòng lặp
ghi_day_duvà thay bằng một lời gọiwrite, rồi copy một tệp lớn qua một ống chậm để thấy nó thiếu byte. - Mở một tệp với
O_CREATmà không đưa tham sốmodevà xem quyền tệp tạo ra. - Mở một trăm tệp mà không đóng và quan sát
openthất bại vớiEMFILE. - Ghi vào một tệp trên phân vùng đầy và kiểm tra lỗi lộ ra ở
writehayclose.
Trình chấm điểm tự động sẽ được bổ sung ở giai đoạn sau. Hiện tại bạn tự chạy thử trên máy.
Tóm tắt
- File descriptor là số nguyên nhỏ chỉ vào bảng tệp mở của nhân; 0, 1, 2 là stdin, stdout, stderr.
opennhận cờ chọn chế độ và hành vi; tham sốmodechỉ dùng khiO_CREATtạo tệp mới.readvàwritecó thể xử lý ít hơn số byte yêu cầu; luôn dùng vòng lặp đầy đủ.EINTRnghĩa là bị tín hiệu ngắt, không phải lỗi; gọi lại.- Luôn kiểm tra
close(lỗi ghi có thể lộ ở đó) và đóng fd ở mọi nhánh để không rò rỉ.