Unix高级编程:文件的基本操作、mmap将文件映射虚拟地址、文件描述符的复制

本文主要是介绍Unix高级编程:文件的基本操作、mmap将文件映射虚拟地址、文件描述符的复制,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、文件的基本操作
"open"(2)
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags,...); //正确写法,真正原型
以写入的方式打开文件,如果文件不存在,则创建文件,指定文件的权限为 664
open(filename, O_RDWR|O_CREAT|O_TRUNC, mode); //mode 0664
/*代码参见 file1.c */ "优先代码框架"
#include <stdio.h>
#include <unistd.h> //close
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(void) {
    int fd;
    int flags = O_RDWR|O_CREAT|O_TRUNC;
    //打开文件
    fd = open("filename", flags, 0664);
    if(fd == -1) {
        perror("open");
        return 1;
    }
    printf("open success...\n");
    //关闭文件
    close(fd);
    return 0;
}


补充:
1)"文件权限"
[d][rwx][rwx][rwx]   2   tarena  tarena  4096 12月4 11:40 mmap.c
[1][234][567][890] 节点 | 拥有人 | 群组 | 大小 | 修改日期 | 文件名


[1] 文件类型
"d" 目录directory
"-" 文件file
"l" 链接文件link file
"s" 通讯文件socket
"p" 管道文件
"b" 区块设备文件,文件里可供存储的接口设备block
"c" 字符设备文件,串行端口设备,如键盘、鼠标character
 
"rwxrwxrwx" 有权限有字母,无权限为"-"
[234]:属主("u")user
[567]:属组("g")group
[890]:其他人("o")other
[2~0]:所有人("a")all
权限更改:
chgrp :改变档案所属群组,格式:chgrp [-R] tarena hello.c
chown :改变档案所属人,格式:chown [-R] users hello.c
chmod :改变档案的属性,格式:chmod [-R] rwx 档案或目录
"chmod" change mode 改变权限
使用chmod让所有人拥有执行权限格式:"chmod a+x (文件名)"//固定格式类推
使用chmod让其他人去除执行权限格式:"chmod o-x (文件名)"//固定格式类推
权限更改:八进制数字 r:4 w:2 x:1
 "chmod 664 (文件名)"
rw-rw-r-- 0664 (210210210 二进制次方)
rwxr-xr-x 0755 (0代表普通权限,不用考虑)
u +(加入) r
chmod g -(除去) w 档案/目录
o =(设定) x
a






2)"权限掩码" //指定创建文件的时候,所要拿掉的权限,即权限掩码
获取权限掩码:"umask" 0002 -------w- 对应 -rw-rw-r-- //默认创建无x
更改umask:umask 0033 ----wx-wx 对应 -rw-r--r--
"touch file1" 
-rw-rw-r-- 1 tarena tarena 0 12月 5 09:57 file1


"tarena"(1)这个文件归谁所有————属主("u")
"tarena"(2)属主指定的组————属组("g")


"close"(2)
(上次笔记)


"read"(2)
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
功能:从fd指定的文件描述符读取内容
参数:
"fd" open(2)的返回值,指定了要从这个文件描述符里读取
"buf" 存储读取内容空间的首地址
"conunt" 本次读取的最大字节数
返回值:
成功 - 返回读取的字节数(也会比count小),0 代表读取到了文件的末尾
失败 - 返回 -1 ,errno被设置
/*举例验证文件读取,代码参见 mycat.c */
#include <stdio.h>
#include <unistd.h> //close
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <strings.h> //bzero
int main(int argc, char *argv[]) {
    int fd, size;
    char arr[128] = {0};
    int flags = O_RDWR;
    fd = open(argv[1], flags);
    if(fd == -1) {
        perror("open");
        return 1;
    }   
    printf("open success...\n");
    bzero(arr, 128); //把arr开始的地址对应数据全部设置为0
    while((size = read(fd, arr, 127)) > 0) {
        printf("%s", arr);
        bzero(arr, 128); //把arr开始的地址对应数据全部设置为0
    }   
    printf("read success...\n");
    close(fd);
    return 0;
}
gcc mycat.c -o mycat
sudo mv mycat /bin //将mycat程序在任何路径下都可以实现命令行执行其功能
sudo mv /bin/mycat . //将mycat程序移出到当前目录下


"write"(2)
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
功能:向fd指定的文件描述符写入内容
参数:
"fd" open(2)的返回值,指定了要写入文件的描述符
"buf" 从buf指定地址的空间里读取数据
"count" 最多向文件中写入的字节数
返回值:
成功 - 返回实际写入文件的字节数(也会比count小),0 代表没有内容被写入了
失败 - 返回 -1,errno被设置
/*举例验证文件写入,代码参见 write.c */
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
int main(void) {
    char buf[128] = "hello,world!welcome!";
    int fd = open("file1.c", O_WRONLY|O_CREAT|O_TRUNC, 0664);
    if(fd == -1) {
        perror("open");
        return 1;
    }   
    write(fd, buf, strlen(buf)); //写入有效字符个数
    close(fd);
    return 0;
}


