文件描述字

低级输入输出

文件描述字的打开、创建和关闭

函数open()或create()用于打开或创建一个文件描述字。

#include <sys/types.h>
#include <sys/stat.h>
#inlcude <fcntl.h>
int open(const char *filename, int flags [, mode_t mode]);
int create(const char *filename, mode_t mode);

open()用于打开或创建一个文件,参数filename指明文件;flags给出文件的打开方式,它是一个位串,其值由表3-1列出的各种常数做位或运算(C中的“|”操作)而形成;mode是可选参数,给出文件的访问方式,其值由表4-6列出的各种常数做位或运算而形成,仅当创建文件时才用它指明新文件的访问方式位,其他情况下提供该参数不起作用。

open()调用成功返回为文件filename新创建的一个描述字,同时文件位置定位于文件的开始处;调用失败则不会创建或修改文件。

open()返回的描述字一定是当前最小未使用的描述字。这个特征特别有用,例如,如果一个程序关闭了它的标准输出,然后再调用open()创建或打开另一个文件,则标准输出使用的文件描述字1将成为这个新打开文件的描述字,从而使得标准输出可以重新定向至不同的文件或设备。在3.4节可看到利用这一特征的例子。

open()返回的文件描述字是唯一的,它不会由正在运行的其他进程所共享。如果两个进程同时打开同一个文件,系统会保证各自有自己的文件描述字。如果这两个进程都写这个文件,它们将按照自己的文件位置来写。写入的数据不会交错地记录在文件内,而是一个被另一个所覆盖。为了避免写入的数据被覆盖,可以利用文件锁(10.1节)来协调。

函数close()用于关闭已打开的文件描述字。

#include <unistd.h>
int close (int filedes);

1)文件描述字总是从最小可用描述字开始分配。例如,如果一个进程已打开5个文件,随后关闭了与描述字0相连的文件,则下一个open()成功返回的描述字是0而不是5。
2)子进程总是继承父进程的所有描述字。例如,如果一个进程打开了5个描述字,则它用fork()派生的子进程也继承它的这5个描述字,且这5个描述字指向与父进程相同的文件。这就是为什么每一个进程总是预先有三个打开的描述字0、1和2,因为所有用户进程都是shell进程的子进程,它们继承了由shell打开的这三个描述字。标准输入输出重定向便利用了这个特点和特点1。
3)每一次write()完成之后,系统打开文件表项中当前文件位置将增加所写的字节数。如果这导致当前文件位置超出了当前文件大小,则用当前文件位置更新inode中的当前文件大小,此时称文件被扩展。
4)如果文件是用O_APPEND标志打开的,该标志将被设置在系统文件打开表项的文件状态标签中。每当对此文件执行write()时,系统打开文件表项中当前文件位置将首先移到由inode给出的当前文件大小所指出的位置,从而强制write()只能在当前文件尾添加数据。
5)如果一个文件位置被lseek()定位于当前文件尾,所发生的只是用inode中的当前文件大小设置系统打开文件表中的当前文件位置。
6)一个进程或不同进程的多个文件描述字可以指向同一个系统打开文件表项,这对应于单次打开文件但复制了多个描述字的情形,如通过dup()或通过fork()继承。多个系统打开文件表项也可以指向同一个vnode结构,这对应于一个进程多次打开或者多个进程同时打开同一文件的情形。

read()和write()函数

对文件描述字进行基本输入输出操作的函数是read()和write()。

#inlcude <unistd.h>
ssize_t read(int filedes, void *buffer, size_t nbytes);
ssize_t write(int filedes, const void *buffer, size_t nbytes);

read()从已打开的、与文件描述字filedes相连的文件中读至多nbytes个字节的数据放到buffer所指缓冲区中。它的正常返回值是实际读入的字节数,若在实际读入字节之前便遇到文件尾,其返回值为0。read()是所有读流的函数(如fgets()等)的低级原语。

