[C++系列] UNIX高级编程 day06 系统调用文件操作,内存映射,文件描述符的复制
in C/C++ with 0 comment

[C++系列] UNIX高级编程 day06 系统调用文件操作,内存映射,文件描述符的复制

in C/C++ with 0 comment

回顾

一,内存映射
代码段,数据段,栈段,堆

Const int global = 8;
Int main(){
    Const int local =4;
    Int *p = &local;
    *p = 6;
    Local =9;//错误
    Return 0;
}

二,mmap映射物理地址到进程的虚拟地址空间
mmap(). Munmap()
二,malloc的实现原理
缓冲机制:
封装
库函数和系统调用之间的关系


#文件的操作(系统调用)

使用库函数操作文件
Open(3) close(3) freed(3) fwrite(3) get(3) fputc(3)

使用系统调用
open(2) close(2) read(2) write(2) lseek(2)

1,需要打开文 open(2)

#include <fcntl.h>

     int
     open(const char *path, int flags);

     int
     open(const char *path, int flags, mode_t mode);

//实际代码中的函数声明。
     int
     open(const char *path, int flags,...);

功能:打开一个文件
参数
Pathname:指定了要打开的文件的路径

Flags:

O_RDONLY:
O_WRONLY:
O_RDWR:

文件的创建标记:在flags参数中可以按位或0个或多个文件创建标记或状态标记。
O_CREAT:如果文件不存在,创建文件。并且需要指定文件的权限。
文件存在,什么都不做。
O_EXCL:如果和它一起同时指定,那么文件存在的情况下,报错。
O_TRUNC:打开文件的时候如果文件存在,将文件清空。
O_APPEND:指定打开文件的时候使用追加的方式打开。

mode:指定了文件的权限。在flags参数中有oncreate标记时,权限才被提供。否则被忽略。
权限有可能被进程的umask修改。修改如下:mode & ~umask

什么是umask:umask是进程创建文件的时候的文件权限的掩码。
在创建文件的时候,使用touch filename。这时候给文件什么权限?
系统提供的
用户需要量身定制文件的权限。
Umask指定用户想拿掉的用户权限。
0002

如何查看umask?
umask

什么是文件的权限:
drwxr-xr-x+ 6 drop staff 204 5 28 18:13 Public
最前面的一个占位符,代表文件的类型。

c 字符设备文件
b 块设备文件
s socket文件
l 软链接文件
p 管道文件

rwxr-xr-x+ 指定文件的权限
R 读 w 写 x 可执行
每三个占位符为一组。共三组
第一组是文件拥有者和超级用户的权限。 u
第二组是文件拥有者所属的组的成员的 对文件的权限。g
第三组其它人对文件的权限 。o. 所有的 a

chmod a+x test 给所有人加执行权限。 类型用来修改文件的权限。
chmod 664 test。可以使用数字来表示

返回值:

:可变参数:参数的个数和类型取决于可变参数前边的那个参数。

`文件描述符
每个进程都有自己的pid。打开文件是在进程中,进程是操作系统分配资源的基本单位。
进程需要有一个记录进程使用资源的结构体变量。可以理解为进程的户口本。这就是进程的控制块,PCB

标准输入 0。STDIN_FILENO
标准输出 0,STDOURT_FILENO

在标C中,使用FILE *p:

在标C中,使用时FILE *fp;

返回值
成功:返回新的文件描述符
失败:返回-1

close(2)
功能:关闭一个文件描述符,在改文件描述符上加的锁都将被移除。
参数:fd指定了要关闭的文件描述符。
返回值
成功返回0
错误 -1 errno被设置。

#文件的读写:

read(2)
#include<unistd.h>
Size_t read(int fd,void *buf,size_t count);

功能:从文件读取数据
参数
Fd:指定了文件的描述符
Buf:指定了文件内容的存放地址。
count:请求读取的最大字节数。
返回值
失败:-1 错误 errno被设置
成功:返回实际读取到的字节数。0代表到达文件的末尾。

向文件写数据:

write(2)
#include <unistd.h>
ssize_t  write(int fildes, const void *buf, size_t nbyte);

功能:将数据写入到文件。
参数
Fd:指定了文件
Buf:指定了存放数据的地址。
Count:申请写入文件的最大字节数。
返回值
成功:返回实际写入文件的字节数。
错误: -1 errno被设置

举例说明;
1,以只读方式打开文件:

 1 #include<stdio.h>
 2 #include<sys/types.h>
 3 #include <fcntl.h>
 4  #include <unistd.h>
 5 int main(int argc,char *argv[]){
 6         int fd;
 7         //打开文件,以只读方式打开文件。
 8         fd=open(argv[1],O_RDONLY);
 9         if(fd==-1){
10                 perror("open");
11                 return -1;
12         }
13         printf("%s open success ...\n",argv[1]);
14         //关闭文件描述符
15         close(fd);
16         return 0;
17 }
~```
2,打开文件的时候,文件不存在,创建文件,指定文件的权限为0664,如果文件存在将文件的内容清空。

