目录
- 文件操作系统调用的基本库函数
- 打开文件
- 读取文件
- 写入文件
- 关闭文件
- 应用
- 文件操作代码举例
- 文件操作与进程复制的结合
- 先打开文件再复制进程
- 先进程复制,再进行打开文件
- 缓冲区的知识回顾
在上一篇讲述僵尸进程的文章中对文件的系统调用做了一点点的代码讲述,这篇文章对操作文件的系统调用进行细节讲述。
文件操作系统调用的基本库函数
打开文件
打开一个已存在的文件:int open(const char* pathname, int flags);
新建一个文件,并设置访问权限:int open(const char* pathname, int flags,mode_t mode);
- pathname:要打开的文件的路径和文件名
- flags:打开标志,即:
参数 | 适应情况 |
---|---|
O_WRONLY | 只写打开 |
O_RDONLY | 只读打开 |
O_RDWR | 读写方式打开 |
O_CREAT | 文件不存在则创建 |
O_APPEND | 文件末尾追加 |
O_TRUNC | 清空文件,重新输入 |
- mode:权限(0600读写权限)
- 返回值:文件描述符(fd)
读取文件
读取文件: ssize_t read(int fd, void* buf, size_t count);
- fd:对应打开的文件描述符
- buf:存放数据的容器
- count:计划一次从文件中读取字节的个数
- 返回值 :实际读到的字节数
写入文件
写入文件:ssize_t write(int fd, const void* buf,size_t count);
- fd:对应打开文件的描述符
- buf:待写入的数据
- count:计划一次向文件中写入多少数据
关闭文件
关闭文件:int close(int fd)
- fd:文件描述符
应用
文件操作代码举例
我们通过编写一个自己的拷贝文件来运用一下刚刚库函数
如下代码,将etc下的passwd文件拷贝到当前目录下:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<fcntl.h>
#include<assert.h>
int main(int argc,char* argv[]){
char* s_name=argv[1];
char* new_name=argv[2];
int fdr=open(s_name,O_RDONLY);
assert(fdr!=-1);
int fdw=open(new_name,O_WRONLY|O_CREAT,0600);
assert(fdw!=-1);
char buff[128]={0};
int num=0;
while(num=read(fdr,buff,128)){
write(fdw,buff,num);
}
close(fdr);
close(fdw);
return 0;
}
在该文件中用了main函数的参数,main函数参数中argc为参数个数,argv[]为参数内容,因为第一个参数是参数个数本身,所以应用时从第二个参数开始使用,我们在执行main程序时,编译后执行用的是./main命令,可以通过./main后直接加入参数来对参数进行赋值。如下图:
此处要想和cp命令一样使用,只需要将该程序重命名为mycp,然后将编译后的文件移动到/bin路径下即可。(用到的命令:mv ./main.c mycp.c ,gcc -o mtco mycp.c ,sudomv ./mycp /bin)
在这里移动了之后我们如何判断是否移动过去了呢,就需要我们查找这个文件:
- 查找命令:sudo find /etc/ -name “mycp”
- 管道过滤:ls /etc | grep “mycp”(这里可以不需要全名,查找需要全名)
文件操作与进程复制的结合
此处我们复习一下僵尸进程:子进程先于父进程结束,且未获取退出码,子进程处于僵死状态。defunct
解决方法:waut,
父进程wait获取子进程退出码,wait(NULL),获取子进程推出码,不用退出码
先打开文件再复制进程
我们看下面程序猜想输出结果是什么?此处b.txt中内容为abcde
输出结果为:
为什么会这样输出呢,我们要从内存进行分析,看下图
在进程的PCB中有一个文件表,就是上图中带有标号0123的,然后调用文件时,实际上用了文件结构体,结构体图中又展示,结构体中又打开文件当时,计数器,节点号,文件偏移量等变量,因为上代码是先打开文件再进行进程复制,所以拷贝了父进程的PCB(PID不同),把相同的文件表复制了一份,所以操作文件的编号都一样,为此操作就在同一个文件的结构上操作,读取文件时,文件偏移量+1,通过偏移量结点号和偏移量找到输出内容,所以才会导致输出不会刷新,输出abcd。此处也只需要close一次,因为公用一个文件。
先进程复制,再进行打开文件
如下图,此处底下代码同上,不做展示,求输出结果:
因为是先复制进程,再输出,所以此是上图,复制进程时PCB中文件表还没有文件的结构体,所以是不同进程调用了不同的结构体,所以两个进程分开,通过不同的结构体在同一文件中找到输出内容。关闭文件时自然也需要关闭两次,此处的计数器是全局变量,所以要计数器的值为2,子进程父进程各close一次,就会计数器减一,等到计数器等于0时就会文件关闭。
缓冲区的知识回顾
write(1,“B”,1);:直接将数据输出屏幕,不经过缓冲区
int main(){
printf("A");
write(1,"B",1);
fork();
exit(0);
}
输出结果为BAA,此处因为进程复制时复制了缓冲区中的一个A,所以是BAA。