Apue第三章

xiaoxiao2021-02-28  100

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 0644 char 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> int main(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"); #endif putchar('\n'); exit(0); }

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

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

转载请注明原文地址: https://www.6miu.com/read-21780.html

最新回复(0)