第4章文件 I/O:通用的I/O模型

        本章所关注的是磁盘文件的I/O操作。然而,监狱可以采用相同的系统调用对诸如管道、终端等所有类型的文件施以输入输出操作,故而本章的大部分内容会与后续章节有关。

        第5章会在本章的而基础上对文件I/O做深入探讨。缓冲是文件I/O的另一要点,其复杂程度足以专辟一张讲述。13章就涵盖了内核和stdio库中的I/O缓冲。

4.1概述

        所有执行I/O操作的系统调用都以文件描述符,一个非负整数(通常是小整数),来指代打开的文件。文件描述符用以表示所有类型的已打开文件,包括管道(pipe)、FIFO、socket、终端、设备和普通文件。针对每个进程,文件描述符都自成一套。

        按照惯例,大多数程序员都期望能够使用3种标准的文件描述符,见表4-1.在程序开始运行之前,shell代表程序打开这三各文件描述符。更确切地说,程序继承了shell文件描述符地副本----在shell地日常操作种,这三个文件描述符始终是打开地。(在交互式shell种,这3个文件描述通常指向shell所运行地终端。)如果命令行指定对输入、输出进行重定向操作,那么shell会对文件描述符做适当修改,然后再启动程序。

 在程序中时代这些文件描述符时,可以使用数字(0、1、2)表示,或者采用<unistd.h>所定义地POSIX标准名称--此方法更为可取。

        虽然stdin、stdout和stderr变量在程序初始化时用于指代进程地标准输入、标准输出和标准错误,但是调用freopen()可函数可以使这些变量指代其他任何文件对象。作为其操作地一部分,freopen()可以在将流(stream)重新打开之际一并更换隐匿其中地文件描述符。换言之,针对stdout调用freopen()函数之后,无法保证stdout变量值仍为1.

        下面介绍执行文件I/O操作地4个主要系统调用。

  • fd=open(pathname,flags,mode)函数打开pathname所标识地文件,并返回文件描述符,用以在后续函数调用中指代打开地文件。如果文件不存在,open()函数可以创建之,这取决于对位掩码参数flags的设置。flags参数还可以指定文件的打开方式:只读、只写、或是读写方式。mode参数则制定了由open()调用创建文件的访问权限,如果open()函数并未创建文件,那么可以忽略或省略mode参数。
  • numread = read(fd,buffer,count)调用从fd所指代的打开文件中读取之多count字节的数据,并存储到buffer中。read调用的返回值为实际读取到的字节数。如果再无字节刻度,则返回值0;
  • numwritten = write(fd,buffer,count)调用从buffer中读取多达count字节的数据写入由fd所指代的已打开文件中。write()调用的返回值为实际写入文件中的字节数;且有可能小于count。
  • status = close(fd)在左右输入/输出操作完成后,调用close(),释放文件描述符fd以及与之相关的内核资源。

        在详细说明这些系统调用之前,程序清单4-1简要展示了他们的使用方法。该程序实现了一个简版的cp(1)命令,将原文件复制到新文件中。在命令行中,程序的第一个参数代表已存在的源文件,第二个代表文件。程序清单4-1如下所示:

程序清单4-1:使用I/O系统调用  --copy.c

#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>

#define BUF_SIZE 1024

int main(int argc,char *argv[])
{
    int inputFd, outputFd, openFlags;
    mode_t filePerms;
    ssize_t numRead;
    char buf[BUF_SIZE];

    if(argc !=3 || strcmp(argv[1],"--help") == 0)
    {
        printf("%s old-file new-filename\n",argv[0]);
        return -1;
    }
    /**open input file and output file*/
    inputFd = open(argv[1],O_RDONLY);
    if(inputFd == -1)
    {
        perror("open:");
        return -1;
    }
    openFlags = O_CREAT | O_WRONLY | O_TRUNC;
    filePerms = S_IRUSR | S_IWUSR | S_IRGRP |S_IWGRP |
                S_IROTH | S_IWOTH;
    
    outputFd = open(argv[2],openFlags,filePerms);
    if(outputFd == -1)
    {
        printf("opening file %s:\n",argv[2]);
        return -1;
    }
    /*Transfer data until we encounter end of input or an error*/

    while((numRead = read(inputFd,buf,BUF_SIZE)) >0)
    {
        if(write(outputFd,buf,numRead) != numRead)
            printf("couldn't write whole buffer");
    }
    if(numRead == -1)
    {
        perror("read:");
        return -1;
    }
    if(close(inputFd) == -1)
    {
        perror("close input:");
        return -1;
    }
    if(close(outputFd) == -1)
    {
        perror("close output:");
        return -1;
    }

    exit(EXIT_SUCCESS);
}

4.2 通用I/O

        UNIX I/O模型的显著特点之一是其输入、输出的通用性概念。这意味着使用4个同样的系统调用open()、read()、write()和close()可以对所有类型的文件执行I/O操作。包括终端之类的设备。因此仅使用这些系统调用编写的程序,对任何类型的文件有效。例如,针对陈晓古清单4-1中的程序,如下操作都是有效的:

         要实现通用I/O,就必须确保每一文件系统和设备驱动程序都实现了相同的I/O系统调用集。由于文件系统或设备所持有的操作细节在内核中处理,在编程时通常可以忽略设备专用的因素。一旦应用程序需要访问文件系统或设备的专有功能时,可以选择瑞士军刀般的ioctl()系统调用(4.8节),改调用为通用I/O模型之外的专有特性提供了访问接口。

4.3 打开一个文件:open()

        open()调用既能打开一个也已存在的文件,也能创建并打开一个新文件。

#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname,int flags,.../*mode_t mode*/);

        Returns file descriptor on success, or -1 on error.

        要打开的文件由参数pathname来标识。如果pathname是一个符号链接,会对其进行解引用。如果调用成功,open()将返回一文件描述符,用于在后续函数调用中指代该文件,若发生错误,则返回-1,并将errno置为相应的错误标识。

        参数flags为位掩码,用于指定文件的访问模式,可选择表4-2所示的常量之一。

        当调用open()创建新文件时,位掩码参数mode制定了文件的访问权限。(SUSv3 规定,mode 的数据类型mode_t属于整数类型。)如果open()并未指定O_CREAT标志,则可以忽略mode参数。

15.4节将详细描述文件权限。之后,读者会了解到新建文件的访问权限不仅仅依赖于参数mode,而且受到进程的umask值(15.4.6节)和(可能存在的)父目录的默认访问控制列表(17.6节)影响。与此同时,需要注意mode参数可以指定为数字(通常为八进制数),更为可取的做法是对0个或多个表15-4(15.4.1节)中所列位掩码常量进行逻辑或操作。

        程序清单4-2展示了open()调用的几个使用实例,其中有些调用用到了其他标志位,后续将会加以介绍。

        程序清单4-2:open函数使用的例子 

/*Open existing file for reading*/

fd = open("startup",O_RDONLY);
if(fd == -1)
{
    perror("open:");
    return -1;
}

/*Open new or existing file for reading and writing,truncating to zero
bytes; file permissions read+write for owner,nothing for all others*/

fd = open("myfile",O_RDWR|O_CREAT|O_TRUNC,S_IRUSR|S_IWUSR);
if(fd == -1)
{
    perror("open:");
    return -1;
}

/*Open new or existing for writing; writes should always
append to end of file*/
fd = open("w.log",O_WRONLY|O_CREAT|O_TRUNC|O_APPEND,
                    S_IRUSR|S_IWUSR);
if(fd == -1)
{
    perror("open:");
    return -1;
}

 open()调用所返回的文件描述符数值

        SUSv3规定,如果open调用成功,必须保证其返回值为进程未用文件描述符中数值最小者。可以利用该特性以特定文件描述符打开某一文件。例如如下代码序列就会确保使用标准输入(文件描述符0)打开以文件。

if(close(STDIN_FILENO) == -1)//Close file descriptor 0
{
    perror("close");
    return -1;
}

fd=open(pathname,O_RDONLY);
if(fd == -1)
{
    perror("open:");
    return -1;
}

        由于文件描述符0未用,所以open()调用势必使用此描述符打开文件。5.5节中所论及的dup2()和fcntl()也可实现类似功能,但对于文件描述符的控制更加灵活。

