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

来源:互联网 发布:sql orderby limit 编辑:程序博客网 时间:2024/05/22 12:27
一、文件的基本操作
"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;
}
0 0