一. IO与映射
1. IO 的共享 与 效率
read 与 write 其中数据缓冲的大小
读取数据的缓冲: getpagesize。
2. 定位与定位读取 (随机读取)
read 与 write 在操作的时候,自动移动读取位置。
2.1 lseek 改变读取位置。
#include <sys/types.h>
#include <unistd.h>
off_t lseek( int fd, // 定位文件描述符号
off_t offset, // 定位位置
int whence); // 定位参照点:文件开始位置/ 文件结束位置/文件结束位置
返回值: 成功返回文件的偏移量,出错返回-1
三个符号常量 SEEK_SET ,SEEK_CUR SEEK_END
/// lseek.c
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
main()
{
int fd;
float score;
int r;
int i = 0;
fd = open("stu.dat",O_RDONLY);
if(fd== -1)printf("open error:%m\n"),exit(-1);
//定位
for(i=0;i<2; ++i)
{
r = lseek(fd,i*28,SEEK_SET);
r = lseek(fd,24,SEEK_CUR);
// r = lseek(fd,i*28+24,SEEK_SET); // 等同上面两句
// 读取
r = read(fd,&score,sizeof(float));
//打印, 输出
printf("%.2f\n",score);
}
close(fd);
}
2.2 lseek 的作用: 定位文件的位置
lseek 移动位置只要合法,都为有效。
2.3 lseek + write = pwrite
lseek + read = pread
for(i=0; i<2; i++)
{
pread(fd,&score,sizeof(float),i*28 +24);
printf("%.2f\n",score);
}
2.4 案例
读取一个特殊文件:
/proc/${pid}/mem 文件 程序的虚拟文件内存文件
/// demo1.c
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
int a= 9999;
main()
{
char filename[100];
int fd;
int data = 88;
// 得到文件名
sprintf(filename,"/proc/%d/mem",getpid());
// 打开文件
fd = open(filename,O_RDWR);
if(fd == -1) printf("open error:%m\n"),exit(-1);
// 读取a地址这个位置的数据
pread(fd,&data,4,(int)&a);
printf("%d\n",a);
pwrite(fd,&data,4,(int)&a);
printf("%d\n",a);
close(fd);
}
3. 文件的其他操作(返回与此命令有关的信息)
fstat 获取文件的状态
ftruncate 改变文件的大小
------------
stat main 获取main文件的信息
------------
// fstat.c
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
main()
{
int fd;
struct stat st;
fd = open("stu.dat",O_RDONLY); // 只读方式打开
if(fd == -1) printf("err:%m\n"),exit(-1);
/// 获取文件状态
fstat(fd,&st);
// 打印数据大小,数据权限
printf("%ld,%o\n",st.st_size,st.st_mode);
close(fd);
}
4. 文件映射:
虚拟地址映射到内存。
虚拟地址可以映射到文件:可以用内存方式访问文件。
mmap / munmap(内存映射)
作用: 建立一段可以被两个或者更多个程序读写的内存。一个程序对它所作的修给可以被其他程序可见。
mmap 函数创建一个指向一段内存区域的指针,该内存区域 与 可以通过一个打开的文件描述符访问的文件的内容相关联。
#include <sys/mman.h>
void *mmap(void *addr, // 如果该值为0,结果指针就将自动分配
size_t length,
int prot, // 用于设置内存段的访问权限
int flags, // 控制程序对该内存段的改变所造成的影响
int fd,
off_t offset); // 改变经共享内存段访问的文件中数据的起始偏移量
int munmap(void *addr, size_t length);
prot 参数:
PROT_READ: 允许读该内存段
PROT_WRITE: 允许写该内存段
PROT_EXEC: 允许执行该内存段
PROT_NONE: 该内存段不能被访问
flags 参数:
MAP_PRIVATE: 内存段是私用的,对它的修改只对本进程有效
MAP_SHARED: 把对该内存段的修改保存到磁盘文件中
MAP_FIXED: 该内存必须位于addr指定的位置
案例:
1. 使用内存方式写入数据
2. 使用内存方式读写数据
// map_write.c
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <string.h>
#include <sys/mman.h>
struct stu
{
char name[20];
int age;
float score;
};
main()
{
int fd;
struct stu *s; /// 文件在虚拟内存的映射首地址
struct stat st;
int size; // 文件大小
int count; // 记录条数
int i;
//1. 打开文件
fd = open("newstu.dat",O_RDWR | O_CREAT | O_EXCL,0666);
if(fd == -1) // 如果文件存在,打开文件
{
fd = open("newstu.dat",O_RDWR);
if(fd == -1) printf("::%m\n"),exit(-1);
}
//2. 得到文件大小,文件记录数
fstat(fd,&st);
size = st.st_size;
count = size / sizeof(struct stu);
//3.改变文件大小,文件大小改变只要在munmp之前都有成效
ftruncate(fd,size + sizeof(struct stu));
//4.映射到一个虚拟地址
s = mmap(0,size + sizeof(struct stu),
PROT_READ | PROT_WRITE,MAP_SHARED,fd,0);
//5. 把数据写入虚拟地址
printf("输入姓名:");
scanf("%s",s[count].name);
printf("输入年龄:");
scanf("%d",&s[count].age);
printf("输入成绩:");
scanf("%f",&s[count].score);
// 打印输入的信息
for(i=0; i<count;++i)
printf("%s,\t%d,\t%.2f\n",s[i].name,s[i].age,s[i].score);
//6. 卸载虚拟地址
munmap(s,sizeof(struct stu)+size);
//7. 关闭文件
close(fd);
}
————————————————————————
$ make
$ main
$ ls
$ ls -l newstu.dat
$cat newstu.dat
-------------------------------------
二. 文件描述符号的操作(IO锁)
文件描述符号是整数。文件描述符号对应内核上下文环境。
1.dup dup2 拷贝文件描述的属性的修改
dup 拷贝文件描述符号,返回系统指定的整数。
dup2拷贝文件描述符号,返回用户指定的整数。
#include <unistd.h>
int dup(int oldfd);
int dup2(int oldfd, int newfd);
返回值:成功则返回新的文件描述符,出错返回-1
2. fcntl 对文件描述符号的属性的修改
#include <unistd.h>
#include <fcntl.h>
int fcntl(int fd, int cmd, ... /* arg */ );
fcntl 函数有5种功能:
(1)复制一个现有的描述符号(cmd = F_DUPFD).
(2)获取/设置文件描述符号标记(cmd = F_GETFD 或 F_SETFD)
(3) 获取/设置文件状态标记(cmd = F_GETFL ,或F_SETFL)
(4)获得/设置异步I/O所有权(cmd = F_GETOWN ,或F_SETOWN)
(5)获取/设置记录锁(cmd = F_GETLK, F_SETLK, 或F_SETLKW)