4.3.1 open()调用中的flags参数

        在程序清单4-2展示的一些open调用例子中,flags参数除了使用文件访问标志外,还使用了其他操作标志(O_CREAT、O_TRUNC和O_APPEND)。现在将详细介绍flags参数。表4-3总结了可参与flags参数逐位或运算(|)的以整套常量,最后一类显式常量标准化与SUSv3还是SUSv4.

         表4-3常量分为如下几组。

  • 文件访问模式标志:先前描述的O_RDONLY、O_WRONLY和O_RDWR标志均在此列,调用open时,上述三者在flags参数中不能同时使用,只能指定其中一种。调用functl()的F_GETFL操作能够检索文件的访问模式(5.3节)。
  • 文件创建标志:这些标志在表4-3中位于第二部分,其控制范围不拘于open()调用行为的方方面面,还涉及后续I/O操作的各个选项。这些标志不能检索,也无法修改。
  • 已打开文件的状态标志:这些标志时表4-3中的剩余部分,使用fcntl()的F_GETFL和F_SETFL操作可以分别检索和修改此类标志。有时干脆将其称之为文件状态标志。

        如下是flags常量的详细描述。

O_APPEND

        总是在文件尾部追加数据,5.1节将讨论此标志的意义。

O_ASYNC

        当对于open()调用所返回的文件描述符可以实施I/O操作时,系统会产生一个信号通知进程。这一特性,也被称为信号驱动I/O,仅对特定类型的文件有效,诸如终端、FIFOS以及socket。(在SUSv3中并未规定O_ASYNC标志,但大多数UNIX实现都支持此标志或者老版本中与其等效的FASYNC标志。)在linux中,调用open()时指定O_ASYNC标志没有任何实质效果。要启用信号驱动I/O特性,必须调用fcntl()的F_SETFL操作来设置O_ASYNC标志(5.3节)。(其他一些UNIX系统的实现有类似行为。)关于O_AASYNC标志的更多内容可参考63.3节。

O_CLOEXEC(自Linux2.6.23版本开始支持)

        为新(创建)的文件描述符启用close-on-flag标志(FD_CLOEXEC)。27.4节将描述FD_CLOEXEC标志。使用O_CLOEXEC标志(打开文件),可以免去程序执行fcntl()的F_GETFD和F_SETFD操作来设置close-on-exec标志的额外工作。在多线程程序中执行fcntl()的F_GETFD和F_SETFD操作有可能导致竞争状态,而是用O_CLOEXEC标志则能够避开这一点,可能引发竞争的场景是:线程甲打开以文件描述符,尝试为描述符标记close-on-exec标志,与此同时。线程乙执行fork()调用,然后调用exec()执行任意一个程序。(假设在甲打开文件描述符和调用fcntl()设置close-on-exec标志之间,乙成功执行了fork()和exec()操作。)此类竞争可能会在无意间将打开的文件描述符泄露给不安全的程序。

O_CREAT

        如果文件不存在,将创建一个新的空文件。即使文件以只读方式打开,此标志依然有效。如果如果在open()调用中指定O_CREAT标志,那么还需要提供mode参数,否则,会将新文件的权限设置为栈中的某个随机值。

O_DIRECT

        无系统缓冲的文件I/O操作。该特性将在13.6中详述。为使O_FIRECT标志的常量定义在<fcntl.h>中有效,必须定义_GNU_SOURCE功能测试宏。

O_DIRECTORY

        如果pathname参数并非目录,将返回错误(错误号errno为ENOTDIR)。这一标志是专为实现opendir()函数(18.8节)而设计的扩展标志。为使O_DIRECTORY标志的常量定义在<fcntl.h>中有效,必须定义_GNU_SOURCE功能测试宏。

O_DSYNC(自Linux 2.6.33版本开始支持)

        根据同步I/O数据完整性的完成要求来执行文件写操作。参见13.3节中关于内核I/O缓冲的讨论。

O_EXCL

        此标志与O_CREAT标志结合使用表明如果文件一ing存在,则不会打开文件,且open()调用失败,并返回错误,错误号errno为EEXIST.换言之,此标志确保了调用者(open()的调用进程)就是创建文件的进程。检查文件存在与否和创建文件这两部属于同一原子操作。5.1节将讨论原子操作的概念。如果在flags参数中同时指定了O_CREAT和O_EXCL标志,且pathname参数是符号链接,则open()函数调用失败(错误号errno为EEXIST)。SUSv3之所以如此规定,是要求有特权的应用程序在已知目录下创建文件,从而消除了如下 安全隐患,使用符号链接打开文件会导致在另一位置创建文件(例如,系统目录)。

O_LARGEFILE

        支持一大文件方式打开文件。在32位操作系统中使用此标志,以支持大文件操作。监管在SUSv3中没有规定这一标志,但其他一些UNIX实现都支持这一特性。

O_NOATIME(Linux 2.6.8版本开始)

        在读文件是,不更新文件的最近访问时间(15.1节中所描述的st_atimes属性)要使用该标志,妖媚调用进程的有效用户ID必须与文件的拥有者相匹配,要么进程需要拥有特权(CAP_FOWNER)。否则,open()调用失败,并返回错误,错误号errno为WPERM。(事实上,如9.5所述,对于非特权进程,当以O_NOATIME标志打开文件时,与文件用户ID必须匹配的是进程的文件系统ID,而非进程的有效用户ID。)此标志是Linux特有的非标准扩展。要从<fcntl.h>中启用此标志,必须定义_GNU_SOURCE功能测试宏。O_NOATIME标志的设计旨在为索引和备份程序服务。该标志的使用能够显著减少磁盘的活动量,省却了既要读取文件内容,又要更新i-node结构中最近访问时间的繁琐,进而节省了磁头在磁盘上的反复寻道时间(14.4节)。mount()函数中MS_NOATIME标志(14.8.1节)和FS_NOATIME_FL标志(15.5节)与O_NOATIME标志功能相似。

O_NOCTTY

        如果正在打开的文件属于终端设备,O_NOCTTY标志防止其称为控制终端。34.4节将讨论控制终端。如果正在打开为文件不是设备终端,则此标志无效

O_NOFOLLOW

        通常,如果pathname参数是符号链接,open()函数将对pathname参数进行解引用。一旦在open()函数中指定了O_NOFOLLOW标志,且path参数属于符号链接,则open()函数将返回失败(错误号errno为ELOOP)。此标志在特权程序中极为有用,能够确保open()函数不对符号链接进行解引用,为使O_NOFOLLOW标志在<fcntl.h>中有效,必须定义_GNU_SOURCE功能测试宏。

O_NONBLOCK

        以非阻塞方式打开文件 5.9节

O_SYNC

        以同步I/O方式打开文件,参见13.3节针对内核I/O缓冲的讨论。

O_TRUNC

        如果文件已经存在且为普通文件,那么将清空文件内容,将其长度置0.在Linux使用此标志,无论以读、写方式打开文件,都可清空文件内容(在这两种情况下,都必须拥有对文件的写权限)。SUSv3对O_RDONLY与O_TRUNC标志组合并未做规定,但多数其他UNIX实现与Linux的处理方式相同。

4.3.2 open()函数的错误

        若打开文件时发生错误,open()将返回-1,错误号errno标识错误原因。以下是一些可能发生的错误

EACCES

        文件权限不允许调用进程以flags参数指定的方式打开文件。无法访问文件,其可能的原因有目录权限的限制、文件不存在并且也无法创建该文件。

EISDIR

        所指定的文件属于目录,而调用者企图打开该文件进行写操作。不允许这种用法。(另一方面,在某些场合中,打开目录进行读操作是必要的。18.11将举例说明。)

EMFILE

进程已打开的文件描述符数量达到了进程资源限制所设定的上限。

ENFILE

        文件打开数量已达到系统允许的上限

EOENT

        要么文件不存在且未指定O_CREAT标志,要么指定了O_CREAT标志,但pathname参数指定路径之一不存在,或者pathname参数为符号链接,而该链接指向的文件不存在(空连接)。

