IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 死锁问题----打印此刻系统中所有的task -> 正文阅读

[数据结构与算法]死锁问题----打印此刻系统中所有的task

一、task_struct的遍历

进程,线程和内核线程的基本属性都是由struct task_struct结构体定义的。
设备上运行的所有task都是通过双链表连成一串,其中第一个task就是init_task,最终又指向会init_task。因此可以通过首个task(init_task)通过双向链表(tasks)遍历所有task,最终回到init_task方式遍历所有的task。?? ? ?

首进程init_task在内核启动的时候静态赋值创建。(/init/init_task.c)

struct task_struct init_task
= {
       .state                	= 0,
       .stack                	= init_stack,
       .usage                	= REFCOUNT_INIT(2),
       .flags                	= PF_KTHREAD,
       .prio                	= MAX_PRIO - 20,
       .static_prio        		= MAX_PRIO - 20,
       .normal_prio        		= MAX_PRIO - 20,
       .policy                	= SCHED_NORMAL,
       .cpus_ptr        		= &init_task.cpus_mask,
       .cpus_mask        		= CPU_MASK_ALL,
       .nr_cpus_allowed			= NR_CPUS,
       .mm                		= NULL,
       .active_mm        		= &init_mm,
       .tasks					= LIST_HEAD_INIT(init_task.tasks),
       ......
       .signal                	= &init_signals,
       .sighand        			= &init_sighand,
       .nsproxy        			= &init_nsproxy,
       .pending        			= {
               .list 	= LIST_HEAD_INIT(init_task.pending.list),
               .signal 	= {{0}}
       },
       .blocked        			= {{0}},
       .alloc_lock        		= __SPIN_LOCK_UNLOCKED(init_task.alloc_lock),
       .journal_info        	= NULL,
       INIT_CPU_TIMERS(init_task)
       .pi_lock        			= __RAW_SPIN_LOCK_UNLOCKED(init_task.pi_lock),
       .timer_slack_ns 			= 50000, /* 50 usec default slack */
       .thread_pid        		= &init_struct_pid,
       .thread_group        	= LIST_HEAD_INIT(init_task.thread_group),
       .thread_node        		= LIST_HEAD_INIT(init_signals.thread_head),
       ......
};
EXPORT_SYMBOL(init_task);

可以在编译生成的linux镜像中查找init_task符号表

nm vmlinux | grep init_task

然而一个进程中往往有很多线程,线程也与进程类似,通过双向链表串联,最终挂在进程task_struct下的thread_group
遍历系统中所有的进程和进程中所有的线程。

其中打印堆栈的方法:https://blog.csdn.net/sydyh43/article/details/119707079

二、遍历的实现

#define BKTRACE_DEPTH	30

#define next_task(p)	list_entry_rcu((p)->tasks.next, struct task_struct, tasks)
#define next_thread(p)	list_entry_rcu((p)->thread_group.next, struct task_struct, thread_group)

static void print_tasks_stack(void)
{
	struct task_struct *p = NULL, *tmp = NULL, *pt = NULL;
	unsigned long backtrace[BKTRACE_DEPTH];
	struct stack_trace trace;
			
	printk("dump stack....\n");
	rcu_read_lock();

	for (p = &init_task; (tmp = next_task(p)) != &init_task; ) {
		pt = p;
		do {
			if (pt->state == TASK_RUNNING) {
				printk("task name:%s\n", pt->comm);
				memset(backtrace, 0x00, sizeof(unsigned long) * BKTRACE_DEPTH);
				memset(&trace, 0x00, sizeof(struct stack_trace));
				trace.max_entries = BKTRACE_DEPTH;
				trace.entries = backtrace;
				
				save_stack_trace_tsk(pt, &trace);
				print_stack_trace(&trace, 0);			
			}
			
			if (pt->state & TASK_INTERRUPTIBLE) {
				printk("task name:%s\n", pt->comm);
				memset(backtrace, 0x00, sizeof(unsigned long) * BKTRACE_DEPTH);
				memset(&trace, 0x00, sizeof(struct stack_trace));
				trace.max_entries = BKTRACE_DEPTH;
				trace.entries = backtrace;
				
				save_stack_trace_tsk(pt, &trace);
				print_stack_trace(&trace, 0);
			}
			
			if (pt->state & TASK_UNINTERRUPTIBLE) {
				printk("task name:%s\n", pt->comm);
				memset(backtrace, 0x00, sizeof(unsigned long) * BKTRACE_DEPTH);
				memset(&trace, 0x00, sizeof(struct stack_trace));
				trace.max_entries = BKTRACE_DEPTH;
				trace.entries = backtrace;
				
				save_stack_trace_tsk(pt, &trace);
				print_stack_trace(&trace, 0);
			}
		}while((pt = next_thread(pt)) != p);
		
		p = tmp;
	}

	rcu_read_unlock();
}

遍历结果

发现个问题,创建线程时,printk("task name:%s\n", pt->comm);打印出来的线程名就是进程的名字,最终不好定位问题。

因此,在创建线程的时候最好指定线程名。创建线程名的方法。

#include <sys/prctl.h>

static void *thread_fun0(void *arg)
{
	prctl(PR_SET_NAME, "thread_fun0");	
	while (1) {
		fun_b();
		
		sleep(5);
	}
	
	return NULL;
}

?三、应用

1、模拟死锁的代码

static void fun_a(void)
{
	pthread_mutex_lock(&mtx0);
	sleep(2);
	printf("1%s\n", __func__);
	pthread_mutex_lock(&mtx1);
	printf("2%s\n", __func__);
	
	return;
}

static void fun_b(void)
{
	pthread_mutex_lock(&mtx1);
	sleep(1);
	printf("1%s\n", __func__);	
	pthread_mutex_lock(&mtx0);
	printf("1%s\n", __func__);	

	return;
}

static void *thread_fun0(void *arg)
{
	prctl(PR_SET_NAME, "thread_fun0");	
	while (1) {
		fun_a();
		
		sleep(5);
	}
	
	return NULL;
}

static void *thread_fun1(void *arg)
{
	prctl(PR_SET_NAME, "thread_fun1");	
	while (1) {
		
		fun_b();
		
		sleep(7);
	}
	
	return NULL;
}

2、打印堆栈

3、因此当设备出现狗咬死复位的前一刻,需要把堆栈都打印出来,方便后续的问题定位

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2021-08-29 09:37:13  更:2021-08-29 09:38:30 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 22:52:41-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码