去年看的APUE最近发现忘得不少,重温顺便记录一下,最近还是懒了,决定系统的写一下勉励自己,加油!!!
目录
文件I/O:
内核缓冲区和用户缓冲区
标准I/O和文件I/O:
网络I/O的buffer:
PCB进程管理模块:
文件结构体:
文件I/O api:
测试代码:
文件系统:
一个ext2的文件系统:
文件控制api
测试代码:
参考:
用户进程和操作系统的关系
这是一个计算机系统运行时的简化模型,我们把所有运行在操作系统上的进程称为用户进程,它们都运行在用户空间(把操作系统运行的空间成为系统空间)
内核态和用户态(kernel mode和user mode),在内核态可以访问系统资源,而在用户进程中是无法被直接访问的,只能通过操作系统来访问,所以也把操作系统提供的这些功能成为:“系统调用”。
提供这些限制的基础就是cpu提供的内核态和用户态。比如intel x86 CPU有四种不同的执行级别0-3,linux只使用了其中的0级和3级分别来表示内核态和用户态。
用户缓冲区:的目的是为了减少系统调用次数,从而降低操作系统在用户态与核心态切换所耗费的时间。
用户进程通过系统调用访问系统资源的时候,需要切换到内核态,而这对应一些特殊的堆栈和内存环境,必须在系统调用前建立好。而在系统调用结束后,cpu会从核心模式切回到用户模式,而堆栈又必须恢复成用户进程的上下文。而这种切换就会有大量的耗时。
内核缓冲区: 当一个用户进程要从磁盘读取数据时,内核一般不直接读磁盘,而是将内核缓冲区中的数据复制到进程缓冲区中。但若是内核缓冲区中没有数据,内核会把对数据块的请求,加入到请求队列,然后把进程挂起,为其它进程提供服务,读外设不需要cpu参与,一般DMA(直接内存读取)。
等到数据已经读取到内核缓冲区时,把内核缓冲区中的数据读取到用户进程中,才会通知进程,当然不同的io模型,在调度和使用内核缓冲区的方式上有所不同。
你可以认为,read是把数据从内核缓冲区复制到进程缓冲区。write是把进程缓冲区复制到内核缓冲区。
当然,write并不一定导致内核的写动作,比如os可能会把内核缓冲区的数据积累到一定量后,再一次写入。这也就是为什么断电有时会导致数据丢失。
所以说内核缓冲区,是为了在OS级别,提高磁盘IO效率,优化磁盘写操作。
缓存和缓冲区
CPU缓存(Cache Memory)是位于CPU与内存之间的临时存储器,因为cpu的计算速度要比内存的读写速度快很多,而把这些可能会被重复访问到的数据存储于cpu缓存中,就会提高读取速度。可以说缓存是cpu和内存之间的临时存储器。
也就是说,buffer是因为减少调用次数,集中调用,提高系统性能。而cache是将读取过的数据保存起来,重新读取时若命中(找到需要的数据)就不要去读硬盘了,若没有命中就读硬盘。
标准I/O(高级I/O)是ANSI C 提供的的I/O库,在stdio.h中,有很多细节,缓存分三种。(在文件I/O上缓存封装,直接操作缓冲区,必要时才操作文件,减少系统调用)
文件I/O(低级I/O),unbuffed I/O无缓存,直接调用内核系统调用,每次直接操作实际文件,但频繁系统调用会导致系统开销。
文件I/O函数: (open、close、read、write)操作文件描述符,失败返回-1
当某个进程/线程需要某段数据时,它只能在用户空间中属于它自己的内存中访问、修改,这段内存暂且称之为app buffer(用户缓冲区)。
假设需要的数据在磁盘上,那么进程首先得发起相关系统调用,通知内核去加载磁盘上的文件。但正常情况下,数据只能加载到内核的缓冲区,暂且称之为kernel buffer。数据加载到kernel buffer之后,还需将数据复制到app buffer。到了这里,进程就可以对数据进行访问、修改了。
现在的存储设备(包括网卡)基本上都支持DMA操作。(direct memory access,直接内存访问)简单地说,就是内存和设备之间的数据交互可以直接传输,不再需要计算机的CPU参与,而是通过硬件上的芯片(可以简单地认为是一个小cpu)进行控制。
再说kernel buffer和app buffer之间的复制方式,这是两段内存空间的数据传输,只能由CPU来控制。
所以,在加载硬盘数据到kernel buffer的过程是DMA拷贝方式,而从kernel buffer到app buffer的过程是CPU参与的拷贝方式。
在linux 中每一个进程都由task_struct 数据结构来定义. task_struct就是我们通常所说的PCB.她是对进程控制的唯一手段. 当我们调用fork()时, 系统会为我们产生一个task_struct结构。然后从父进程那里继承一些数据, 并把新的进程插入到进程树中, 以待进行进程管理。
内核每打开一个文件就创建一个struct file结构体与之关联,并将其传递给所有操作此文件的函数,当文件的所有实例都关闭后内核释放掉这个结构体。结构体两个重要字段:文件描述符和缓冲区。
文件描述符fd:fd只是一个小整数,在open时产生。起到一个索引的作用,进程通过PCB中的文件描述符表找到该fd所指向的文件指针filp。
文件描述符的操作(如:open)返回的是一个文件描述符,内核会在每个进程空间中维护一个文件描述符表, 所有打开的文件都将通过此表中的文件描述符来引用;
而流(如:fopen)返回的是一个FILE结构指针, FILE结构是包含有文件描述符的可以看作是对fd系统调用的封装, 它的优点是带有I/O缓存。
open close read write lseek
int open(const char *pathname,int flags,int perms)
用于打开或创建文件,在打开或创建文件时可以指定文件的属性及用户的权限等各种参数。
int close(int fd)
用于关闭一个被打开的文件,成功返回0,失败返回1。
ssize_t read(int fd, void *buf, size_t count);
从文件读,返回所读取的字节数;0(读到EOF);-1(出错)。
ssize_t write(int fd, void *buf, size_t count);
功能:write 函数向 filedes 中写入 count 字节数据,数据来源为 buf 。返回值一般总是等于 count,否则就是出错了。写入文件的字节数(成功);-1(出错)
off_t lseek(int fd, off_t offset,int whence);
用于在指定的文件描述符中将文件指针定位到相应位置。
//测试open函数
/*************************************************************************
> File Name: open.c
> Author: ma6174
> Mail: [email protected]
> Created Time: 2018年08月22日 星期三 11时14分57秒
************************************************************************/
#include
#include
#include
#include
#include
#include
#include
int main(int argc,char *argv[])
{
int fd;
char buf[1024] = "I am happy";
if(argc < 2)
{
printf("./app,filename\n");
exit(1);
}
umask(0000);
//fd = open(argv[1],O_CREAT|O_RDWR|O_EXCL,0644);
//O_CREAT creat file O_EXCL if file has exist ,then return false -1
fd = open(argv[1],O_RDWR|O_APPEND);
printf("fd = %d\n",fd);
write( fd,buf, strlen(buf));
close(fd);
return 0;
}
//测试非阻塞
/*************************************************************************
> File Name: unblock.c
> Author: ma6174
> Mail: [email protected]
> Created Time: 2018年08月22日 星期三 19时00分02秒
************************************************************************/
#include
#include
#include
#include
#include
#include
#define MSG_TRY "try again\n"
int main(void)
{
char buf[10];
int fd,n;
fd = open("/dev/tty",O_RDONLY|O_NONBLOCK);//open curent tty in the way of unblock
if(fd < 0)
{
perror("open/dev/tty");//open failed
exit(1);
}
tryagain://tty open successfully
n = read(fd,buf,10);
if(n < 0)//reading fail
{
if(errno == EAGAIN)//no message
{
sleep(1);
write(STDOUT_FILENO,MSG_TRY,strlen(MSG_TRY));
goto tryagain;
}
perror("open/dev/tty");//other err
exit(1);
}
write(STDOUT_FILENO,buf,n);//reading successfully,then write on the sreen
close(fd);
return 0;
}
更改文件属性 stat chomd chown link unlink symlink readlink rename chdir mkdir rmdir dup
int stat(const char *file_name, struct stat *buf);
通过文件名filename获取文件信息,并保存在buf所指的结构体stat中,执行成功则返回0,失败返回-1,错误代码存于errno。
int chmod(const char *filename,int mode)
修改文件权限参数: filename为文件名,mode为文件权限,八进制数。
返回值:成功返回0,失败返回-1,同时errno会被设置为合适值。
int chown(const char *path,uid_t owner,gid_t group)
修改文件所有者和所属组。参数:path为文件路径。 owner,用户ID。group组ID。
返回值:成功返回0,失败返回-1,同时errno会被设置为一个合适的值。
int link(const char *oldpath,const char *newpath);
函数说明:link()函数为一个已经存在的文件创建一个新的链接(“硬链接”)
成功,返回0,一旦错误,返回-1。并且erron被设置了结果
unlink()会删除参数pathname 指定的文件. 如果该文件名为最后连接点, 但有其他进程打开了此文件, 则在所有关于此文件的文件描述符皆关闭后才会删除. 如果参数pathname 为一符号连接, 则此连接会被删除。
int symlink(const char *oldpath, const char *newpath);
描述:symlink() 创建一个符号链接,软连接指向oldpath
readlink读符号链接所指向的文件名字,不读文件内容
ssize_t readlink(const char *path, char *buf, size_t bufsiz)
mkdir rmdir等目录操作符
文件重定向:
int dup(int oldfd);
int dup2(int oldfd,int newfd);
描述复制一个现有的文件描述符,使得新老文件描述符指向同一个file struct,dup复制给最小未使用文件描述符,文件引用计数+1。一个文件open两次则有两个file struct有不同的文件状态和读写位置。
/*************************************************************************
> File Name: stat.c
> Author: ma6174
> Mail: [email protected]
> Created Time: 2018年08月24日 星期五 18时32分32秒
************************************************************************/
//仿写运用stat 仿写ls
#include
#include
#include
#include
#include
#include
//stat is command and func; stat file:will return file'Inode
//stat --->ls
void file_type(struct stat s,char buf[])
{
if(( s.st_mode & S_IFDIR) == S_IFDIR)
buf[0] = 'd';
if(( s.st_mode & S_IFLNK) == S_IFLNK)
buf[0] = 'l';
if(( s.st_mode & S_IFREG) == S_IFREG)
buf[0] = '-';
if(( s.st_mode & S_IRUSR) == S_IRUSR)
buf[1] = 'r';
else
buf[1] = '-';
if(( s.st_mode & S_IWUSR) == S_IWUSR)
buf[2] = 'w';
else
buf[2] = '-';
if(( s.st_mode & S_IXUSR) == S_IXUSR)
buf[3] = 'x';
else
buf[3] = '-';
if(( s.st_mode & S_IRGRP) == S_IRGRP)
buf[4] = 'r';
else
buf[4] = '-';
if(( s.st_mode & S_IWGRP) == S_IWGRP)
buf[5] = 'w';
else
buf[5] = '-';
if(( s.st_mode & S_IXGRP) == S_IXGRP)
buf[6] = 'x';
else
buf[6] = '-';
if(( s.st_mode & S_IROTH) == S_IROTH)
buf[7] = 'r';
else
buf[7] = '-';
if(( s.st_mode & S_IWOTH) == S_IWOTH)
buf[8] = 'w';
else
buf[8] = '-';
if(( s.st_mode & S_IXOTH) == S_IXOTH)
buf[9] = 'x';
else
buf[9] = '-';
buf[10]= '\0';
}
int main(int argc,char*argv[])
{
int n;
struct stat file_stat;
char buf[10] = {0};
if(argc < 2)
{
printf("./app filename");//perror() when fun error; logical error using printf()
exit(1);//if correct,exit(0);else no zero;
}
if( stat(argv[1], &file_stat)<0)
{
perror("stat:");
exit(1);
}
else
{
printf("filename:%s size: %ld\n",argv[1],file_stat.st_size);
printf("BlockSize for IO:%ld\n",file_stat.st_blksize);
file_type(file_stat,buf);
printf("file_type %s",buf);
}
return 0;
}
/*************************************************************************
> File Name: dm06_dup.c
> Author: ma6174
> Mail: [email protected]
> Created Time: 2018年08月26日 星期日 16时51分22秒
************************************************************************/
//重定向标准输出和文件输入
#include
#include
#include
#include
#include
#include
#include
int main()
{
int fd,save_fd;
char msg[] = "This is a test\n";
fd = open("wch_test.txt",O_RDWR|O_CREAT,0600);
if(fd < 0)
{
perror("open:");
exit(-1);
}
save_fd = dup(STDOUT_FILENO);
dup2(fd,STDOUT_FILENO);
close(fd);//fd point STDOUT_FILENO ,the cnt minus 1
write(STDOUT_FILENO,msg,strlen(msg));
dup2(save_fd,STDOUT_FILENO);
write(STDOUT_FILENO,msg,strlen(msg));
close(save_fd);
return 0;
}
APUE
linux_sys
https://www.cnblogs.com/0xcafebabe/p/4423699.html
https://www.cnblogs.com/0xcafebabe/p/4426126.html
https://www.cnblogs.com/0xcafebabe/p/4430632.html
https://www.cnblogs.com/alantu2018/p/8461749.html