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;
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)
{
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;
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)
{
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;
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));
}
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)
{
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 调试器分类
2.2 GDB调试器相比GUI可视化图形调试器拥有更高的速度,占用系统性能更低
2.3 调试工具允许用户在程序运行中任意位置控制程序暂停或继续,可以在程序运行时观察程序的内部变化,发现问题
2.4 编程领域的虫
2.5 使程序具有调试能力
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