1. 标识
每个进程都有一个非负整数表示的唯一进程ID。
查看进程命令: ps -ef | grep 进程名
代码获取进程id
getpid(void) 获取进程id
getppid(void) 获取父进程id
#include <iostream>
#include <unistd.h>
using namespace std;
int main(){
cout << getpid() << endl;
cout << getppid() << endl;
sleep(100);
}
2. 特殊进程
idle进程
- pid为0
- linux系统中的第一个进程,加载完成之后演变成1、2号进程
- 是init进程和kthreadd进程的父进程
systemd(init)进程
- pid为1
- linux中第一个用户空间的进程
- 系统初始化,是其他用户空间进程的直接或间接父进程
kthreadd进程
- pid为2
- 内核空间其他进程的直接或间接父进程
- 负责内核线程的调度和管理
3. fork函数
- 一个现有的进程调用fork函数会创建一个新的进程
- 子进程和父进程继续执行fork函数后的代码
- fork函数调用一次,返回两次
- 子进程返回0,父进程返回子进程的进程id
- 子进程是父进程的副本
- 子进程获得了父进程的数据空间、堆和栈的副本,不是共享
- 一般会执行成功,如果内存不足或资源不够,执行失败时返回-1
- 父进程中打开的文件描述符也被复制到子进程中,子父进程可以同时向文件写入数据。如果在子进程中关闭文件,父进程不受影响,可以继续写,关闭父进程中的文件,子进程也可以继续写文件,不受影响。
代码
#include <iostream>
#include <unistd.h>
using namespace std;
int main(){
int pid = fork();
if(pid > 0) {
cout << "this is parent, pid=" << getpid() << endl;
sleep(20);
} else {
cout << "this is child, pid=" << getpid() << endl;
sleep(20);
}
}
4. 孤儿进程
如果父进程先退出,子进程会成为孤儿进程,将被1号进程收养,由1号进程对他们完成状态收集工作。
孤儿进程没有危害。
#include <iostream>
#include <unistd.h>
using namespace std;
int main(){
int pid = fork();
if(pid > 0) {
cout << "this is parent, pid=" << getpid() << endl;
sleep(10);
} else {
cout << "this is child, pid=" << getpid() << endl;
sleep(20);
}
}
父进程睡眠10秒
5. 僵尸进程
如果子进程先退出,内核向父进程发送SIGCHLD信号,如果父进程不处理这个信号,子进程就会成为僵尸进程。
僵尸进程的危害
如果子进程在父进程之前终止,内核为每个子进程保留了一个数据结构,包括进程编号、终止状态和使用cpu时间等。父进程如果处理了子进程退出的信息,内核就会释放这个数据结构。如果父进程没有处理子进程退出的信息,内核就不会释放这个数据结构,子进程编号就会一直被占用,但是系统可用的进程号是有限的。如果大量的产生僵尸进程,将因为没有可用的进程号而导致系统不能产生新的进程
#include <iostream>
#include <unistd.h>
using namespace std;
int main(){
int pid = fork();
if(pid == 0) {
cout << "this is child , pid=" << getpid() << endl;
sleep(10);
} else {
cout << "this is parent, pid=" << getpid() << endl;
sleep(30);
}
return 0;
}
运行结果:
[fork01] <defunct> 表示僵尸进程。
解决僵尸进程的三种方式:
1. 父进程中忽略SIGCHLD信号
#include <iostream>
#include <unistd.h>
#include <signal.h>
using namespace std;
int main(){
// 忽略SIGCHLD信号
signal(SIGCHLD, SIG_IGN);
int pid = fork();
if(pid == 0) {
cout << "this is child , pid=" << getpid() << endl;
sleep(10);
} else {
cout << "this is parent, pid=" << getpid() << endl;
sleep(30);
}
return 0;
}
十秒以后,子进程退出。
2. 在父进程中增加等待子进程退出的代码
#include <iostream>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
using namespace std;
int main(){
int pid = fork();
if(pid == 0) {
cout << "this is child , pid=" << getpid() << endl;
sleep(10);
} else {
cout << "this is parent, pid=" << getpid() << endl;
int sts;
wait(&sts);
sleep(30);
}
return 0;
}
wat函数会阻塞父进程的执行,等待子进程退出。
3. 设置SIGCHLD信号的处理函数,在处理函数中调用wait函数
#include <iostream>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
using namespace std;
void func(int sig){
int sts;
wait(&sts);
}
int main(){
// 设置信号处理函数
signal(SIGCHLD, func);
int pid = fork();
if(pid == 0) {
cout << "this is child , pid=" << getpid() << endl;
sleep(10);
} else {
cout << "this is parent, pid=" << getpid() << endl;
sleep(30);
}
return 0;
}
休眠10秒后,父子进程会一起退出,原因是子进程运行10秒钟后退出,然后内核给父进程发了一个SIGCHLD信号,该信号是软中断信号,会中断父进程的sleep的调用,然后进入func函数,执行完func函数,程序就退出了,可以sleep两次来解决这个问题。