print函数
printf函数的调用链: printf write int 0x80 sys_write
printf和write属于用户层函数,int 0x80相当于一闪门,进入到内核函数sys_write。
sys_write 完成后,再一层一层的将结果返回到 printf。
open函数
//包含头文件
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
//函数原型
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
1、 flags有3个必选项 O_RDONLY、O_WRONLY、O_RDWR。这三个值是互斥的,只能选一个。
2、flags有很多可选项,O_CREAT、O_APPEND、O_EXCL、O_TRUNC、O_NONBLOCK这些值可以多选。不能和必选项发生冲突。
3、若选择了O_CREAT选项、那么open函数第三个参数mode就必须写。O_EXCL只和O_CREAT配合使用,若开启O_EXCL,而且创建的文件存在,函数返回-1。
4、O_APPEND表示追加的方式打开文件。
5、O_TRUNC表示打开文件将长度截取为0。
6、O_NONBLOCK针对设备文件,比如屏幕网络,表示非阻塞方式打开IO。
flag选项示例:
open('test', O_RDONLY); // 只读方式打开
open('test', O_WRONLY | O_APPEND); // 追加的方式打开
open('test', O_RDWR); // 读写的方式打开
open('test', O_WRONLY | O_CREAT, 0666); // 创建文件,只写,权限是 0666
open('test', O_WRONLY | O_TRUNC); // 只写打开,同时把文件长度截断成 0.
read函数
//头文件
#include <unistd.h>
//函数原型
ssize_t read(int fd, void *buf, size_t count);
返回值:返回读到的字节数,返回0表示读到文件末尾,返回-1表示出错。
write函数
//头文件
#include <unistd.h>
//函数原型
ssize_t write(int fd, const void *buf, size_t count);
返回值 返回写入的字节数,出错返回-1
lseek函数
//头文件
#include <sys/types.h>
#include <unistd.h>
//函数原型
off_t lseek(int fd, off_t offset, int whence);
改变文件的当前的偏移量。
当open函数打开一个文件的时候,偏移量默认为0
whence的值: SEEK_SET 文件头部 SEEK_CUR 文件尾部 SEEK_END 文件尾部
阻塞IO
通常情况下,从普通文件传数据,会在有限的时间内返回,如果从设备、网络中读取数据如果没有数据可读read一定会堵塞不会返回。
read的这种行为称为block,一旦发生block进程会被操作系统投入随眠,直到等到事件发生进程才被唤醒。
系统调用write同样有可能被阻塞,比如向网络写入数据,若对方不接收,本端缓冲区一旦写满就会被阻塞。
阻塞IO与非阻塞IO效率
阻塞IO与非阻塞IO的几个问题:
1、阻塞与非阻塞是文件本身的特性,不是系统调用read/write可以控制的。
2、终端默认是阻塞的,我们可以重新open设备文件/dev/tty(表示当前终端),打开时指定O_NONBLOCK标志就行了。
3、非阻塞read,如果有数据到来返回读到的字节数。如果没有数据返回-1。
fcntl函数
//头文件
#include <unistd.h>
#include <fcntl.h>
//函数原型
int fcntl(int fd, int cmd, ... /* arg */ );
fcntl函数可以修改文件状态位的标志,fcntl函数第二个参数取不同的值有着不同的功能,如果失败返回-1 。
以上函数使用测试代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
int main(int argc, char const *argv[])
{
if (argc != 3)
{
printf("输入参数有误。\n");
return 0 ;
}
int srcfd = open(argv[1],O_RDONLY);//只读方式打开文件
perror("open");
if(srcfd == -1)
{
return -1 ;
}
int dsfd = open(argv[2],O_CREAT | O_WRONLY,0666);
perror("open");
if (dsfd == -1)
{
return -1 ;
}
int len = 0 ;
char* buff[1024] ;
memset(buff,0,sizeof(buff));
while(len = read(srcfd,buff,sizeof(buff)) > 0)
{
write(dsfd,buff,sizeof(buff));
memset(buff,0,sizeof buff);
}
lseek(srcfd,-20,SEEK_END); //将文件指针冲文件末尾向文件头部偏移20字节
read(srcfd,buff,sizeof buff);
write(STDOUT_FILENO,buff,sizeof(buff));
close(dsfd); //关闭文件
close(srcfd);
//阻塞I0实验
write(STDOUT_FILENO,"阻塞实验开始\n",sizeof("阻塞实验开始\n"));
memset(buff,0,sizeof(buff));
read(STDIN_FILENO,buff,sizeof(buff));
printf("read : %m\n");
write(STDOUT_FILENO,buff,sizeof(buff));
write(STDOUT_FILENO,"阻塞实验结束\n",sizeof("阻塞实验结束\n"));
write(STDOUT_FILENO,"非阻塞实验开始\n",sizeof("非阻塞实验开始\n"));
//非阻塞
int fd = open("/dev/tty", O_RDONLY | O_NONBLOCK);
printf("open : %m\n");
printf("fd = %d\n", fd);
int i = 0 ;
for (i = 0; i < 10; ++i)
{
len = read(fd, buff, sizeof(buff));
printf("len = %d\n",len );
if (len < 0)
{
if (errno == EAGAIN)
{
sleep(1); // 让出 CPU,避免CPU长时间空转
}
else
{
exit(1);
}
}
else
{
write(STDOUT_FILENO,buff,sizeof buff);
memset(buff,0,sizeof buff);
}
}
close(fd);
printf("非阻塞实验结束\n");
printf("fcntl实验\n");
fd = open(argv[1],O_RDWR|O_APPEND);
perror("open");
if(fd < 0)
{
exit(0);
}
int flags = fcntl(fd,F_GETFL);
perror("fcntl");
if (flags < 0)
{
exit(-1);
}
if (flags & O_RDONLY)
{
printf("O_RDONLY\n");
}
if (flags & O_WRONLY)
{
printf("O_WRONLY\n");
}
if (flags & O_RDWR)
{
printf("O_RDWR\n");
}
if (flags & O_NONBLOCK)
{
printf("O_NONBLOCK\n");
}
return 0;
}