1 #include<stdio.h>
2 #include<sys/types.h>
3 #include <fcntl.h>
4 #include <unistd.h>
5 int main(int argc,char *argv[]){
6 int fd;
7 int flags=O_WRONLY|O_CREAT|O_TRUNC;
8 mode_t mode=0664;
9 //打开文件
10 fd=open(argv[1],flags,mode);
11 if(fd==-1){
12 perror("open");
13 return -1;
14 }
15 printf("file %s open success ..."\
16 ,argv[1]);
17 //关闭文件
18 return 0;
19 }
~`

举例说明:
编写代码实现cat命令的功能:

  1 #include<stdio.h>
  2 #include <unistd.h>
  3 #include <fcntl.h>
  4  #include<sys/types.h>
  5 int main(int argc,char *argv[]){
  6         char str[128];
  7         int fd;
  8         void *buf;
  9         size_t count;
10         int flags = O_RDONLY;
11         fd = open(argv[1],flags);
12         if(fd==-1){
13                 perror("open");
14                 return -1;
15         }
16         int r;
17         r = read(fd,str,128);
18         /*
19         while((r=read(fd,str,1024))>0){
20                 write(1,str,r);
21         }
22         */
23         printf("%s\n",str);
24         close(fd);
25         return 0;
26 }

作业:

**编写代码实现cp命令的功能。
讲一个文件的内容复制到另一个文件中。
编译生成可执行文件rcp**

读写位置的定位:

lseek(2)
#include <unistd.h>
off_t     lseek(int fildes, off_t offset, int whence);

功能:重定位文件的读写位置。
参数
Fd:
Offset:指定字节数
Whence:
SEEK_SET:将文件开头计算的offset的位置,设置为文件的读写位置。
SEEK_CUR:当前 的读写位置+offset个字节。将这个位置设置为读写位置。
SEEK_END:读写位置被定位在,文件的末尾+offset个字节处。
返回值:
-1。 设置errno
成功。返回相对于文件开头的便宜字节

#举例说明:

使用lseek重定位文件的读写位置,然后将文件的内容读取出来
od -tx1 -tc w_file.c

1 #include<stdio.h>
  2 #include<sys/types.h>
  3 #include <fcntl.h>
  4 #include <unistd.h>
  5 int main(int argc,char *argv[]){
  6         char c[1];
  7         int fd;
  8         //打开文件
  9         fd = open(argv[1],O_RDONLY);
10         if(fd==-1){
11                 perror("open");
12                 return -1;
13         }
14         //重新定位读取的位置。
15         lseek(fd,4,SEEK_SET);
16         int r = read(fd,c,1);
17         printf("%c\n",c[0]);
18 //      write(1,c,r);
19         close(fd);
20         return 0;
21 }
~```
二,使用`mmap`将文件映射到进程的虚拟地址空间。


举例说明:
使用`mmap`将文件映射到指定进程的虚拟空间。

1 #include<stdio.h>
2 #include<sys/mman.h>
3 #include<sys/types.h>
4 #include <fcntl.h>
5 #include <unistd.h>
6 int main(){
7
8 int fd;
9 //打开文件,以读写的方式打开文件
10 fd = open("hello",O_RDWR);
11 if(fd==-1){
12 perror("open");
13 return -1;
14 }
15 //将文件映射到当前进程的虚拟地址
16 int prot = PROT_READ|PROT_WRITE;
17 int flags = MAP_SHARED;
18 int p = (int )mmap(NULL,6,prot,flags,fd,0);
19 if(p==MAP_FAILED){
20 return -1;
21 }
22 p[0]=0x30313233;//文件被映射到当前进程虚拟内存中,对虚拟内存的修改会被同步到文件的修改。
23 //关闭文件描述符
24 close(fd);
25 //解除映射
26 munmap(p,6);
27 return 0;
28 }
~


上述经常被应用在可执行文件加载到内存。加载动态库使用。

## `#`文件重定向

**文件输出重定向**:

流式文件(文件流)    
文件重定向就是改变文件流的流向。
文件输出重定向就是改变文件输出的流向

程序员需要完成文件的重定向。
系统提供了文件描述符的复制函数。

`dup(2)    dup2(2)    `

include <unistd.h>

int dup(int fildes);

**功能**:复制一个文件描述符
**参数**:
`Oldfd`:指定了源文件描述符
**返回值**:
成功: 返回一个新的文件描述符。最小的,没有被使用的那个文件描述符。
失败。返回 -1 设置`errno`的值

` int     dup2(int fildes, int fildes2);`
**功能**:复制一个文件描述符
**参数**:
oldfd:指定了源文件描述符
newfd;指定新的文件描述符。用户可以自己指定。如果必要,先关闭新的文件描述符。
**返回值**:
成功:返回新的文件描述符
失败:` -1 errno `   被设置

举例说名:

使用文件描述符的复制函数实现文件输出的重定向:

1 #include<stdio.h>
2 #include<sys/mman.h>
3 #include<sys/types.h>
4 #include <fcntl.h>
5 #include <unistd.h>
6 #include<string.h>
7 int main(int argc,char *argv[]){
8 char *msg = "this is a test !";
9 int fd,s_fd;
10 //打开指定的文件
11 int flags=O_WRONLY|O_CREAT|O_TRUNC;
12 mode_t mode = 0664;
13 fd=open(argv[1],flags);
14 if(fd == -1){
15 perror("open");
16 return -1;
17 }
18 //保存标准输出文件描述符
19 s_fd=dup(1);
20 //将fd文件描述符复制到标准输出
21 dup2(fd,1);
22 close(fd);
23 //向标准输出输出消息,标准输出现在是打开的文件
24 write(1,msg,strlen(msg));
25 //将s_fd的文件描述符复制到1中
26 dup2(s_fd,1);
27 close(s_fd);
28 //向标准输出输出消息
29 write(1,msg,strlen(msg));
30 return 0;
31 }


文件输入重定向

总结:
一,通过系统调用对文件的基本操作
`Open read     close    read    write    lseek  `  

文件描述符
文件的权限
文件的类型
`umask`权限掩码

二,使用`mmap`将文件映射到进程的虚拟地址空间
在内存里对文件的操作直接反应到文件里。
`mmap`的使用结束
三,文件描述符的复制。
使用文件描述符复制函数实现了文件的输出重定向。
文件的输入重定向,在以后会说明




Responses