EROFS

        所指定的文件隶属于只读文件系统,而调用者企图以写让是打开文件。

ETXTBSY

        所指定的文件为可执行文件(程序),且正在运行。系统不允许修改正在运行的程序(比如以写方式打开文件)。(必须首先终止程序运行,然后方可修改可执行文件。)

        后续在描述其他系统调用或库函数时,一般不会再以上述方式展现可能发生的一系列错误。(每个系统调用或库函数的错误列表可从相关手册中查询获得。)采用上述方式原因有二,一是因为open()是本书详细描述的首个系统调用,而上述列表表明任一原因都有可能导致系统调用或库函数的调用失败。二是open()调用失败的具体原因列表v恩深就颇为指的玩味,他展示了影响文件访问的若干因素,以及访问文件系统所执行的一系列检查。(上述错误列表并不完整,更多open()调用失败的错误原因请查看open(2)的操作手册。)

4.3.3 creat()系统调用

        在早期的UNIX实现中,open()只有两个参数,无法创建新文件,而是使用creat()系统调用来创建并打开一个新文件。

#include<fcntl.h>

int creat(const char *pathname,mode_t mode);
        Returns file descriptor, or -1 on error

        creat()系统调用根据pathname参数创建并打开一个文件,若文件已存在,则打开文件,并清空文件内容,将其长度清0.creat()返回一文件描述符,供后续系统调用使用。creat()系统调用等价于如下open()调用:

fd = open(pathname,O_WRONLY | O_CREAT | O_TRUNC,mode)

        尽管crea()在一些老旧程序中还存在,但由于open()的flags能对文件打开提供更多控制。对creat()的使用现在已不多见。

4.4 读取文件内容:read()

        read()系统调用从文件描述符fd所指代的打开文件中读取数据。

#include <unistd.h>
ssize_t read(int fd,void *buffer,size_t count);
        Returns number of bytes read,0 on EOF,or -1 on error

        count 参数指定最多能读取的字节数。(size_t 数据类型属于无符号整数类型。)buffer参数提供用来存放输入数据的内存缓冲地址。缓冲区至少应有count个字节。

        系统调用不会分配内存缓冲区以返回信息给调用者。所以,必须预先分配合适的缓冲区并肩缓冲区指针传递给系统调用。与此相反,有些库函数却会分配内存缓冲区用以返回信息给调用者。

        如果read()调用成功,将返回实际读取的字节数,如果遇到文件结束(EOF)则返回0,如果出现错误则返回-1.ssize_t数据类型属于有符号的整数类型,用来存放(读取的)字节数或-1(表示错误)。

        一次read()调用所读取的字节数可以小于请求的字节数。对于普通文件而言,这有可能是因为当前读取位置靠近文件尾部。

        当read()应用于其它文件类型时,比如管道、FIFO、socket或终端,在不同环境下也会出现read()调用读取的字节数小于请求字节数的情况。例如,默认情况下从终端读取字符,一遇到换行符(\n),read()调用就会结束。

        使用read()从终端读取一连串字符,我们也许期望下面的代码会起作用:

#define MAX_READ  20
char buffer[MAX_READ +1];
ssize_t numRead;

numRead = read(STDIN_FILENO,buffer,MAX_READ);
if(numRead == -1)
{
    perror("read:");
    return -1;
}

printf("The input data was: %s\n",buffer);

        这段代码的输出可能会很奇怪,因为输出结果除了实际输入的字符串外还会包括其他字符。这是因为read()没有在printf()函数打印的字符串尾部添加一个表示终止的空字符。思索片刻就会意识到这肯定是症结所在,因为read()能够从文件中读取任意序列的字节。有些情况下,输入信息可能是文本数据,但在其他情况下,有可能是二进制整数或者二进制形式的C语言数据结构。read()无从区分这些数据,故而也无法遵从C语言对字符串处理的约定,在字符串尾部追加标识字符串结束的空字符。如果输入缓冲区的结尾处需要一个标识终止的空字符,必须显式追加

#define MAX_READ  20
char buffer[MAX_READ +1];
ssize_t numRead;

numRead = read(STDIN_FILENO,buffer,MAX_READ);
if(numRead == -1)
{
    perror("read:");
    return -1;
}

buffer[numRead] = '\0';
//如果读取的是字符串,组要在已读字符数后手动添加null符号
printf("The input data was: %s\n",buffer);

        由于表示字符串终止的空字符需要一个字节的内存,所以缓冲区的大小至少要比预计读取的最大字符串长度多出一个字节。

4.5 数据写入文件:write()

        write()系统调用将数据写入一个已打开的文件中。

#include <unistd.h>
ssize_t write(int fd,void *biffer,size_t count);
        Returns number of bytes writen,or -1 on error

        write()调用的参数含义与read()调用类似。BUffer参数为要写入数据的内存地址,count参数为欲从buffer写入文件的数据字节数,fd参数为一文件描述符,指代数据要写入的文件。

        如果write()调用成功,将返回实际写入文件的字节数,该返回值可能小于count参数值。这被称为部分写。对磁盘文件来说,造成部分写的原因可能是由于磁盘已满,或是因为进程资源对文件大小的限制。

        对磁盘文件执行I/O操作时,write()调用成功并不能保证数据已经写入磁盘。因为为了减少磁盘活动量和加快write()系统调用,内核会缓存磁盘的I/O操作

4.6 关闭文件:close()

        close()系统调用关闭一个打开的文件描述符、,并将其释放回调用进程,供该进程继续使用。当一进程终止时,将自动关闭已打开的所有的文件描述符。

#include <unistd.h>

int close(int fd);
        Returns 0 on success, or -1 on error

        显式关闭不再需要的文件描述符往往时良好的编程习惯,会使代码在后续修改时更具可读性,也更可靠。进而言之,文件描述符属于优先资源,因此文件描述符关闭失败可能会导致一个进程将文件描述符资源消耗殆尽。在编写需要长期运行并处理大量文件的程序时,比如shell或者网络服务器软件,需要特别加以关注。

        像其他所有系统调用一样,应对close进行错误检查,如下所示:

if(close(fd) == -1)
{
    perror("close:");
    return -1;
}

        上述代码能够捕获的错误有:企图关闭一个未打开的文件描述符,或者两次关闭同一文件描述符,也能捕获特定文件系统再关闭操作中诊断出的错误条件。

        针对特定文件系统的错误,NFS(网络文件系统)就是一例。如果NFS出现提交失败,这意味着数据没有抵达远程磁盘,随之将这一错误作为lose调用失败的原因递给应用系统。

4.7 改变文件偏移量

        对于每个打开的文件,系统内核会记录器文件偏移量,有时也将文件偏移量称为读写偏移量或指针。文件偏移量是指执行下一个read()或write()操作的文件起始位置,会议相对于文件头部起点的文件当前位置来标识。文件第一个字节的偏移量为0。

        文件打开时,会将文件偏移量设置为指向文件开始,以后每次read()或write()调用将自动对其调整,以指向已读或已写数据后的下一字节。因此,连续的read()和write()调用将按照顺序递进,对文件进行操作。

        针对文件描述符fd参数所指代的已打开文件,lseek()系统调用依照offset和whence参数调整该文件的偏移量。

#include <unistd.h>
off_t lseek(int fd,off_t offset,int whence);
        Returns new file offset if successful, or -1 on error

        offset参数指定了一个以字节为单位的数值。(SUSv3规定offset_t数据类型为有符号整型数。)whence擦拭农户则表明应参照哪个基点来解释offset参数,应为下列其中之一:

SEEK_SET

        将文件偏移量设置为从文件头部起始点开始的offset个字节。

SEEK_CUR

        相对于当前文件偏移量,将文件偏移量调整offset个字节。

SEEK_END

        将文件偏移量设置为起始于文件尾部的offset个字节。也就是说,offset参数应该从文件最后一个字节之后的下一个字节算起。

        在早期的UNIX实现中,whence参数用整数0、1、2来表示,而非正文中显示的SEEK_*常量。

        如果whence参数值为SEEK_CUR或SEEK_END,offset参数可以为正数,也可以为负数。如果wgen参数值为SEEK_SET,offset参数值必须为非负数。

        lseek()调用成功会返回新的文件偏移量。下面的调用只是获取文件偏移量的当前位置,并没有修改它。