数据类型ssize_t与size_t类似,UNIX系统也用它表示在一次操作中可以读写的块大小。不过与size_t不同的是,它是一个有符号整型以便能够表示错误情形的返回值。read()返回的实际读入字节数在以下几种情况下可能小于nbytes:
当读普通文件且在还未读够所请求的字节数便遇到了文件尾时。例如,当文件中只剩下30个字节便到达文件尾,而我们却企图读100个字节时,read()将返回30。下一次再调用read(),它将返回0指出文件结束。
当所读的文件对应于终端设备时,通常每次至多只读一行。
当从网络读时,网络的内部缓冲可能导致读入的字节数少于所请求的字节数。
当被信号中断时,若已读入若干字节,则返回已读的字节数;否则返回-1,并置errno为EINTR。
对于普通文件或其他可以定位的文件,read()从文件的当前位置开始读数据;在成功返回之前,文件位置增加实际已读的字节数。

read()调用成功的返回值大于等于0,遇到文件尾返回0(除非nbytes之值为0)。若在文件结束时继续调用read(),它将仍然返回0并且无其他动作。

write()将buffer的前nbytes个字节写到与描述字filedes相连的文件。buffer中的数据不必是字符串,空字符同其他字符一样写出。

write()的返回值是实际写出的字节数,正常情况下它等于nbytes,但也可能小于nbytes(例如,被写的物理介质满时)。

程序3-1 用文件描述字函数连接两个文件之例

int main(int argc, char **argv) {
    if (argc != 3) {
		printf("Usage : %s from-file to-file\n", argv[0]);
		exit (1);
	}
	int from, to, n;
	char buf[1024];
	if ((from = open(argv[1], O_RDONLY)) < 0) {
		printf("open failed");
		exit (1);
	}
	if ((to= open(argv[1], O_RDONLY|O_CREAT|O_APPEND, 0644)) < 0) {
		printf("open failed");
		exit (1);
	}
	while ((n = read(from, buf, sizeof(buf))) > 0) {
		write(to, buf, n);
	}
	close(from);
	close(to);
    exit(0);
}

由于每次调用read()和write()都会使得系统访问磁盘,因此在读写时指定较大的缓冲很重要,否则程序的运行效率将会很低。这个例子中指定缓冲大小为1024字节,建议你指定不同的缓冲大小来运行这个程序,从中仔细体会缓冲大小对效率的影响。

设置描述字的文件位置

如同可以用fseek()设置流的文件位置一样,对文件描述字也有类似的操作。函数lseek()可改变一个描述字相连文件的文件位置。

#include <sys/types.h>
#include <unistd.h>
off_t lseek (int filedes, off_t offset, int whence);

参数filedes给出已打开的文件描述字,offset指明相对whence的位移字节数,whence的取值同fseek()一样,只能是符号常数SEEK_SET、SEEK_CUR、SEEK_END之一,分别指明offset是相对文件开始、当前文件位置还是文件尾的偏移。
lseek()的返回值是新文件位置相对文件开始的字节数。例如,如下调用

lseek(fd, 0L, SEEK_SET);
lseek(fd, 0L, SEEK_END):

分别移动文件位置到文件开始和文件尾。而

n = lseek(fd, 0, SEEK_CUR);

读当前文件位置值。描述字没有专门的与ftell()类似的函数。

lseek()只移动文件的当前位置,它并不引起任何I/O动作。文件的新位置可以大于文件的当前大小,在这种情况下,下一次写将扩展该文件。这种情况也称为在文件中生成“空洞”,因为系统并不真正在磁盘为这段区间保留存放空间。如果在数据尾部之后到lseek()设置的偏移量之前这段区域的数据尚未写入,则后继读这段区域将读出0,程序3-2是这种情况的一个示例程序。

例3-2 程序3-2用lseek创建一个含有空洞的文件。

char buf1[] = "abcdefghij";
char buf2[] = "ABCDEFGHIJ";
int main () {
    int fd;
	if ((fd = open("file.hole",O_WRONLY|O_CREAT/*|O_APPEND*/, 0644)) < 0) {
		perror("create error");
		exit(1);
	}
	if (write(fd, buf1, 10) != 10) {
		perror("create error");
		exit(1);
	}
	if (lseek(fd, 40, SEEK_SET) == -1) {
		perror("lseek error");
		exit(1);
	}
	if (write(fd, buf2, 10) != 10) {
		perror("create error");
		exit(1);
	}
    return 0;
}

当同一个文件有多个打开的描述字时(被多次打开或由dup()重复),由不同open()得到的文件描述字具有独立的文件位置。lseek()只作用于指定的描述字而不会对另外的描述字造成影响,例如:

{
    int d1,d2;
	char buf[4];
	d1 = open("file", O_RDONLY);
	d2 = open("file", O_RDONLY);
	lseek(d1, 1024, SEEK_SET);
	read(d2, buf, 4);
}

