linux编程之进程初探

    什么是进程?一个可执行程序?NO,可执行程序在我们的磁盘上只是一个文件而已,当可执行程序运行起来以后,那才叫一个进程。

    进程有几种基本的状态,运行,就绪,睡眠,停止。

             运行:当CPU在执行这个程序的时候,该进程就处于运行状态。

             就绪:进程已准备好所有的事情,就只差CPU来运行了,我们称该进程处于就绪状态。

             睡眠:也称阻塞状态。比如说当进程阻塞去读一个终端文件时,我们输入ps aux可以看到进程的状态为s或s+,此时进程处于睡眠状态。

             停止:当进程运行完以后,我们称该进程处于停止状态。

进程状态之间的关系如下:

                                                                linux编程之进程初探_第1张图片

进程的创建:

linux下创建进程非常简单,只需调用fork函数即可,而且fork函数的原型非常的精简,精简到不能再精简了,其声明如下:

pid_t  fork(void);

该函数没有参数,只有返回值。虽然如此精简,但它的含义却一点也不简单。当我们调用fork时,会创建一个子进程,之后函数返回。但是请注意该函数会返回两次。一次是在父进程中返回子进程的pid,而在子进程返回0,当然这是该函数调用成功的情况,若该函数调用失败,会在父进程返回-1,那么在子进程返回什么呢?既然,都已经调用失败了,自然也就没有子进程产生了。我们可以将该函数理解为:调用一次,返回两次。

当我们在父进程调用fork时,底层做了哪些事情呢?底层会将子进程映射到父进程的地址上,也就是说父子两个进程共享同一个代码段,数据段等。当子进程想修改数据段内可修改的数据时,底层会执行拷贝操作,也就是说,它会将那块数据做一个拷贝,将其放置新的内存里,这就是渎时共享,写时复制机制。

既然,父子进程共享同一代码段,那么如何区分父子进程呢?前面提到,fork函数会返回两次,我们只需判断它的返回值即可,例如:

			pid_t pid;
			pid = fork();
			//pid > 0说明是在父进程中
			if (pid > 0)
			{
				// 在父进程中执行需要的代码
			}
			else if (pid == 0)	//pid == 0 说明是在子进程中
			{
				//在子进程中执行需要的代码
			}
			else
			{
				exit(-1);
			}

父进程从开头往下执行,遇到fork后,子进程创建,那么子进程是如何执行的呢?是否是同父进程一样从头开始执行?其实不是,子进程是从调用fork函数后的语句开始执行,以上例代码来说,就是从if语句那条开始执行。


前面说过,子进程会和父进程共享同一代码段,其实我们更希望子进程能做一些别的事情,这时我们可以使用exec系列函数。exec系列函数一共有6个,分别是execl、execlp、execle、execv、execvp、execve,这些函数功能都是相同的,只是在传参时有所不同。这里介绍一个我比较常用的一个execvp,其声明如下:

int execvp(const char *file, char *const agrv[ ]);

第一个参数传的是一个文件名,比如说我们的另一个程序的文件名,第二个参数传的是指针数组,里面保存了想要传递的命令行参数。下面是它的调用示例:

			char *const ls_argv[] = {"ls","-l",NULL};
			execvp("ls",ls_argv);

当我们调用exec系列函数时,该函数会将我们原有的地址空间给替换掉,也就是说,在exec后面的代码是执行不到的,除非exec系列函数调用失败。还有就是如果exec函数返回了,则说明exec系列函数调用失败,为什么呢?因为该函数的返回值只有错误返回值,所以只要该系列函数返回,就一定是出错了。


文章开头介绍了进程的几种基本状态,现在来介绍一下另外一种状态----僵尸态,看到这个名字感觉有点懵逼了,进程什么时候跟僵尸搞到一起了?其实起这个名字主要由于该进程的状态和僵尸很像,僵尸的特点是身体还在,魂没了,是吧。进程如何达到这个状态的呢?当父进程fork出子进程的时候,子进程先结束时,子进程此时就处于僵尸态,通过ps aux命令可以看到它的标志为Z,可能有疑问,子进程都结束了,怎么还有状态啊?这是由于子进程的内核资源没有被释放,父进程创建出子进程后,父亲有义务要为儿子收尸(回收资源),如果父进程一直不退出的话,那么子进程不就一直处于僵尸进程了吗,所以我们要使用wait和waitpid这两个函数来对子进程进行资源回收。

wait函数它是阻塞的,也就是说,它要等子进程结束后才能继续执行之后的代码,而且调用一次只能回收一个子进程。这又有问题了,父进程创建了很多个子进程,然后父进程调用wait函数,等待子进程结束,那么父亲什么也不用干了,专门等着儿子死了,来给它们收尸是吧。这个也不合理。

waitpid函数它是非阻塞的,它的声明如下:

			pid_t waitpid(pid_t pid, int *status, int options);
其中参数pid有四种情况:

pid < -1 :表示要回收指定进程组内的任意子进程

pid = -1 :表示要回收任意子进程

pid = 0  :表示要回收和当前调用waitpid一个组的所有子进程

pid > 0  :表示要回收指定pid的子进程

第二个参数是一个传出参数,它保存了进程的退出状态,若不关心可以传NULL。

第三个参数比较重要,我们可以传一个WNOHANG标志,来表示该函数是非阻塞的。

你可能感兴趣的:(linux)