5.
名称:
|
write
|
目标:
|
将内存中的数据写入文件。
|
头文件:
|
#include < unistd.h>
|
函数原形:
|
size_t write(int fd, const void *buf, size_t count)
|
参数
:
|
fd
文件描述符
|
|
buf
内存数据
|
|
count
要写的字节数
|
返回值:
|
-1
遇到错误
|
|
Num written
成功写入,返回写入的字节数目。
|
在实际的写入过程中,可能会出现写入的字节数少于所要求的。这可能有两个原因,第一是有的系统对文件的最大尺寸有限制,第二是磁盘空间接近满了。在上述两种情况下内核都会尽力把数据往文件中写,并将实际写入的字节数返回,所以调用
write
后都必须检查返回值是否与要写入的相同,如果不同就要采取相应的措施。
学完上面几个系统调用,我们就可以自己编写的
cp
命令了。它的基本思路是从原文件读取数据写入缓冲,再将缓冲的数据写入目标文件。
/*1_3.c*/
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#define BUFFERSIZE 4096
#define COPYMODE 0644
void oops(char *s1,char *s2);
main(int argc,char *argv[])
{
int in_fd,out_fd,n_chars;
char buf[BUFFERSIZE];
if(argc!=3)
{
fprintf(stderr,”usage:%s source destination\n”,*argv);
exit(1);
}
if((in_fd=open(argv[1],O_RDONLY))==-1)
oops(“Cannot open”,argv[1]);
if((out_fd=creat(argv[2], COPYMODE))==-1)
oops(“Cannot creat”,argv[2]);
while((n_chars=read(in_fd,buf,BUFFERSIZE))>0)
{
if(write(out_fd,buf,n_chars)!=n_chars)
oops(“Write error to”,argv[2]);
}
if(n_chars==-1)
opps(“Read error form”,argv[1]);
if(close(in_fd)==-1||close(out_fd)==-1)
oops(“Error clising files”);
}
void oops(char *s1,char *s2)
{
fprintf(stderr,”Error:%s”,s1);
perror(s2);
exit(1);
}
|
三、文件描述符操作函数
6
.
名称
:
:
|
lseek
|
目标:
|
使指针指向文件中的指定位置。
|
头文件:
|
#include <sys/types.h>
#include <unistd.h>
|
函数原形:
|
off_t lseek(int fildes,off_t offset,int whence)
|
参数:
|
fildes
文件描述符
offset
移动的距离
wence SEEK_SET=>
文件的开始
SEEK_CUR=>
当前位子
SEEK_END=>
文件结束
|
返回值:
|
-1
遇到错误
Ildpos
指针变化前的位子
|
lseek
改变文件描述符所关联的指针的位置,新的位置由
offset
和
wence
来指定,
wence
是基准位置,基准位子可以是文件的开始(
0
)、当前位子(
1
)或文件的结束(
2
)。
offset
是从基准位子开始的偏移量。若
wence
为
SEEK_SET
,该文件的偏移设置为距文件开始处
offset
个字节数。若
wence
是
SEEK_CUR
,该文件的偏移设置为其当前值加
offset, offset
可为正或负。若
wence
是
SEEK_END,
该文件的偏移设置为文件长度加
offset, offset
可为正或负。
lseek(fd,0,SEEK_END);
文件位偏移量可以大于文件的当前长度,在这种情况下,对该文件的下一次写将延长该文件,并在文件中构成一个空洞,这一点是允许的。位于文件中没有写过的字节都被读为
0
。
下面是一个例子:
/*1_4.c*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
char buf1[]=”abcdefg”;
char buf2[]=”ABCDEFG”;
int main(void)
{
int fd;
if((fd=creat(“file.hole”,0644))==-1)
printf(“creat error”);
if(write(fd,buf1,7)!=7)
printf (“buf1 write error”);
if(lseek(fd,40,SEEK_SET)==-1)
printf (“lseek error”);
if(write(fd,buf2,7)!=7)
printf (“buf2 write error”);
exit(0);
}
|
7.
名称
:
:
|
dup/dup2
|
目标:
|
复制一个现存的文件描述符
.
|
头文件:
|
#include <unistd.h>
|
函数原形:
|
int dup(int oldfd)
int dup2(int oldfd,int newfd)
|
参数:
|
oldfd
原有
文件描述符
newfd
新的文件描述符
|
返回值:
|
-1
遇到错误
int
新文件描述符
|
系统调用dup是用来复制一个文件描述符,也就是将进程u区的文件描述符表中的一项复制一份,使得这两项同时指向系统稳健表的同一表项。
系统调用dup复制由参数oldfd指定的文件描述到进程文件描述符表的第一个空表项处。而系统调用dup2复制由参数oldfd指定的文件描述到参数newfd指定的文件描述符表项处。老的文件描述符和新复制的文件描述符可以互换使用。它们共享锁、文件指针和文件状态。例如,对其中一个文件描述符使用系统调用lseek修改文件指针的位置,对另一文件描述符来说文件指针也改变了,其实我们了解了内核的工作原理,这一点很容易理解。因为我们知道,文件指针是放在系统文件表中的。但这两个文件描述符具有不同的close-on-exec标志,因为该标志是存放在文件描述符表中的。
该调用成功时,返回值为新的描述符;错误时,返回-1,并设置相应的错误代码errno:
-
EBADF
:参数oldfd不是一个已经打开的文件描述符;或者参数newfd超出允许的文件描述符的取值范围。
-
EMFILE
:进程打开的文件描述符数量已经到达最大值,但仍然企图打开新的文件描述符。
下面我们来看一个简单的例子。在这个例子中,我们将标准输出(文件描述符为1)关闭,并将一个打开了普通文件
“
output
”的文件描述符复制到标准输出上,因为刚关闭了文件描述符1,所以,文件描述符表的第一个空表项是1。所以,程序以后的printf等向标准输出写的内容都写到了文件中。
利用这个功能我们可以把输出/输入重定向到文件中。下面是一个例子。
/*1_5.c*/
#include <stdio.h>
#include <unistd.h>
#include <systypes.h>
#include <sys/stat.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
main(
int argc,char *argv[]
)
{
int fd;
if ((fd=open(argv[1],O_CREAT|O_RDWR,0644))==-1)
printf("cannot open output file ");
close(1); /* 关闭标准输出
*/ if(dup(fd)==-1); /* 复制fd到文件描述符1上
*/ perror(
“
error
”
);
close(fd); /*
即时关闭不用的文件描述符是一个好习惯
*/ printf("This line will write to file ");
}
该程序执行过程的屏幕拷贝:
[wap@wapgw /tmp]$ gcc -o 1_5 1_5.c
[wap@wapgw /tmp]$ ./1_5
[wap@wapgw /tmp]$ more test1
This line will write to file
|
dup2
的功能和dup基本相同,只不过是dup2复制oldfd到newfd上。下面是用dup2实现同样的例子。
/*1_6.c*/
#include <stdio.h>
#include <unistd.h>
#include <systypes.h>
#include <sys/stat.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
main(int argc,char *argv[])
{
int fd;
if((fd=open(argv[1],O_WRONLY|O_CREAT,0644))==-1)
perror(“error”);
close(1);
if(dup2(fd,1)==-1)
perror(“error”);
close(fd);
printf(“
This line will write to file
\n”);
close(fd);
}
|
利用这两个函数我们可以实现管道的功能,有关管道的内容将在后面介绍。
8
.
名称
:
:
|
fcntl
|
目标:
|
改变已经打开文件的性质。
|
头文件:
|
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
|
函数原形:
|
int fcntl(int fd,int cmd);
int fcntl(int fd,int cmd,long arg);
int fcntl(int fd,int cmd,struct flock *lock);
|
参数:
|
fd
文件描述符
cmd
功能描述位
|
返回值:
|
-1
遇到错误
int
依赖于
cmd
|
fcntl
函数有
5
种功能:
1
复制一个现存的文件描述符(
cmd=F_DUPFD
)
2
获得
/
设置文件描述符标记(
cmd=F_GETFD
或
F_SETFD
)
3
获得
/
设置文件状态标志(
cmd=F_GETFL
或
F_SETFL
)
4
获得
/
设置异步
I/O
所有权(
cmd=F_GETOWN
或
F_SETOWN
)
5
获得
/
设置记录锁(
cmd=F_GRTLK,F_SETLK
或F
_SETLKW
)
fcntl
的功能之一是重复文件描述字。
fcntl (FileDescriptor, F_DUPFD, 0)
等价于
Dup(FileDescriptor)
close (New); fcntl(Old, F_DUPFD, New)
等价于
dup2 (Old,New)
fcntl
的功能之二获得
/
设置文件描述符标记。
获得文件描述标记
fcntl (FileDescriptor, F_GETFD)
设置文件描述标记
fcntl(FileDescriptor, F_SETFD, flags)
fcntl
的功能之三获得
/
设置文件状态标签,文件状态标签分为
3
类它们是文件访问方式、打开时标志和
I/O
操作方式。