read()将读文件foo的前4个字符。但是,由dup()重复而得的描述字与原描述字共享一个公共的文件位置,改变两个重复描述字中一个的文件位置,包括读或写数据,同时也改变了另一个描述字的文件位置。例如:

{
    int d1, d2;
	char buf1[80], buf2[80];
	d1 = open("file", O_RDONLY);
	d2 = dup(d1);
	lseek(d1, 1024, SEEK_SET);
	read(d1, buf1, 4);
	read(d2, buf2, 4);
}

第一个read()将从foo的第1024个字符处开始读4个字节,第二个read()则从第1028个字符处再读4个字节。

dup()和dup2()函数

用同一个open()打开的文件可以有多个描述字与它相连,这种描述字称为重复描述字。重复一个描述字有两种方法:用函数dup()或dup2(),或用函数fcntl()。

#include <unistd.h>
int dup(int old);
int dup2(int old, int new);

dup()复制描述字old至一个新描述字,新描述字保证是当前未打开的最小编号可用描述字。dup2()复制描述字old至编号为new的描述字。如果new已经打开,它将首先被关闭。如果new等于old,dup2()返回new但不关闭它。

假定进程一开始便执行

newfd = dup(1);

因此newfd的值一定是3(因为描述字0、1、2已经由shell打开),它与描述字1都指向标准输出文件,因为它的进程打开文件表项由描述字1的表项复制而来。

正因为重复描述字共享同一个系统打开文件表项,因此,它们共享文件位置和一组文件状态标签。但是它们都有自己的文件描述字标签。这两个dup函数总是清除新描述字中的执行即关闭标签FD_CLOEXEC。

重复一个文件描述字的主要用途是实现输入输出重定向,即改变一个特定文件描述字对应的文件或管道。当使用管道进行进程间的通信时,这两个函数十分有用。

例3-3 程序3-3是用dup2()简单重定向的例子。它将标准输出文件重定向至名为myoutput的文件。运行这个程序可以看到printf()的输出不在终端而在文件myoutput中。

int main () {
    int fd;
    if ((fd = open("myoutput", O_WRONLY|O_CREATE, 0644)) == -1) {
		perror("open failed");
		exit(1);
	}
	if (dup2(fd, STDOUT_FILENO) == -1) {
		perror("dup2 failed");
		exit(1);
	}
	printf("this is a test program for redirect \n");
	close(fd);
    exit(0);
}

fdopen()和fileno()函数

文件描述字函数是流函数的初等函数,每一个流都与一个描述字相连。给定一个打开的文件描述字,可以用fdopen()函数为它创建一个流。反过来,已知一个流,也可以用fileno()函数得到它的文件描述字。

#include <stdio.h>
FILE *fdopen(int filedes, const char *opentype);
int fileno(file *stream);

fdopen()使描述字filedes与一个流相连。它的返回值是一个新的流,如果不能创建此流,则返回空指针。

参数opentype的取值与fopen()的opentype参数完全相同,但“w”和“w+”不导致文件截断,因为截断是文件打开时的动作,而在此情形下,文件已经被打开。注意,调用fdopen()时必须保证opentype参数与打开文件描述字时使用的opentype参数一致。

fdopen()建立的新流的文件位置与描述字filedes的文件位置相同,且流的错误指示器和文件结束指示器均被清除。fdopen()的实质是为已打开的文件描述字提供标准I/O缓冲。

fileno()函数返回与流stream相连的文件描述字。利用它可以确定流的底层文件描述字。例如,当调用dup()或fcntl()时就需要知道与流相连的文件描述字。

文件控制函数fcntl()

函数fcntl()提供了进一步管理低级文件描述字的各种手段,用它可以对已打开的描述字执行各种控制操作,例如,重复一个文件描述字,查询或设置文件描述字标签,查询或设置文件描述字标签或文件状态标签,操纵文件锁等。

#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
int fcntl(int filedes, int cmd, ...);

fcntl()对打开的文件描述字filedes执行各种控制操作,具体是哪一种操作由它的第二个参数cmd给出。根据参数cmd的值,有一些操作还要求提供第三个参数。除了文件锁操作之外,其他操作均要求第三个参数是int类型。对于文件锁操作,第三个参数是一个指向结构struct flock的指针。fcntl()的正常返回值也取决于cmd参数。

