每个系统都有自己的专属函数,我们习惯称其为系统函数。系统函数并不是内核函数,因为内核函数是不允许用户使用的,系统函数就充当了二者之间的桥梁,这样用户就可以间接的完成某些内核操作了。
如:open、close、lseek、read、write
这些系统IO函数又被称为不带缓冲的IO (unbuffered IO)。
术语不带缓冲指的是每个read和write都调用内核中的一个系统调用,因此也常叫做系统IO,
与之相对应的还有标准IO(fopen、fread、fwrite、fclose等)。
应用层程序编写,一种是直接调用系统层接口IO(即open、 read、 write 等函数)、
另一种则是调用后面C库的接口IO(即fopen、 fread、 fwrite 等 函数)间接地调用系统调用层接口。
各有优势:
系统IO是低级IO,通过API直接与操作系统内核进行操作;标准IO属于C库,在用户空间的可移植性强。
标准IO在打开文件时候就会建立起缓冲区,缓冲区有三种模式(全缓冲,行缓冲以及无缓冲。)。而系统IO没有,需要手动创建。
文件描述符
对于内核而言,所有打开的文件都通过文件描述符引用。文件描述符是一个非负整数。
当打开一个现有文件或创建一个新文件时,内核向进程返回一个文件描述符。
当读、写一个文件时,使用open或creat返回的文件描述符标识该文件,将其作为参数传送给read或write。
一般来说,Linux系统shell把文件描述符0与进程的标准输入关联,文件描述符1与标准输出关联,文件描述符2与标准错误关联。这是各种shell以及很多应用程序使用的惯例,与内核无关。尽管如此,如果不遵循这种惯例,很多系统应用程序就不能正常工作。
STDIN_FILENO:标准输入,可以通过这个文件描述符将数据输入到终端文件中,宏值为 0。
STDOUT_FILENO:标准输出,可以通过这个文件描述符将数据通过终端输出出来,宏值为 1。
STDERR_FILENO:标准错误,可以通过这个文件描述符将错误信息通过终端输出出来,宏值为 2。
函数open
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
/** @description : 调用open可以打开或创建一个文件* @param - path : 指定打开或创建的文件的文件名* @param - flags : 指定文件的打开方式,选项后面有说明* @param - mode : 在创建新文件的时候才需要指定这个参数的值,用于指定新文件的权限(常用值0664==rw-rw-r--);* @return : 若成功,返回内核分配的文件描述符(大于0),失败返回-1;*/
int open(const char *path, int flags, mode_t mode);
flags参数指定了文件打开方式等信息。用下列一个或多个宏常量进行“或 | ”运算构成flags参数:
(这些常量在头文件<fcntl.h>中定义)。
以下为必选属性 ,在这五个宏常量属性中必须指定一个且只能指定一个
flags | 说明 |
---|---|
O_RDONLY | 以只读方式打开文件 |
O_WRONLY | 以只写方式打开文件 |
O_RDWR. | 以读写方式打开文件 |
O_EXEC | 只执行打开 |
O_SEARCH | 只搜索打开(应用于目录) |
以下为可选属性 , 可以和上边的属性一起使用。
flags | 说明 |
---|---|
O_APPEND | 新数据追加到文件尾部,不会覆盖文件的原来内容 |
O_CREAT. | 若此文件不存在则创建它,后面必须跟mode参数指定该新文件的访问权限。如果文件存在什么也不做。 |
O_EXCL | 检测文件是否存在,必须要和 O_CREAT 一起使用,不存在则创建,存在则出错返回-1。 |
O_CLOEXEC | 把PD_CLOEXEC常量设置为文件描述符标志。 |
O_DIRECTORY | 如果path引用的不是目录,则出错。 |
O_NOCTTY. | 如果path引用的是终端设备,则不将该设备分配作为此进程的控制终端。 |
O_NOFOLLOW | 如果path引用的是一个符号链接,则出错。 |
O_TRUNC | 如果此文件存在,而且为只写或读-写成功打开,则将其长度截断为0。 |
函数close
#include <unistd.h>
/** @description : 调用close可以关闭一个已打开的文件* @param - fd : 指定关闭的文件的描述符;* @return : 若成功,返回0,失败返回-1;*/
int close(int fd);
关闭一个文件时还会释放该进程加在该文件上的所有记录锁。
当一个进程终止时,如果不调用close()手动关闭打开的文件,内核将自动关闭它所有的打开文件。
函数lseek
每个打开文件都有一个与其相关联的“当前文件偏移量”。
它通常是一个非负整数,用以度量从文件开始处计算的字节数。
通常,读、写操作都从当前文件偏移量处开始,并使偏移量增加所读写的字节数。
按系统默认的情况,当打开一个文件时,除非指定O_APPEND选项,否则该偏移量被设置为0。
当然也可以调用 lseek 显式地为一个打开文件设置偏移量。
#include <sys/types.h>
#include <unistd.h>
/** @description : 调用lseek函数可以移动文件指针,也可以通过这个函数进行文件的拓展。* @param - fd : 指定需要偏移操作的文件描述符;* @param - offset : 指定偏移量,需要和第三个参数配合使用* @param - whence : 通过这个参数指定函数实现什么样的功能:* SEEK_SET: 从文件头部开始偏移 offset 个字节* SEEK_CUR: 从当前文件指针的位置向后偏移 offset 个字节* SEEK_END: 从文件尾部向后偏移 offset 个字节* @return : 若成功,返回文件指针从头部开始计算总的偏移量;出错,返回-1*/
off_t lseek(int fd, off_t offset, int whence);
对参数offset的解释与参数whence的值(符号常量)有关:
若whence是 SEEK_SET,则将该文件的偏移量设置为距文件开始处 offset个字节。
若whence是SEEK_CUR,则将该文件的偏移量设置为其当前值加offset,offset可为正或负。
若whence是 SEEK_END,则将该文件的偏移量设置为文件长度加offset,offset可为正或负。
若lseek成功执行,则返回新的文件偏移量,为此可以用下列方式确定打开文件的当前偏移量:
off_t currpos;
currpos = lseek(fd, 0,SEEK_CUR);
这种方法也可用来确定所涉及的文件是否可以设置偏移量。
如果文件描述符指向的是一个管道、FIFO或网络套接字,则lseek 返回-1,并将errno设置为ESPIPE。
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
if(lseek(STDIN_FILENO, 0, SEEK_CUR) == -1)printf("can't seek\n");elseprintf("seek OK\n");exit(0);
}
在比较lseek 的返回值时应当谨慎,不要测试它是否小于0,而要测试它是否等于 -1。
函数read
可调用一个read函数从一个打开文件中读取数据。
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
//返回值:若成功,返回读的字节数(带符号整型);若已到文件尾,返回0;出错,返回-1
有多种情况可使实际读到的字节数少于要求读的字节数:
读普通文件时,在读到要求字节数之前已到达了文件尾端。例如,若在到达文件尾端之前有30个字节,
而要求读100个字节,则read返回30。下一次再调用read时,它将返回0(文件尾端)。 当从终端设备读时,通常一次最多读一行。当从网络读时,网络中的缓冲机制可能造成返回值小于所要求读的字节数。当从管道或FIFO读时,如若管道包含的字节少于所需的数量,那么read将只返回实际可用的字节数。当从某些面向记录的设备(如磁带〉读时,一次最多返回一个记录。当一信号造成中断,而已经读了部分数据量时。读操作从文件的当前偏移量处开始。在成功返问之前,该偏移量将增加实际读到的字节数。
函数write
可调用一个write函数向打开文件写数据。
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
//返回值:若成功,返回已写的字节数(带符号整型);若出错,返回-1
其返回值通常与参数count的值相同,否则表示出错。
write出错的一个常见原因是磁盘已写满,或者超过了一个给定进程的文件长度限制。
对于普通文件,写操作从文件的当前偏移量处开始。
如果在打开该文件时,指定了O_APPEND选项,则在每次写操作之前,将文件偏移量设置在文件的当前结尾处。在一次成功写之后,该文件偏移量增加实际写的字节数。
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>int main()
{
char *writeBuf = "chenxx Good Luck";int fd = open("./filename",O_RDWR);if(fd < 0){
printf("can't open no file,creat...\n");fd = open("./filename",O_RDWR|O_CREAT,0600);if(fd > 0)printf("creat file Ok\n"); }printf("open file suceess\n"); int ret1 = write(fd,writeBuf,strlen(writeBuf));if(ret1 > 0)printf("write data byte=%d\n",ret1);elseprintf("write error\n");int ret2 = lseek(fd, 0,SEEK_CUR);printf("lseek byte=%d\n",ret2);lseek(fd, 0, SEEK_SET);char* readBuf = (char* )malloc(sizeof(char)*128);memset(readBuf,'\0',sizeof(char)*128);int ret3 = read(fd,readBuf,sizeof(char)*128);if(ret3 < 0)printf("read error\n");elseprintf("read byte=%d\ndata:\n%s\n",ret3,readBuf);free(readBuf);close(fd);exit(0);
}