- Back to Home »
- Beginning Linux Programming »
- Low-Level File Access
Sunday, July 19, 2015
1. File Descriptor
Khi bạn muốn điều khiển một file/device, việc đầu tiên là phải open file/device đó, việc open sẽ trả về cho bạn một con số kiểu int có mối liên hệ với file/device bạn vừa mở, được gọi là File Descriptor. Các thao tác read, write, ioctl, close sau này sẽ thao tác trên file descriptor).
Khi khởi động hệ thống linux thì sẽ có 3 file desciptor mặc định được tạo ra như dưới đây:
+ 0: Standard input (keyboard, mouse)
+ 1: Standard output (screen)
+ 2: Standard error (screen)
Ex: Đọc tên từ bàn phím và print ra màn hình
#!/bin/sh
echo "Type your name !"
read your_name
echo "Your name: $your_name"
$ ./test.sh
Type your name !
Obama
Your name: Obama
2. Low-level File Access
2.1 open
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
int open(const char *path, int oflags); //(1)
int open(const char *path, int oflags, mode_t mode); //(2)
int creat(const char *pathname, mode_t mode); //(3)
+ path
Là đường dẫn đến file (/home/ninhld/Documents/test.txt) hoặc device (/dev/ttyUSB0)
+ oflags
oflags là cờ chỉ ra kiểu truy cập như bảng dưới đây, oflags có thể được kết hợp bởi nhiều kiểu/thuộc tính với nhau thông qua phép OR .
oflags | Description |
---|---|
O_RDONLY | Open for read-only |
O_WRONLY | Open for write-only |
O_RDWR | Open for reading and writing |
Ex:
open("/home/ninhld/Documents/test.txt", O_WRONLY);
open("/dev/ttyUSB0", O_RDWR);
oflags còn được dùng với các giá trị dưới đây để biểu diễn cách thức mở hoặc tạo ra một file mới chưa tồn tại, các cờ sau còn được gọi là file creation flag:
oflags | Description |
---|---|
O_APPEND | Place written data at the end of the file |
O_TRUNC | Set the length of the file to zero, discarding existing contents |
O_CREAT | Creates the file, if necessary, with permissions given in mode |
O_EXCL | Used with O_CREAT , ensures that the caller creates the file. The open is atomic; that is,it’s performed with just one function call. This protects against two programs creating the file atthe same time. If the file already exists, open will fail |
+ mode
mode chỉ ra quyền truy cập - permission khi oflags = O_CREAT
mode | Description |
---|---|
S_IRUSR | Read permission, owner |
S_IWUSR | Write permission, owner |
S_IXUSR | Execute permission, owner |
S_IRGRP | Read permission, group |
S_IWGRP | Write permission, group |
S_IXGRP | Execute permission, group |
S_IROTH | Read permission, others |
S_IWOTH | Write permission, others |
S_IXOTH | Execute permission, others |
Ex:
open ("/home/ninhld/Documents/test.txt", O_CREAT, S_IRUSR|S_IXOTH);
+ return
Trả về file descriptor > 0 nếu open thành công.
Hàm creat tương tự như open với oflags = O_CREAT|O_WRONLY|O_TRUNC
2.2 write
#include <unistd.h>
size_t write(int fildes, const void *buf, size_t nbytes);
+ fildes
File desriptor.
+ buf
Buffer có chưa giá trị để ghi.
+ nbytes
Số byte muốn ghi, nbytes <= sizeof(buf)
+ return
Số byte thực sự được ghi, lưu ý return byte <= nbytes; nếu return về 0 thì không có byte nào được ghi, nếu return về -1 thì có lỗi xảy ra.
simple_write.c
#include <unistd.h>
#include <stdlib.h>
/*
* write string to standard output / error (screen)
*/
int main()
{
if ((write(1, "Here is some data\n", 18)) != 18)
write(2, "A write error has occurred on file descriptor 1\n",46);
exit(0);
}
$ gcc simple_write.c
$ ./a.out
Here is some data
2.3 read
#include <unistd.h>
size_t read(int fildes, void *buf, size_t nbytes);
+ fildes
File desriptor.
+ buf
Buffer để lưu giá trị đọc được.
+ nbytes
Số byte muốn đọc, thường là kích thước của buf (sizeof(buf)).
+ return
Số byte thực sự được đọc, lưu ý return byte <= nbytes
Ex:
simple_read.c
#include <unistd.h>
#include <stdlib.h>
/*
* read from standard input (keyboard)
* write to standard output (screen)
*/
int main()
{
char buffer[128];
int nread;
nread = read(0, buffer, 128);
if (nread == -1)
write(2, "A read error has occurred\n", 26);
if ((write(1,buffer,nread)) != nread)
write(2, "A write error has occurred\n",27);
exit(0);
}
Compile & Execute:
$ gcc simple_read.c
$ ./a.out
hello
hello
2.4 ioctl
#include <unistd.h>
int ioctl(int fildes, int cmd, ...);
+ fildes
File descriptor
+ cmd
Chỉ ra thao tác cần thực hiện với fildes, nó có thể là tổ hợp của nhiều thao tác thông qua phép OR.
+ ...
Các đối số để truyền dữ liệu đi kèm với cmd tương ứng.
ioctl viết tắt của Input Output Control, function này thường được dùng với file desciptor liên hệ với device chứ không dùng với file dữ liệu thông thường.
ioctl là một hàm mở rộng hơn của các hàm read/write để thực hiện thêm nhiều thao tác khác ngoài các thao tác đọc/ghi cơ bản, vì thế ioctl hoàn toàn có thể thay thế đươc read/write. Lưu ý với mỗi cmd được dùng thì dưới device driver cần được implement các thao tác tương ứng với cmd.
Ex:
ioctl(sock_fd, CMD_SET, &data);
2.5 close
#include <unistd.h>
int close(int fildes);
+ fildes
File descriptor cần đóng
+ return
Trả về 0 nếu đóng thành công, trả về -1 nếu xảy ra lỗi
2.6 Ví dụ
copy_system.c
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
int main()
{
char c;
int in, out;
in = open("file.in", O_RDONLY);
out = open("file.out", O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR);
while(read(in,&c,1) == 1)
write(out,&c,1);
exit(0);
}
copy_block.c
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
int main()
{
char block[1024];
int in, out;
int nread;
in = open("file.in", O_RDONLY);
out = open("file.out", O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR);
while((nread = read(in,block,sizeof(block))) > 0)
write(out,block,nread);
exit(0);
}
3. Other System Calls for Managing Files
3.1 lseek
#include <unistd.h>
#include <sys/types.h>
off_t lseek(int fildes, off_t offset, int whence);
+ fildes
File descriptor (thường liên hệ với file chứ không phải device)
+ offset
Khi mở file với hàm open, mặc định con trỏ sẽ trỏ đến vị trí đầu tiên của file; offset (tính bằng số byte) sẽ là vị trí mới của con trỏ sẽ nhảy đến.
+ whence
whence chỉ ra vị trí gọi là mốc để tính offset
+ return
lseek trả về offset tính từ vị trí mốc được xác định bởi whence, hoặc trả về -1 nếu có lỗi xảy ra.
mode | Description |
---|---|
SEEK_SET | offset is an absolute position |
SEEK_CUR | offset is relative to the current position |
SEEK_END | offset is relative to the end of the file |
Ex:
lseek.c
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
int main()
{
int file=0;
if((file=open("testfile.txt",O_RDONLY)) < -1)
return 1;
char buffer[19];
if(read(file,buffer,19) != 19) return 1;
printf("%s\n",buffer);
if(lseek(file,10,SEEK_SET) < 0) return 1;
if(read(file,buffer,19) != 19) return 1;
printf("%s\n",buffer);
return 0;
}
Compile & Execute:
$ gcc lseek.c
$ cat testfile.txt
This is a test file that will be used
to demonstrate the use of lseek.
$ ./a.out
This is a test file
test file that will
3.2 fstat, stat, and lstat
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
int fstat(int fildes, struct stat *buf);
int stat(const char *path, struct stat *buf);
int lstat(const char *path, struct stat *buf);
struct stat:
stat Member | Description |
---|---|
st_mode | File permissions and file-type information |
st_mode | The inode associated with the file |
st_mode | The device the file resides on |
st_uid | The user identity of the file owner |
st_uid | The group identity of the file owner |
st_uid | The time of last access |
st_uid | The time of last change to permissions, owner, group, or content |
st_uid | The time of last modification to contents |
st_uid | The number of hard links to the file |
Lấy thông tin về trạng thái của file
+ fildes
File descriptor liên hệ với file đang mở
+ path
Đường dẫn đến file
+ buf
Cấu trúc để lưu trữ trông tin lấy được bởi fstat
+ return
Trả về giá trị âm nếu có lỗi.
Ex:
stat.c
#include <unistd.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
int main(int argc, char **argv)
{
if(argc != 2)
return 1;
struct stat fileStat;
if(stat(argv[1],&fileStat) < 0)
return 1;
printf("Information for %s\n",argv[1]);
printf("---------------------------\n");
printf("File Size: \t\t%d bytes\n",fileStat.st_size);
printf("Number of Links: \t%d\n",fileStat.st_nlink);
printf("File inode: \t\t%d\n",fileStat.st_ino);
printf("File Permissions: \t");
printf( (S_ISDIR(fileStat.st_mode)) ? "d" : "-");
printf( (fileStat.st_mode & S_IRUSR) ? "r" : "-");
printf( (fileStat.st_mode & S_IWUSR) ? "w" : "-");
printf( (fileStat.st_mode & S_IXUSR) ? "x" : "-");
printf( (fileStat.st_mode & S_IRGRP) ? "r" : "-");
printf( (fileStat.st_mode & S_IWGRP) ? "w" : "-");
printf( (fileStat.st_mode & S_IXGRP) ? "x" : "-");
printf( (fileStat.st_mode & S_IROTH) ? "r" : "-");
printf( (fileStat.st_mode & S_IWOTH) ? "w" : "-");
printf( (fileStat.st_mode & S_IXOTH) ? "x" : "-");
printf("\n\n");
printf("The file %s a symbolic link\n", (S_ISLNK(fileStat.st_mode)) ? "is" : "is not");
return 0;
}
Compile & Execute:
$ gcc stat.c
$ ./a.out stat.c
Information for stat.c
---------------------------
File Size: 1209 bytes
Number of Links: 1
File inode: 3039505
File Permissions: -rw-rw-r--
The file is not a symbolic link
3.3 dup and dup2
#include <unistd.h>
int dup(int fildes);
int dup2(int fildes, int fildes2);
Tạo ra một file descriptor mới cùng liên hệ với file đang mở.
+ fildes
File descriptor nguồn liên hệ với file đang mở.
+ fildes2
File descriptor đích cùng trỏ đến file mà fildes trỏ đến, dữ liệu từ file có liên hệ với fildes2 sẽ chuyển hướng đến file có liên hệ với fildes.
+ return
Trả về file descriptor mới, trả về giá trị âm nếu lỗi.
Ex:
dup.c
#include <unistd.h> /*Included for dup(2) and write(2)*/
#include <stdlib.h> /*Included for exit(3)*/
#define MESSAGE "Hey! Who redirected me?\r\n\0"
int main() {
int newfd = dup(1); /*Call dup for an aliased fd*/
char buff[] = MESSAGE;
if (newfd < 0) { /*Negative file descriptors are errors*/
exit(EXIT_FAILURE);
}
else if (write(newfd, buff, sizeof(buff)) < 0) { /*See: man 2 write*/
exit(EXIT_FAILURE);
}
return EXIT_SUCCESS;
}
Compile & Execute:
$ gcc dup.c
$ ./a.out
Hey! Who redirected me?
dup2.c
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
int main()
{
//First, we're going to open a file
int file = open("testfile.txt", O_APPEND | O_WRONLY);
if(file < 0) return 1;
//Now we redirect standard output to the file using dup2
if(dup2(file, 1) < 0) return 1;
//Now standard out has been redirected,
//we can write to the file
printf("This will print in testfile.txt\n");
return 0;
}//end of function main
Compile & Execute:
$ gcc dup2.c
$ cat testfile.txt
Hello, I am a testfile.txt
$ ./a.out
$ cat testfile.txt
Hello, I am a testfile.txt
This will print in myfile.txt
$ ./a.out
$ cat testfile.txt
Hello, I am a testfile.txt
This will print in myfile.txt
This will print in myfile.txt