文件IO流中的三个函数,fcntl,ioctl,mmp函数
- fcntl
- ioctl
mmp
提示:
博主:章飞 _906285288的博客
博客地址:http://blog.csdn.net/qq_29924041
fcntl函数
之前写过一篇博客是关于linux系统IO方面的,这里是对其做的一些简单补充的一些函数信息。http://blog.csdn.net/qq_29924041/article/details/56497700
以前的相关函数,open,creat函数都是在打开或者创建文件的时候,去选择一些属性信心,如O_RDONLY,O_NONBLOCK等一些属性信息。但是如果在使用过程中的时候,需要改变其属性呢?这个时候再去重新打开一个,是极其不合理的吧,所以系统为我们提供了fcntl函数,这个函数就是用来在使用过程中来修改文件的相关属性的。
函数原型
#include <unistd.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:选择是获取还是修改。F_GETFL(获取)/F_SETFL(设置)
arg:要设置的相关属性
属性类型参考上述链接部分的文章,可以为O_RDONLY,O_WRONLY,O_NONBLOCK等
代码实例:案例来自linuxC一站式编程
#include <sys/types.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
int val;
if (argc != 2) {
fputs("usage: a.out <descriptor#>\n", stderr);
exit(1);
}
if ((val = fcntl(atoi(argv[1]), F_GETFL)) < 0) {
printf("fcntl error for fd %d\n", atoi(argv[1]));
exit(1);
}
switch(val & O_ACCMODE) {
case O_RDONLY:
printf("read only");
break;
case O_WRONLY:
printf("write only");
break;
case O_RDWR:
printf("read write");
break;
default:
fputs("invalid access mode\n", stderr);
exit(1);
}
if (val & O_APPEND)
printf(", append");
if (val & O_NONBLOCK)
printf(", nonblocking");
putchar('\n');
return 0;
}
ioctl函数
与fcntl函数相似但是又完全不同的函数,ioctl函数。ioctl 用于向设备发控制和配置命令,有些命令也需要读写一些数据,但这些数据是不能用 read/write 读写的,称为 Out-of-band 数据,也就是说,read/write 读写的数据是 inband 数据,是 I/O 操作的主体,而 ioctl 命令传送的是控制信息,其中的数据是辅助的数据。
如;
例如,在串口线上收发数据通过 read/write 操作,而串口的波特率、校验位、停止位通过 ioctl 设置,A/D 转换的结果通过 read 读取,而 A/D 转换的精度和工作频率通过ioctl 设置
这些参数配置都是只能使用ioctl函数来进行配置的。
函数原型:
#include <sys/ioctl.h>
int ioctl(int d, int request, ...);
返回:若出错则返回-1,若成功则返回其他值,返回值也是取决于 request
参数:
d 是某个设备的文件描述符
request 是 ioctl 的命令,可变参数取决于 request,通常是一个指向变量或结构体的指针
关于request的类型参考:
https://baike.baidu.com/item/ioctl/6392403
实例代码:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
int main(void)
{
struct winsize size;
if (isatty(STDOUT_FILENO) == 0)
exit(1);
if(ioctl(STDOUT_FILENO, TIOCGWINSZ, &size)<0) {
perror("ioctl TIOCGWINSZ error");
exit(1);
}
printf("%d rows, %d columns\n", size.ws_row, size.ws_col);
return 0;
}
mmap函数
mmap 可以把磁盘文件的一部分直接映射到内存,这样文件中的位置直接就有对应的内存地址,对文件的读写可以直接用指针来做而不需要 read/write 函数
如下图所示:
函数类型
#include <sys/mman.h>
void *mmap(void *addr, size_t len, int prot, int flag, int filedes, off_t off);
int munmap(void *addr, size_t len);
解析:
1:如果 addr 参数为 NULL,内核会自己在进程地址空间中选择合适的地址建立映射。
2:如果addr 不是 NULL,则给内核一个提示,应该从什么地址开始映射,内核会选择 addr 之上的某个合适的地址开始映射
返回值:
返回的是映射的地址
参数:
len 参数是需要映射的那一部分文件的长度
off 参数是从文件的什么位置开始映射,必须是页大小的整数倍(在 32 位体系统结构上通常是 4K)
filedes 是代表该文件的描述符
prot 参数:
PROT_EXEC 表示映射的这一段可执行,例如映射共享库
PROT_READ 表示映射的这一段可读
PROT_WRITE 表示映射的这一段可写
PROT_NONE 表示映射的这一段不可访问
flag 参数有很多种取值
MAP_SHARED 多个进程对同一个文件的映射是共享的,一个进程对映射的内存做了修改,另一个进程也会看到这种变化
MAP_PRIVATE 多个进程对同一个文件的映射不是共享的,一个进程对映射的内存做了修改,另一个进程并不会看到这种变化,也不会真的写到文件中去
#include <stdlib.h>#include <sys/mman.h>
#include <fcntl.h>
int main(void)
{
int *p;
int fd = open("hello", O_RDWR);
if (fd < 0) {
perror("open hello");
exit(1);
}
p = mmap(NULL, 6, PROT_WRITE, MAP_SHARED, fd, 0);
if (p == MAP_FAILED) {
perror("mmap");
exit(1);
}
close(fd);
p[0] = 0x30313233;
munmap(p, 6);
return 0;
}
以上案例来自linuxC一站式编程,其实有兴趣的可以自己去查阅相关书籍。