小小千想和您聊一聊

当前位置: 首页> 技术分享> Linux系统编程之进程控制

Linux系统编程之进程控制

  一、结束进程

  首先,我们回顾一下 C 语言中 continue, break, return 的作用:

  continue: 结束本次循环

  break: 跳出整个循环,或跳出 switch() 语句

  return: 结束当前函数

  而我们可以通过 exit() 或 _exit() 来结束当前进程。

  所需头文件:

#include <stdlib.h>

void exit(int value);

  功能:结束调用此函数的进程。

  参数:

  status:返回给父进程的参数(低 8 位有效),至于这个参数是多少根据需要来填写。

  返回值:无

  所需头文件:

#include <unistd.h>

void _exit(int value);

  功能:结束调用此函数的进程。

  参数:

  status:返回给父进程的参数(低 8 位有效),至于这个参数是多少根据需要来填写。

  返回值:无

  exit() 和 _exit() 函数功能和用法是一样的,无非时所包含的头文件不一样,还有的区别就是:exit()属于标准库函数,_exit()属于系统调用函数。

  下面的例子验证调用 exit() 函数,会刷新 I/O 缓冲区

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
 
int main(int argc, char *argv[])
{
	printf("hi, lh, you are so good"); // 打印,没有换行符"\n"
	
	exit(0);      // 结束进程,标准库函数,刷新缓冲区,printf()的内容能打印出来
	// _exit(0);  // 结束进程,系统调用函数,printf()的内容不会显示到屏幕
	
	while(1);	// 不让程序结束
 
	return 0;
}

  运行结果:

  上面的例子,结束进程的时候改为调用 _exit(),代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
 
int main(int argc, char *argv[])
{
	printf("hi, mike, you are so good"); // 打印,没有换行符"\n"
	
	//exit(0);      // 结束进程,标准库函数,刷新缓冲区,printf()的内容能打印出来
	_exit(0);  // 结束进程,系统调用函数,printf()的内容不会显示到屏幕
	
	while(1);	// 不让程序结束
	return 0;
}

  运行结果:

  接下来,我们一起验证一下结束函数( return )和结束进程( exit() )的区别。

  测试代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
 
void fun()
{
	sleep(2);
	
	exit(0);  // 结束当前进程
	
	while(1);
}
 
int main(int argc, char *argv[])
{
	fun();
	
	printf("after fun\n");
	
	while(1);	// 不让程序结束
 
	return 0;
}

  exit() 是可以结束 fun() 所在的进程,即可让程序结束运行,结果图如下:

  再看如下代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
 
void fun()
{
	sleep(2);
	
	return;  // 结束 fun() 函数
	
	while(1);
}
 
 
int main(int argc, char *argv[])
{
	fun();
	
	printf("after fun\n");
	
	while(1);	// 不让程序结束
 
	return 0;
}

  运行结果:

  通过上面的运行结果得知,return 的作用只是结束调用 return 的所在函数,只要这个函数不是主函数( main() ),只要主函数没有结束,return 并不能结束进程。

  二、等待进程结束

  当一个进程正常或异常终止时,内核就向其父进程发送 SIGCHLD 信号,相当于告诉父亲他哪个儿子挂了,而父进程可以通过 wait() 或 waitpid() 函数等待子进程结束,获取子进程结束时的状态,同时回收他们的资源(相当于,父亲听听死去儿子的遗言同时好好安葬它)。

  wait() 和 waitpid() 函数的功能一样,区别在于,wait() 函数会阻塞,waitpid() 可以设置不阻塞,waitpid() 还可以指定等待哪个子进程结束

  所需头文件:

#include <sys/types.h>
#include <sys/wait.h>


