一、进程替换方式:
1>使用exec系列库函数
#include <unistd.h>
extern char **environ;
int execl(const char *pathname, const char *arg, .../* (char *) NULL */);
int execlp(const char *file, const char *arg, .../* (char *) NULL */);
int execle(const char *pathname, const char *arg, .../*, (char *) NULL, char *const envp[]*/);
int execv(const char *pathname, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[], char *const envp[]);
2>使用系统调用execve
#include <unistd.h>
int execve(const char *pathname, char *const argv[],char *const envp[]);
execl系列库函数(execl , execlp , execve , execv , execvp )底层还是调用 execve系统调用,这6个本质上没有区别
二、进程替换
main进程被ps替换之后就消失了,装载ps进程,PCB中的pid,ppid不会发生改变,name会改为替换进程的名称
fork()复制 + exec()替换是系统创建新进程的方式
1)execl
函数原型:int execl(const char *pathname, const char *arg, .../* (char *) NULL */);
参数:
pathname:路径+名称,要替换的进程
arg:进程的参数,任何程序执行后主函数第一个参数都是当前程序的名称
(char*)NULL*:规定已NULL(char*)0结束
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<unistd.h>
4
5 int main()
6 {
7 printf("main pid =%d\n",getpid());
8 execl("/usr/bin/ps","ps","-f",(char*)0);//将当前进程替换为ps -f,替换之后,原来
的程序就直接消失了,就没了,直接执行新的进程
9 printf("execl err\n");
10
11 exit(0);
12
13 }
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
int main()
{
printf("main pid =%d\n",getpid());
execl("/usr/bin/ps","abc","-f",(char*)0);//第一个参数为程序的名称,后面的参数> 为指令,名称可以错,但指令不能错,错了就无法解析成功
printf("execl err\n");
exit(0);
}
名称传错了不影响程序的执行,但路径万万不可传错
2)execlp
函数原型:int execlp(const char *file, const char *arg, .../* (char *) NULL */);
参数:
execlp(p指的是环境变量)
file :文件名称,会自动在环境变量PATH路径去寻找
arg: 参数
(char*)NULL*:规定已NULL(char*)0结束
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
int main()
{
printf("main pid =%d\n",getpid());
execlp("ps","ps","-f",(char*)0);//与execl区别就在于不用传路径
//execl("/usr/bin/ps","ps","-f",(char*)0);
printf("execl err\n");
exit(0);
}
execlp与execl区别就在于不用传路径
3)execle
函数原型:int execle(const char *pathname, const char *arg, .../*, (char *) NULL, char *const envp[]*/);
char *const envp[]* :要求传入环境变量
execl和execlp不需要传环境变量,系统默认给,如果想自己改变环境变量,就需要使用execle
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
int main(int argc,char* argv[],char *envp[])
{
printf("main pid =%d\n",getpid());
execle("/usr/bin/ps","ps","-f",(char*)0,envp);//需要传入环境变量
// execlp("ps","ps","-f",(char*)0);
//execl("/usr/bin/ps","ps","-f",(char*)0);
printf("execl err\n");
exit(0);
}
变量前加$ ,就可以打变量的值
打印环境变量
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
int main(int argc,char* argv[],char* envp[])
{
int i=0;
printf("argc=%d\n",argc);
for(;i<argc;i++)
{
printf("argv[%d]=%s\n",i,argv[i]);
}
for(i=0;envp[i] !=NULL;i++)//打印环境变量
{
printf("envp[%d]=%s\n",i,envp[i]);
}
exit(0);
}
环境变量下标从0开始,总共53个环境变量
自己添加环境变量
export 变量名 可以将普通变量变为环境变量
eg: export MYSTR ,将普通变量MYSTR变为环境变量
环境变量总数变为54个,下标为30的环境变量就是刚刚我们添加的环境变量。
4)execv
函数原型: int execv(const char *pathname, char *const argv[]);
execv与前几个相比,变化在于将参数放在了数组里
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
int main(int argc,char* argv[],char *envp[])
{
printf("main pid =%d\n",getpid());
char *myargv[] = {"ps","-f",0};//参数放在数组里,以0结尾
execv("/usr/bin/ps",myargv);
// execle("/usr/bin/ps","ps","-f",(char*)0,envp);
// execlp("ps","ps","-f",(char*)0);
//execl("/usr/bin/ps","ps","-f",(char*)0);
printf("execl err\n");
exit(0);
}
5)execvp
函数原型:int execvp(const char *file, char *const argv[]);
文件名,参数放在数组里
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
int main(int argc,char* argv[],char *envp[])
{
printf("main pid =%d\n",getpid());
char *myargv[] = {"ps","-f",0};//参数放在数组里,以0结尾
execvp("ps",myargv);
// execv("/usr/bin/ps",myargv);
// execle("/usr/bin/ps","ps","-f",(char*)0,envp);
// execlp("ps","ps","-f",(char*)0);
//execl("/usr/bin/ps","ps","-f",(char*)0);
printf("execl err\n");
exit(0);
}
6)execve
函数原型:int execve(const char *pathname, char *const argv[],char *const envp[]);
execve是系统调用,上述几个库函数底层都是使用系统调用execve
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
int main(int argc,char* argv[],char *envp[])
{
printf("main pid =%d\n",getpid());
char *myargv[] = {"ps","-f",0};//参数放在数组里,以0结尾
execve("/usr/bin/ps",myargv,envp);
// execvp("ps",myargv);
// execv("/usr/bin/ps",myargv);
// execle("/usr/bin/ps","ps","-f",(char*)0,envp);
// execlp("ps","ps","-f",(char*)0);
//execl("/usr/bin/ps","ps","-f",(char*)0);
printf("execl err\n");
exit(0);
}
三、malloc
char *s = malloc(1024);
exit(0);
1.申请1G内存不free会不会产生内存泄漏?
以下测试在红帽子系统上进行的
运行过程中申请的空间不free,就会产生内存泄漏,但是程序结束后,申请的空间会被释放回收。
所以没有产生内存泄漏,操作系统具有一定的健壮性,一旦程序结束,操作系统会自动回收所有空间。
当内存只有2G时,申请2G空间能否成功
可以成功
虚拟内存
内存总是不够用的,要想运行一个程序,此程序必须能放在内存中才可以运行。有的程序想要运行,但由于太大了放不进内存,就无法运行。一个程序要放在内存中,就需要能创建一个页表,将物理地址和逻辑地址对应起来。程序太大,放不进去就无法运行,所以我们引入了虚拟内存。
虚拟内存:我们在磁盘上划分一块空间,把它当做内存来用
引入虚拟内存后,将程序的一部分放在内存中,这个程序就可以运行了,想要运行在虚拟内存的部分,就会产生缺页中断,将在虚拟内存中的部分调到内存中,将内存中暂时不用的换到虚拟内存中(一般是将阻塞进程换出去),已经调出去的程序不会在内存空闲后立即再调回来,而是在用到的时候才会调回来。
2.如何判断申请空间是否可以成功?
1>物理内存剩余空间足够申请的空间,能申请成功
2>物理内存剩余空间不够,但是有虚拟内存,则看物理内存和虚拟内存总共的剩余空间够不够,如果够申请空间,则能申请成功
3.申请3G空间能不能成功
不能成功
对于32位系统来说,进程地址空间最大4G,其中1G内核使用,剩余3G被栈、堆,数据段,代码段使用,所以malloc申请空间不能超过3G,所以申请不能成功。
64位系统可以
在程序中,如果没有执行malloc,就不会有堆空间,当执行一次 malloc后,系统会在堆那个位置划分一块空间,这个空间不大,大概100多k,初始化后,会在堆空间攒一些内存空间,划分为4字节,8字节,16字节,32字节,64字节...的小空间,malloc多少,给多少空间,给的都是整的(malloc7个字节,给8个字节,malloc24个字节,给32个字节,malloc64,给64),free后,我们认为是给操作系统了,实则是还给了堆,下一次malloc会重新分配出去,当进程结束或满足回收策略才会将这些空间还给操作系统。如果一次性malloc很大空间(eg:1G),直接从内存中划分,不经过堆。