虚拟内存
Linux采用虚拟内存管理技术,虚拟地址空间0~4G,用户空间(3G),内核空间(1G),访问内核空间的方式是通过系统调用。Linux内核由系统内所有进程共享。每个进程都有独立的进程地址空间,大小为3G。
作用:使用虚拟地址很好的保护内核空间不被用户空间破坏
进程的状态转换
fork()和vfork()函数
fork()函数通过系统调用创建一个与原来几乎完全相同的进程,俩个进程可以做完全相同的事,如果初始参数不同,俩个进程也可以做不同的事。系统先给新的进程分配存储数据和代码的空间,把原来的进程所有值 复制copy 到新的进程中,只有少数值与原来不同,相当于克隆了一个自己。
***大多数是父进程先运行,少数子进程先运行,由调度进程决定。
vfork()函数父子进程的内存数据share一起用。
***通常vofrk()函数和exec()函数搭配使用
***保证子进程先执行
***当子进程调用exit()或exec()后,父进程往下执行。
**有关进程的命令
查看进程:ps ajx
杀死进程kill -9 pid //给进程发送一个杀死信号
孤儿进程
父进程先结束,子进程变孤儿
解决方法:父进程sleep()等待一会。
#include <stdio.h>
#include <stdlib.h>
int main(){
printf("start....\n");
pid_t pid = fork();
if(pid == -1){
printf("fork error!\n");
exit(1);
}
if(pid == 0){
printf("child pid:%d,ppid:%d\n",getpid(),getppid());
}else if(pid > 0){
printf("parent pid:%d,ppid:%d\n",getpid(),getppid());
}
printf("end...\n");
return 0;
}
执行结果:
start…
parent pid:17587,ppid:11964
end…
child pid:17588,ppid:1
end…
注:父进程先于子进程退出
子进程没有父进程获取自身的退出状态,也就变成了孤儿进程
但是,所有的孤儿进程都被一号(init)进程所收养,由一号进程作为所有孤儿进程的父进程,负责孤儿进程的资源释放。
*非孤儿进程:子进程的ppid为父进程的pid。
wait和waitpid函数
wait()函数用于使父进程(也就是调用wait()的进程)阻塞,知道一个子进程结束或者收到一个指定的信号为止。如果该父进程没有子进程或者它的子进程已经结束,则wait()函数就会立即返回。
waitpid()的作用和wait()一样,但它并不一定要等待第一个终止的子进程(它可以指定需要等待终止的子进程)。
僵尸进程
概念:
子进程退出了,但是父进程没有用wait或waitpid去获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中,这种进程称为僵死进程。
危害:
如果父进程不调用wait或waitpid的话,那么保留的信息就不会被释放,其进程号就会被一直占用,但是系统所能使用的进程号是有限的,如果大量产生僵死进程,将因没有可用的进程号而导致系统无法产生新的进程,这就是僵尸进程的危害。
解决方法:
1)kill杀死元凶父进程(一般不用)
严格的说,僵尸进程并不是问题的根源,罪魁祸首是产生大量僵死进程的父进程。因此,我们可以直接除掉元凶,通过kill发送SIGTERM或者SIGKILL信号。元凶死后,僵尸进程进程变成孤儿进程,由init充当父进程,并回收资源。
或者运行:kill -9 父进程的pid值、
2)父进程用wait或waitpid去回收资源(方案不好)
父进程通过wait或waitpid等函数去等待子进程结束,但是不好,会导致父进程一直等待被挂起,相当于一个进程在干活,没有起到多进程的作用。
3)通过信号机制,在处理函数中调用wait,回收资源
通过信号机制,子进程退出时向父进程发送SIGCHLD信号,父进程调用signal(SIGCHLD,sig_child)去处理SIGCHLD信号,在信号处理函数sig_child()中调用wait进行处理僵尸进程。什么时候得到子进程信号,什么时候进行信号处理,父进程可以继续干其他活,不用去阻塞等待。
2)代码
#include <stdio.h>
#include <stdlib.h>
int main(){
printf("start....\n");
pid_t pid,pw;
pid = fork();
if(pid == -1){
printf("fork error!\n");
exit(1);
}
if(pid == 0){
printf("child process pid:%d\n",getpid());
sleep(5);
exit(0);
}else if(pid > 0){
pw = wait(NULL);//wait函数参数设置为NULL的时候,就可以销毁这个僵尸进程
printf("I catch a child process and this pid is%d",pw);
}
exit(0);
return 0;
}
3)代码
1 #include <stdio.h>
2 #include <unistd.h>
3 #include <errno.h>
4 #include <stdlib.h>
5 #include <signal.h>
6
7 static void sig_child(int signo);
8
9 int main()
10 {
11 pid_t pid;
12 //创建捕捉子进程退出信号
13 signal(SIGCHLD,sig_child);
14 pid = fork();
15 if (pid < 0)
16 {
17 perror("fork error:");
18 exit(1);
19 }
20 else if (pid == 0)
21 {
22 printf("I am child process,pid id %d.I am exiting.\n",getpid());
23 exit(0);
24 }
25 printf("I am father process.I will sleep two seconds\n");
26 //等待子进程先退出
27 sleep(2);
28 //输出进程信息
29 system("ps -o pid,ppid,state,tty,command");
30 printf("father process is exiting.\n");
31 return 0;
32 }
33
34 static void sig_child(int signo)
35 {
36 pid_t pid;
37 int stat;
38 //处理僵尸进程
39 while ((pid = waitpid(-1, &stat, WNOHANG)) >0)//参考上面的表格,这里是由pid指定的子进程结束标志
40 printf("child %d terminated.\n", pid);
41 }
exit()
1.exit()和wait()分别在子进程和父进程中配合使用。
2.exit()用于结束子进程,并把状态返回给父进程,父进程使用wait(&state)等待子进程结束,子进程结束后,可以通过state获得子进程之前通过exit()返回的状态,但是该状态必须左移8位才和exit()相同。
#include <stdio.h>
#include clude <stdlib.h>
int main(){
pid_t pid;
int i = 0;
int state;
pid = fork();
if(pid == -1){
printf("fork error!\n");
exit(1);
}
if(pid == 0){
printf("child:i=%d,pid=%d\n",i++,getpid());
exit(5);
}else if(pid > 0){
wait(&state);
printf("-------------\n");
printf("child state:%d\n",state);
printf("parent:i=%d,pid=%d\n",i++,getpid());
}
return 0;
}
结果:
child:i=0,pid=19431
//左移8位为5与exit(5)相同。
child state:1280
parent:i=0,pid=19430