pid_t wait(int *status);

  功能:

  等待任意一个子进程结束,如果任意一个子进程结束了,此函数会回收该子进程的资源。

  在每个进程退出的时候,内核释放该进程所有的资源、包括打开的文件、占用的内存等。但是仍然为其保留一定的信息,这些信息主要主要指进程控制块的信息(包括进程号、退出状态、运行时间等)。

  调用 wait() 函数的进程会挂起(阻塞),直到它的一个子进程退出或收到一个不能被忽视的信号时才被唤醒(相当于继续往下执行)。

  若调用进程没有子进程,该函数立即返回;若它的子进程已经结束,该函数同样会立即返回,并且会回收那个早已结束进程的资源。

  所以,wait()函数的主要功能为回收已经结束子进程的资源。

  参数:

  status: 进程退出时的状态信息。

  如果参数 status 的值不是 NULL,wait() 就会把子进程退出时的状态取出并存入其中,这是一个整数值(int),指出了子进程是正常退出还是被非正常结束的。

  这个退出信息在一个 int 中包含了多个字段,直接使用这个值是没有意义的,我们需要用宏定义取出其中的每个字段。

  下面我们来学习一下其中最常用的两个宏定义,取出子进程的退出信息:

  WIFEXITED(status)

  如果子进程是正常终止的,取出的字段值非零。

  WEXITSTATUS(status)

  返回子进程的退出状态,退出状态保存在 status 变量的 8~16 位。在用此宏前应先用宏 WIFEXITED 判断子进程是否正常退出,正常退出才可以使用此宏。

  返回值:

  成功:已经结束子进程的进程号

  失败:-1

  从本质上讲,系统调用 waitpid() 和 wait() 的作用是完全相同的,但 waitpid() 多出了两个可由用户控制的参数 pid 和 options,从而为我们编程提供了另一种更灵活的方式。

  pid_t waitpid(pid_tpid, int *status, intoptions);

  功能:等待子进程终止,如果子进程终止了,此函数会回收子进程的资源。

  参数:

  pid: 参数 pid 的值有以下几种类型:

  pid > 0

  等待进程 ID 等于 pid 的子进程。

  pid = 0

  等待同一个进程组中的任何子进程,如果子进程已经加入了别的进程组,waitpid 不会等待它。

  pid = -1

  等待任一子进程,此时 waitpid 和 wait 作用一样。

  pid < -1

  等待指定进程组中的任何子进程,这个进程组的 ID 等于 pid 的绝对值。

  status: 进程退出时的状态信息。和 wait() 用法一样。

  options: options 提供了一些额外的选项来控制 waitpid()。

  0:

  同 wait(),阻塞父进程,等待子进程退出。

  WNOHANG;

  没有任何已经结束的子进程,则立即返回。

  WUNTRACED:

  如果子进程暂停了则此函数马上返回,并且不予以理会子进程的结束状态。(由于涉及到一些跟踪调试方面的知识,加之极少用到,这里就不多费笔墨了,有兴趣的读者可以自行查阅相关材料)

  返回值:

  waitpid() 的返回值比 wait() 稍微复杂一些,一共有 3 种情况:

  当正常返回的时候,waitpid() 返回收集到的已经子进程的进程号;

  如果设置了选项 WNOHANG,而调用中 waitpid() 发现没有已退出的子进程可等待,则返回 0;

  如果调用中出错,则返回 -1,这时 errno 会被设置成相应的值以指示错误所在,如:当 pid 所对应的子进程不存在,或此进程存在,但不是调用进程的子进程,waitpid() 就会出错返回,这时 errno 被设置为 ECHILD;

  测试代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

int main(int argc, char *argv[])
{
	pid_t pid;
	pid = fork(); // 创建进程
	if( pid < 0 ){ // 出错
		perror("fork");
		exit(0);
	}
	if( pid == 0 ){// 子进程
		int i = 0;
		for(i=0;i<5;i++)
		{
			printf("this is son process\n");
			sleep(1);
		}
		_exit(2); // 子进程退出,数字 2 为子进程退出的状态
	}else if( pid > 0){ // 父进程
		int status = 0;
		// 等待子进程结束,回收子进程的资源
		// 此函数会阻塞
		// status 某个字段保存子进程调用 _exit(2) 的 2,需要用宏定义取出
		wait(&status); 
		// waitpid(-1, &status, 0); // 和 wait() 没区别,0:阻塞
		// waitpid(pid, &status, 0); // 指定等待进程号为 pid 的子进程, 0 阻塞
		// waitpid(pid, &status, WNOHANG); // WNOHANG:不阻塞
		if(WIFEXITED(status) != 0){ // 子进程是否正常终止
			printf("son process return %d\n", WEXITSTATUS(status));
		}
		printf("this is father process\n");	
	}
	return 0;
}

  运行结果:

上一篇:HTML5工具初识之网页编辑器

下一篇:Linux系统编程之多任务实现(一)

QQ技术交流群

千锋物联网官方①群
858310440

加入群聊