Apue第三章

来源:互联网 发布:矩阵归一化处理公式 编辑:程序博客网 时间:2024/05/16 15:30

PS:推荐最近刚撸完的一本书<linux程序设计>,此书着重讲述了关于linux下程序设计的一些简单设计,但是不够深入,拿来入门还是非常不错的,如果apue读着有些困难,那么此书是一个不错的选择。再分享一个提问须知:https://zhuanlan.zhihu.com/p/20752519

Apue中文第三版下载地址:http://download.csdn.net/download/xiaoyu5256/9803116

  废话少说,开始第三章。我还是直接拿实例程序直接干,书上的原理性问题我会用通俗的语言来描述,主要是为了让新手更好的理解。

  文件i/o,顾名思义,in and out,举个例子,当你打开一个文件的时候,就和你打开一本书是一个道理,是看书,还是去写,是你来决定的。再说几个关于本章的几个关键性词语,以下是对原理的阐述。

  三个幻数0:in(STDIN_FILENO) 1:out(STDOUT_FILENO) 2:error(STDERR_FILENO)(一会用到了就明白了)

  文件描述符:非负数,通俗来讲就是当你打开一个文件的时候,计算机会找一个数字来表示你当前打开的文件流,最大是63.

  偏移量:你可以把文件看成一条定长的公路,这个长度也就是是文件所占的磁盘块数大小,你如果想使公路增加长度,那么你需要阔路,但这并不是真实的文件大小,只是表面的,路还是那么长。你想扩展的大小就是所谓的偏移量。

  相对路径和绝对路径:相对路径就是相对于当前文件所在目录的文件所在位置,绝对路径就是相对于根目录(/)的文件所在位置。

  文件空洞:一会用代码说明,看下这篇博客也可以(http://blog.csdn.net/clamercoder/article/details/38361815)(空洞文件作用很大,例如迅雷下载文件,在未下载完成时就已经占据了全部文件大小的空间,这时候就是空洞文件。下载时如果没有空洞文件,多线程下载时文件就都只能从一个地方写入,这就不是多线程了。如果有了空洞文件,可以从不同的地址写入,就完成了多线程的优势任务。)

  缓冲区:缓冲区大致分为用户缓冲区和内核缓冲区,都是在内存当中,用户缓冲区是用户可以直接操作的。这两者之间的数据频繁交换非常的占用系统资源

  原子操作:比方说小明喜欢上了一个女神,他想让女神直到自己的爱慕,此时假设他有两种选择,第一种是让女神的室友吹枕边风,那么他需要先告诉女神室友自己的爱慕之情,之后女神室友去旁敲侧击。第二种是直接向女神告白,成不成在此一搏。两者都可以完成小明的倾诉,但是假设女神室友还没有旁敲侧击的时候,隔壁老王趁机向女神告白把女神拿下了,小明就尴尬了2333,我个人偏向第二种,第二种便是原子操作,但是也需要看什么样的情况再做决断。

  用户态和内核态:一般的程序或者说是进程是运行在用户态的,他们需要向网络发送数据的时候,数据会从用户缓冲区到内核缓冲区再发送到网络上。举个简单的例子,线程池:当有一个用户连接过来的时候,此时再去创建线程进行服务是需要用户态和内核态之间切换的,频繁的切换占用大量cpu时间片。所以我先创建很多线程等待用户的连接,节省了两者的切换,提高了性能。有点扯远了,大致就是这样。

  | & ^:这三个位操作符,自己查一下吧。(我手边的书上就有:c++primer第六版的137)

  文件访问权限:随便ls -l一个文件看看,文件所有者权限,用户组权限,其他用户权限,看一下鸟哥的书吧,上边写的很详细,隐藏文件权限和umask也着重去看以下,将来也会用到。

  书中在openopenat函数后写了很多关于标志位的解释,在后面的程序都会有用到,所以可以先跳过,用到的时候可以再回来查询。

  虽然很枯燥,但是还是需要继续看代码。

#include <sys/types.h>#include <unistd.h>#include<stdio.h>#include<stdlib.h>int main(){    if(lseek(STDIN_FILENO, 0, SEEK_CUR) == -1)        printf("cannot seek\n");    else {        printf("seek OK\n");    }    exit(0);}

 1. SEEK_SET,文件偏移量将被设置为 offset 

  2. SEEK_CUR,文件偏移量将被设置为当前值加上 offsetoffset可以为正也可以为负。 

  3. SEEK_END,文件偏移量将被设置为文件长度加上 offsetoffset可以为正也可以为负。



此程序是测试文件是否可以被设置偏移量,代码很简单,不再多说。

#include<stdio.h>#include<stdlib.h>#include<fcntl.h>#include <sys/types.h>#include <sys/stat.h>#include <unistd.h>#define FILE_MODE 0644char buf1[] = "abcdefghij";char buf2[] = "ABCDEFGHIJ";int main(){int fd;if((fd = creat("file.hole", FILE_MODE)) < 0)perror("create error\n");if(write(fd, buf1, 10) != 10)perror("buf1 write error");if((lseek(fd, 16384, SEEK_SET)) == 10)perror("lseek error");if(write(fd, buf2, 10) != 10)perror("buf2 write error");exit(0);}

这个程序是创建一个具有空洞的文件,使用ls查看该文件大小你会发现它和没有空洞的文件是一样的,但是所占的磁盘块(block)不一样,具有空洞的文件占的很少,也就是说,其表面大小和物理大小是完全不同的,见博客http://blog.csdn.net/clamercoder/article/details/38361815

关于多个文件描述符打开同一个文件的说明:



由此图可以看出当前文件偏移量和当前文件长度不是一个东西,偏移量大于文件长度就造成了文件的空洞,而前面所说的文件描述符也就是上图中进程维护的一张表。i节点中的文件长度表示的是实际所占的磁盘块数量。值得注意的是lseek只改变文件偏移量大小,并不进行任何IO操作。


Dup函数的功能就是如上图所示,书上讲述的也很明白,不再赘述。

  原子操作上文也用了通俗的语言去解释,书上举得例子是lseek之后去read,也就是先去更改偏移量,然后再去写,这里面实际上是牵涉了内核用户态切换的内容。直接pwrite是一个原子操作,也就是直接陷入内核态去执行写操作。

  Fcntl函数在后边的网络编程中经常用到,用来去设置一个非阻塞socket

#include<stdio.h>#include<stdlib.h>#include<fcntl.h>intmain(int argc, char *argv[]){int val;if(argc != 2)perror("usage: fcntl_2 <description#>");if(val = fcntl(atoi(argv[1]), F_GETFL, 0) < 0)printf("fcntl error for fd %d", atoi(argv[1]));switch(val & O_ACCMODE){case O_RDONLY:printf("read only");break;case O_WRONLY:printf("write noly");break;case O_RDWR:printf("read write");break;default:perror("unknown access mode");}if(val & O_APPEND)printf(", append");if(val & O_NONBLOCK)printf(", nonblocking");if(val & O_SYNC)printf(", synchronous writes");#if !defined(_POSIX_C_SOURCE) && defined(O_FSYNC) && (O_FSYNC != O_SYNC)if(val & O_FSYNC)printf(", synchronous writes");#endifputchar('\n');exit(0);}

程序也不难,先获取一个文件描述符的当前标志位,使用O_ACCMODE这个宏作为一个掩码与文件状态标识值做AND位运算,产生一个表示文件访问模式的值然后判断该文件该文件访问权限。书中的set_fl函数是经常用的,请牢记在心。

关于内核和用户态切换开销的问题我本周会用netcat进行一个小的网络测试来说明这个问题。


原创粉丝点击