Linux进程总结(下)
进程创建
fork()
原理:为子进程重新创建一个task_struct,以及对应的虚拟地址空间和页表,并且将父进程的 数据(包括缓冲区的数据)和代码拷贝一份至子进程pcb中
返回值:对父进程而言:fork()成功返回子进程的进程id;失败返回-1.
对子进程而言:fork()成功返回0;失败返回-1。
fork返回值验证代码:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
int main()
{
pid_t pid;
int count = 0;
printf("before: pid = %d\n", getpid());
pid = fork();
if(pid < 0) {
perror("fork");
}
else if(pid == 0){
printf("child:pid = %d, return = %d\n", getpid(), pid);
exit(0);
}
printf("after: pid = %d, return = %d\n", getpid(), pid);
return 0;
}
失败原因:系统资源不够,无法再次创建进程
用户的进程数超过了限制
模拟不可再创建进程代码:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
int main()
{
pid_t pid;
int count = 0;
while(1){
pid = fork();
usleep(2000);
if(pid < 0)
{
printf("this computer can fork %d times\n",count);
return 1;
}
else if(pid == 0){
printf("child have %d\n", count);
exit(0);
}
count++;
}
return 0;
}
vfork()
原理: 只为子进程创建一个task_struct,父子进程共享虚拟地址空间,父子进程中的任意一 个进程修改了数据,那么父子进程中的数据也会被修改
保证子进程先于父进程运行,并且只有子进程调用了exit之后,父进程才可以运行
代码验证:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
int main()
{
int value = 100;
pid_t pid = vfork();
if(pid == 0)
{
printf("child, pid = %d, ppid = %d, value = %d\n",getpid(), getppid(), value);
value = 2020;
sleep(1);
printf("child, pid = %d, ppid = %d, value = %d\n",getpid(), getppid(), value);
exit(0);
}
printf("child, pid = %d, ppid = %d, value = %d\n",getpid(), getppid(), value);
sleep(2);
printf("child, pid = %d, ppid = %d, value = %d\n",getpid(), getppid(), value);
return 0;
}
进程终止:
三种退出情景:
1.代码运行完毕,结果正确
2.代码运行完毕,结果不正确
3.代码运行异常终止
退出方法:(echo $? : 查看上一条命令的退出码)
main函数(主函数)中退出
return
exit(num)
调用exit(num)退出
调用_exit(num)退出
exit()、return、_exit()的区别:
1.return只有在主函数中才能结束进程,在非主函数中,只能退出该函数
2.return 在主函数中调用相当于exit()
3.exit() 是温和式退出,在调用了exit之后,进程会刷新缓冲区,关闭文件等…
4._exit() 是粗暴式退出,一旦调用_exit()后,进程会直接释放所有资源,结束进程
5.exit()和_exit() 在进程任意位置都能直接结束进程,其参数代表进程退出码!
注:char* strerror(int num); 作用是将错误编号num 转换为字符串的错误信息
进程等待
必要性:避免形成僵尸进程,造成内存泄漏,资源浪费
等待方法
1.阻塞式等待:
pid_t wait(int* status);
成功返回被等待的进程的进程id
失败返回-1
status是一个输出型参数,代表获取子进程的退出状态(退出码)
2.非阻塞式等待:
pid_t waitpid(pid, status, options)
pid 设置为-1,表示等待任意进程
>0 表示要等待的进程的进程id
option :一个选项参数 可man了解
status(子进程退出状态(退出码))
如图:
相当于一个位图,4个字节,32个比特位
但是只用到了 低16位比特位
其中高8位代表退出码,低8位表示核心转储(第七位)和终止信号
进程若正常退出,那么只有退出码,没有终止信号(只用到高8位)
若异常退出,那么没有退出码,只有终止信号(只用到低8位)
进程程序替换
重点:程序替换,并没有创建新的进程,只是将原有的代码和数据替换为新的可执行程序的代码 和数据
程序替换函数
execl(文件路径,变长的命令行参数…) 命令行参数必须以NULL结尾
execlp(文件名,变长的命令行参数…)
execle(文件路径,变长的命令行参数…,自己配置的环境变量数组)
execv(文件路径,命令行参数数组)
execvp(文件名,命令行参数数组)
execve(文件路径,命令行参数数组,自己配置的环境变量数组)
注:替换成功之后,程序便从替换处开始执行新的程序,不再返回
替换成功,没有返回值,替换失败,返回-1
只有execve是系统调用函数,其他均是被封装的库函数
进程替换 ls 指令 代码:
#include<unistd.h>
#include<stdlib.h>
int main()
{
//execl("/usr/bin/ls", "ls", "-al", NULL);
printf("-----------\n");
//execlp("ls", "ls", "-al", NULL);
char* env[] = {
"AAAA=aaaa",
NULL, //must use NULL become last
};
//execle("./a.out", "./a.out", NULL, env);
//printf("---------------\n");
char* argv[] = {
"ls",
"-al",
NULL
};
char *argv1[] = {"./a.out", NULL};
//execv("/usr/bin/ls", argv);
//execvp("ls", argv);
execve("./a.out", argv1, env);
}
关于一些细的知识点,还是有些不熟,我会下次补全!!