重复文件描述字

调用

fcntl(filedes, F_DUPFD, arg);

的作用是重复文件描述字filedes:它返回一个其值等于或大于arg的新文件描述字。该文件描述字与filedes共享同一个系统打开文件表,但有自己的文件描述字标签,并且它的FD_CLOEXEC标签被清除。

dup()是fcntl()这种操作的特例,它等价于

fcntl(fd, F_DUPFD, 0);

而dup2()在filedes与arg不同且filedes是合法的文件描述字的情况下,等价于

close(arg);
fcntl(fd, F_DUPFD, arg);

不过,dup2()是原子操作,即关闭arg以及复制filedes这两个动作的完成保证是完整的,在它们之间不会出现arg被关闭但filedes还未复制便被中断的情形。

文件描述字标签

如下调用:

fcntl(filedes, F_SETFD, flags);

设置与文件filedes相连的文件描述字标签,它需要类型为int的第三个参数给出新标签值。

例3-4 当修改文件描述字标签时,首先应当用F_GETFD获得当前标志,然后修改其值。不要假定本书所列的标志是实现提供的全部标志。尽管当前只支持一个标志,但我们写的程序可能从现在起还需运行多年且今后可能存在更多标志。应当如程序3-4所示来设置或清除标志FD_CLOEXEC,这样便不会对其他标志造成影响。

int set_cloexec_flag(int desc, int value) {
	int oldflags = fcntl(desc, F_GETFD, 0);
	if (oldflags < 0)
		return oldflags;
	if (value != 0) {
		oldflags |= FD_CLOEXEC;
	} else {
		oldflags &= ~FD_CLOEXEC;
	}
	return fcntl(desc, F_SETFD, oldflags);
}

文件状态标签

文件状态标签指明文件的打开属性,它们由open()的flags参数指明。与描述字标签不同,文件状态标签由与同一次打开文件相连的所有重复文件描述字所共享。文件状态标签中的标志可分为三类:访问方式、打开时标志和I/O操作方式。

