第一章
1.1 文件IO
c标准函数与系统函数的区别 FILE文件类型是一个结构体类型,包括文件描述符(inode)、位置指针(f_pos)、缓冲器(buffer)(8192byte)。
c标准文件函数都是带有缓冲区的,系统函数是不带缓冲区的。对于网络编程是不能有缓冲区的。
c标准函数->应用层API->内核层API->驱动函数->设备
写一个hello到文件中:用c标准函数fopen和fwrite将hello写入文件,这之间首先将hello写入C标准缓冲区然后调用应用层API的write将hello写入内核缓冲区然后依据系统的守护进程中定义的时间或者体积量调用内核层函数sys_write在调用驱动层将hello写入文件。
1.2 PCB(进程控制块)概念
PCB是一个task_struct结构体,里面包含了进程的所有信息。
每个PCB(taskstruct)中都有一个filestruct结构体指针,此指针指向的是文件描述符表,即文件的地址。
1.3 open/close
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
int main(int argc,char *argv[])
{
int fd;
if (argc < 2) {
printf("./app filename
");
exit(1);
}
fd = open(argv[1],O_CREAT,0644);
printf("fd = %d
", fd);
close(fd);
return 0;
}
open() 主要用到的属性:
O_CREAT O_RDWR O_RDONLY O_WRONLY O_APPEND
fd=open(argv[1], O_CREAT | O_RDWR | O_EXCL, 0644);
1.4 最大打开文件个数
一个进程默认最大打开文件个数1021个文件。
int main(int argc,char *argv[])
{
char name[1024];
int i = 0;
int fd;
while( 1 ) {
sprintf(name, "file%d", ++i);
fd = open(name, O_CREAT, 0777);
if(-1 == fd) exit(1);
printf("%d
", i);
}
}
cat /proc/sys/fs/file-max 查看当前电脑能承受的最大打开文件个数
ulimit -n 4096 修改默认设置最大打开文件个数为4096个
ulimit -a 查看当前设置的能打开文件的最大个数
1.5 read/write
#include <unistd.h>
ssize_t read(int fd,void *buf, size_t count);
/*ssize_t 有符号整形类型 size_t 无符号整形类型*/
读成功返回读到的字节个数,读完返回0,读失败返回-1
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#define SIZE 8192
int main(int argc, char *argv[])
{
char buf[SIZE];
int fd_src, fd_dest, len;
if(argc < 3){
printf("./mycp src dest
");
exit(1);
}
fd_src = open(argv[1], O_RDONLY);
fd_dest = open(argv[2], O_CREAT | O_WRONLY | O_TRUNC, 0644);
while( len = read ( fd_src, buf, sizeof(buf)) >0 )
write (fd_dest, buf, len);
close(fd_src);
close(fd_dest);
}
1.6 阻塞与非阻塞
1.6.1 阻塞
一个进程默认打开3个文件描述符
STDIN_FILENO 0 相当于stdin
STDOUT_FILENO 1 相当于stdout
STDERR_FILENO 3 相当于stderr
#include <sys/types.h>
#include
int main(int argc,char *argv[])
{
char buf[1024];
int len;
len = read(STDIN_FILENO, buf, sizeof(buf));//阻塞
if(len<0){
exit(1);
}
write(STDOUT_FILENO, buf, len);
return 0;
}
读磁盘上常规文件一般不会出现阻塞,不管读多少字节,read一定会有限时间内返回,从终端设备或网络设备上读一般会发生阻塞。
当一个进程处于阻塞时,这个进程会被置于SLEEP状态。在linux下用S+表示SLEEP状态。
1.6.2 非阻塞
终端的概念:tty是linux的终端文件,当开启新的窗口时会将tty指向新开的窗口。
open() creat()返回新的文件描述符,如果失败返回-1,同时将errno置相应的错误值
perro()函数根据errno的错误号,打印相应的信息
errno 全局标量 错误原因标志位
strerror()是perro下层的函数(个人理解)
char *strerror(int errnum)
1.7 lseek
lseek()函数类似于fseek()函数,fseek()函数有两个作用:当前读写位置指针和扩展文件。
int fseek(FILE *stream, long offset, int whence)
off_t lseek(int fd, off_t offset, int whence) //fseek的底层函数
扩展文件时,lseek(fd, 0x1000, SEEK_SET),fd是一个空文件描述符,扩展后不会增加,只有在write后才能增加,增加的内容随意。(扩展一个文件大小,一定要有一次写操作)
lseek()可以扩展一个文件,可以得到一个文件的大小。
1.8 fcntl
fcntl()获取一个文件的访问控制属性(FGETFL)和设置一个文件的访问控制属性(FSETFL)
1.9 ioctl
uart_write
uart_read
uart_ioctl
ioctl获取和设置文件的物理特性
fcntl获取和设置文件的访问特性
应用层write调用系统层syswrite调用驱动层uartwrite
第二章
对于ext2文件系统,每个分区组都有特定的格式:
每个block是4096Bytes,每个block占8个磁盘扇区,每个磁盘扇区是512Bytes,每个block也就是32768bit位。对于每个分区将被分为若干个block,第一个block是一个boot block是产商约定的,第二块是超级block记录了整个分区的情况,之后是GDT块描述表,记录了它之后的一些块(指的是描述性的块,不是数据块)的位置,再之后是块位图(block bit map),记录了那些块还是空闲的,之后是inode bit map,记录的各个文件的起始位置,之后是inode table,记录的对应inode节点文件的文件属性和块指针,之后才是数据块。。。一个文件要想存入系统,需要首先根据GDT找到inode bit map申请一个inode节点数,然后对应inodetable写入描述信息,之后根据block bit map看哪些块是空闲的写入inode table的块指针中,之后写入相应的数据,完成存储。
重点说明(1)inode table中的块指针,默认用一个块记录指针,也就是4096Bytes记录,也就是4096/4=1024个指针,一般来说1024个指针只能存储1024*4096(Bytes)/1024(M)/1024(G)=4G不能满足大文件的存储,因此设计1024个指针的最后三个指针为一级,二级,三级指针,分别指针另外的块,这样就被大大的增加了指针的数量。(2)从第二块开始被分为若干组,每个组的大小由block bit map中字节位数大小决定,block bit map的字节位数由block的大小决定,block的大小可以人为设定。