文件描述符-内存申请与管理-fork()与execl联合创建进程代码
导语
事虽难做则必成,路虽远行则将致.–荀子*修身
1.写时拷贝复习
提高fork()的效率-修改时再复制
fork(),在复制父进制时,如果有修改页面时再进行复制,其它的时期进行共享,降低内存的占用率
写实拷贝并不复制整个进程的地址空间,而是让父进程和子进程共享一个拷贝。只有在需要写入的时候,数据才会被复制,从而使各个进程拥有各自的拷贝。资源的复制只有在写入的时候才进行,在此之前,只是以只读方式共享。
2.文件操作的系统调用-open,read,write,close
2.1如何创建一个可读可可写文件
//创建一个可读可写的a.tet 文件
#include <stdio.h>
#include<fcntl.h>
#include<stdlib.h>
#include <unistd.h>
int main(){
int fd=open("a.tet",O_WRONLY | O_CREAT,0600);//err -1,success fd>=0 文件描述符,0600表示可读可写
if(fd==-1){
exit(-1);
}
write(fd,"hello",5);
close(fd);
exit(0);
2.2与文件操作有关的系统调用
//打开一个已经存在的文件 Pathname:将要打开文件的路径和名称 flag:打卡标志,如以只读或者只写的方式打开
int open(const char *pathname,int flags);
int open(const char * pathname,int flags,mode_t mode);
权限标识
O_WRONLY 只写方式打开
O_RDONLY 只读方式打开
O_RDWR 读写方式打开
O_CREAT 文件不存在则创建
O_APPEND 文件末尾追加
O——TRUNC 清空文件,重新写入
mode:权限 如:0600 可读可写
返回值:文件描述符–参数介绍
ssize_t read(int fd,void *buf,size_t count);
fd 对应文件打开的描述符
buf 存放数据的空间
count 计划从文件中读取多少字节数据
返回值:实际读到的字节数
int close(int fd)
fd 为要关闭的文件描述符
2.3 父进程 先 打开一个文件,fork后子进程是否可以共享使用?
🤪可以共享
为什么可以❓
由于fork创建的子进程的PCB是拷贝父进程的,子进程中PCB的文件表指向打开文件的指针,只是拷贝了父进程PCB的值,所以父子进程会共享fork之前打开的所有文件描述符。
2.2自己动手写cp函数
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include <fcntl.h>
#include <unistd.h>
/*将当前目录下的a.tet的普通文件复制一份,新文件名交newa.tet*/
int main(){
//buff 存放数据的空间
char buff[256]={0};
int fdr=open("a.tet",O_RDONLY);
assert(fdr!=-1);
int fdw=open("newa.tet",O_WRONLY|O_CREAT,0600);
assert(fdr!=-1);
int n=0;
//在这里一定要注意,先将读到的值返回给n如果n>0的时候那么就写数据
while((n=read(fdr,buff,256))>0){
write(fdw,buff,n);
}
close(fdr);
close(fdw);
exit(0);
}
我们截图看下,现在a.tet文件中的内容
我们运行一下上述代码,我们来看下效果
我们再来看下newa.tet当中共的内容
3.从进程视角看堆区内存申请与释放的有关问题
3.1.1C语言当中的内存泄漏
- 一直向堆区申请内存空间而没有释放时,就会造成内存泄漏
- 丢失了指向堆区申请的内存空间的指针-找不到释放地点-造成内存泄漏
💥不论是否释放和指针丢失-造成的内存泄漏,在程序运行结束之后,操作系统都会自动回收
3.1.2 当物理内存中有1G的空间时,通过malloc申请1G空间是否能够成功
- 如果存在虚拟内存,且虚拟内存+物理内存的容量大于1G的时候其就可以申请成功
- 但没有虚拟内存,只有物理内存时,时无法申请成功的
3.1.3在物理内存只有2G的系统中,malloc能否申请2G空间?
不能够申请2G的内存空间,因为只有2G的内存空间,所以进程地址空间肯定是小于2G的,所以是无法申请成功的
与内存空间多大没有关系了,因为只有2G的空间,要再大也给不了
3.1.4 malloc 与fork ,父进程堆区申请的空间复制后,子进程也会有一份,需要释放吗?
需要两次释放,因为当malloc申请内存空间之后,我的父进程用有一个指针指向了这块儿内存空间,子进程中也有要给指针,指向了这块儿内存空间,所以二者都需要进行对应的释放
int main(){
char *s=(char *)malloc(128);
assert(s!=NULL);
pid_t pid=fork();
assert(pid!=-1);
if(pid==0){
strcpy(s,"child");
}else{
strcpy(s,"parent");
}
printf("s=%s\n",s);
free(s);
exit(0);
}
4.进程替换
exec系列替换过程:PCB使用之前的只是做略微调整,进程实体进行更换
4.1通过man exec查看替换函数
/*
参数解释
*panthname:新替换的主程序的路径名称
*agr:传给新程序主函数的第一个参数,一般为程序名字
*arg:后面是剩余的参数列表,参数个数可变
⭐最后一个参数必须为空指针
*/
4.2通过ececl 将当前的进程替换为ls
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <string.h>
int main(){
printf("execl pid=%d\n",getpid());
printf("将当前进程替换为ls\n");
//ececl 执行成功不返回,直接从新程序的主函数开始,只有失败才返回错误码
execl("/bin/ls","ls","-l",(char *)0);
perror("execl error");
exit(0);
}
4.3fork 和exec联合创建一个全新进程
我们先看文件中的内容
我们再来看下,我们 my_copy当中的内容-就是复制 a.tet当中的内容 到 newa.tet中
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include <fcntl.h>
#include <unistd.h>
/*将当前目录下的a.tet的普通文件复制一份,新文件名交newa.tet*/
int main(){
//buff 存放数据的空间
char buff[256]={0};
int fdr=open("a.tet",O_RDONLY);
assert(fdr!=-1);
int fdw=open("newa.tet",O_WRONLY|O_CREAT,0600);
assert(fdr!=-1);
int n=0;
//在这里一定要注意,先将读到的值返回给n如果n>0的时候那么就写数据
while((n=read(fdr,buff,256))>0){
write(fdw,buff,n);
}
close(fdr);
close(fdw);
exit(0);
}
4.3.1fork与execl联合创建进程代码
先复制一个进程来执行,
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <string.h>
#include <wait.h>
int main(int argc,char* argv[],char *envp[]){
printf("当前进程ID为: pid=%d\n",getpid());
//printf("将当前进程替换为ls\n");
//ececl 执行成功不返回,直接从新程序的主函数开始,只有失败才返回错误码
//execl("/bin/ls","ls","-l",(char *)0);
//fork 一个子进程来进行文件的复制
pid_t pid=fork();
assert(pid!=-1);
if(pid==0){
printf("子进程的当前ID=%d\t 子进程的父进程ID=%d \n",getpid(),getppid());
execl("./my_copy","my_copy",(char*)0);
printf("\n\n\n");
}
else{
printf("将当前进程替换为ls\n");
execl ("/bin/ls","ls","-k",(char*)0);
}
wait(NULL);
printf("over");
exit(0);
}