curr  = lseek(fd,0,SEEK_CUR);

 这里给出了lseek()调用的其他一些例子,在注释中说明了将文件偏移到的具体位置。

lseek(fd,0,SEEK_SET);       /*Start of file*/
lseek(fd,0,SEEK_END);       /*Next byte after the end of the file*/
lseek(fd,-1,SEEK_END);      /*Last byte of file*/
lseek(fd,-10,SEEK_CUR);     /*Ten bYTES prior to current location*/
lseek(fd,1000,SEEK_END);    /*1001 bytes past last byte of file*/

        lseek()调用只是调整内核中与文件描述符相关的文件偏移记录,并没有引起对任何物理设备的访问。

        5.4节将进一步描述文件偏移量、文件描述符、已打开文件三者的关系。

        lseek()并不适用于所有类型的文件。不允许将lseek()应用于管道、FIFOsocket或者终端。一旦如此,调用将会失败,并将errno置为ESPIPE。另一方面,只要heqingheli-,也可以将lseek()应用于设备。例如,在磁盘或者磁带上查找一处具体位置。

文件空洞

        如果程序的文件偏移量依然跨越了文件结尾,然后再执行I/O操作,将会发生什么情况?read()调用将返回0,表示文件结尾。有点令人惊讶的是,write()函数可以在文件结尾后的任意位置写入数据。

        从文件结尾后道心写入数据见的这段空间被称为文件空洞。从变成角度看,文件空洞中是存在字节的,读取空洞将返回以0(空字节)填充的缓冲区 。

        然而,文件空洞不占用任何磁盘空间。知道后续某个时间点,在文件空洞中写入了数据,文件系统曹辉位置分配磁盘块。文件空洞的主要优势在于,与为实际需要的空字节分配磁盘块相比,系数填充的文件会占用较少的磁盘空间。核心咋混储文件(core dump 22.1节)是包含空洞文件的常见例子。

        对文件空洞不占用磁盘空间的说法需要稍微限定一下。在大多数文件系统中,文件空间的分配是以块为单位的(14.3节)。块的大小取决于文件系统,通常是2014字节、2048字节、4096字节。如果空洞的边界落在块内,而非恰好落在边界上,则会分配一个完整的块来存储数据,块中与空洞相关的部分则以空字节填充。

        空洞的存在意味着一个文件名义上的大小可能要比其占用的磁盘存储量要大(有时会大出许多)。向文件空洞中写入字节,内核需要为其分配存储单元,即使文件大小不变,系统的可用磁盘空间也将减少。这种情况并不常见,但也需要了解。

        SUSv3的函数posix_fallocate(fd,offset,len)规定,针对文件描述符fd所知带的文件,能确保按照由offse参数和len参数所确定的字节范围为其在磁盘山分配存储空间。这样,应用程序对文件的后续write()调用不会因磁盘空间耗尽而失败(否则,当文件中一个空洞被填满后,或者因其他应用程序消耗了磁盘空间时,都可能因磁盘空间耗尽而引发此类错误)。在过去,glibc库在实现posix_fallocate()函数时,通过向指定范围内的每个块写入一个值为0的字节以达到预期结果。自内核版本2.6.23开始,Linux系统提供了fallocate()系统掉哦那个,能更为高效的确保所需存储空间的分配。当fallocate调用可用时,glibc库会利用其来实现posix_fallocate()函数的功能。

        14.4节将描述空洞在文件中的表示方式。25.1节将描述stat()系统调用,该调用能够提供文件当前大小和实际分配给文件的块数量等信息。

示例程序

       程序清单4-3 演示了lseek()与read()、write()的协作使用。该程序的第一个命令行参数为要打开的文件名称,剩余的参数则指定了在文件上执行的输入/输出操作。每个表示操作的参数都以一个字母开头,紧跟以相关值(中间无空格分隔)。

  • softset:从文件开始检索到offset字节位置。
  • rlength:在当前文件便宜量处,从文件中读取legnth字节数据,并以文本形式显示。
  • Rlength:在当前文件偏移量处,从文件中读取length字节数据,并以十六进制形式显示。
  • wstr:在当前文件偏移量处,向文件写入由str指定的字符串。

程序清单4-3:read()、write()和lseek()使用示范  --seek_io.c

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <ctype.h>
#include <errno.h>

