Linux高级 2019-6-2上午

1.进程回收问题

1.1 信号回收

  • 父进程捕捉 SIGCHLD 信号,通过 wait 函数或者 waitpid 函数进行回收子进程

1.2 信号回收可能产生的问题

  • 可能导致某些系统调用被强制中断
  • 中断发生的原因是由于我们需要从内核模式向用户模式进行转换,转换的过程中可能会出现转换未完成而信号已被强制回收的情况

1.3 父进程回收僵尸进程的步骤

  • 1.kernel 向父进程发送一个 SIGCHLD 信号;
  • 2.父进程进行信号捕捉(回收)
  • 3.accept 或者 read (阻塞功能)

1.4 解决上述问题

  • 创建一个回收线程专门来回收子进程
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <pthread.h>
#include <sys/wait.h>

void SigWait(int code)
{
	pid_t wpid;
	// 接受一个信号进行多次回收,一直回收到返回0或者-1
	while((wpid = waitpid(-1, NULL, WNOHANG)) > 0)
		printf("Child Process ID:%d\tChild Thread TID:0x%x\tWait Pro PID:%d\n", getpid(), (unsigned int)pthread_self(), wpid);
}

// 线程进行的回收工作
void* ThreadWait(void* arg)
{
	// 解决对 SIGCHLD 信号的屏蔽
	sigset_t p_set;
	sigemptyset(&p_set);
	sigprocmask(SIG_SETMASK, &p_set, NULL);

	while(1)
		sleep(1);
}

int main()
{
	// 主控线程设定屏蔽字
	sigset_t o_set, n_set;
	sigemptyset(&n_set);
	sigaddset(&n_set, SIGCHLD);
	sigprocmask(SIG_BLOCK, &n_set, &o_set);

	// 主控线程信号捕捉设定
	struct sigaction n_act, o_act;
	n_act.sa_handler = SigWait;
	n_act.sa_flags = 0;
	sigemptyset(&(n_act.sa_mask));
	sigaction(SIGCHLD, &n_act, &o_act);

	// 创建回收线程
	pthread_t tid;
	pthread_create(&tid, NULL, ThreadWait, NULL);

	// 创建多进程
	pid_t pid;
	int i;
	printf("Main Process ID:%d\tMain Thread TID:0x%x\n", getpid(), (unsigned int)pthread_self());
	for(i=0; i<10; i++)
	{
		pid = fork();
		if(pid == 0)
			break;
	}
	if(pid > 0)
	{
		while(1);
	}
	else if(pid == 0)
	{
		printf("Child PID:%d\n", getpid());
		exit(i);
	}
	else
	{
		perror("fork call failed");
		exit(0);
	}

	return 0;
}
  • 查看某个进程中的线程信息: ps -LF 进程ID

1.5 回收状态(status)的获取

  • 在SigWait函数中使用一个int型变量来接收 waitpid 函数中的第二个参数,该参数是回收的进程的返回值
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <pthread.h>
#include <sys/wait.h>

void SigWait(int code)
{
	pid_t wpid;
	int status;
	// 接受一个信号进行多次回收,一直回收到返回0或者-1
	while((wpid = waitpid(-1, &status, WNOHANG)) > 0)
		if(WIFEXITED(status))
		{
			printf("Child Process ID:%d\tChild Thread TID:0x%x\tWait Pro PID:%d\tExit NO:%d\n", getpid(), (unsigned int)pthread_self(), wpid, WEXITSTATUS(status));
		}
}

// 线程进行的回收工作
void* ThreadWait(void* arg)
{
	// 解决对 SIGCHLD 信号的屏蔽
	sigset_t p_set;
	sigemptyset(&p_set);
	sigprocmask(SIG_SETMASK, &p_set, NULL);

	while(1)
		sleep(1);
}

int main()
{
	// 主控线程设定屏蔽字
	sigset_t o_set, n_set;
	sigemptyset(&n_set);
	sigaddset(&n_set, SIGCHLD);
	sigprocmask(SIG_BLOCK, &n_set, &o_set);

	// 主控线程信号捕捉设定
	struct sigaction n_act, o_act;
	n_act.sa_handler = SigWait;
	n_act.sa_flags = 0;
	sigemptyset(&(n_act.sa_mask));
	sigaction(SIGCHLD, &n_act, &o_act);

	// 创建回收线程
	pthread_t tid;
	pthread_create(&tid, NULL, ThreadWait, NULL);

	// 创建多进程
	pid_t pid;
	int i;
	printf("Main Process ID:%d\tMain Thread TID:0x%x\n", getpid(), (unsigned int)pthread_self());
	for(i=0; i<10; i++)
	{
		pid = fork();
		if(pid == 0)
			break;
	}
	if(pid > 0)
	{
		while(1);
	}
	else if(pid == 0)
	{
		printf("Child PID:%d\n", getpid());
		exit(i);
	}
	else
	{
		perror("fork call failed");
		exit(0);
	}

	return 0;
}

