contributed by < YLowy
>
<unistd.h>
STDIN_FILENO
STDOUT_FILENO
STDERR_FILENO
file position
每個 file descriptor 存在 file position (offset) ,由 kernel 管理
seek
管理 file position
Read & Write
Read:
從 file position 後讀取 k 個 bytes 或者讀取到 EOF , 更新 file position
Write:
寫入 k 個 bytes, 更新 file position
close
close fd 後 fd 會被回收並且釋放其資源
int open (char *filename, int flags, mode_t mode)
ssize_t read(int fd, void *buf, size_t count);
ssize_t write(int fd, const void *buf, size_t count);
read write 的 short count 原因
size_t vs ssize_t 差別
size_t 為無號數而 ssize_t 為有號數
負數率為了判斷 w/r 有無成功
也因為如此一次 w/r 最大的資料量非為 4GB 而為 2GB
unbuffered IO : no application-level buffer, 適合用在 網路 socket
buffered IO : efficiently read data line
----- -rio_t -------
rio_read
rio_readlineb
rio_readnb
----- no rio_t ------
rio_readn
rio_writen
若 rio 建立在 heap 中或者為 static 要考慮 multi-thread 的 安全性。
information about a file.
int stat(const char *filename, struct stat *buf)
int fstat(int fd, struct stat *buf)
狀態
struct stat * status;
給定一文件透過 gdb 觀察 :
(gdb) p stat1
$1 = {st_dev = 2053, st_ino = 2104329, st_nlink = 1, st_mode = 33204,
st_uid = 1000, st_gid = 1000, __pad0 = 0, st_rdev = 0, st_size = 27,
st_blksize = 4096, st_blocks = 8, st_atim = {tv_sec = 1613892772,
tv_nsec = 447094144}, st_mtim = {tv_sec = 1613726300,
tv_nsec = 252177149}, st_ctim = {tv_sec = 1613726300,
tv_nsec = 252177149}, __glibc_reserved = {0, 0, 0}}
再透過 Mask Marco 判斷
S_ISREG(stat1.st_mode)
Kernel 使用 3 種資料結構實現 open file
Descriptor table
每一個 process 存在一張 Descriptor table 紀錄 fd 與 file table
File Table
所有 process 共享 file table ,而 file table 紀錄 set of file
存在 pointer to an entry in the v-node table
file position
refcnt 有多少數量指向該 file ,可以說是有多少 process open 該 file, close 就 -1,直到其變為 0 Kernel 才會回收該空間
下圖為其最基礎概念
下圖考慮 fork 之後 不同的 process 對於 fd 之關係(Decriptor table copied)
參考 fcntl()
shell 的 IO 功能
簡單而言
$ ls -al > foo.txt
$ cat foo.txt
total 136
drwxrwxr-x 2 yyc yyc 4096 二 21 16:35 .
drwxrwxr-x 4 yyc yyc 4096 二 21 15:24 ..
-rw-rw-r-- 1 yyc yyc 0 二 21 16:36 foo.txt
-rw-rw-r-- 1 yyc yyc 0 二 21 16:35 Foo.txt
-rw-rw-r-- 1 yyc yyc 0 二 21 16:36 ls
-rwxrwxr-x 1 yyc yyc 20384 二 21 15:46 metadata
-rwxrwxr-x 1 yyc yyc 16968 二 21 16:32 pp3
-rwxrwxr-x 1 yyc yyc 16784 二 19 15:20 systemi01
-rwxrwxr-x 1 yyc yyc 16880 二 19 15:30 systemi02
-rwxrwxr-x 1 yyc yyc 22280 二 19 18:28 systemio
-rw-rw-r-- 1 yyc yyc 470 二 19 15:20 systemio1.c
-rw-rw-r-- 1 yyc yyc 243 二 19 15:30 systemio2.c
-rw-rw-r-- 1 yyc yyc 334 二 21 15:45 systemio3_Metadata.c
-rw-rw-r-- 1 yyc yyc 3159 二 19 18:28 systemio.c
-rw-rw-r-- 1 yyc yyc 338 二 21 16:21 systemio_pp2.c
-rw-rw-r-- 1 yyc yyc 368 二 21 16:32 systemio_pp3.c
如何做到的?
-> dup2
int dup2(int old fd, int newfd);
lib for fread, fwrite, fclose, fopen… , printf, scanf also.
stdio.h 將 open file 視作 stream,也就是一個 指向 FILE 的 pointer。
stream 可想成 fd + buffer(原因如同 rio,避免過多次的 system call)
絕大多數情況可以使用 stdio.h 提供 API -> 全雙工介面
網路通訊(socket)則不行,由於以下兩限制
在socket 中無法使用 lseek()
在限制一中,可以透過flush buffer 再input
但是在限制二中,只能透過兩個 fd 實現(一個寫一個讀)但是也延伸其他問題,此必須同時close(fd) 以避免 memmory leak。
double close fd may be desaster in thread program such as server.
socket 可以使用 rio 進行讀寫
#include <stdio.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
int main(){
int fd1 = open("Foo.txt",O_RDWR, 0);
int fd2 = open("Foo.txt",O_RDWR, 0);
int fd3 = open("Foo.txt",O_RDWR, 0);
int fd4 = open("Foo.txt",O_RDWR, 0);
int fd5 = open("Foo.txt",O_RDWR, 0);
int fd6 = open("Foo.txt",O_RDWR, 0);
//close(fd);
printf("%d\n%d\n%d\n%d\n%d\n%d\n",fd1,fd2,fd3,fd4,fd5,fd6);
return 0;
}
$ ./systemi01
3
4
5
6
7
8
#include <stdio.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
int main(){
int fd1 = open("Foo.txt",O_RDWR, 0);
int fd2 = open("Foo.txt",O_RDWR, 0);
close(fd1);
close(fd2);
int fd3 = open("Foo.txt",O_RDWR, 0);
int fd4 = open("Foo.txt",O_RDWR, 0);
int fd5 = open("Foo.txt",O_RDWR, 0);
int fd6 = open("Foo.txt",O_RDWR, 0);
//close(fd);
printf("%d\n%d\n%d\n%d\n%d\n%d\n",fd1,fd2,fd3,fd4,fd5,fd6);
return 0;
}
$ ./systemi01
3
4
3
4
5
6
#include <stdio.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
int main(){
int fd1 = open("Foo.txt",O_RDWR, 0);
char c;
while(read(fd1,&c,1)!=0)
write(STDOUT_FILENO,&c,1);
close(fd1);
return 0;
}
$ ./systemi02
123123
open file table 存在各自的 file position
Foo.txt -> hi
int main(int argc, char **argv)
{
char c;
int fd1, fd2;
fd1 = open("Foo.txt", O_RDWR, 0);
fd2 = open("Foo.txt", O_RDWR, 0);
read(fd1, &c, 1);
printf("%c\n", c);
read(fd2, &c, 1);
printf("%c\n", c);
return 0;
}
$ ./pp2
h
h
fork 則繼承各自的 open file table (refcnt+1)
Foo.txt -> hi
int main(int argc, char **argv)
{
char c;
int fd1;
fd1 = open("Foo.txt", O_RDWR, 0);
if(fork()==0){
read(fd1, &c,1);
exit(0);
}
wait(NULL);
read(fd1, &c,1);
printf("%c\n",c);
return 0;
}
./pp3
i
dup2 -> make the new fd point to the entry of open file table pointed by old fd.
int main(int argc, char **argv)
{
char c;
int fd1;
fd1 = open("Foo.txt", O_RDWR, 0);
int fd2=4;
if(dup2(STDOUT_FILENO,fd2))
printf("fd2 = %d\n",fd2);
while(read(STDIN_FILENO,&c,1)){
write(fd2,&c,1);
}
return 0;
}
Foo.txt ->hi~
int main(int argc, char **argv)
{
char c;
int fd1;
fd1 = open("Foo.txt", O_RDWR, 0);
int fd2= open("Foo.txt", O_RDWR, 0);
read(fd2,&c,1);
dup2(fd2,fd1);
read(fd1,&c,1);
printf("%c\n",c);
return 0;
}
./pp5
i
int main(int argc, char **argv)
{
char c;
int fd1;
fd1 = open("Foo.txt", O_RDWR, 0);
int fd2= open("foo.txt", O_RDWR, 0);
close(fd2);
fd2= open("foo.txt", O_RDWR, 0);
printf("%d\n",fd2);
return 0;
}
$ ./pp6
4
int main(){
printf("IO\n");
struct stat * status;
rio_t rp;
int n;
char *buf = malloc(RIO_BUFSIZE*sizeof(char));
rio_readinitb(&rp,STDIN_FILENO);
while((n = rio_readnb(&rp,buf,MAX_LINE))!=0)
rio_writen(STDOUT_FILENO,buf,n);
return 0;
}
int main(int argc, char **argv)
{
struct stat stat1, stat2;
char *type1, *type2, *readok;
int fd = atoi(argv[1]);
int fd1, fd2;
fd1 = open("foo.txt", O_RDONLY, 0);
fd2 = open("file", O_RDONLY, 0);
fstat(fd, &stat1);
if (S_ISREG(stat1.st_mode))
{
type1 = "reg";
}
else if (S_ISDIR(stat1.st_mode))
{
type1 = "dir";
}
else
{
type1 = "oth";
}
//check access
readok = (stat1.st_mode & S_IRUSR) ? "yes" : "no";
printf("fd : %s , type : %s read : %s\n", argv[1], type1, readok);
return 0;
}
$ ./pp8 0
fd : 0 , type : oth read : yes
$ ./pp8 1
fd : 1 , type : oth read : yes
$ ./pp8 2
fd : 2 , type : oth read : yes
$ ./pp8 3
fd : 3 , type : reg read : yes
$ ./pp8 4
fd : 4 , type : dir read : yes