0 标准输入 STDIN_FILENO
1 标准输出 STDOUT_FILENO
2 标准错误输出 STDERR_FILENO


"lseek"(2)
#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);
功能:重新设置文件读写的位置
参数:
"fd" 指定了文件。open(2)的返回值
"offset" 相对与位置的偏移字节数
"whence" 
SEEK_SET 文件的头部
SEEK_CUR 文件的当前位置
SEEK_END 文件的尾部
返回值:
成功 - 返回相对文件头的字节数
失败 - 返回 -1,errno被设置
/*举例验证lseek的使用,代码参见 lseek.c */
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(void) {
    int fd; 
    char buf;
    fd = open("aaa.txt", O_RDWR); //aaa.txt内容:tarenaesd1610
    if(fd == -1) {
        perror("open");
        return 1;
    }   
    //要直接读取出第7个字符
    lseek(fd, 6, SEEK_SET);
    int r = read(fd, &buf, 1); 
    write(1, &buf, r); //1 == STDOUT_FILENO
    write(STDOUT_FILENO, "\n", 2); //\n是字符串,包含\0,c
    off_t var_f = lseek(fd, 0, SEEK_CUR);//获取当前读写位置
    printf("%lu\n", var_f); //无符号长整形 7
    close(fd);
    return 0;
}




二、使用mmap将文件直接映射到进程的虚拟地址空间里,然后在内存里更新文件内容,直接反映到文件里
MAP_SHARED 对内存的操作反映的文件里
MAP_PRIVATE 只对内存操作,不反映到文件里
命令行:"od -tx1 -tc aaa.txt" //显示文件当中的偏移量及ASCII码
0000000  74  61  72  65  6e  61  0a
          t   a   r   e   n   a  \n
0000007
/*举例验证,代码参考 mmap_file.c*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
int main(void) {
    //以读写方式打开文件aaa.txt
    int fd = open("aaa.txt", O_RDWR);
    if(fd == -1) {
        perror("open");
        return 1;
    }   
    //将文件映射到进程的虚拟地址空间里
    void *p = mmap(NULL,6,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
    if(p == MAP_FAILED) { //映射失败
        perror("mmap");
        return 2;
    }   
    printf("mmap success...\n");
    close(fd);
    int *q = p; //类型转换
    q[0] = 0x30313233; //十六进制转换为十进制的数,就是ASCII码
    munmap(p, 6); //解除映射
    return 0;
}


命令行:"od -tx1 -tc aaa.txt" 
0000000  33  32  31  30  6e  61  0a
          3   2   1   0   n   a  \n
0000007




三、文件描述符的复制
"文件重定向"
使用文件描述符的复制,可以改变进程的输入源或输出目的文件。
这样就改变的文件流的流向,这就是文件的输入/输出(I/O)"重定向"。
输入重定向
输出重定向
《鸟哥linux私房菜》——晚自习:专门看这块儿内容。


"dup"(2)
#include <unistd.h>
int dup(int oldfd);
功能:复制一个文件描述符
参数:"oldfd" 源描述符
返回值:
成功 - 返回一个新的文件描述符,未被使用的、最小的数字
失败 - 返回 -1,errno被设置


"dup2"(2)
int dup2(int oldfd, int newfd);
功能:复制一个新的文件描述符
参数:
"oldfd" 旧的文件描述符
"newfd" 新的文件描述符
返回值:
成功 - 返回一个新的文件描述符,未被使用的、最小的数字
失败 - 返回 -1,errno被设置


/*举例说明文件描述符的复制,代码参见 dup.c*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
int main(void) {
    int fd, s_fd;
    char msg[20] = "hello,world!";
    fd = open("hello", O_RDWR|O_CREAT, 0664); //fd(hello)->3
    if(fd == -1) {
        perror("open");
        return 1;
    } //s_fd(临时)->4
    s_fd = dup(1); //STDOUT_FILENO->4
    dup2(fd, 1); //fd(hello)->1
    close(fd); //3不被使用,不再指向hello
    write(1, msg, strlen(msg));
    printf("\n");
    dup2(s_fd, 1); //STDOUT_FILENO->1,回原位,hello无指向,释放
    write(1, msg, strlen(msg));
    write(1, "\n", 2);
    close(s_fd); //4不被使用,不再指向STDOUT_FILENO
    return 0;
}



cp 源文件 目标文件 (不要求拷贝文件夹,仅文件即可)
使用今天的open/read/write编写程序完成cp命令的功能,生成可执行文件mycp
/*库函数实现:代码*/
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
    FILE *p_old, *p_new;
    int size = 0;
    char buf[128] = {0};
    p_old = fopen(argv[1], "rb");
    if(!p_old) {
        printf("文件打开失败!\n");
        return 1;
    }   
    p_new = fopen(argv[2], "wb");
    if(!p_new) {
        printf("文件打开失败!\n");
        fclose(p_old);
        p_old = NULL;
        return 2;
    }   
    while(1) {
        size = fread(buf, sizeof(char), 128, p_old);
        fwrite(buf, sizeof(char), size, p_new);
        if(!size) {
            break;
        }   
    }   
    fclose(p_old);
    p_old = NULL;
    fclose(p_new);
    p_new = NULL;
    return 0;
}
/*系统调用函数实现:代码*/  "老王标准答案"
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
/*
function: 实现文件内容的拷贝
parameter:
s 源文件描述符
d 目标文件描述符
return value:
void
author:Jan
date:2016.12.07
*/
void copy_file(int s, int d) {
    char buf[128];
    int r, w;
    char *tmp;
    while((r = read(s, buf, 128)) > 0) {
        tmp = buf;
        while(1) { //将一次读出的内容,完全写入
            w = write(d, tmp, r); //w实际写入可能会比r小
            r = r - w; //未写入内容的字节数
            if(!r) {
                break;
            }   
            tmp += w; //tmp往后挪
        }   
    }   
    return;
}
int main(int argc, char *argv[]) {
    int s_fd, d_fd;
    s_fd = open(argv[1], O_RDONLY);
    if(s_fd == -1) {
        perror("open source");
        return 1;
    }   
    d_fd = open(argv[2], O_RDWR|O_CREAT, 0664);
    if(d_fd == -1) {
        perror("open dest");
        close(s_fd);
        return 2;
    } //文件内容拷贝:
    copy_file(s_fd, d_fd);
    close(s_fd);
    close(d_fd);
    return 0;
}

