Unix编程——进程

版权声明:欢迎提问:[email protected] https://blog.csdn.net/include_heqile/article/details/83548499

exit函数

终止方式分为两种:

  • 正常终止:
    • main函数中执行return
    • 调用exit函数,该函数会关闭所有标准I/O
    • 调用_exit系统调用函数,此函数由exit函数调用
  • 异常终止
    • 调用abort,它产生一个SIGABRT信号
    • 当进程接收到某个信号时

不管进程如何终止,最后都会执行同一行代码,这段代码会为相应进程关闭所有打开的描述符,释放它所使用的存储器

进程为了通知它的父进程自己是如何终止的,在终止的时候会返回一个退出状态终止状态,父进程可以使用waitwaitpid函数来获得其子进程的终止状态

退出状态终止状态是有区别的:
在最后调用_exit时内核将其退出状态转换成终止状态
一个C程序是如何启动和终止的

内核使程序执行的唯一方法是调用一个exec函数
进程自愿终止的唯一方式时调用一个_exit(exit)函数

atexit函数

从入中可以看到,进程在调用exit函数时,exit函数又调用了一系列
终止处理程,这些终止处理程序是由atexit函数负责注册的,ANSI C规定一个进程至多注册32个函数,这些函数由exit自动调用

atexit函数:
#include <stdlib.h>
int atexit(void (*func)(void)) ;

该函数的参数是一个函数地址,作为参数的那个函数地址所指向的函数无参且无返回值atexit成功返回0,否则为1

atexit函数使用示例:

#include "ourhdr.h"
#include "my_err.h"

static void my_exit1(void);
static void my_exit2(void);

int main(void)	
{
	if(atexit(my_exit2) != 0)
		err_sys("can't register my_exit2");
	
	if(atexit(my_exit1) != 0)
		err_sys("can't register my_exit1");
	
	printf("main is done\n");
	return 0;
}

static void my_exit1(void)
{
	printf("first exit handler\n");
}

static void my_exit2(void)
{
	printf("second exit handler\n");
}

现在回来接着说exit函数,上面说父进程接收子进程的退出状态,但是如果父进程先于子进程结束呢?

  • 在这种情况下,子进程的父进程会自动改变为init进程,它的PID为1

UNIX中,一个已经终止,但是其父进程却没有给它收尸的进程,被称作僵尸进程(Zombie),这里说的没有收尸,意思就是父进程没有调用waitwaitpid函数来对子进程进行回收操作,回收操作具体为:

获取终止子进程的有关信息、释放它仍占用的资源

wait、waitpid函数

现在我们来说一下waitwaitpid函数

当进程终止时(不管是正常终止还是异常终止),内核都会向其父进程发送SIGCHLD信号
因为子进程终止是个异步事件(这可以在父进程运行的任何时候发生 ),所以这种信号也是内核向父进程发的异步通知

对于该信号,父进程有两种处理选项

  • 不处理,忽略信号
  • 为该信号提供一个处理函数

调用wait或者waitpid的进程会出现以下这三种状况:

  • 阻塞(如果其所有子进程都还在运行)
  • 带着子进程的终止状态立即返回(如果一个子进程已终止,正等待父进程存取其终止状态)
  • 出错立即返回(如果它没有任何子进程)

如果我们随意调用wait函数,很可能会导致进程阻塞,因为此时并不一定有子进程结束,也就是说没有SIGCHLD信号发出

wait函数和waitpid函数的区别

#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *statloc);
pid_t waitpid(pid_t pid, int *statloc, int options);
										两个函数返回:若成功则为进程ID,若出错则为-1
  • 在一个子进程终止前,wait 使其调用者阻塞,而waitpid有一选择项,可使调用者不阻塞
  • waitpid并不等待第一个终止的子进程—它有若干个选择项,可以控制它所等待的进程

它俩有一个共同的参数statloc,一个int指针,它所指向的内存单元是用来存放子进程的终止状态的,如果不关心子进程的终止状态,则设为null即可
UNIX中,使用以下三个宏来获取终止状态,他们定义在<sys/wait.h>头文件中,均以WIF开头

说 明
WIFEXITED(status) 若为正常终止子进程返回的状态,则为真。对于这种情况可执行WEXITSTATUS(status)取子进程传送给exit_exit参数的低8
WIFSIGNALED(status) 若为异常终止子进程返回的状态,则为真(接到一个不捕捉的信号)。对于这种情况,可执行WTERMSIG(status)取使子进程终止的信号编号。另外,SVR4和4.3+ BSD(但是,非POSIX.1)定义宏:WCOREDUMP(status),若已产生终止进程的core文件,则它返回真
WIFSTOPPED(status) 若为当前暂停子进程的返回的状态,则为真。对于这种情况,可执行WSTOPSIG(status)取使子进程暂停的信号编号

下面是关于wait函数和这三个宏的示例程序:

#include <sys/wait.h>
#include "ourhdr.h"
#include "my_err.h"
void pr_exit(int status) 
{
	if(WIFEXITED(status))
		printf("normal termination, exit status = %d\n", WEXITSTATUS(status));
	else if(WIFSIGNALED(status))
		printf("abnormal termination, signal number = %d%s\n", WTERMSIG(status),
#ifdef WCOREDUMP
		WCOREDUMP(status) ? " (core file.generated)" : "");
#else 
		"");
#endif
	else if(WIFSTOPPED(status))
		printf("child stopped, signal number = %d\n", WSTOPSIG(status));
}

int main(void)
{
	pid_t pid;
	int status;
	
	if((pid =fork()) < 0)
		err_sys("fork error");
	else if(pid == 0)
		exit(7);
	
	if(wait(&status) != pid)
		err_sys("wait error");
	pr_exit(status);
		
		if((pid = fork) < 0)
		err_sys("fork error");
	else if(pid == 0)
		abort();
	
	if(wait(&status) != pid)
		err_sys("wait error");
	pr_exit(status);
	
	if((pid = fork()) < 0)
		err_sys("fork error");
	else if(pid == 0)
		status /= 0;
	
	if(wait(&status) != pid)
		err_sys("wait error");
	pr_exit(status);
	
	exit(0);
}

猜你喜欢

转载自blog.csdn.net/include_heqile/article/details/83548499