linux系统编程、系统函数
大多数U N I X文件I / O只需用到5个函数:o p e n、r e a d、w r i t e、lseek 以及c l o s e。
系统函数介绍
函数open()
调用o p e n函数可以打开或创建一个文件。
===============================================================================
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char * p a t h n a m e, int o f l a g,.../*, mode_t m o d e * / ) ;
返回:若成功为文件描述符,若出错为- 1
===============================================================================
于o p e n函数而言,仅当创建新文件时才使用第三个参数。
p a t h n a m e是要打开或创建的文件的名字。
o f l a g参数可用来说明此函数的多个选择项。
用下列一个或多个常数进行或运算构成o f l a g参数(这些常数定义在< f c n t l . h >头文件中):
• O_RDONLY 只读打开。
• O_WRONLY 只写打开。
• O_RDWR 读、写打开。
很多实现将O _ R D O N LY定义为0,O _ W R O N LY定义为1,O _ R D W R定义为2,以与早期的系统兼容。
在这三个常数中应当只指定一个。下列常数则是可选择的:
• O_APPEND 每次写时都加到文件的尾端。3 . 11节将详细说明此选择项。
• O_CREAT 若此文件不存在则创建它。使用此选择项时,需同时说明第三个参数m o d e,
用其说明该新文件的存取许可权位。 ( 4 . 5节将说明文件的许可权位,那时就能了解如何说明m o d e,以及如何用进程的u m a s k值修改它。 )
• O_EXCL 如果同时指定了O _ C R E AT,而文件已经存在,则出错。这可测试一个文件是
否存在,如果不存在则创建此文件成为一个原子操作。 3 . 11节将较详细地说明原子操作。
• O_TRUNC 如果此文件存在,而且为只读或只写成功打开,则将其长度截短为0。
• O_NOCTTY 如果p a t h n a m e指的是终端设备,则不将此设备分配作为此进程的控制终端。9 . 6节将说明控制终端。
• O_NONBLOCK 如果p a t h n a m e指的是一个F I F O、一个块特殊文件或一个字符特殊文件,则此选择项为此文件的本次打开操作和后续的I / O操作设置非阻塞方式。1 2 . 2节将说明此工作方式。
函数creat()
===============================================================================
也可用c r e a t函数创建一个新文件。
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int creat(const char * p a t h n a m e, mode_tm o d e) ;
返回:若成功为只写打开的文件描述符,若出错为- 1
注意,此函数等效于:
o p e n (p a t h n a m e, O_WRONLY |O _ C R E A T|O_TRUNC, m o d e) ;
===============================================================================
注意: 在早期的U N I X版本中,o p e n的第二个参数只能是0、 1或2。没有办法打开一
c r e a t的一个不足之处是它以只写方式打开所创建的文件。在提供 o p e n的新版本之前,如果要创建一个临时文件,并要先写该文件,然后又读该文件,则必须先调用c r e a t,c l o s e,然后再调用o p e n。现在则可用下列方式调用o p e n: |
函数close()
===============================================================================
#include <unistd.h>
int close (int f i l e d e s);
返回:若成功为 0,若出错为- 1
===============================================================================
关闭一个文件时也释放该进程加在该文件上的所有记录锁。 当一个进程终止时,它所有的打开文件都由内核自动关闭。很多程序都使用这一功能而不显式地用c l o s e关闭打开的文件。
记录锁( record locking)的功能是:一个进程正在读或修改文件的某个部分时,可以阻止其他进程修改同一文件区。对于U N I X, “记录”这个定语也是误用,因为U N I X内核根本没有使用文件记录这种概念。一个更适合的术语可能是“区域锁”,因为它锁定的只是文件的一个区域(也可能是整个文件)。
函数lseek()
按系统默认,当打开一个文件时,除非指定O _ A P P E N D选择项,否则该位移量被设置为0。可以调用l s e e k显式地定位一个打开文件。
===============================================================================
#include <sys/types.h>
#include <unistd.h>
off_t lseek(int f i l e d e s, off_t o f f s e t, int w h e n c e) ;
返回:若成功为新的文件位移,若出错为- 1
===============================================================================
对参数offset 的解释与参数w h e n c e的值有关。
• 若w h e n c e是S E E K _ S E T,则将该文件的位移量设置为距文件开始处offset 个字节。
• 若w h e n c e是S E E K _ C U R,则将该文件的位移量设置为其当前值加offset, offset可为正或负。
• 若w h e n c e是S E E K _ E N D,则将该文件的位移量设置为文件长度加offset, offset可为正或负。
若l s e e k成功执行,则返回新的文件位移量,为此可以用下列方式确定一个打开文件的当前位移量:
off_t currpos;
currpos = lseek(fd, 0, SEEK_CUR);
这种方法也可用来确定所涉及的文件是否可以设置位移量。如果文件描述符引用的是一个管道或F I F O,则l s e e k返回-1,并将e r r n o设置为E P I P E。
三个符号常数 S E E K _ S E T,S E E K _ C U R和S E E K _ E N D是由系统V引进的。在系统V之前,w h e n c e被指定为0 (绝对位移量), 1 ( 相对于当前位置的位移量)或2 (相对文件尾端的位移量)。很多软件仍直接使用这些数字进行编码。
在l s e e k中的字符l表示长整型。在引入o ff _ t数据类型之前,o f f s e t参数和返回值是长整型的。l s e e k是由V 7引进的,当时C语言中增加了长整型。(在V 6中,用函数s e e k和t e l l提供类似功能。 )
实例:
下面截图是使用lseek() 函数拓展一个文件
下面截图是使用lseek()函数 计算一个文件的大小:
代码如下:
/*************************************************************************
> File Name: lseek.c
> Created Time: 2016年07月30日 星期六11时54分04秒
************************************************************************/
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<errno.h>
int main(void)
{
int fd = open("abc", O_RDWR);
if(fd < 0){
perror("open abc");
exit(-1);
}
//拓展一个文件,必须有一次写操作
lseek(fd, 0x1000, SEEK_SET); 0x1000 = 16^3 = 2^4^3 = 2^12 = 4096
write(fd, "a", 1);
close(fd);
//计算打开文件的大小
fd = open("hello", O_RDWR);
if(fd < 0){
perror("open hello");
exit(-1);
}
printf("hello size = %d\n", lseek(fd, 0, SEEK_END));
close(fd);
return 0;
}
函数read()
===============================================================================
用r e a d函数从打开文件中读数据。
#include <unistd.h>
ssize_t read(int f i l e d e s, void * b u f f, size_tn b y t e s) ;
返回:读到的字节数,若已到文件尾为 0,若出错为- 1
===============================================================================
如r e a d成功,则返回读到的字节数。如已到达文件的尾端,则返回0。
有多种情况可使实际读到的字节数少于要求读字节数:
• 读普通文件时,在读到要求字节数之前已到达了文件尾端。例如,若在到达文件尾端之
前还有3 0个字节,而要求读1 0 0个字节,则r e a d返回3 0,下一次再调用r e a d时,它将返回0 (文件尾端)。
• 当从终端设备读时,通常一次最多读一行 (第11章将介绍如何改变这一点)。
• 当从网络读时,网络中的缓冲机构可能造成返回值小于所要求读的字节数。
• 某些面向记录的设备,例如磁带,一次最多返回一个记录。
读操作从文件的当前位移量处开始,在成功返回之前,该位移量增加实际读得的字节数。
P O S I X . 1在几个方面对此函数的原型作了更改。其经典定义是:
int read(intf i l e d e s, char * b u f f, unsignedn b y t e s) ;
首先,为了与ANSI C一致,其第二个参数由char *改为void *。在ANSI C中,类型void *用于表示类属指针。其次,其返回值必须是一个带符号整数(s s i z e _ t),以返回正字节数、0(表示文件尾端)或-1(出错)。最后,第三个参数在历史上是一个不带符号整数,以允许一个1 6位的实现可以一次读或写至6 5 5 3 4个字节。在1990 POSIX.1标准中,引进了新的基本系统数据类型ssize_t以提供带符号的返回值, s i z e _ t则被用于第三个参数(见表2 - 7中的S S I Z E _ M A X常数)。
函数write()
===============================================================================
用w r i t e函数向打开文件写数据。
#include <unistd.h>
ssize_t write(int f i l e d e s, const void * b u f f, size_t n b y t e s) ;
返回:若成功为已写的字节数,若出错为- 1
===============================================================================
其返回值通常与参数 n b y t e s的值不同,否则表示出错。w r i t e出错的一个常见原因是:磁盘已写满,或者超过了对一个给定进程的文件长度限制(见7 . 11节及习题1 0 . 11 )。
对于普通文件,写操作从文件的当前位移量处开始。如果在打开该文件时,指定了
O _ A P P E N D选择项,则在每次写操作之前,将文件位移量设置在文件的当前结尾处。在一次成功写之后,该文件位移量增加实际写的字节数。
函数read和write实现cp 命令
/*************************************************************************
> File Name: mycp.c
> Author: xuxing
> Mail: xuxing@163.com
> Created Time: 2016年07月29日 星期五13时49分00秒
************************************************************************/
#include<stdio.h>
#include<stdlib.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<fcntl.h>
#include<unistd.h>
#define SIZE 8192
int main(int argc, char *argv[])
{
char buf[SIZE];
int fd_src, fd_dest, len;
if(argc < 3 ) {
printf("./mycp src dest\n");
exit(1);
}
fd_src = open(argv[1], O_RDONLY);
fd_dest = open(argv[2],O_CREAT | O_WRONLY | O_TRUNC, 0644 );
/*
*success return read the number of bytes
*read the file last
*failed return -1
*/
while((len = read(fd_src, buf, sizeof(buf))) > 0)
write(fd_dest, buf, len);
close(fd_src);
close(fd_dest);
return 0;
}