Linux C高级编程文件操作之系统调用
宗旨:技术的学习是有限的,分享的精神是无限的!
库函数是一些完成特定功能的函数,一般由某个标准组织制作发布,并形成一定的标准。使用库函数编写的函数一般可以应用于不同的平台而不需要做任何修改,具有很好的可移植性。
系统调用函数与操作系统直接相关,不同的操作系统所使用的系统调用可能不太一样,因此,如果两个操作系统差异很大,系统调用函数的可移植性就不高。例如windows采用的系统调用的应用程序不能直接在Linux下编译运行。
之所以使用系统调用是因为系统资源的有限性以及内核管理的方便,系统调用将上层内的应用开发与底层的硬件实现分开,上层应用不需要关注底层硬件的具体实现。Linux的系统调用使用软中断实现,使用系统调用后,该程序的状态将从用户态切换到内核态。库函数实现最终也要调用系统调用函数,但它封装了系统调用操作,从而增加了代码的可移植性。
1、open()函数
——用于打开或者创建一个文件
(1)函数原型:
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
int open(constchar* pathname, int flags, ...)
(2)参数
pathname:要创建或者打开的文件名
flags: 指定文件的打开模式、标志等信息
必须指定一个:O_RDONLY ——只读 O_WRONLY——只写 O_RDWR——读写
可选标志(按位或):O_APPEND——追加
O_TRUNC——若文件存在,读写方式打开或只写打开,则文件长度为0
O_CREAT——若文件不存在,则创建文件,此时,open需要第三个参数,用于指定该 文件的访问权限(umask可以看掩码)
O_EXCL——若同时指定为O_CREAT标志,而文件已经存在,则会出错,可用于文件 是否存在
O_NONBLOCK对于设备文件,以O_NONBLOCK方式打开可以做非阻塞I/O
(3)返回值
整数类型——成功时,返回文件描述符;出错时,返回-1
(4)文件描述符
(文件描述符——已打开文件的索引——通过索引找到已打开文件)
文件描述符是一个非负的整数
文件描述符0,1,2分别表示标准输入,标准输出,标准错误输出,在进程创建时,已经打开。open返回的文件描述符一定是该进程尚未使用的最小描述符
(5)出错处理
errno.h头文件中,定义了errno,当API调用出错时,errno说明出错的具体原因
可简单的将errno理解成整型数据
出错信息转换成可读字符串
#include<string.h>
char* strerror(int errno);
perror函数根据当前的errno,输出一条出错信息(void perror(constchar* msg))
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
int main(int argc, char *argv[])
{
int fd;
if(argc < 2)
{
puts("please input the open filepathname!\n");
exit(1);
}
//如果flag参数里有O_CREAT表示,该文件如果不存在,系统则会创建该文件,该文 件的权限由第三个参数决定,此处为0755
//如果flah参数里没有O_CREAT参数,则第三个参数不起作用.此时,如果要打开的 文件不存在,则会报错.
//所以fd=open(argv[1],O_RDWR),仅仅只是打开指定文件
if((fd = open(argv[1], O_CREAT | O_RDWR, 0755)) < 0)
{
perror("open filefailure!\n");
exit(1);
}
else
{
printf("open file %d success!\n", fd);
}
close(fd);
return 0;
}<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"> </span>
2、 creat()函数
——用于创建一个新文件
(1)函数原型
int creat(const char *pathname,mode_t mode)
(2)参数
pathname:要创建的文件名(包括路径信息)
mode:同open的第二个参数,讨论文件的访问权限位时分析:
S_IRUSR——可读 S_IWUSR——可写 S_IXUSR——可执行 S_IRWXU——可读、写、执行
除了用上述宏之外,还可以用数字来表示文件的权限:
可读——4 可写——2 可执行——1 无任何权限——0
(3)返回值
成功返回只写打开的文件描述符,出错返回-1
(4)creat函数缺点:它以只写方式打开创建的文件。若要创建一个临时文件,并先写该文件,然后又读该文件,则必须先调用creat,close,然后再open.简便方法:
open(pathname,O_RDWR| O_CREAT | O_TRUNC,mode);
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
void create_file(char *filename)
{
/*创建的文件具有什么样的属性?*/
if(creat(filename, 0755) < 0)
{
printf("create file %sfailure!\n", filename);
exit(EXIT_FAILURE);
}
else
{
printf("create file %s success!\n", filename);
}
}
int main(int argc, char *argv[])
{
int i;
if(argc < 2)
{
perror("you haven't input thefilename,please try again!\n");
exit(EXIT_FAILURE);
}
for(i = 1; i < argc; i++)
{
create_file(argv[i]);
}
return 0;
}
3、lseek函数(和fseek类似)
——用于修改当前文件偏移量
(当前文件偏移量的作用:规定了从文件什么地方开始进行读、写操作)
——通常,读写操作结束时,会使文件偏移量增加读写的字节数
——当打开一个文件时,除非指定了O_APPEND标志,否则偏移量被设置为0
(1) 函数原型
#include<sys/types.h>
#include<unistd.h>
off_t lseek(int filedes, off_t offset, int whence)
(2) 参数
第一个参数filedes:open/creat函数返回的文件描述符
第二个参数offset:
相对偏移量:需结合whence才能计算出真正的偏移量
类型off_t:通常情况下是32为数据类型
第三个参数whence:该参数取值是三个常量
SEEK_SET:当前文件偏移量为——距文件开始出的offset个字节
SEEK_CUR:当前文件偏移量 + offset(可正可负)
SEEK_END:当前文件长度 + offset(可正可负)
(3)返回值:
成功返回新的文件偏移量,失败返回-1
(4)获取当前的偏移量:
Off_tcurrent_position;
Current_position= lseek(fd,0,SEEK_CUR);
lseek操作并不引起任何I/O操作,只是修改内核中的记录(并不引起磁盘的访问 操作)
(5)空洞文件
——使用lseek修改文件偏移量后,当前文件偏移量有可能大于文件的长度
——在这种情况下,对该文件的下一次写操作,将加长该文件
——这样文件中形成了一个空洞。对空洞区域进行读,均返回0
4、read函数
——用于从文件中读出数据
(1)函数原型
#include<unistd.h>
ssize_t read(int fd, void *buff, size_t nbytes)
(2)参数
第一个参数fd:文件描述符
第二个参数buff:指向缓冲区,用于存放从文件读出的数据
第三个参数nbytes:unsigned int;需要从文件中读出的字节数
——缓冲区的大小 >= nbytes
(3)返回值
返回值类型:ssize_t,即int
出错返回-1成功:返回从文件中实际读到的字节数,当文件读到结尾时,则返回0
(4)很多情况下,read实际读出的字节数都小于要求读出的字节数
——读普通文件,在读到要求的字节数之前,就到达了文件尾端
——当从终端设备读时,通常一次最多读一行
——当从网络读时,网络中的缓冲机构可能造成read函数返回值小于所要求读出 的字节数
——某些面向记录的设备,如磁盘,一次最多返回一个记录
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdio.h>
#define LENGTH 100
int main(void)
{
int fd, len;
char str[LENGTH];
/* 创建并打开文件 */
fd = open("hello.txt", O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
if(fd)
{
/* 写入 Hello, software weekly字符串 */
write(fd, "Hello,Software Weekly", strlen("Hello, software weekly"));
close(fd);
}
fd = open("hello.txt", O_RDWR);
len = read(fd, str, LENGTH);/* 读取文件内容 */
str[len] = '\0';
printf("%s\n", str);
close(fd);
return 0;
}<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"> </span>
5、write函数
——用于向文件里面写入数据
(1) 函数原型
#include<unistd.h>
ssize_t write(int fd,const void*buff,size_t nbytes)
(2) 参数
第一个参数fd:文件描述符
第二个参数buff:指向缓冲区,存放了需要写入文件的数据
第三个参数nbytes:需要写入文件的字节数
(3) 返回值
返回值类型:ssize_t,即int
出错返回-1,成功返回实际写入文件的字节数
(4) write出错的原因
——磁盘满
——没有访问权限
——超过了给定进程的文件长度限制
6、close函数
——用于关闭一个已打开的文件
(1)函数原型
int close(int filedes)
(2) 返回值
成功返回0,出错返回-1
(3)参数
filedes:文件描述符
(4)当close函数关闭文件时,会释放进程加在该文件上的所有记录锁
内核会对进程打开文件表,文件对象,索引节点表项等结构进行修改,释放相关的 资源
当进程退出时,会关闭当前所有已打开的文件描述符
7、ioctl函数
——用于向设备发控制和配置命令(这些数据不能用read/write读写)
(1) 函数原型
#include <sys/ioctl.h>
int ioctl(int d, int request, ...)
(2) 参数
第一个参数d:某个设备的文件描述符
第二个参数request:是ioctl的命令,可变参数取决于request,通常是是一个指向 变量或者结构体的指针
(3)返回值
若出错返回-1,成功返回其他值,取决于request
ioctl(STDOUT_FILENO,TIOCGWINSZ,&size)——获得终端设备的窗口大小
manioctl_list——可以看require的各种参数
8、mmap函数
——可以把磁盘文件的一部分直接映射到内存,这样文件中的位置直接就有对应的内存地址,对文件的读写可以直接用指针来做而不需要read/write函数
(1) 函数原型
#include<sys/mman.h>
void* mmap(void *addr,size_t len,int prot,int flag,int filedes,off_t off)
(2) 参数
addr:NULL——内核会自己在进程地址空间中选择合适的地址建立映射
len:需要映射的那部分文件的长度
off:从文件的什么位置开始映射,必须是页大小的整数倍(32位——4K)
filedes:该文件的文件描述符
prot:
PROT_EXEC——映射的这一段可执行,例如映射的共享库
PROT_READ——映射的这一段可读
PROT_WRITE——映射的这一段可写
PROT_NONE——映射的这一段不可访问
flag:
MAP_SHARED——多个进程对同一文件的映射是共享的,一个进程对映射的内存做了修改,另一个进程也会看到这种变化。
MAP_PRIVATE——多个进程对同一文件的映射不是共享的,一个进程对映射的内存做了修改,另一个进程不会看到这种变化,也不会真的写到文件中去。
(3)返回值
成功则返回映射的首地址,出错返回常数MAP_FAILED。
9、access函数
——判断文件是否可以进行某种操作
(1)函数原型
int access(const char *pathname,int mode)
(2)参数
pathname:文件名
mode:判断的访问权限:R_OK:文件可读 W_OK:文件可写
X_OK:文件可执行 F_OK:文件存在
(3)返回值
成功时,返回0;如果一个条件不符合时,返回-1
10 dup函数
——复制文件描述符
(1) 函数原型
#include <unistd.h>
int dup(int oldfd);
(2) 参数
oldfd:待复制的文件描述符
(3)返回值
成功返回新的文件描述符;失败返回-1
11 dup2函数
——复制文件描述符
(1) 函数原型
#include <unistd.h>
int dup2(int oldfd, int newfd);
(2) 参数
oldfd:待复制的文件描述符
newfd:新的文件描述符
(3) 返回值
成功返回新的文件描述符;失败返回-1
//小项目:用系统调用函数实现文件拷贝的功能:
/*************************************************************************
> File Name: copy.c
> Author: libang
> Mail: 18211438613@163.com
> Created Time: 2014年07月16日 星期三 00时15分12秒
************************************************************************/
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<fcntl.h>
#include<stdlib.h>
int copy_file(intsrc_fd, int dest_fd);
int main(intargc, char *argv[]) //注意先搭建主框架
{
if(argc < 3)
{
printf("inputerror!\n");
exit(1);
}
int src_fd, dest_fd;
src_fd = open(argv[1], O_RDONLY);
dest_fd = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0666);
if(src_fd < 0 | dest_fd < 0)
{
printf("file openfail!\n");
exit(0);
}
copy_file(src_fd, dest_fd);
printf("success!\n");
close(src_fd);
close(dest_fd);
return 0;
}
int copy_file(intsrc, int dest)
{
char buf[128];
int r_ret, w_ret;
char *tmp;
//memset(buf,0,128);
printf("success!\n");
while((r_ret = read(src, buf, 128)) > 0) //经典的拷贝算法
{
tmp = buf;
while(r_ret)
{
w_ret = write(dest, tmp, r_ret);
r_ret = r_ret - w_ret;
printf("%d\n", r_ret);
tmp += w_ret;
}
//memset(buf,0,128);
}
printf("success!\n");
return 0;
}