Ⅰ. 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)