1 C标准函数与系统函数的区别
文件的结构体:
1.1 I/O缓冲区
每一个FILE文件流都有一个缓冲区buffer,默认大小8192Byte。
1.2 效率
文件缓冲区会降低效率。这里提供缓冲区主要是为了减少磁盘的读取。
1.3 程序的跨平台性
事实上Unbuffered I/O这个名词是有些误导的,虽然write系统调用位于C标准库I/O缓
冲区的底层,但在write的底层也可以分配一个内核I/O缓冲区,所以write也不一定是直接
写到文件的,也可能写到内核I/O缓冲区中,至于究竟写到了文件中还是内核缓冲区中对于
进程来说是没有差别的,如果进程A和进程B打开同一文件,进程A写到内核I/O缓冲区中的数
据从进程B也能读到,而C标准库的I/O缓冲区则不具有这一特性。
如图所示:
2 PCB概念
2.1 ask_struct结构体
/usr/src/linux-headers/include/linux/sched.h (这里的linux-headers每一用户不一样,直接按tab键补齐,或者手动cd进入目录)
如图所示:
2.2 files_struct结构体 (打开文件的描述表)
模型如下:
3 open/close
3.1 文件描述符
一个进程默认打开三个文件描述符
STDIN_FILENO 0
STDOUT_FILENO 1
STDERR_FILENO 2
新打开文件返回文件描述符表中未使用的最小文件描述符。
open函数可以打开或创建一个文件
1 #include <sys/types.h>
2 #include <sys/stat.h>
3 #include <fcntl.h>
4 int open(const char *pathname, int flags);
5 int open(const char *pathname, int flags, mode_t mode);
6 返回值:成功返回新分配的文件描述符,出错返回-1并设置errno
1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <sys/types.h>
4 #include <sys/stat.h>
5 #include <fcntl.h>
6
7
8 int main()
9 {
10 int fd=0;
11 //打开文件,如果文件不存在则创建,权限为0777
12 fd = open("abc",O_CREAT,0777);
13 printf("fd = %d
",fd);
14 return 0;
15 }
运行效果:
权限为0777-0002 = 0775 换算成二进制 111 111 101 rwx rwx r-x
在Man Page中open函数有两种形式,一种带两个参数,一种带三个参数,其实在C代码
中open函数是这样声明的:
int open(const char *pathname, int flags, ...);
最后的可变参数可以是0个或1个,由flags参数中的标志位决定,见下面的详细说明。
pathname参数是要打开或创建的文件名,和fopen一样,pathname既可以是相对路径也
可以是绝对路径。flags参数有一系列常数值可供选择,可以同时选择多个常数用按位或运
算符连接起来,所以这些常数的宏定义都以O_开头,表示or。
必选项:以下三个常数中必须指定一个,且仅允许指定一个。
* O_RDONLY 只读打开
* O_WRONLY 只写打开
* O_RDWR 可读可写打开
以下可选项可以同时指定0个或多个,和必选项按位或起来作为flags参数。可选项有很多,
这里只介绍一部分,其它选项可参考open(2)的Man Page:
* O_APPEND 表示追加。如果文件已有内容,这次打开文件所写的数据附加到文件的末尾
而不覆盖原来的内容。
* O_CREAT 若此文件不存在则创建它。使用此选项时需要提供第三个参数mode,表示该
文件的访问权限。
* O_EXCL 如果同时指定了O_CREAT,并且文件已存在,则出错返回。
* O_TRUNC 如果文件已存在,并且以只写或可读可写方式打开,则将其长度截断(Truncate)为0字节。
* O_NONBLOCK 对于设备文件,以O_NONBLOCK方式打开可以做非阻塞I/O(Nonblock I/
O),非阻塞I/O在下一节详细讲解。
1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <sys/types.h>
4 #include <sys/stat.h>
5 #include <fcntl.h>
6 #include <unistd.h>
7
8 int main(int argc,char *argv[])
9 {
10 int fd=0;
11 char buf[]="helloworld";
12 //打开文件,如果文件不存在则创建,默认权限为750
13 //创建,读写权限
14 //fd = open("1233",O_CREAT | O_RDWR);
15 fd = open("1233",O_CREAT | O_RDWR|O——APPEND);
16 write(fd,buf,sizeof(buf));
17 printf("fd = %d
",fd);
18 return 0;
19 }
3.2 最大打开文件个数
查看当前系统允许打开最大文件个数
cat /proc/sys/fs/file-max
查看当前默认设置最大打开文件个数
ulimit -a
修改默认设置最大打开文件个数为4096
ulimit -n 4096
4 read/write
read函数从打开的设备或文件中读取数据。
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
返回值:成功返回读取的字节数,出错返回-1并设置errno,如果在调read之前已到达文件末尾,则这次read返回0
参数count是请求读取的字节数,读上来的数据保存在缓冲区buf中,同时文件的当前读
写位置向后移。注意这个读写位置和使用C标准I/O库时的读写位置有可能不同,这个读写
位置是记在内核中的,而使用C标准I/O库时的读写位置是用户空间I/O缓冲区中的位置。比
如用fgetc读一个字节,fgetc有可能从内核中预读1024个字节到I/O缓冲区中,再返回第一
个字节,这时该文件在内核中记录的读写位置是1024,而在FILE结构体中记录的读写位置是
1。注意返回值类型是ssize_t,表示有符号的size_t,这样既可以返回正的字节数、0(表
示到达文件末尾)也可以返回负值-1(表示出错)。read函数返回时,返回值说明了buf中
前多少个字节是刚读上来的。有些情况下,实际读到的字节数(返回值)会小于请求读的字
节数count,例如:
读常规文件时,在读到count个字节之前已到达文件末尾。例如,距文件末尾还有30个
字节而请求读100个字节,则read返回30,下次read将返回0。
从终端设备读,通常以行为单位,读到换行符就返回了。
从网络读,根据不同的传输层协议和内核缓存机制,返回值可能小于请求的字节数,后
面socket编程部分会详细讲解。
write函数向打开的设备或文件中写数据。
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
返回值:成功返回写入的字节数,出错返回-1并设置errno
写常规文件时,write的返回值通常等于请求写的字节数count,而向终端设备或网络写
则不一定。
拷贝文件程序:
1 #include <unistd.h>
2 #include <sys/stat.h>
3 #include <sys/types.h>
4 #include <fcntl.h>
5 #include <stdlib.h>
6 #include <stdio.h>
7 #define SIZE 8192
8
9 int main(int argc,char *argv[])
10 {
11 char buf[SIZE];
12 int fd_src,fd_dest,len;
13
14 if(argc<3){
15 printf("./mycp src dest
");
16 exit(1);
17 }
18
19 fd_src = open(argv[1],O_RDONLY);
20 fd_dest = open(argv[2],O_CREAT|O_WRONLY|O_TRUNC,0777);
21
22 //成功返回读到的字节数,读到文件末尾返回0,读失败返回-1
23 while( (len = read(fd_src,buf,sizeof(buf))) >0)
24 {
25 write(fd_dest,buf,len);
26 }
27
28 close(fd_src);
29 close(fd_dest);
30
31 return 0;
32 }
输出屏幕输入信息:
#include <sys/types.h> #include <unistd.h> #include <sys/stat.h> int main() { char buf[1024]; int len = read(STDIN_FILENO,buf,sizeof(buf)); write(STDOUT_FILENO,buf,len); return 0; }