linux多进程
进程的基础知识
进程是操作系统中的基本概念,代表着正在执行的程序的实例。每个进程都有自己的地址空间、内存、文件描述符和其他系统资源。在 Linux 中,进程是系统中最基本的资源之一,下面是关于进程的基础知识以及相应的 Linux 命令:
进程的基本概念:
-
进程与程序的关系:
- 程序是存储在磁盘上的可执行文件,而进程是程序在内存中执行时的实例。一个程序可以同时运行多个进程的实例。
-
进程的特点:
- 每个进程都有自己的内存空间、堆栈、代码段等资源。
- 进程之间相互独立,但可以通过进程间通信(IPC)来进行交互。
- 进程可以拥有子进程,形成进程树的结构。
-
进程的状态:
- 运行(Running):进程正在 CPU 上执行。
- 就绪(Ready):进程已准备好运行,正在等待 CPU 时间片。
- 阻塞(Blocked):进程正在等待某个事件发生,如 I/O 完成。
- 终止(Terminated):进程执行完成或被终止。
Linux 中处理进程的基本命令:
-
ps查看进程
-
ps -ef 查看当前所有进程
-
ps 查看当前终端的进程
-
ps -ef | grep book 查看当前所有进程。从结果中过滤出包含"book"的记录
-
-
getpid库函数
- 获取程序运行时进程的编号
typedef int pid_t; pid_t getpid();
/* 本程序主要是演示getpid函数 */ #include <stdio.h> #include <sys/types.h> #include <unistd.h> int main() { printf("本程序的进程编号为 = %d\n", getpid()); return 0; }
多进程的基础知识
多进程是指在一个系统中同时存在多个正在执行的进程。每个进程都是程序的一个实例,它拥有自己的地址空间、堆栈、数据段等资源。
fork函数
- fork函数用于产生一个新的进程,函数返回值pid_t是一个整数。在父进程中,返回值是子进程编号,在子进程中,返回值是0。
/*
* 本程序主要演示fork函数的使用
* */
#include <stdio.h>
#include <unistd.h>
int main()
{
printf("本程序的进程编号 = %d\n", getpid());
int ipid = fork();
sleep(1);
printf("pid = %d\n", ipid);
if (ipid != 0)
{
printf("父进程的编号 = %d\n", getpid());
}
else
{
printf("子进程的编号 = %d\n", getpid());
}
return 0;
}
/*
本程序主要用于演示fork函数创建一个进程,父子进程执行不同的流程
* */
#include <stdio.h>
#include <unistd.h>
void fatherFunc()
{
printf("父进程的流程\n");
}
void sonFunc()
{
printf("子进程的流程\n");
}
int main()
{
if (fork() != 0)
{
printf("这是父进程\n");
fatherFunc();
}
else
{
printf("这是子进程\n");
sonFunc();
}
printf("父子进程执行完均来到此处\n");
return 0;
}
- 子进程拷贝了父进程的堆栈段和数据段,在父进程中定义的变量,子进程中会复制一个副本,fork之后,子进程对变量的操作不会影响父进程,父进程对变量的操作不会影响子进程。
/*
本程序主要演示子进程拷贝了父进程的堆栈段和数据段,
在父进程中定义的变量,子进程中会复制一个副本,
fork之后,子进程对变量的操作不会影响父进程,父进程对变量的操作不会影响子进程
*/
#include <stdio.h>
#include <unistd.h>
int globalVariable = 100;
int main()
{
int ii = 10, jj = 11;
if (fork() != 0)
{
ii = 11;
jj = 12;
globalVariable = 101;
printf("父进程中ii = %d, jj = %d, gloableVaribale = %d\n", ii, jj, globalVariable);
}
else
{
ii = 12;
jj = 13;
globalVariable = 102;
printf("子进程中ii = %d, jj = %d, gloableVaribale = %d\n", ii, jj, globalVariable);
}
return 0;
}
多进程的应用
僵尸进程
僵尸进程是当子进程比父进程先结束,而父进程又没有回收子进程,释放子进程占用的资源,此时子进程将成为一个僵尸进程。如果父进程先退出 ,子进程被init接管,子进程退出后init会回收其占用的相关资源。
一个进程在调用exit命令结束自己的生命的时候,其实它并没有真正的被销毁, 而是留下一个称为僵尸进程(Zombie)的数据结构(系统调用exit,它的作用是 使进程退出,但也仅仅限于将一个正常的进程变成一个僵尸进程,并不能将其完全销毁)
由于子进程的结束和父进程的运行是一个异步过程,即父进程永远无法预测子进程 到底什么时候结束. 那么会不会因为父进程太忙来不及wait子进程,或者说不知道 子进程什么时候结束,而丢失子进程结束时的状态信息呢? 不会。因为UNⅨ提供了一种机制可以保证只要父进程想知道子进程结束时的状态信息, 就可以得到。这种机制就是: 在每个进程退出的时候,内核释放该进程所有的资源,包括打开的文件,占用的内存等。但是仍然为其保留一定的信息(包括进程号the process ID,退出状态the termination status of the process,运行时间the amount of CPU time taken by the process等)。直到父进程通过wait / waitpid来取时才释放. 但这样就导致了问题,如果进程不调用wait / waitpid的话,那么保留的那段信息就不会释放,其进程号就会一直被占用,但是系统所能使用的进程号是有限的,如果大量的产生僵尸进程,将因为没有可用的进程号而导致系统不能产生新的进程. 此即为僵尸进程的危害,应当避免。
解决方法:
如果父进程不关心子进程什么时候结束,那么可以用signal(SIGCHLD,SIG_IGN) 通知内核,自己对子进程的结束不感兴趣,那么子进程结束后,内核会回收, 并不再给父进程发送信号。
signal(SIGCHLD, SIG_IGN);//忽略子进程退出的信号,避免产生僵尸进程