1.访问方式
访问方式指明允许文件描述字用于读、写或两者兼之,包括O_RDONLY、O_WRONLY和O_RDWR。这些访问方式在文件被打开时选定,之后便不能再改变。
为了确定文件访问方式,必须从fcntl()取回的文件状态标签中抽取访问方式。因为读和写访问方式不一定是独立的标志位,因此抽取文件访问方式的可移植方法是用宏常数O_ACCMODE位串与文件状态标签值作按位与操作(C中的’&'运算),由此生成表示文件访问方式的值,即O_RDONLY、O_WRONLY或O_RDWR(参见程序3-5)。

2.打开时标志
打开时标志指明打开文件时影响open()行为的一些选项。这些选项一旦文件打开就不保留,但有一个例外是O_NONBLOCK,因为O_NONBLOCK同时也是一个I/O操作方式,故此标志被保留。
O_CREAT:若设置,当该文件不存在时创建此文件。
O_EXCL:若O_CREAT和O_EXCL同时设置,则当指定的文件已经存在时,open()失 败。这保证不会破坏已存在的文件。
O_NONBLOCK:防止open()为打开文件而阻塞很长时间。这通常仅对诸如串行端口的设备文件才有意义。
O_NONBLOCK标志同时也作为I/O操作方式标志,这意味着在open()中指明O_NONBLOCK就同时设置了非阻塞I/O方式。为了非阻塞地打开一个文件且不影响正常的阻塞I/O,必须先设置O_NONBLOCK来调用open(),然后调用fcntl()关闭此位。
O_NOCTTY:若命名的文件是终端设备,不让它成为该进程的控制终端。
O_TRUNC:截断文件为零长度,这一选项只对普通文件有用,对诸如目录 或FIFO之类的特殊文件无用。由open()来做截断而不直接调用ftruncate()函数并没有什么太好的理由,只是因为O_TRUNC标志在ftruncate()引入之前就已存在于UNIX中,保留它只是为了向下兼容。

3.I/O操作方式
I/O操作方式影响使用文件描述字进行输入输出操作的工作方式。这些标志由open()设置,之后可以用fcntl()获取和改变。
O_APPEND:文件的附加方式位。若此位设置,所有write()操作写数据至文件尾而不管文件位置在何处。这是附加数据至文件尾唯一可靠的方法。用附加方式可以保证无论是否有其他进程正在写同一个文件,write()操作总是将数据写在当前文件尾。相反,在未设置此位的情况下,如果通过简单地移动文件位置到文件尾,然后再写数据,则在设置文件位置之后开始写之前,可能有其他进程扩展此文件(对应于两个不同的进程打开同一个文件的情形,它们共享同一个vnode,但各自有自己的系统打开文件表,因而有自己的文件位置,参见图3-2),从而导致所写的数据出现在实际文件尾之前的某个地方。
O_NONBLOCK:此标志同时也作为I/O操作方式标志。如果这一位设置,对文件的read()请求,当无立即可用的输入时能以EAGAIN错误状态立即返回,而不是阻塞。类似地,write()请求也能在输出不能写出时以EAGAIN错误状态立即返回。

4.获取和改变文件状态标签
文件状态标签可以用fcntl()函数获取或改变。如下调用:

fcntl(filedes, F_GETFL, new_flags);

获取描述字filedes的文件状态标签。它的正常返回值是一个非负整数,此数解释为各个独立标志的按位或。因为文件访问方式不是用独立的位表示的,所以,为比较它们需要用O_ACCMODE屏蔽返回值中的其他位。

例3-5 程序3-5是读取文件状态标签的例子,它打印出指定的文件描述字中已设置了的文件状态标签。为了读取文件访问方式标志,我们先用fcntl()返回的标签值val和O_ACCMODE进行“与”操作,然后再比较其结果。而对于其他标志,则直接从val中抽取。

int display_file_status_flags(int fd) {
	int accmode, val;
	if((val = fcntl(fd, F_GETFL, 0)) < 0){
		perror("error");
		exit(1);
	}
	accmode = val & O_ACCMODE;
	if (accmode == O_RDONLY)
		printf("read only");
	else if (accmode == O_WRONLY)
		printf("write only");
	else if (accmode == O_RDWR)
		printf("read write");
	else {
		printf("unknown access mode\n");
		exit(1);
	}
	if (val & O_APPEND)
		printf(", append");
	if (val & O_NONBLOCK)
		printf(", nonblocking");
#if !defined(POSIX_SOURCE) && defined (O_SYNC)
	if (val & O_SYNC)
		printf(", synchronous writes");
#endif
	putchar('\n');
	exit(0);
}

为了设置描述字filedes的文件状态标签,用命令F_SETFL调用fcntl(),该命令要求int类型的第三个参数以指明新标签:

fcntl(filedes, F_SETFL, new_flags);

它的正常返回值是一个非-1的不确定值。-1指出出错。

文件状态标签中,文件的访问方式是在打开文件时设定的,一旦设定便不能改变;打开时标志只在打开时使用,之后便不再保留。因此,应用唯一可以设置的是文件的I/O操作方式,即O_APPEND、O_NONBLOCK。

同设置文件描述字标签类似,如果想修改文件状态标签,同样应当先用F_GETFL调用fcntl()获得当前标签,然后再修改其值。

例3-6 程序3-6给出的函数保证在设置或清除标志O_NONBLOCK时不会改变其他标签。

int set_nonblock_flag (int desc, int value) {
	int oldflags = fcntl(desc, F_GETFL, 0);
	if (oldflags == -1)
		return -1;
	if (value != 0)
		oldflags |= O_NONBLOCK;
	else
		oldflags &= ~O_NONBLOCK;
	return fcntl(desc, F_SETFL, oldflags);
}

非阻塞I/O

前面几节已介绍了完成各种I/O的系统调用,如read()、write()、open()等,这些系统调用在默认情形下均是阻塞的,也就是说,调用必须等待操作完成,即读写到数据,才能返回。但在有些应用中往往还有需要非阻塞I/O的情形。本节我们讨论使得这些调用成为非阻塞的方法。

UNIX系统调用根据阻塞还是非阻塞分为两类:一类是所谓的“慢”系统调用,其他的则归为另一类。慢系统调用是以下有可能被永久阻塞的调用:

调用read()读管道、终端设备或网络设备文件时,如果数据不出现则可能永久阻塞调用者;调用write()写同样的文件,如果数据不能立即被接收也可能永久阻塞调用者。
打开一个文件将阻塞直至出现某种条件。例如,打开一个终端设备将等待相连的调制解调器回应;打开一个只写的FIFO,但没有其他进程打开它用于读。
读写一个具有强制锁的文件。
某些ioctl()操作。
某些进程间通信函数。
默认情况下,关于I/O的系统调用总是阻塞的,调用必须等待操作完成,即读写到数据才能返回。但是慢系统调用并不包括所有阻塞I/O,这里包括的只是可能永久阻塞的系统调用。例如,磁盘I/O有关的系统调用就不是慢系统调用,尽管读写磁盘文件也可能临时阻塞调用者。

非阻塞I/O可以使得I/O操作如open()、read()或write()不会被永久阻塞,如果操作不能完成,这些调用将立即返回并给出错误码指明操作可能被阻塞。

对于给定的文件描述字,有两种方法指定非阻塞I/O:
1)在open()时指定O_NONBLOCK文件状态标志。
2)对已经打开的描述字,调用fcntl()函数设置O_NONBLOCK文件状态标志。

