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

相关文章

Linux 网络编程 --- 应用层

一、自定义协议和序列化反序列化 代码: 序列化反序列化实现网络版本计算器 二、HTTP协议 1、谈两个简单的预备知识 https://www.baidu.com/ --- 域名 --- 域名解析 --- IP地址 http的端口号为80端口,https的端口号为443 url为统一资源定位符。CSDNhttps://mp.csdn.net/mp_blog/creation/editor

【Python编程】Linux创建虚拟环境并配置与notebook相连接

1.创建 使用 venv 创建虚拟环境。例如,在当前目录下创建一个名为 myenv 的虚拟环境: python3 -m venv myenv 2.激活 激活虚拟环境使其成为当前终端会话的活动环境。运行: source myenv/bin/activate 3.与notebook连接 在虚拟环境中,使用 pip 安装 Jupyter 和 ipykernel: pip instal

系统架构师考试学习笔记第三篇——架构设计高级知识(20)通信系统架构设计理论与实践

本章知识考点:         第20课时主要学习通信系统架构设计的理论和工作中的实践。根据新版考试大纲,本课时知识点会涉及案例分析题(25分),而在历年考试中,案例题对该部分内容的考查并不多,虽在综合知识选择题目中经常考查,但分值也不高。本课时内容侧重于对知识点的记忆和理解,按照以往的出题规律,通信系统架构设计基础知识点多来源于教材内的基础网络设备、网络架构和教材外最新时事热点技术。本课时知识

【编程底层思考】垃圾收集机制,GC算法,垃圾收集器类型概述

Java的垃圾收集(Garbage Collection,GC)机制是Java语言的一大特色,它负责自动管理内存的回收,释放不再使用的对象所占用的内存。以下是对Java垃圾收集机制的详细介绍: 一、垃圾收集机制概述: 对象存活判断:垃圾收集器定期检查堆内存中的对象,判断哪些对象是“垃圾”,即不再被任何引用链直接或间接引用的对象。内存回收:将判断为垃圾的对象占用的内存进行回收,以便重新使用。

Go Playground 在线编程环境

For all examples in this and the next chapter, we will use Go Playground. Go Playground represents a web service that can run programs written in Go. It can be opened in a web browser using the follow

深入理解RxJava:响应式编程的现代方式

在当今的软件开发世界中,异步编程和事件驱动的架构变得越来越重要。RxJava,作为响应式编程(Reactive Programming)的一个流行库,为Java和Android开发者提供了一种强大的方式来处理异步任务和事件流。本文将深入探讨RxJava的核心概念、优势以及如何在实际项目中应用它。 文章目录 💯 什么是RxJava?💯 响应式编程的优势💯 RxJava的核心概念

函数式编程思想

我们经常会用到各种各样的编程思想,例如面向过程、面向对象。不过笔者在该博客简单介绍一下函数式编程思想. 如果对函数式编程思想进行概括,就是f(x) = na(x) , y=uf(x)…至于其他的编程思想,可能是y=a(x)+b(x)+c(x)…,也有可能是y=f(x)=f(x)/a + f(x)/b+f(x)/c… 面向过程的指令式编程 面向过程,简单理解就是y=a(x)+b(x)+c(x)

禁止复制的网页怎么复制

禁止复制的网页怎么复制 文章目录 禁止复制的网页怎么复制前言准备工作操作步骤一、在浏览器菜单中找到“开发者工具”二、点击“检查元素(inspect element)”按钮三、在网页中选取需要的片段,锁定对应的元素四、复制被选中的元素五、粘贴到记事本,以`.html`为后缀命名六、打开`xxx.html`,优雅地复制 前言 在浏览网页的时候,有的网页内容无法复制。比如「360

Java并发编程之——BlockingQueue(队列)

一、什么是BlockingQueue BlockingQueue即阻塞队列,从阻塞这个词可以看出,在某些情况下对阻塞队列的访问可能会造成阻塞。被阻塞的情况主要有如下两种: 1. 当队列满了的时候进行入队列操作2. 当队列空了的时候进行出队列操作123 因此,当一个线程试图对一个已经满了的队列进行入队列操作时,它将会被阻塞,除非有另一个线程做了出队列操作;同样,当一个线程试图对一个空

OpenCV结构分析与形状描述符(11)椭圆拟合函数fitEllipse()的使用

操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C++11 算法描述 围绕一组2D点拟合一个椭圆。 该函数计算出一个椭圆,该椭圆在最小二乘意义上最好地拟合一组2D点。它返回一个内切椭圆的旋转矩形。使用了由[90]描述的第一个算法。开发者应该注意,由于数据点靠近包含的 Mat 元素的边界,返回的椭圆/旋转矩形数据