赞
踩
按照进程的功能和运行的程序分类,进程可划分为两大类:
一般操作系统将进程分为五个状态:
Linux上进程有5种状态,这5中状态可以与一般操作系统的状态对应起来:
说明:
PCB(进程控制块) 结构体 task struct,负责管理进程的所有资源,它的成员 mm_struct 指向这个进程相关的内存资源,mm_struct指向一个结构体,包括:
栈 :给局部变量(自动变量)分配空间的地方
堆 :使用malloc、new… 分配的空间(也叫自由区)
BSS段
数据段
代码段:代码区是只读的,程序代码会被读入此区,程序执行期间执行的就是代码区中的代码。
进程空间管理图片
Linux系统把进程的数据段又划分成三部分:
————————————————————————————————
在Linux成功fork进程后,会在系统中创建一个task_struct(也称PCB, process control block),用来描述当前进程的状态、进程间关系、优先级和资源等信息。
标识符: 与进程相关的唯一标识符,用来区别正在执行的进程和其他进程。
状态: 描述进程的状态,因为进程有挂起,阻塞,运行等好几个状态,所以都有个标识符来记录进程的执行状态。
优先级: 如果有好几个进程正在执行,就涉及到进程被执行的先后顺序的问题,这和进程优先级这个标识符有关。
程序计数器: 程序中即将被执行的下一条指令的地址。
内存指针: 程序代码和进程相关数据的指针。
上下文数据: 进程执行时处理器的寄存器中的数据。
I/O状态信息: 包括显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表等。
记账信息: 包括处理器的时间总和,记账号等等。
PCB中包含4个部分
一个进程使用fork创建子进程,如果子进程退出,而父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵死进程。
每个进程退出的时候,内核释放该进程所有的资源,包括打开的文件,占用的内存等。 但是仍然为其保留一定的信息:
直到父进程通过wait / waitpid来取时才释放。但这样就导致了问题,如果进程不调用wait / waitpid的话, 那么保留的那段信息就不会释放,其进程号就会一直被占用,但是系统所能使用的进程号是有限的,如果大量的产生僵死进程,将因为没有可用的进程号而导致系统不能产生新的进程.
Ubuntu命令找到系统僵尸进程并杀死:
1、 ps aux | grep 'Z'
来找到僵尸进程
2、pstree -p -s PID
来寻找编号为PID进程也就是僵尸进程的父级进程
通常,僵尸进程的父进程是gnome-session,终结他会注销系统
#include <stdio.h> #include <unistd.h> #include <errno.h> #include <stdlib.h> int main() { pid_t pid; pid = fork(); if (pid < 0) { perror("fork error:"); exit(1); } else if (pid == 0) { printf("I am child process.I am exiting.\n"); exit(0); } printf("I am father process.I will sleep two seconds\n"); //等待子进程先退出 sleep(2); //输出进程信息 system("ps -o pid,ppid,state,tty,command"); printf("father process is exiting.\n"); return 0; }
gcc zombie.c -o test1
./test1
子进程退出时向父进程发送SIGCHILD信号,父进程处理SIGCHILD信号。在信号处理函数中调用wait进行处理僵尸进程。测试程序如下所示:
#include <stdio.h> #include <unistd.h> #include <errno.h> #include <stdlib.h> #include <signal.h> static void sig_child(int signo); int main() { pid_t pid; //创建捕捉子进程退出信号 signal(SIGCHLD,sig_child); pid = fork(); if (pid < 0) { perror("fork error:"); exit(1); } else if (pid == 0) { printf("I am child process,pid id %d.I am exiting.\n",getpid()); exit(0); } printf("I am father process.I will sleep two seconds\n"); //等待子进程先退出 sleep(2); //输出进程信息 system("ps -o pid,ppid,state,tty,command"); printf("father process is exiting.\n"); return 0; } static void sig_child(int signo) { pid_t pid; int stat; //处理僵尸进程 while ((pid = waitpid(-1, &stat, WNOHANG)) >0) printf("child %d terminated.\n", pid); }
原理是将子进程成为孤儿进程,从而其的父进程变为init进程,通过init进程可以处理僵尸进程。
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <errno.h> int main() { pid_t pid; //创建第一个子进程 pid = fork(); if (pid < 0) { perror("fork error:"); exit(1); } //第一个子进程 else if (pid == 0) { //子进程再创建子进程 printf("I am the first child process.pid:%d\tppid:%d\n",getpid(),getppid()); pid = fork(); if (pid < 0) { perror("fork error:"); exit(1); } //第一个子进程退出 else if (pid >0) { printf("first procee is exited.\n"); exit(0); } //第二个子进程 //睡眠3s保证第一个子进程退出,这样第二个子进程的父亲就是init进程里 sleep(3); printf("I am the second child process.pid: %d\tppid:%d\n",getpid(),getppid()); exit(0); } //父进程处理第一个子进程退出 if (waitpid(pid, NULL, 0) != pid) { perror("waitepid error:"); exit(1); } exit(0); return 0; }
一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作。
孤儿进程是没有父进程的进程,孤儿进程这个重任就落到了init进程身上,init进程就好像是一个民政局,专门负责处理孤儿进程的善后工作。每当出现一个孤儿进程的时候,内核就把孤 儿进程的父进程设置为init,而init进程会循环地wait()它的已经退出的子进程。这样,当一个孤儿进程凄凉地结束了其生命周期的时候,init进程就会代表党和政府出面处理它的一切善后工作。因此孤儿进程并不会有什么危害。
注意:
#include <stdio.h> #include <stdlib.h> #include <errno.h> #include <unistd.h> int main() { pid_t pid; //创建一个进程 pid = fork(); //创建失败 if (pid < 0) { perror("fork error:"); exit(1); } //子进程 if (pid == 0) { printf("I am the child process.\n"); //输出进程ID和父进程ID printf("pid: %d\tppid:%d\n",getpid(),getppid()); printf("I will sleep five seconds.\n"); //睡眠5s,保证父进程先退出 sleep(5); printf("pid: %d\tppid:%d\n",getpid(),getppid()); printf("child process is exited.\n"); } //父进程 else { printf("I am father process.\n"); //父进程睡眠1s,保证子进程输出进程id sleep(1); printf("father process is exited.\n"); } return 0; }
Linux Daemon(守护进程)是运行在后台的一种特殊进程,并且不被任何终端产生的终端信息所打断。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。它不需要用户输入就能运行而且提供某种服务,不是对整个系统就是对某个用户程序提供服务。
注意:
一个守护进程的父进程是init进程,因为它真正的父进程在fork出子进程后就先于子进程exit退出了,所以它是一个由init继承的孤儿进程。
Linux 服务器在启动时也需要启动很多系统服务,它们向本地或网络用户提供了 Linux 的系统功能接口,直接面向应用程序和用户,而提供这些服务的程序就是由运行在后台的守护进程来执行的。
Linux系统的大多数服务器就是通过守护进程实现的。常见的守护进程包括:
1、 stand alone类型的守护进程
所谓 stand alone,指的是可独立启动的守护进程,这种类型的守护进程以下 2 大特点:
基于以上 2 个特点,这种守护进程就拥有一个非常突出的优点,即响应最快。stand alone 守护进程非常多,比如常见的 apache、mysql 等。
2、 xinetd类型的守护进程
2、创建守护进程的过程:
守护线程每隔100s就向文件写入一句话
//https://www.cnblogs.com/yinbiao/p/11203225.html #include <iostream> #include<pthread.h> #include<stdio.h> #include<stdlib.h> #include<string.h> #include<unistd.h> #include<errno.h> #include<semaphore.h> #include<sys/wait.h> #include<sys/types.h> #include<fcntl.h> #include<sys/stat.h> using namespace std; #define max_file 65535 int main() { pid_t pc; int fd,len; char buf[]="this is a demo\n"; len=strlen(buf); pc=fork();//第一步,创建子进程 if(pc<0) { cout<<"fork error"<<endl; exit(1); } else if(pc>0) { exit(0);//第二步,父进程退出 } else { setsid();//第三步,创建新会话 chdir("/");//第四步,将当前目录改为根目录 umask(0);//第五步,重新设置文件权限掩码 for(int i=0; i<max_file; i++) { close(i);//第六步,关闭不需要的文件描述符 } while(1) { if((fd=open("/tmp/dameo.log",O_CREAT|O_WRONLY|O_APPEND,0600))<0) { cout<<"open erro"<<endl; exit(1); } write(fd,buf,len+1); close(fd); sleep(100);//每隔100s输出一句话到文件 } } }
实时显示系统中各个进程的资源占用情况,按"q"退出top命令
kill -9 pid杀掉指定进程
1、https://www.cnblogs.com/Anker/p/3271773.html
2、https://www.cnblogs.com/shijiaqi1066/p/3836017.html
3、https://zhuanlan.zhihu.com/p/145245419
4、https://www.cnblogs.com/mickole/p/3188321.html
5、https://www.cnblogs.com/yinbiao/p/11203225.html
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。