int main(int argc,char *argv[])
{
    size_t len;
    off_t offset;
    int fd,ap,j;
    char *buf;
    ssize_t numRead,numWritten;

    if(argc <3 || strcmp(argv[1],"--help") ==0)
    {
        printf("%s file {r<length> | R<length> |W<string> | s<offset>}...\n",argv[0]);
        return -1;
    }
    fd = open(argv[1],O_RDWR | O_CREAT,S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
    if(fd == -1)
    {
        perror("open:");
        return -1;
    }

    for(ap=2;ap<argc;ap++){
        switch(argv[ap][0]){
            case 'r': /*Display bytes at current offset,as text*/
            case 'R': /*Display bytes at current offset,in hex*/
                len = atoi(&argv[ap][1]);
                buf = malloc(len);
                if(buf ==NULL)
                {
                    perror("buf:");
                    return -1;
                }
                numRead = read(fd,buf,len);
                if(numRead == -1)
                {
                    perror("read:");
                    return -1;
                }
                if(numRead == 0){
                    printf("%s : end-of-file\n",argv[ap]);
                }else{
                    printf("%s:",argv[ap]);
                    for(j=0;j<numRead;j++)
                    {
                        if(argv[ap][0] == 'r')
                            printf("%c",isprint((unsigned char)buf[j])?buf[j]:'j');
                        else
                            printf("%02x ",(unsigned int)buf[j]);
                    }
                    printf("\n");
                }

                free(buf);
                break;
            case 'w':   /*Write string at current offset*/
                numWritten = write(fd,&argv[ap][1],strlen(&argv[ap][1]));
                if(numWritten == -1)
                {
                    perror("write:");
                    return -1;
                }
                printf("%s: wrote %ld bytes \n",argv[ap],(long)numWritten);
                break;
            case 's':
                offset = atoi(&argv[ap][1]);
                if(lseek(fd,offset,SEEK_SET) == -1)
                {
                    perror("lseek:");
                    return -1;
                }
                printf("%s seek succeded\n",argv[ap]);
                break;

            default:
                printf("Argument must start with [rRws]: %s\n",argv[ap]);
        }
    }

    exit(EXIT_SUCCESS);
}

        下面的shell会话演示了程序清单4-3程序的使用,还显示了从文件空洞中读取字节的情况:

4.8 通用I/O模型以外的操作:ioctl()

        在本章上述通用I/O模型之外,ioctl()系统调用又为执行文件和设备操作提供了一种多用途机制。

#include <sys/ioctl.h>
 int ioctl(int fd,int request,.../*argp*/);
          Value returned on success depends on request,or -1 on error

        fd参数为某个设备或文件已打开的文件描述符,request参数指定了将在fd上执行的控制操作。具体设备的头文件定义了可传递给request参数的常量。

        ioctl()调用的第三个参数采用了标准C语言的省略符号(...)来表示(称之为argp),k可以是任意数据类型。ioctl()根据request的参数值来确定argp所期望的类型。通常情况下argp是指向整数或结构的指针,有些情况下不需要使用argp。

        SUSv3 为ioctl()制定的唯一规定时针对流(STREAM)设备的控制操作。(流是 System V操作系统中的特性。尽管为其开发有一些插件,主流的Linux内核并不支持该特性。)本书述及的ioctl()的其他操作都不在SUSv3的规范之列。然而,从早期版本开始,ioctl()调用就是UNIX系统的一部分在许多其他UNIX系统中都已实现,在讨论ioctl()调用的各个操作时,会点出存在的可移植性问题。

4.9 总结

        为了对普通文件执行I/O操作,首先必须调用open()以获得一个文件描述符。随之使用read()和write()执行文件的I/O操作,然后应使用close()释放文件描述符及相关资源。这些系统调用可对所有的类型的文件执行I/O操作。

        所有类型的文件和设备驱动都实现了相同的I/O接口,这保证了I/O操作的通用性,同时意味着在无需针对特定文件类型编写代码的情况下,程序通常就能够操作所有类型的文件。

        对于已打开的每个文件,内核都维护由一个文件偏移量,这决定了下一次读或写操作的起始位置。读和写操作会隐式修改文件偏移量。使用lseek()函数可以显示地将文件偏移量置为文件中或文件结尾后的任一位置。在文件原结尾处之后的某一位置写入数据将导致文件空洞。从文件空洞处读取文件将返回全0字节

        对未纳入标准I/O模型的所有设备和文件操作而言,ioctl()系统调用是个百宝箱。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Visual.Basic.2010.&.NET4.高级编程(第6版)-文字版.pdf 第I部分 语言结构和环境 第1 visual studio 2010 3 1.1 visual studio 2010:从express到ultimate的各种版本 4 1.2 visual basic的关键字和语法 7 1.2.1 控制台应用程序 10 1.2.2 从项目模板上创建项目 11 1.2.3 solution explorer窗口 13 1.2.4 项目属性 14 1.2.5 assembly information屏幕 15 1.2.6 编译设置 16 1.2.7 调试属性 20 1.2.8 引用 21 1.2.9 资源 23 1.2.10 设置 24 1.2.11 其他项目属性选项卡 26 1.3 provb_vs2010项目 27 1.3.1 在代码中设置窗体属性 29 1.3.2 visual studio的其他组件 37 1.4 增强示例应用程序的功能 37 1.4.1 定制代码 39 1.4.2 构建应用程序 43 1.4.3 重用第一个windows窗体 50 1.5 visual studio 2010中的特色功能 51 1.5.1 构建配置 51 1.5.2 任务列表 53 1.5.3 command窗口 54 1.5.4 server explorer 54 1.5.5 在visual studio 2010中记录和使用宏 55 1.5.6 类图 57 1.5.7 应用程序生命周期管理 58 1.5.8 性能工具 60 1.6 小结 62 第2 对象和visual basic 63 2.1 面向对象的术语 64 2.1.1 对象、类和实例 64 2.1.2 对象的组成 65 2.1.3 system.object 68 2.2 使用visual basic类型 68 2.2.1 值类型和引用类型 69 2.2.2 基本类型 71 2.3 命令:条件语句 72 2.3.1 if then 73 2.3.2 比较运算符 73 2.3.3 select case 75 2.4 值类型(结构) 75 2.4.1 布尔类型 76 2.4.2 整数类型 77 2.4.3 无符号类型 78 2.4.4 小数类型 78 2.4.5 char和byte类型 81 2.4.6 datetime类型 81 2.5 引用类型(类) 82 2.5.1 object类 82 2.5.2 string类 83 2.5.3 xml字面量 87 2.5.4 dbnull类和isdbnull()函数 89 2.6 参数传递 89 2.7 变量的作用域 91 2.8 使用对象 92 2.8.1 对象的声明和实例化 92 2.8.2 对象引用 93 2.8.3 取消对象的引用 93 2.8.4 前期绑定与后期绑定 94 2.9 数据类型转换 95 2.10 创建类 100 2.10.1 类 100 2.10.2 事件的处理 110 2.10.3 处理多个事件 111 2.10.4 withevents关键字 111 2.10.5 触发事件 111 2.10.6 声明和触发定制事件 112 2.10.7 用withevents关键字接收事件 113 2.10.8 用addhandler接收事件 115 2.10.9 构造函数方法 116 2.10.10 终止和清除 117 2.11 高级概念 118 2.11.1 重载方法 119 2.11.2 重载构造函数方法 121 2.11.3 共享方法、变量和事件 122 2.11.4 运算符重载 127 2.11.5 委托 129 2.11.6 类和组件 133 2.11.7 lambda表达式 134 2.12 小结 135 第3 定制对象 137 3.1 继承 138 3.1.1 继承的实现 139 3.1.2 继承的层次 157 3.1.3 与基类、类及对象交互 159 3.1.4 构造函数 164 3.1.5 protected作用域 169 3.1.6 事件与继承 171 3.1.7 共享方法 174 3.1.8 共享事件 176 3.1.9 创建抽象基类 176 3.2 多接口 178 3.2.1 对象接口 178 3.2.2 辅助接口 180 3.3 抽象性 185 3.4 封装性 188 3.5 多态性 190 3.5.1 方法签名 190 3.5.2 实现多态性 191 3.6 进一步讨论继承 200 3.7 小结 211 第4 公共语言运行库 213 4.1 .net应用程序的组成元素 214 4.1.1 模块 214 4.1.2 程序集 215 4.1.3 类型 215 4.2 版本化与部署 216 4.2.1 对版本化更好的支持 216 4.2.2 major.minor.build.revision版本介绍 217 4.2.3 更好的部署 217 4.3 跨语言集成 218 4.3.1 通用类型系统 218 4.3.2 元数据 219 4.3.3 对元数据更好的支持 219 4.3.4 属性 220 4.3.5 reflection api 222 4.4 il反汇编程序 222 4.5 内存管理 223 4.5.1 传统的垃圾回收机制 223 4.5.2 更快地为对象分配内存 230 4.5.3 垃圾回收器的优化 231 4.6 名称空间 232 4.6.1 名称空间的概念 233 4.6.2 名称空间与引用 236 4.6.3 常用的名称空间 237 4.6.4 导入名称空间并指定别名 239 4.6.5 为名称空间指定别名 240 4.6.6 在asp.net中引用名称空间 241 4.7 创建自己的名称空间 241 4.8 my关键字 244 4.8.1 my.application名称空间 244 4.8.2 my.computer名称空间 248 4.8.3 my.forms名称空间 251 4.8.4 my.resources名称空间 251 4.8.5 my.user名称空间 251 4.8.6 my.webservices名称空间 251 4.9 扩展my名称空间 252 4.10 小结 254 第5 用visual basic进行声明式编程 255 5.1 声明式编程与visual basic 256 5.2 使用xaml创建窗口 257 5.3 xaml语法 260 5.3.1 xaml语言基础 261 5.3.2 使用xaml声明工作 264 5.4 小结 265 第6 异常处理和调试 267 6.1 visual studio 2010 team system的新增内容:历史调试 267 6.2 与visual basic 6兼容的注意事项 268 6.3 .net中的异常处理 268 6.4 结构化异常处理的关键字 269 6.4.1 try、catch和finally关键字 270 6.4.2 throw关键字 271 6.4.3 抛出新的异常 272 6.4.4 exit try语句 273 6.4.5 嵌套的try结构 274 6.4.6 异常属性的使用 275 6.4.7 message属性 276 6.4.8 innerexception和targetsite属性 276 6.5 与visual basic 6样式的错误处理交互操作 280 6.6 记录错误 281 6.6.1 事件日志 281 6.6.2 事件、方法和属性 282 6.6.3 写入跟踪文件 284 6.7 小结 286 第7 测试驱动的开发 287 7.1 测试的内容和方式 288 7.2 visual studio中的tdd工具 290 7.3 单元测试过程 291 7.3.1 创建测试程序 291 7.3.2 运行测试程序 294 7.3.3 测试数据访问代码 295 7.3.4 使用generate from usage特性 302 7.4 其他visual studio版本 306 7.5 第三方测试框架 306 7.6 小结 307 第ii部分 业务对象和数据访问第8 数组、集合和泛型 311 8.1 数组 312 8.1.1 多维数组 313 8.1.2 ubound函数 314 8.1.3 redim语句 314 8.1.4 preserve关键字 315 8.2 集合 315 8.2.1 循环语句 317 8.2.2 装箱 319 8.3 泛型 320 8.3.1 泛型的使用 321 8.3.2 nullable类型 322 8.3.3 泛型类型 323 8.3.4 泛型方法 326 8.4 创建泛型 327 8.4.1 泛型类型 328 8.4.2 泛型方法 334 8.4.3 约束 335 8.4.4 泛型和后期绑定 338 8.4.5 协变和逆变 339 8.5 小结 340 第9 在vb中使用xml 341 9.1 xml简介 342 9.2 xml序列化 343 9.3 system.xml文档支持 348 9.4 xml样式分析程序 348 9.4.1 写入xml 349 9.4.2 读取xml 352 9.4.3 文档对象模型(dom) 360 9.5 xslt转换 364 9.5.1 使用xslt转换不同的xml标准 367 9.5.2 system.xml.xsl中定义的其他类和接口 370 9.6 asp.net中的xml 370 9.6.1 xmldatasource服务器控件 370 9.6.2 xmldatasource控件的名称空间问题 374 9.6.3 xml服务器控件 375 9.7 linq to xml 376 9.8 linq to xml帮助对象 376 9.8.1 xdocument对象 377 9.8.2 xelement对象 377 9.8.3 xnamespace对象 378 9.8.4 xattribute对象 380 9.9 visual basic和xml字面量 381 9.10 使用linq查询xml文档 382 9.10.1 查询静态的xml文档 382 9.10.2 查询动态的xml文档 384 9.11 处理xml文档 385 9.11.1 读取xml文档 385 9.11.2 写入xml文档 386 9.12 vb中的lambda表达式 387 9.13 小结 389 第10 ado.net和linq 391 10.1 ado.net的体系结构 392 10.2 ado.net的基本功能 393 10.2.1 ado.net的常见任务 393 10.2.2 ado.net的基本名称空间和类 398 10.2.3 ado.net组件 399 10.3 .net数据提供程序 400 10.3.1 connection对象 400 10.3.2 command对象 401 10.3.3 通过command对象使用存储过程 402 10.3.4 datareader对象 405 10.3.5 命令的异步执行 407 10.3.6 dataadapter对象 409 10.3.7 sql server .net数据提供程序 413 10.3.8 ole db .net数据提供程序 413 10.4 dataset组件 413 10.4.1 datatablecollection对象 414 10.4.2 datarelationcollection对象 414 10.4.3 extendedproperties属性 414 10.4.4 创建和使用dataset对象 415 10.4.5 ado.net的datatable对象 417 10.4.6 dataset和datatable对象的高级ado.net特性 418 10.5 使用通用提供程序模型 420 10.6 ado.net中的连接池 422 10.7 transactions类和system.transactions名称空间 423 10.7.1 创建事务 423 10.7.2 创建资源管理器 425 10.8 linq to sql 425 10.9 linq to sql和visual basic 426 10.9.1 用linq to sql提取数据:创建控制台应用程序 426 10.9.2 o/r设计器 427 10.9.3 创建product对象 428 10.10 对象到linq对象的映射 429 10.10.1 datacontext对象 430 10.10.2 table(tentity)对象 432 10.11 查询数据库 433 10.11.1 使用查询表达式 433 10.11.2 查询表达式详述 433 10.11.3 用表达式过滤 434 10.11.4 联接 434 10.11.5 数据项的组合 435 10.12 存储过程 437 10.13 更新数据库 438 10.14 小结 440 第11 使用entity framework访问数据 441 11.1 对象关系映射 441 11.2 entity framework体系结构 442 11.2.1 概念模型 443 11.2.2 存储模型 446 11.2.3 映射模型 447 11.2.4 linq to entities 448 11.2.5 objectcontext 449 11.3 把对象映射到实体上 451 11.3.1 简单映射 451 11.3.2 对多个对象使用一个表 453 11.3.3 对一个对象使用多个表 455 11.4 从模型中生成数据库 457 11.5 小结 460 第12 使用sql server 461 12.1 sql server compact 462 12.1.1 连接sql server compactedition数据库 463 12.1.2 同步数据 466 12.2 sql server内置的xml功能 472 12.3 sql server中的clr集成 474 12.3.1 决定使用t-sql还是vb 475 12.3.2 创建用户定义的类型 475 12.3.3 创建存储过程 487 12.3.4 在sql server中使用web服务 493 12.3.5 sql server 2008特性 498 12.4 wcf数据服务 499 12.4.1 rest 499 12.4.2 atom和json 499 12.4.3 使用wcf数据服务提供数据 500 12.4.4 wcf数据服务的客户端库 504 12.5 小结 508 第13 服务(xml/wcf) 509 13.1 服务 510 13.1.1 网络角度 510 13.1.2 应用程序的发展 510 13.1.3 合并网络和应用程序开发 510 13.1.4 web服务基础 511 13.1.5 存在的问题 512 13.1.6 其他技术 512 13.1.7 web服务 513 13.1.8 组合起来 514 13.1.9 wcf服务的构成 514 13.2 向soa迈出一大步 515 13.2.1 wcf的功能 516 13.2.2 协定和元数据 516 13.2.3 使用ws-*协议 517 13.3 建立wcf服务 518 13.4 建立wcf使用者应用程序 524 13.4.1 添加服务引用 525 13.4.2 查看引用 526 13.4.3 配置文件的修改 529 13.4.4 编写使用者应用程序的代码 531 13.5 使用数据协定 533 13.6 名称空间 535 13.6.1 建立主机应用程序 535 13.6.2 建立使用者应用程序 536 13.6.3 查看hellocustomerservice的wsdl和架构 538 13.7 小结 540 第iii部分 智能客户端应用程序第14 windows窗体 543 14.1 system.windows.forms名称空间 543 14.2 窗体的使用 544 14.2.1 设置启动窗体 544 14.2.2 通过sub main显示窗体 545 14.2.3 application类的更多内容 545 14.2.4 窗体的启动位置 545 14.2.5 窗体边框 545 14.2.6 始终置顶——topmost属性 546 14.2.7 附属窗体 546 14.2.8 改变窗体的透明度 547 14.2.9 可视化继承 549 14.2.10 滚动窗体 549 14.2.11 mdi窗体 549 14.2.12 vb 2010中的mdi样例 550 14.2.13 对话框窗体 551 14.2.14 运行时的窗体 553 14.2.15 默认的窗体实例 554 14.3 控件 554 14.3.1 以tab键切换控件的顺序 554 14.3.2 所有控件的属性 555 14.3.3 动态调整控件的大小和布局 555 14.3.4 flowlayoutpanel控件 557 14.3.5 tablelayoutpanel控件 558 14.3.6 panel和groupbox容器控件 559 14.3.7 扩展的provider控件 560 14.3.8 数据输入的高级功能 562 14.3.9 验证数据输入 564 14.3.10 工具栏与toolstrip控件 565 14.3.11 菜单 568 14.3.12 通用对话框 569 14.3.13 拖放操作 571 14.3.14 标准windows窗体控件小结 573 14.3.15 处理相关控件组 575 14.3.16 在运行时添加控件 576 14.4 其他编程技巧 577 14.5 小结 577 第15 windows窗体的高级功能 579 15.1 在可视化控件中封装逻辑 579 15.2 在windows窗体中开发自定义的控件 580 15.2.1 继承现有的控件 580 15.2.2 构建复合控件 580 15.2.3 从头编写控件 581 15.3 继承现有的控件 581 15.3.1 基本步骤 581 15.3.2 给派生的控件添加代码 581 15.3.3 其他有用的特性 584 15.3.4 为派生的控件自定义事件 585 15.3.5 限制选中项数的checkedlistbox 586 15.4 control与usercontrol基类 589 15.4.1 control类 589 15.4.2 usercontrol类 589 15.5 复合控件 590 15.5.1 创建复合的user-control 591 15.5.2 改变控件的大小 591 15.5.3 提供子控件的属性 592 15.5.4 一个具体的例子 592 15.6 从头构建控件 595 15.7 给工具箱中的控件添加图标 600 15.8 在控件中嵌入其他控件 601 15.9 小结 602 第16 集成wpf和windows 窗体的用户控件 605 16.1 集成库 606 16.2 在windows窗体中包含wpf控件 607 16.2.1 创建wpf控件库 608 16.2.2 windows窗体应用程序 610 16.3 在wpf中包含windows 窗体控件 616 16.4 集成的限制 621 16.5 小结 622 第17 wpf桌面应用程序 623 17.1 内容、位置、原因、方式——wpf策略 624 17.2 光栅图形和矢量图形 625 17.3 下一个windows项目应使用wpf吗 625 17.4 创建wpf应用程序 626 17.4.1 实现定制的wpf应用程序 627 17.4.2 定制用户界面 639 17.4.3 定制按钮 647 17.4.4 wpf用户控件 651 17.5 小结 672 第18 expression blend 3 675 18.1 了解blend 676 18.2 sketchflow 682 18.2.1 第一个sketchflow 682 18.2.2 sketchflow player 685 18.2.3 归档sketchflow 686 18.3 小结 686 第19 silverlight 687 19.1 什么是silverlight 687 19.1.1 smooth streaming特性 688 19.1.2 业界标准视频 688 19.1.3 数字版权管理 688 19.2 启动silverlight项目 688 19.2.1 silverlight应用程序 689 19.2.2 silverlight导航应用程序 689 19.2.3 silverlight类库 690 19.3 silverlight解决方案 691 19.3.1 web应用程序 691 19.3.2 应用程序库缓存 691 19.3.3 silverlight应用程序 692 19.4 控件 695 19.5 给silverlight项目添加项 702 19.5.1 silverlight用户控件 703 19.5.2 silverlight应用程序类 703 19.5.3 silverlight页面 703 19.5.4 silverlight子窗口 703 19.5.5 silverlight模板控件 703 19.5.6 silverlight资源字典 704 19.6 浏览器之外的silverlight 704 19.7 小结 705 第iv部分 internet应用程序技术 第20 silverlight和服务 709 20.1 服务和silverlight 709 20.1.1 asmx web服务 709 20.1.2 wcf服务 712 20.1.3 ado.net数据服务 716 20.2 model-view-viewmodel 725 20.2.1 分割 725 20.2.2 model 725 20.2.3 view 728 20.2.4 viewmodel 729 20.3 小结 729 第21 使用asp.net 731 21.1 asp.net的历史 731 21.2 asp.net的重要特性 732 21.2.1 开发效率 732 21.2.2 性能和可伸缩性 732 21.2.3 本地化 732 21.2.4 健康监控 733 21.2.5 易于访问数据 733 21.2.6 管理和维护 733 21.3 visual studio对asp.net 的支持 733 21.3.1 web site和web application项目 733 21.3.2 asp.net应用程序文件夹 734 21.3.3 web服务器选项 735 21.4 用web窗体构建asp.net应用程序 735 21.5 数据驱动的应用程序 746 21.5.1 使用sqldatasource控件绑定数据 746 21.5.2 使用linqdatasource控件绑定数据 754 21.5.3 使用objectdastasource控件绑定数据 757 21.6 小结 759 第22 asp.net的高级功能 761 22.1 母版页 761 22.1.1 创建母版页 762 22.1.2 创建内容页 765 22.1.3 为母版页提供默认内容 767 22.2 导航 767 22.2.1 使用sitemappath服务器控件 769 22.2.2 menu服务器控件 770 22.3 使用asp.net的提供程序模型 771 22.4 成员和角色管理 776 22.5 配置文件属性 781 22.6 microsoft ajax(asp.net ajax) 783 22.6.1 理解对ajax的需求 783 22.6.2 microsoft ajax 的实现 784 22.6.3 updatepanel控件和客户端服务调用 785 22.6.4 示例项目 785 22.6.5 添加updatepanel控件 789 22.6.6 使用客户端服务调用和客户端模板 790 22.7 小结 795 第23 asp.net mvc 797 23.1 mvc和asp.net 798 23.2 构建asp.net mvc应用程序 798 23.2.1 创建项目 798 23.2.2 控制器和操作 800 23.2.3 添加模型 802 23.2.4 视图 804 23.2.5 路由 807 23.2.6 搭框架和crud操作 808 23.2.7 验证 815 23.3 小结 817 第24 sharepoint 2010开发 819 24.1 简介 819 24.1.1 sharepoint foundation 2010 820 24.1.2 sharepoint server 2010 820 24.1.3 sharepoint的术语 820 24.1.4 sharepoint开发环境 821 24.2 feature和solution framework 821 24.2.1 feature 821 24.2.2 solution framework 829 24.3 用于sharepoint开发的visual studio工具 833 24.4 sharepoint 2010对象模型 839 24.4.1 服务器对象模型 840 24.4.2 客户端对象模型 843 24.5 构建web 部件 845 24.6 小结 851 第v部分 库和专业主题技术 第25 visual studio tools foroffice 855 25.1 vsto的各个版本 856 25.1.1 office的自动化功能和vsto 856 25.1.2 免pia部署 856 25.1.3 vsto项目类型 857 25.2 office业务应用程序的体系结构 858 25.3 使用vba和vsto 859 25.4 创建文档模板(word) 864 25.4.1 给文档添加内容 866 25.4.2 添加ribbon和操作窗格 867 25.4.3 激活操作窗格 870 25.4.4 更新内容控件 872 25.5 创建office插件(excel) 875 25.6 outlook form regions 881 25.7 小结 889 第26 windows workflow foundation 891 26.1 应用程序中的工作 891 26.2 建立工作 892 26.2.1 用windows workflowfoundation添加工作 892 26.2.2 一个简单的工作 894 26.2.3 标准活动 897 26.2.4 一个不太简单的工作 899 26.2.5 建立定制活动 907 26.2.6 动态加载工作 911 26.3 重新构建工作设计器 912 26.4 小结 915 第27 本地化 917 27.1 文化和区域 917 27.1.1 理解文化类型 918 27.1.2 线程 919 27.1.3 在asp.net中声明全局文化 921 27.1.4 在asp.net中使用文化设置 922 27.2 转换数值和操作 923 27.2.1 理解日期之间的区别 923 27.2.2 理解数字和货币的区别 925 27.2.3 理解排序字符串的区别 927 27.3 asp.net资源文件 929 27.3.1 使用本地资源 929 27.3.2 全局资源 933 27.4 windows窗体中的资源文件 935 27.5 小结 938 第28 与com的交互操作 939 28.1 理解com 940 28.2 com和.net的交互 940 28.2.1 传统的组件 941 28.2.2 .net应用程序 942 28.2.3 调试 945 28.2.4 直接使用tlbimp 945 28.2.5 后期绑定 946 28.3 activex控件 950 28.3.1 传统的activex控件 950 28.3.2 另一个.net应用程序 952 28.3.3 再次调试 954 28.4 在com应用程序中使用.net组件 954 28.4.1 .net组件 954 28.4.2 regasm 956 28.4.3 tlbexp 957 28.5 p/invoke 957 28.6 小结 957 第29 网络编程 959 29.1 协议、地址和端口 959 29.1.1 地址与计算机名 961 29.1.2 端口:指定应用程序 961 29.1.3 防火墙:不离不弃 962 29.2 system.net名称空间 963 29.2.1 web请求与响应 963 29.2.2 使用webclient简化常用的web请求 969 29.3 套接字 970 29.3.1 构建应用程序 971 29.3.2 创建conversation窗口 973 29.3.3 发送消息 980 29.3.4 关闭应用程序 984 29.4 在应用程序中使用internetexplorer 988 29.5 小结 991 第30 应用程序服务 993 30.1 给应用程序服务使用iis 993 30.2 windows服务 993 30.3 windows服务的特性 994 30.4 与windows服务交互 995 30.5 创建windows服务 996 30.5.1 用于windows服务的.net framework类 996 30.5.2 其他类型的windows服务 998 30.6 在vb中创建windows服务 998 30.7 创建文件监视器服务 1000 30.7.1 创建windows服务的解决方案 1000 30.7.2 给服务添加.net组件 1000 30.7.3 安装服务 1003 30.7.4 启动服务 1004 30.7.5 卸载服务 1005 30.8 与服务通信 1005 30.8.1 servicecontroller类 1006 30.8.2 把servicecontroller集成到例子中 1007 30.8.3 servicecontroller的更多内容 1008 30.9 定制命令 1008 30.10 给服务传递字符串 1010 30.11 调试服务 1010 30.12 小结 1012 第31 程序集和反射 1013 31.1 程序集 1013 31.2 清单 1014 31.2.1 程序集标识部分 1016 31.2.2 引用的程序集 1018 31.3 程序集与部署 1018 31.3.1 应用程序私有的程序集 1018 31.3.2 共享程序集 1019 31.4 版本化问题 1020 31.4.1 应用程序隔离 1020 31.4.2 并行执行 1020 31.4.3 自描述 1021 31.4.4 版本策略 1021 31.4.5 配置文件 1022 31.5 反射基础 1025 31.5.1 assembly类 1026 31.5.2 获得当前加载的程序集 1026 31.5.3 type类 1027 31.6 程序集的动态加载 1028 31.6.1 assembly类的loadfrom方法 1028 31.6.2 动态加载示例 1029 31.6.3 传入程序集 1030 31.7 小结 1031 第32 .net framework中的安全性 1033 32.1 安全的概念与定义 1034 32.2 system.security.permissions名称空间中的权限 1035 32.2.1 代码访问权限 1037 32.2.2 身份权限 1038 32.2.3 基于角色的权限 1038 32.3 管理代码访问权限集合 1041 32.4 用户访问控制 1043 32.5 定义应用程序的uac设置 1043 32.5.1 安全性工具 1045 32.5.2 使用securityexception类处理异常 1046 32.6 加密基础 1047 32.7 小结 1060 第33 使用任务和线程进行并行编程 1061 33.1 启动并行任务 1061 33.1.1 system.threading.tasks.parallel类 1062 33.1.2 parallel.invoke 1062 33.2 把串行代码转换为并行代码 1066 33.2.1 检测热点 1067 33.2.2 测试并行执行获得的速度提升 1069 33.2.3 理解并行和并发执行 1070 33.3 并行循环 1071 33.3.1 parallel.for 1071 33.3.2 parallel.foreach 1076 33.3.3 退出并行循环 1081 33.4 指定希望的并行度 1086 33.4.1 paralleloptions 1086 33.4.2 理解硬件线程和逻辑核心 1087 33.5 创建和管理任务 1088 33.5.1 system.threading.tasks.task 1089 33.5.2 理解任务的生命周期 1090 33.5.3 使用任务并行化代码 1091 33.5.4 从任务中返回值 1099 33.5.5 为并发和并行准备代码 1102 33.5.6 理解并发集合特性 1103 33.5.7 把linq转换为plinq 1106 33.6 小结 1108 第34 部署 1109 34.1 应用程序部署 1110 34.1.1 .net中的部署很简单 1110 34.1.2 xcopy部署 1110 34.1.3 使用windows installer 1110 34.1.4 clickonce部署 1111 34.2 选择framework版本 1111 34.3 visual studio部署项目 1112 34.3.1 项目模板 1112 34.3.2 创建部署项目 1113 34.4 修改部署项目 1117 34.4.1 项目属性 1117 34.4.2 file system编辑器 1119 34.4.3 registry编辑器 1122 34.4.4 file types编辑器 1124 34.4.5 user interface编辑器 1125 34.4.6 custom actions编辑器 1127 34.4.7 launch conditions编辑器 1129 34.4.8 构建 1132 34.5 windows应用程序的internet部署 1132 34.5.1 “无接触”部署 1132 34.5.2 clickonce部署 1133 34.6 iis web部署工具 1140 34.7 小结 1142 第vi部分 附 录 附录 a vb编译器 1145 附录 b visual basic powerpacks tools 1161 附录 c workflow 2008 1173 附录 d 企业服务 1193 附录 e 云的编程 1215
目录 1 虚拟文件系统概述 5 1.1 通用文件模型 7 1.2 VFS所处理的系统调用 9 2 虚拟文件系统架构 11 2.1 VFS对象数据结构 11 2.1.1 超级块对象 11 2.1.2 索引节点对象 15 2.1.3 文件对象 18 2.1.4 目录项对象 22 2.2 把Linux中的VFS对象串联起来 24 2.2.1 与进程相关的文件 25 2.2.2 索引节点高速缓存 29 2.2.3 目录项高速缓存 30 2.2.4 VFS对象的具体实现 32 2.3 文件系统的注册与安装 38 2.3.1 文件系统类型注册 38 2.3.2 文件系统安装数据结构 41 2.3.3 安装普通文件系统 52 2.3.4 分配超级块对象 58 2.3.5 安装根文件系统 60 2.3.6 卸载文件系统 65 2.4 路径名的查找 66 2.4.1 查找路径名的一般程 67 2.4.2 父路径名查找 82 2.4.3 符号链接的查找 84 2.5 VFS系统调用的实现 88 2.5.1 open()系统调用 88 2.5.2 read()和write()系统调用 96 2.5.3 close()系统调用 97 3 第二扩展文件系统 99 3.1 Ext2磁盘数据结构 101 3.1.1 磁盘超级块 102 3.1.2 组描述符和位图 105 3.1.3 磁盘索引节点表 105 3.2 VFS接口数据结构 110 3.2.1 Ext2 超级块对象 110 3.2.2 Ext2 的索引节点对象 121 3.2.3 创建Ext2文件系统 124 3.2.4 Ext2的方法总结 126 3.3 Ext2索引节点分配 129 3.3.1 创建索引节点 130 3.3.2 删除索引节点 143 3.4 Ext2数据块分配 144 3.4.1 数据块寻址 145 3.4.2 文件的洞 147 3.4.3 分配数据块 148 4 页面高速缓存 160 4.1 页高速缓存数据结构 160 4.1.1 address_space对象 161 4.1.2 基树 164 4.2 高速缓存底层处理函数 166 4.2.1 查找页 166 4.2.2 增加页 168 4.2.3 删除页 173 4.3 文件系统与高速缓存 175 4.3.1 缓冲头数据结构 175 4.3.2 分配块设备缓冲区页 178 4.3.3 释放块设备缓冲区页 184 4.4 在页高速缓存中搜索块 185 4.4.1 __find_get_block()函数 185 4.4.2 __getblk()函数 188 4.4.3 __bread()函数 190 4.5 把脏页写入磁盘 191 4.5.1 pdflush内核线程 192 4.5.2 搜索要刷新的脏页 193 4.5.3 回写陈旧的脏页 196 5 文件读写 199 5.1 系统调用VFS层的处理 200 5.2 第二扩展文件系统Ext2层的处理 201 5.2.1 Ext2的磁盘布局 202 5.2.2 Ext2的超级块对象 206 5.2.3 Ext2索引节点对象的创建 210 5.2.4 Ext2索引节点对象的读取 218 5.2.5 Ext2层读文件入口函数 225 5.3 页高速缓存层的处理 237 5.3.1 创建一个bio请求 238 5.3.2 得到文件的逻辑块号 244 5.3.3 普通文件的readpage方法 251 5.3.4 块设备文件的readpage方法 252 5.3.5 文件的预读 260 5.4 通用块层的处理 264 5.4.1 块设备的基础知识 265 5.4.2 通用块层相关数据结构 269 5.4.3 提交I/O传输请求 271 5.4.4 请求队列描述符 273 5.5 块设备I/O调度层的处理 281 5.5.1 块设备的初始化 284 5.5.2 建立块设备驱动环境 288 5.5.3 关联block_device结构 295 5.5.4 为设备建立请求队列 306 5.5.5 块设备I/O调度程序 311 5.5.6 真实的I/O调度层处理 321 5.6 块设备驱动层的处理 330 5.6.1 scsi总线驱动的初始化 330 5.6.2 scsi设备驱动体系架构 342 5.6.3 scsi块设备驱动层处理 347 5.6.4 scsi命令的执行 369 5.6.5 scsi命令的第一次转变 372 5.6.6 scsi命令的第二次转变 380 5.7 写文件 384 5.7.1 generic file_write函数 384 5.7.2 普通文件的prepare_write方法 386 5.7.3 块设备文件的prepare_write方法 387 5.7.4 将脏页写到磁盘 388 6 直接I/O与异步I/O 391 6.1 直接I/O 391 6.2 异步I/O 393 6.2.1 Linux 2.6中的异步I/O 394 6.2.2 异步I/O环境 394 6.2.3 提交异步I/O操作 395

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值