这篇关于Unix高级编程:文件的基本操作、mmap将文件映射虚拟地址、文件描述符的复制的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/541371

相关文章

MySQL的JDBC编程详解

《MySQL的JDBC编程详解》:本文主要介绍MySQL的JDBC编程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录前言一、前置知识1. 引入依赖2. 认识 url二、JDBC 操作流程1. JDBC 的写操作2. JDBC 的读操作总结前言本文介绍了mysq

JavaScript中的高级调试方法全攻略指南

《JavaScript中的高级调试方法全攻略指南》什么是高级JavaScript调试技巧,它比console.log有何优势,如何使用断点调试定位问题,通过本文,我们将深入解答这些问题,带您从理论到实... 目录观点与案例结合观点1观点2观点3观点4观点5高级调试技巧详解实战案例断点调试:定位变量错误性能分

SpringBoot路径映射配置的实现步骤

《SpringBoot路径映射配置的实现步骤》本文介绍了如何在SpringBoot项目中配置路径映射,使得除static目录外的资源可被访问,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一... 目录SpringBoot路径映射补:springboot 配置虚拟路径映射 @RequestMapp

从基础到高级详解Python数值格式化输出的完全指南

《从基础到高级详解Python数值格式化输出的完全指南》在数据分析、金融计算和科学报告领域,数值格式化是提升可读性和专业性的关键技术,本文将深入解析Python中数值格式化输出的相关方法,感兴趣的小伙... 目录引言:数值格式化的核心价值一、基础格式化方法1.1 三种核心格式化方式对比1.2 基础格式化示例

C#文件复制异常:"未能找到文件"的解决方案与预防措施

《C#文件复制异常:未能找到文件的解决方案与预防措施》在C#开发中,文件操作是基础中的基础,但有时最基础的File.Copy()方法也会抛出令人困惑的异常,当targetFilePath设置为D:2... 目录一个看似简单的文件操作问题问题重现与错误分析错误代码示例错误信息根本原因分析全面解决方案1. 确保

Android协程高级用法大全

《Android协程高级用法大全》这篇文章给大家介绍Android协程高级用法大全,本文结合实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友跟随小编一起学习吧... 目录1️⃣ 协程作用域(CoroutineScope)与生命周期绑定Activity/Fragment 中手

Python异步编程之await与asyncio基本用法详解

《Python异步编程之await与asyncio基本用法详解》在Python中,await和asyncio是异步编程的核心工具,用于高效处理I/O密集型任务(如网络请求、文件读写、数据库操作等),接... 目录一、核心概念二、使用场景三、基本用法1. 定义协程2. 运行协程3. 并发执行多个任务四、关键

AOP编程的基本概念与idea编辑器的配合体验过程

《AOP编程的基本概念与idea编辑器的配合体验过程》文章简要介绍了AOP基础概念,包括Before/Around通知、PointCut切入点、Advice通知体、JoinPoint连接点等,说明它们... 目录BeforeAroundAdvise — 通知PointCut — 切入点Acpect — 切面

深度解析Python yfinance的核心功能和高级用法

《深度解析Pythonyfinance的核心功能和高级用法》yfinance是一个功能强大且易于使用的Python库,用于从YahooFinance获取金融数据,本教程将深入探讨yfinance的核... 目录yfinance 深度解析教程 (python)1. 简介与安装1.1 什么是 yfinance?

MySQL 临时表与复制表操作全流程案例

《MySQL临时表与复制表操作全流程案例》本文介绍MySQL临时表与复制表的区别与使用,涵盖生命周期、存储机制、操作限制、创建方法及常见问题,本文结合实例代码给大家介绍的非常详细,感兴趣的朋友跟随小... 目录一、mysql 临时表(一)核心特性拓展(二)操作全流程案例1. 复杂查询中的临时表应用2. 临时