例3-7 在非阻塞I/O情况下传输数据,当读写不能立即完成时会返回-1并置errno为EAGAIN。这时,往往需要再次调用read()或write()。我们来看一个非阻塞I/O的程序例子,这个例子使用了程序3-6给出的函数set_nonblock_flag()来设置和清除O_NONBLOCK标志。

#define B_SIZE 100000
char buf[B_SIZE];
char fmt[] = "%d Hi :( -> :) ---aha!---";
int main() {
	int nbytes = 0, j = 0, nwrite, ntimes = 1, success = 0;
	char *ptr;
	if (set_nonblock_flag(STOUT_FILENO, 1) < 0)
		exit(1);
	while (nbytes + sizeof(fmt) < B_SIZE) {
		sprintf(&buf[nbytes], fmt, j++);
		nbytes += sizeof(fmt);
	}
	for (ptr = buf; nbytes > 0; ntimes++) {
		errno = 0;
		nwrite = write(STDOUT_FILENO, ptr, nbytes);
		if (nwrite < 0) {
			fprintf(stderr, "\n%d nwrite = %d, error = %d", ntimes, nwrite, errno);
			perror("");
		} else {
			fprintf(stderr, "\n%d nwrite = %d, error = %d", ntimes, nwrite, errno);
			ptr += nwrite;
			success++;
			nbytes -= nwrite;
		} 
	}
	exit(0);
}

程序3-7的这类循环称为“轮询”,它存在两个问题:在多用户系统中它浪费了不少CPU时间,本来这些用于等待的CPU时间可以做其他事情或者让给其他进程。另外,它仍然没有避免永久阻塞的问题,虽然write()能立即返回,但它可能由于总是不能写出数据而导致出现死循环。为了避免死循环,我们必须设置一定的时间限制,以便时间到时能跳出循环。在10.3节介绍多路I/O时,我们可以看到解决这个问题更有效的方法。

readv()和writev()函数

read()和write()系统调用每次在文件和进程的地址空间之间传送一块连续的数据。但是,应用有时也需要将分散在内存多处地方的数据连续写到文件中,或者反之。在这种情况下,如果要从文件中读一片连续的数据至进程的不同区域,使用read()则要么一次将它们读至一个较大的缓冲区中,然后将它们分成若干部分复制到不同的区域,要么调用read()若干次分批将它们读至不同区域。同样,如果想将程序中不同区域的数据块连续地写至文件,也必须进行类似的处理。

UNIX提供了另外两个函数—readv()和writev(),它们只需一次系统调用就可以实现在文件和进程的多个缓冲区之间传送数据,免除了多次系统调用或复制数据的开销。readv()称为散布读,即将文件中若干连续的数据块读入内存分散的缓冲区中。writev()称为聚集写,即收集内存中分散的若干缓冲区中的数据写至文件的连续区域中。

#include <sys/uio.h>
ssize_t readv(int fildes, const struct iovec *iov, int iovcnt);
ssize_t writev(int fildes, const struct iovec *iov, int iovcnt);

参数fildes是文件描述字。iov是一个结构数组,它的每个元素指明存储器中的一个缓冲区。结构类型iovec有下述成员,分别给出缓冲区的起始地址和字节数:

struct iovec {
	void *iov_base;
	size_t iov_len;
}

参数iovcnt指出数组iov的元素个数,元素个数至多不超过IOV_MAX。Linux中定义IOV_MAX的值为1024。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值