Linux c fork进程实践

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

extern char* *environ;

int showenv()
{

	char **p = environ;
	while(*p){
		fprintf(stderr, "%s\n", *p);
		p++;
	}
}
//函数fork创建新进程, 函数exec执行新程序, 函数sleep休眠进程, 函数wait同步进程和函数exit结束进程.
int testfork()
{
	pid_t pid;
	if((pid = fork()) == 0){
		fprintf(stderr, "---- begin ----\n");
		sleep(10); // 睡眠3秒会导致子进程成为僵死进程
		execl("/bin/uname", "uname", "-a", 0);
		fprintf(stderr, "---- end ----\n");
	}
	else if(pid > 0)
		fprintf(stderr, "fork child pid = [%d]\n", pid);
	else
		fprintf(stderr, "Fork failed.\n");
	return 0;

}

//exec家族命名规律: 字母 l 表示变长命令行参数: 字母 v 表示函数采用指针数组命令行参数: 字母 P表示函数采用 PATH 变量查找程序: 字母 e 表示函数使用 envp 显示传递环境变。
//最终是调用execve系统调用
int testexec()
{
	//执行 ls -l /root
	//extern char **environ;
	
	char *argv[] = {"-l","/root",0} ;
	execl("/bin/ls","-l","/root",0) ;
	//exec后 后面的代码都不会执行
	execle("/bin/ls"," -l","/root",0,environ) ;
	execlp("ls", "-l", "/root",0) ;
	execv("/bin/ls", argv);
	execve ("/bin/ls" , argv, environ) ;
	execvp("ls" , argv) ;


}
/*vfork比起fork函数更快, 二者的区别如下:
a) vfork创建的子进程并不复制父进程的数据, 在随后的exec调用中系统会复制新程序的数据到内存, 继而避免了一次数据复制过程

b) 父进程以vfork方式创建子进程后将被阻塞, 知道子进程退出或执行exec调用后才能继续运行.
当子进程只用来执行新程序时, vfork-exec模型比fork-exec模型具有更高的效率, 这种方法也是Shell创建新进程的方式.

函数system会阻塞调用它的进程, 并执行字符串string中的shell命令.他和vfork-exec模型是一样的
*/
int testvfork()
{

	pid_t pid;
	if((pid = vfork()) == 0){
		fprintf(stderr, "---- begin ----\n");
		sleep(3);
		execl("/bin/uname", "uname", "-a", 0);
		fprintf(stderr, "---- end ----\n");
	}
	else if(pid > 0)
		fprintf(stderr, "fork child pid = [%d]\n", pid);
	else
		fprintf(stderr, "Fork failed.\n");
	return 0;

}
//僵尸进程
//查看僵尸进程:
//ps -ef | grep 13707(父进程id)  其中, 'defunct'代表僵死进程.
/**
预防僵死进程:
(1) wait法
	父进程主动调用wait接收子进程的死亡报告, 释放子进程占用的系统进程表资源.
	(2) 托管法
	如果父进程先于子进程而死亡, 则它的所有子进程转由进程init领养, 即它所有子进程的父进程ID号变为1. 当子进程结束时init为其释放进程表资源.
	(3) 忽略SIGC(H)LD信号
	当父进程忽略SIGC(H)LD信号后, 即使不执行wait, 子进程结束时也不会产生僵死进程.
	(4) 捕获SIGC(H)LD信号
	当父进程捕获SIGC(H)LD信号, 并在捕获函数代码中等待(wait)子进程
*/
int testszobm()
{
	pid_t pid;
	if((pid = fork()) == 0){
		printf("child[%d]\n", getpid());
		exit(0);
	}
	// wait();
	printf("parent[%d]\n", getpid());

	sleep(10);//父进程不退出,不处理子进程的信号。子进程会成为僵尸进程。

	return 0;

}

void ClearChild(int nSignal){
	pid_t pid;
	int nState;
	// WNOHANG非阻塞调用waitpid, 防止子进程成为僵死进程
	while((pid = waitpid(-1, &nState, WNOHANG)) > 0);
	//程序在接收到SIGCLD信号后立即执行函数ClearChild, 并调用非阻塞的waitpid函数结束子进程结束信息, 如果结束到子进程结束信息则释放该子进程占用的进程表资源,
	//否则函数立刻返回. 这样既保证了不增加守护进程负担, 又成功地预防了僵死进程的产生.
	signal(SIGCLD, ClearChild); // 重新绑定 SIGCLD信号
}
int InitServer(){
	pid_t pid;
	assert((pid = fork()) >= 0); // 创建子进程
	if(pid != 0){ // 父进程退出, 子进程被init托管
		sleep(1);
		exit(0);
	}
	assert(setsid() >= 0); // 子进程脱离终端
	umask(0); // 清除文件创建掩码
	signal(SIGINT, SIG_IGN); // 忽略SIGINT信号
	signal(SIGCLD, ClearChild); // 处理SIGCLD信号,预防子进程僵死
	return 0;
}
//守护进程是一个在后台长期运行的进程, 它们独立于控制终端, 周期性地执行某项任务, 或者阻塞直到事件发生, 默默地守护着计算机系统的正常运行.
int testdamo()
{
	InitServer();
	sleep(100);


}

int main(int argc, char *argv[])
{
	
	int n;
	showenv();
	//testfork();
	//testexec();
	//testvfork();
	//testszobm();
	testdamo();

	scanf("%d",&n);
	printf("%d\n",n);
	return 0;
}

你可能感兴趣的:(Linux c fork进程实践)