什么是进程:
程序加载进内存以后开始执行,执行中的程序就叫做进程。
linux下程序的加载过程:
bash程序首先调用fork函数创建出一个新的进程,随后调用exec函数执行指定的elf文件,读取elf文件头,判断elf文件的文件类型,完成程序代码段,数据段到进程空间的地址映射。
linux下进程管理的相关命令:
top:实时显示进程状态,相当于windows的任务管理器。
pstree:以树形结构显示进程,根节点是Init进程,进程id为1。
pstree pid:以pid为根,显示进程树。
ps aux 显示所有进程
ps ef 显示所有进程
ps ef | grep test 显示名称为test的进程
kill pid 杀死进程号为pid的进程
kill -9 pid 暴力杀死进程号为pid的进程
fork()函数:
fork()函数用于创建一个新进程(子进程),fork函数调用一次返回两次,在父进程和子进程中分别返回一次,在父进程中返回子进程id,在子进程中返回0,如果创建子进程失败返回0。调用fork函数以后,子进程复制父进程的数据段和堆栈段,共享父进程的代码段。所以在父子进程中对同一个变量的读写是互相不影响的,因为两个进程的数据段堆栈段是独立的。
#include <iostream> #include <unistd.h> using namespace std; int g_value = 1; int main() { int value = 20; int *ptr = new int(10); auto pid = fork(); if (pid < 0) { cout << "fork failed" << endl; delete ptr; return 0; } if (pid == 0) {
// 子进程 g_value++; value++; (*ptr)++; cout << "child g_value:" << g_value << ",value:" << value << ",*ptr:" << *ptr << endl; } if (pid > 0) {
// 父进程 sleep(2); cout << "father g_value:" << g_value << ",value:" << value << ",*ptr:" << *ptr << endl; } delete ptr; return 0; }
运行结果:在父进程中sleep(2),让子进程先运行对变量进行+1,然后在父进程中输出变量,发现变量值没有被改变,说明父子进程的内存的独立的。
vfork()函数:
vfork函数也可以创建一个子进程,和fork()函数的区别在于:
1:fork函数,父子进程只共享代码段,数据段堆栈段不共享,vfork函数父子进程共享代码段,数据段,堆栈段。
2:fork函数,不保证父子进程的执行顺序,vfork函数保证子进程先执行,执行结束以后再执行父进程。
孤儿进程和僵尸进程:
子进程的结束和父进程的结束是一个异步的过程,父进程永远无法预测子进程到底什么时候结束。
孤儿进程:如果父进程先执行结束后退出,子进程还没执行结束,这时子进程就会成为孤儿进程,孤儿进程会被Init进程收养,收养后就不再是孤儿进程了,被收养后就由init进程来回收子进程的退出状态。孤儿进程在系统中只是短暂存在后就被init进程收养,所以孤儿进程是无害的,不需要我们刻意解决孤儿进程。
僵尸进程:如果子进程退出,父进程还没有退出并没有回收子进程的退出状态,那么子进程的进程描述符会一致存在于系统中,此时的子进程就会变成僵尸进程,如果不对僵尸进程进行处理,越来越多的僵尸进程会耗尽系统资源,导致不能再创建新的进程。所以僵尸进程需要被解决。
#include <iostream> #include <unistd.h> using namespace std; int g_value = 1; int main() { int value = 20; int *ptr = new int(10); auto pid = fork(); if (pid < 0) { cout << "fork failed" << endl; delete ptr; return 0; } if (pid == 0) { g_value++; value++; (*ptr)++; cout << "child g_value:" << g_value << ",value:" << value << ",*ptr:" << *ptr << endl; } if (pid > 0) { sleep(60); cout << "father g_value:" << g_value << ",value:" << value << ",*ptr:" << *ptr << endl; } delete ptr; return 0; }
运行结果:父进程等待60秒,子进程先退出,就成为了僵尸进程,用top命令可以看到当前存在一个僵尸进程。
怎样解决僵尸进程:
方法1:pid_t wait(int *status)函数,调用wait函数回收子进程的退出状态,成功返回子进程id,失败返回-1,子进程的退出状态保存在status中,该函数是阻塞的,如果没有子进程退出,将一致阻塞父进程,知道有子进程退出才会唤醒父进程。
方法2:pid_t waitpid(int pid, int *status, int options)函数,pid表示等待终止的目标进程,传入-1代表任意进程,options设置成WHOHANG表示不阻塞等待,但需要不断循环调用waitpid函数判断是否有子进程退出。
方法3:利用信号机制,子进程结束时会产生一个信号传到父进程,我们在父进程中注册子进程退出信号的处理函数,在处理函数中调用wait函数或waitpid函数回收子进程退出状态就可以销毁僵尸进程了。