Ⅰ. lseek/fcnt/ioctl/mmap 高级控制
lseek
每个打开的文件都记录着当前读写位置,打开文件时读写位置是0, 表示文件开头,通常读写多少个字节就会将读写位置往后移多少个字节。
但是有一个例外,如果以 O_APPEND 方式打开,每次写操作都会在文件末尾追加数据,然后将读写位置移到新的文件末尾。 lseek 和标准I/0 库的 fseek 函数类似。
#include <sys/types.h>
#include <unitsd.h>
off_t lseek(int fd, off_t offset, int whence);
- 返回值:当前读写位置,相当于ftell(FILE* stream)
- 参数 whence 为下列其中一种:
- SEEK_SET 参数offset 即为新的读写位置(即以文件头位置为起始位置,将读写位置移动offset个字节,此时offset不能为负值)
- SEEK_CUR 以当前的读写位置增加offset个位移量
- SEEK_END 将读写位置指向文件尾后再增加offset个位移量(即以文件末尾尾位置为起始位置,将读写位置移动offset个字节)
当whence 值为SEEK_CUR 或SEEK_END 时, 参数offet 允许负值的出现
fcntl
可以用 fcnt 函数改变一个已打开的文件的属性而不必重新 open 文件,可以重新设置读、写、追加、非阻塞等标志。
#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, flock *lock);
除了F_GETFL和F_SETFL命令之外,fcntl还有很多命令做其它操作,例如设置文件记录锁等。可以通过fcntl设置的都是当前进程如何访问设备或文件的访问控制属性
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
/*
将阻塞的标准输入改为非阻塞的
*/
int main() {
int flags;
flags = fcntl(STDIN_FILENO, F_GETFL); // 获取标准输入的所有标志
if (flags < 0) {
perror("fcntl get flags");
exit(-1);
}
flags |= O_NONBLOCK; // 在原先的基础上加上非阻塞权限
flags = fcntl(STDIN_FILENO, F_SETFL, flags);
if (flags < 0) {
perror("fcntl set flags");
exit(-2);
}
char buf[10];
ssize_t n;
while (1) {
n = read(STDIN_FILENO, buf, 5);
if (n > 0) {
break; //读到东西了
}
if (errno != EAGAIN) {
perror("read");
exit(-3);
}
write(STDOUT_FILENO, "try again\n", 10); // 本次没读到
sleep(1);
}
write(STDOUT_FILENO, buf, n);
return 0;
}
/*
获取文件的权限
*/
int main(int argc, char* argv[]) {
if (argc < 2) {
printf("usage: cmd filename\n");
exit(-1);
}
int flags;
flags = fcntl(atoi(argv[1]), F_GETFL);
if (flags < 0) {
perror("fcntl get flags");
exit(-2);
}
printf("flags=%d\n", flags);
switch (flags & O_ACCMODE) {
case O_RDONLY:
printf("read only");
break;
case O_WRONLY:
printf("write only");
break;
case O_RDWR:
printf("read write");
break;
default:
printf("invaild access mode\n");
}
if (flags & O_APPEND) {
printf(", append");
}
if (flags & O_NONBLOCK) {
printf(", nonblock");
}
return 0;
}
ioctl
ioctl用于向设备发控制和配置命令
#include <sys/ioctl.h>
int ioctl(int d, int request, ...);
- d是某个设备的文件描述符
- request 是 ioct 的命令,可变参数取決于 request,通常是一个指向变量或结构体的指针
- 若出错则返回-1, 若成功返回其他值,返回值也是取决于 request
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <unistd.h>
int main() {
struct winsize size;
if (!isatty(1)) { //判断是否是终端
printf("1 is not tty\n");
exit(-1);
}
int rtn = ioctl(1, TIOCGWINSZ, &size); //获取终端尺寸size
if (rtn < 0) {
perror("ioctl");
exit(-2);
}
printf("%d rows, %d columns\n", size.ws_row, size.ws_col);
return 0;
}
mmap
mmap可以把磁盘文件的一部分直接映射到内存,这样文件中的位置直接就有对应的内存地址,对文件的读写可以直接用指针来做而不需要 read/ write 函数
#include <sys/mmap.h>
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
- addr:如果 addr 参数为 NULL,内核会自己在进程地址空间中选择合适的地址建立映射。如果 addr不是NULL,则给内核一个提示,应该从什么地址开始映射,内核会选择 adrr 之上的某个合适的地址开始映射。建立映射后,真正的映射首地址通过返回值可以得到。
- length:是需要映射的那一部分文件的长度
- offset:从文件的什么位置开始映射,必须是页大小的整数倍
- fd:该文件的描述符
- prot:有四种取值:
- PROT_EXEC 表示映射的这一段可执行,例如映射共享库
- PROT_READ 表示映射的这一段可读
- PROT_WRITE:表示映射的这一段可写
- PROT_NONE 表示映射的这一段不可访问
- flag:
- MAP_SHARED 多个进程对相同文件映射共享
- MAP_PRIVATE 多个进程对相同文件映射不共享
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
/*
使用mmap修改文件test.txt,可以通过命令od -tx1 -tc test.txt观察程序执行前后的变化
*/
int main() {
int fd = open("./test.txt", O_RDWR);
if (fd < 0) {
perror("open file");
exit(-1);
}
// int *p = mmap(NULL, 6, PROT_WRITE, MAP_SHARED, fd, 0); // p是指向int类型的指针变量,一次可以修改4个字节
// p[0] = 0x30313233; // 修改前4个字节,注意高低位,33是低位
// ((int *)(((char *)p) + 1))[0] = 0x30313233; //第一个字节不修改,改第2-5个字节
char *p = mmap(NULL, 6, PROT_WRITE, MAP_SHARED, fd, 0); // p是指向char类型的指针变量,一次可以修改1个字节
p[0] = 'A';
p[1] = 'B';
p[2] = 'C';
munmap(p, 6); // 释放映射的地址
return 0;
}
评论 (0)