1.6 在创建进程的过程中,我们让前9个进程变为僵尸进程,第10个子进程死循环,此时第10个子进程需要我们手动在终端中kill掉,上述代码不会有任何显示,我们希望父进程在我们kill第10个子进程时收到消息,此时应该使用 WIFSIGNALED 函数

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

void SigWait(int code)
{
	pid_t wpid;
	int status;
	// 接受一个信号进行多次回收,一直回收到返回0或者-1
	while((wpid = waitpid(-1, &status, WNOHANG)) > 0)
	{
		// 判断回收的子进程是否时正常退出,若是则返回1
		if(WIFEXITED(status))
		{
			printf("Child Process ID:%d\tChild Thread TID:0x%x\tWait Pro PID:%d\tExit NO:%d\n", getpid(), (unsigned int)pthread_self(), wpid, WEXITSTATUS(status));
		}

		// 判断是否是信号杀死的进程
		if(WIFSIGNALED(status))
		{
			printf("Process PID:%d\tWait Thread TID:0x%x\tWait Child Process:%d\t[Signal NO:%d]\n", getpid(), (unsigned int)pthread_self(), wpid, WTERMSIG(status));
		}
	}
}

// 线程进行的回收工作
void* ThreadWait(void* arg)
{
	// 解决对 SIGCHLD 信号的屏蔽
	sigset_t p_set;
	sigemptyset(&p_set);
	sigprocmask(SIG_SETMASK, &p_set, NULL);

	while(1)
		sleep(1);
}

int main()
{
	// 主控线程设定屏蔽字
	sigset_t o_set, n_set;
	sigemptyset(&n_set);
	sigaddset(&n_set, SIGCHLD);
	sigprocmask(SIG_BLOCK, &n_set, &o_set);

	// 主控线程信号捕捉设定
	struct sigaction n_act, o_act;
	n_act.sa_handler = SigWait;
	n_act.sa_flags = 0;
	sigemptyset(&(n_act.sa_mask));
	sigaction(SIGCHLD, &n_act, &o_act);

	// 创建回收线程
	pthread_t tid;
	pthread_create(&tid, NULL, ThreadWait, NULL);

	// 创建多进程
	pid_t pid;
	int i;
	printf("Main Process ID:%d\tMain Thread TID:0x%x\n", getpid(), (unsigned int)pthread_self());
	for(i=0; i<10; i++)
	{
		pid = fork();
		if(pid == 0)
			break;
	}
	if(pid > 0)
	{
		while(1);
	}
	else if(pid == 0)
	{
		if(i == 9)
			while(1);
		printf("Child PID:%d\n", getpid());
		exit(i);
	}
	else
	{
		perror("fork call failed");
		exit(0);
	}

	return 0;
}

2.调试器

2.1 调试器分类

  • 1.GDB调试器
  • 2.GUI可视化图形调试器

2.2 GDB调试器相比GUI可视化图形调试器拥有更高的速度,占用系统性能更低

2.3 调试工具允许用户在程序运行中任意位置控制程序暂停或继续,可以在程序运行时观察程序的内部变化,发现问题

2.4 编程领域的虫

  • bug : 臭虫
  • spider : 爬虫
  • 蠕虫

2.5 使程序具有调试能力

  • gcc *.c -g -o app

2.6 常用的 GDB命令

  • 1.启动调试: gdb NAME_EXE
  • 2.查看代码: list/l 函数名或行号
  • 3.GDB命令具有记忆功能,执行一个命令之后直接按enter键就会重复上一个执行的命令
  • 4.在指定位置设置断点: break/b 函数名或行号
  • 5.全速运行: run/r 命令参数
  • 6.调试运行: start 命令参数
  • 7.逐过程调试: next/n
  • 8.逐语句调试: step/s
  • 9.打印输出任意的程序元素: print/p 想打印的东西
  • 10.查看当前下的所有断点: info breakpoints
  • 11.取消断点: delete/d 断点编号
  • 12.退出调试: quit/q
  • 13.禁用断点: disable
  • 14.启用断点: enable
  • 15.查看变量类型: whatis 变量名
  • 16.立即退出返回: finish
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值