遍历进程链表,打印pcb相关字段的内核模块
Table of Contents
代码
代码
makefile
附上linux内核4.20.11源代码
sched.h include\linux 53922 2/20/2019 208
https://mp.weixin.qq.com/s/8KSB1IsvHHW7_Vo2ApYuZg
原创: 梁金荣 Linux内核之旅
代码
代码
当内核要根据我们的PID导出对应PCB的时候,遍历进程链表检查PCB中的PID太慢了,于是引入了哈希表,Linux OS中用了一个叫做pid_hashfn的宏,来实现把PID转换成表的索引的哈希函数。在这里献上一个内核模块,功能为遍历进程链表,打印pcb相关字段的内核模块,并配上详细注释:
# include <linux/kernel.h>
# include <linux/module.h>
# include <uapi/linux/sched.h>
# include <linux/init_task.h>
# include <linux/init.h>
# include <linux/fdtable.h>
# include <linux/fs_struct.h>
# include <linux/mm_types.h>
//内核模块初始化函数
static int __init traverse_pcb(void)
{struct task_struct *task, *p;//定义指向task_struct类型的指针struct list_head *pos;//定义双向链表指针int count=0;//定义统计系统进程个数的变量printk("Printf process'message begin:\n");//提示模块开始运行task = &init_task;//指向0号进程的PCBlist_for_each(pos,&task->tasks)//使用list_for_each宏来遍历进程链表{p = list_entry(pos,struct task_struct,tasks);//指向当前进程的task_struct结构count++;//统计系统进程个数printk("\n\n");//方便查看后续打印信息/*打印task_struct中的字段,其中pid:进程的pid号;state:进程的状态;prio:动态优先级;static_prio:静态优先级; parent'pid:父进程的pid号;count:文件系统信息,文件被使用的次数; umask:进程权限位的默认设置;使用atomic_read原子操作是为了(p->files)->count字段计数不被打断*/printk("pid:%d; state:%lx; prio:%d; static_prio:%d; parent'pid:%d; count:%d; umask:%d;", \p->pid,p->state,p->prio,p->static_prio,(p->parent)->pid, \atomic_read((&(p->files)->count)),(p->fs)->umask);//打印进程地址空间的信息if((p->mm)!=NULL)printk("total_vm:%ld;",(p->mm)->total_vm);//total_vm:线性区总的页数}printk("进程的个数:%d\n",count);//打印进程个数return 0;
}//内核模块退出函数
static void __exit end_pcb(void)
{printk("traverse pcb is end.");
}
module_init(traverse_pcb);//入口
module_exit(end_pcb);//出口
MODULE_LICENSE("GPL");//许可证
makefile
#产生目标文件
obj-m:=traverse_pcb.o
#路径变量,指明当前路径
CURRENT_PATH:=$(shell pwd)
#指明内核版本号
LINUX_KERNEL:=$(shell uname -r)
#指明内核源码的绝对路径
LINUX_KERNEL_PATH:=/usr/src/linux-headers-$(LINUX_KERNEL)
#编译模块
all:make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules
#清理模块
clean:make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) clean
每个进程的pid号,进程的状态,动态优先级,静态优先级,父进程的pid号,文件系统信息(文件被使用的次数),进程权限位的默认设置,总进程数等。有些进程的total_vm是空的,说明他们是内核线程。
附上linux内核4.20.11源代码
sched.h include\linux 53922 2/20/2019 208
struct task_struct {
#ifdef CONFIG_THREAD_INFO_IN_TASK/** For reasons of header soup (see current_thread_info()), this* must be the first element of task_struct.*/struct thread_info thread_info;
#endif/* -1 unrunnable, 0 runnable, >0 stopped: */volatile long state;/** This begins the randomizable portion of task_struct. Only* scheduling-critical items should be added above here.*/randomized_struct_fields_startvoid *stack;atomic_t usage;/* Per task flags (PF_*), defined further below: */unsigned int flags;unsigned int ptrace;#ifdef CONFIG_SMPstruct llist_node wake_entry;int on_cpu;
#ifdef CONFIG_THREAD_INFO_IN_TASK/* Current CPU: */unsigned int cpu;
#endifunsigned int wakee_flips;unsigned long wakee_flip_decay_ts;struct task_struct *last_wakee;/** recent_used_cpu is initially set as the last CPU used by a task* that wakes affine another task. Waker/wakee relationships can* push tasks around a CPU where each wakeup moves to the next one.* Tracking a recently used CPU allows a quick search for a recently* used CPU that may be idle.*/int recent_used_cpu;int wake_cpu;
#endifint on_rq;int prio;int static_prio;int normal_prio;unsigned int rt_priority;const struct sched_class *sched_class;struct sched_entity se;struct sched_rt_entity rt;
#ifdef CONFIG_CGROUP_SCHEDstruct task_group *sched_task_group;
#endifstruct sched_dl_entity dl;#ifdef CONFIG_PREEMPT_NOTIFIERS/* List of struct preempt_notifier: */struct hlist_head preempt_notifiers;
#endif#ifdef CONFIG_BLK_DEV_IO_TRACEunsigned int btrace_seq;
#endifunsigned int policy;int nr_cpus_allowed;cpumask_t cpus_allowed;#ifdef CONFIG_PREEMPT_RCUint rcu_read_lock_nesting;union rcu_special rcu_read_unlock_special;struct list_head rcu_node_entry;struct rcu_node *rcu_blocked_node;
#endif /* #ifdef CONFIG_PREEMPT_RCU */#ifdef CONFIG_TASKS_RCUunsigned long rcu_tasks_nvcsw;u8 rcu_tasks_holdout;u8 rcu_tasks_idx;int rcu_tasks_idle_cpu;struct list_head rcu_tasks_holdout_list;
#endif /* #ifdef CONFIG_TASKS_RCU */struct sched_info sched_info;struct list_head tasks;
#ifdef CONFIG_SMPstruct plist_node pushable_tasks;struct rb_node pushable_dl_tasks;
#endifstruct mm_struct *mm;struct mm_struct *active_mm;/* Per-thread vma caching: */struct vmacache vmacache;#ifdef SPLIT_RSS_COUNTINGstruct task_rss_stat rss_stat;
#endifint exit_state;int exit_code;int exit_signal;/* The signal sent when the parent dies: */int pdeath_signal;/* JOBCTL_*, siglock protected: */unsigned long jobctl;/* Used for emulating ABI behavior of previous Linux versions: */unsigned int personality;/* Scheduler bits, serialized by scheduler locks: */unsigned sched_reset_on_fork:1;unsigned sched_contributes_to_load:1;unsigned sched_migrated:1;unsigned sched_remote_wakeup:1;
#ifdef CONFIG_PSIunsigned sched_psi_wake_requeue:1;
#endif/* Force alignment to the next boundary: */unsigned :0;/* Unserialized, strictly 'current' *//* Bit to tell LSMs we're in execve(): */unsigned in_execve:1;unsigned in_iowait:1;
#ifndef TIF_RESTORE_SIGMASKunsigned restore_sigmask:1;
#endif
#ifdef CONFIG_MEMCGunsigned in_user_fault:1;
#endif
#ifdef CONFIG_COMPAT_BRKunsigned brk_randomized:1;
#endif
#ifdef CONFIG_CGROUPS/* disallow userland-initiated cgroup migration */unsigned no_cgroup_migration:1;
#endif
#ifdef CONFIG_BLK_CGROUP/* to be used once the psi infrastructure lands upstream. */unsigned use_memdelay:1;
#endif/** May usercopy functions fault on kernel addresses?* This is not just a single bit because this can potentially nest.*/unsigned int kernel_uaccess_faults_ok;unsigned long atomic_flags; /* Flags requiring atomic access. */struct restart_block restart_block;pid_t pid;pid_t tgid;#ifdef CONFIG_STACKPROTECTOR/* Canary value for the -fstack-protector GCC feature: */unsigned long stack_canary;
#endif/** Pointers to the (original) parent process, youngest child, younger sibling,* older sibling, respectively. (p->father can be replaced with* p->real_parent->pid)*//* Real parent process: */struct task_struct __rcu *real_parent;/* Recipient of SIGCHLD, wait4() reports: */struct task_struct __rcu *parent;/** Children/sibling form the list of natural children:*/struct list_head children;struct list_head sibling;struct task_struct *group_leader;/** 'ptraced' is the list of tasks this task is using ptrace() on.** This includes both natural children and PTRACE_ATTACH targets.* 'ptrace_entry' is this task's link on the p->parent->ptraced list.*/struct list_head ptraced;struct list_head ptrace_entry;/* PID/PID hash table linkage. */struct pid *thread_pid;struct hlist_node pid_links[PIDTYPE_MAX];struct list_head thread_group;struct list_head thread_node;struct completion *vfork_done;/* CLONE_CHILD_SETTID: */int __user *set_child_tid;/* CLONE_CHILD_CLEARTID: */int __user *clear_child_tid;u64 utime;u64 stime;
#ifdef CONFIG_ARCH_HAS_SCALED_CPUTIMEu64 utimescaled;u64 stimescaled;
#endifu64 gtime;struct prev_cputime prev_cputime;
#ifdef CONFIG_VIRT_CPU_ACCOUNTING_GENstruct vtime vtime;
#endif#ifdef CONFIG_NO_HZ_FULLatomic_t tick_dep_mask;
#endif/* Context switch counts: */unsigned long nvcsw;unsigned long nivcsw;/* Monotonic time in nsecs: */u64 start_time;/* Boot based time in nsecs: */u64 real_start_time;/* MM fault and swap info: this can arguably be seen as either mm-specific or thread-specific: */unsigned long min_flt;unsigned long maj_flt;#ifdef CONFIG_POSIX_TIMERSstruct task_cputime cputime_expires;struct list_head cpu_timers[3];
#endif/* Process credentials: *//* Tracer's credentials at attach: */const struct cred __rcu *ptracer_cred;/* Objective and real subjective task credentials (COW): */const struct cred __rcu *real_cred;/* Effective (overridable) subjective task credentials (COW): */const struct cred __rcu *cred;/** executable name, excluding path.** - normally initialized setup_new_exec()* - access it with [gs]et_task_comm()* - lock it with task_lock()*/char comm[TASK_COMM_LEN];struct nameidata *nameidata;#ifdef CONFIG_SYSVIPCstruct sysv_sem sysvsem;struct sysv_shm sysvshm;
#endif
#ifdef CONFIG_DETECT_HUNG_TASKunsigned long last_switch_count;unsigned long last_switch_time;
#endif/* Filesystem information: */struct fs_struct *fs;/* Open file information: */struct files_struct *files;/* Namespaces: */struct nsproxy *nsproxy;/* Signal handlers: */struct signal_struct *signal;struct sighand_struct *sighand;sigset_t blocked;sigset_t real_blocked;/* Restored if set_restore_sigmask() was used: */sigset_t saved_sigmask;struct sigpending pending;unsigned long sas_ss_sp;size_t sas_ss_size;unsigned int sas_ss_flags;struct callback_head *task_works;struct audit_context *audit_context;
#ifdef CONFIG_AUDITSYSCALLkuid_t loginuid;unsigned int sessionid;
#endifstruct seccomp seccomp;/* Thread group tracking: */u32 parent_exec_id;u32 self_exec_id;/* Protection against (de-)allocation: mm, files, fs, tty, keyrings, mems_allowed, mempolicy: */spinlock_t alloc_lock;/* Protection of the PI data structures: */raw_spinlock_t pi_lock;struct wake_q_node wake_q;#ifdef CONFIG_RT_MUTEXES/* PI waiters blocked on a rt_mutex held by this task: */struct rb_root_cached pi_waiters;/* Updated under owner's pi_lock and rq lock */struct task_struct *pi_top_task;/* Deadlock detection and priority inheritance handling: */struct rt_mutex_waiter *pi_blocked_on;
#endif#ifdef CONFIG_DEBUG_MUTEXES/* Mutex deadlock detection: */struct mutex_waiter *blocked_on;
#endif#ifdef CONFIG_TRACE_IRQFLAGSunsigned int irq_events;unsigned long hardirq_enable_ip;unsigned long hardirq_disable_ip;unsigned int hardirq_enable_event;unsigned int hardirq_disable_event;int hardirqs_enabled;int hardirq_context;unsigned long softirq_disable_ip;unsigned long softirq_enable_ip;unsigned int softirq_disable_event;unsigned int softirq_enable_event;int softirqs_enabled;int softirq_context;
#endif#ifdef CONFIG_LOCKDEP
# define MAX_LOCK_DEPTH 48ULu64 curr_chain_key;int lockdep_depth;unsigned int lockdep_recursion;struct held_lock held_locks[MAX_LOCK_DEPTH];
#endif#ifdef CONFIG_UBSANunsigned int in_ubsan;
#endif/* Journalling filesystem info: */void *journal_info;/* Stacked block device info: */struct bio_list *bio_list;#ifdef CONFIG_BLOCK/* Stack plugging: */struct blk_plug *plug;
#endif/* VM state: */struct reclaim_state *reclaim_state;struct backing_dev_info *backing_dev_info;struct io_context *io_context;/* Ptrace state: */unsigned long ptrace_message;kernel_siginfo_t *last_siginfo;struct task_io_accounting ioac;
#ifdef CONFIG_PSI/* Pressure stall state */unsigned int psi_flags;
#endif
#ifdef CONFIG_TASK_XACCT/* Accumulated RSS usage: */u64 acct_rss_mem1;/* Accumulated virtual memory usage: */u64 acct_vm_mem1;/* stime + utime since last update: */u64 acct_timexpd;
#endif
#ifdef CONFIG_CPUSETS/* Protected by ->alloc_lock: */nodemask_t mems_allowed;/* Seqence number to catch updates: */seqcount_t mems_allowed_seq;int cpuset_mem_spread_rotor;int cpuset_slab_spread_rotor;
#endif
#ifdef CONFIG_CGROUPS/* Control Group info protected by css_set_lock: */struct css_set __rcu *cgroups;/* cg_list protected by css_set_lock and tsk->alloc_lock: */struct list_head cg_list;
#endif
#ifdef CONFIG_INTEL_RDTu32 closid;u32 rmid;
#endif
#ifdef CONFIG_FUTEXstruct robust_list_head __user *robust_list;
#ifdef CONFIG_COMPATstruct compat_robust_list_head __user *compat_robust_list;
#endifstruct list_head pi_state_list;struct futex_pi_state *pi_state_cache;
#endif
#ifdef CONFIG_PERF_EVENTSstruct perf_event_context *perf_event_ctxp[perf_nr_task_contexts];struct mutex perf_event_mutex;struct list_head perf_event_list;
#endif
#ifdef CONFIG_DEBUG_PREEMPTunsigned long preempt_disable_ip;
#endif
#ifdef CONFIG_NUMA/* Protected by alloc_lock: */struct mempolicy *mempolicy;short il_prev;short pref_node_fork;
#endif
#ifdef CONFIG_NUMA_BALANCINGint numa_scan_seq;unsigned int numa_scan_period;unsigned int numa_scan_period_max;int numa_preferred_nid;unsigned long numa_migrate_retry;/* Migration stamp: */u64 node_stamp;u64 last_task_numa_placement;u64 last_sum_exec_runtime;struct callback_head numa_work;struct numa_group *numa_group;/** numa_faults is an array split into four regions:* faults_memory, faults_cpu, faults_memory_buffer, faults_cpu_buffer* in this precise order.** faults_memory: Exponential decaying average of faults on a per-node* basis. Scheduling placement decisions are made based on these* counts. The values remain static for the duration of a PTE scan.* faults_cpu: Track the nodes the process was running on when a NUMA* hinting fault was incurred.* faults_memory_buffer and faults_cpu_buffer: Record faults per node* during the current scan window. When the scan completes, the counts* in faults_memory and faults_cpu decay and these values are copied.*/unsigned long *numa_faults;unsigned long total_numa_faults;/** numa_faults_locality tracks if faults recorded during the last* scan window were remote/local or failed to migrate. The task scan* period is adapted based on the locality of the faults with different* weights depending on whether they were shared or private faults*/unsigned long numa_faults_locality[3];unsigned long numa_pages_migrated;
#endif /* CONFIG_NUMA_BALANCING */#ifdef CONFIG_RSEQstruct rseq __user *rseq;u32 rseq_len;u32 rseq_sig;/** RmW on rseq_event_mask must be performed atomically* with respect to preemption.*/unsigned long rseq_event_mask;
#endifstruct tlbflush_unmap_batch tlb_ubc;struct rcu_head rcu;/* Cache last used pipe for splice(): */struct pipe_inode_info *splice_pipe;struct page_frag task_frag;#ifdef CONFIG_TASK_DELAY_ACCTstruct task_delay_info *delays;
#endif#ifdef CONFIG_FAULT_INJECTIONint make_it_fail;unsigned int fail_nth;
#endif/** When (nr_dirtied >= nr_dirtied_pause), it's time to call* balance_dirty_pages() for a dirty throttling pause:*/int nr_dirtied;int nr_dirtied_pause;/* Start of a write-and-pause period: */unsigned long dirty_paused_when;#ifdef CONFIG_LATENCYTOPint latency_record_count;struct latency_record latency_record[LT_SAVECOUNT];
#endif/** Time slack values; these are used to round up poll() and* select() etc timeout values. These are in nanoseconds.*/u64 timer_slack_ns;u64 default_timer_slack_ns;#ifdef CONFIG_KASANunsigned int kasan_depth;
#endif#ifdef CONFIG_FUNCTION_GRAPH_TRACER/* Index of current stored address in ret_stack: */int curr_ret_stack;int curr_ret_depth;/* Stack of return addresses for return function tracing: */struct ftrace_ret_stack *ret_stack;/* Timestamp for last schedule: */unsigned long long ftrace_timestamp;/** Number of functions that haven't been traced* because of depth overrun:*/atomic_t trace_overrun;/* Pause tracing: */atomic_t tracing_graph_pause;
#endif#ifdef CONFIG_TRACING/* State flags for use by tracers: */unsigned long trace;/* Bitmask and counter of trace recursion: */unsigned long trace_recursion;
#endif /* CONFIG_TRACING */#ifdef CONFIG_KCOV/* Coverage collection mode enabled for this task (0 if disabled): */unsigned int kcov_mode;/* Size of the kcov_area: */unsigned int kcov_size;/* Buffer for coverage collection: */void *kcov_area;/* KCOV descriptor wired with this task or NULL: */struct kcov *kcov;
#endif#ifdef CONFIG_MEMCGstruct mem_cgroup *memcg_in_oom;gfp_t memcg_oom_gfp_mask;int memcg_oom_order;/* Number of pages to reclaim on returning to userland: */unsigned int memcg_nr_pages_over_high;/* Used by memcontrol for targeted memcg charge: */struct mem_cgroup *active_memcg;
#endif#ifdef CONFIG_BLK_CGROUPstruct request_queue *throttle_queue;
#endif#ifdef CONFIG_UPROBESstruct uprobe_task *utask;
#endif
#if defined(CONFIG_BCACHE) || defined(CONFIG_BCACHE_MODULE)unsigned int sequential_io;unsigned int sequential_io_avg;
#endif
#ifdef CONFIG_DEBUG_ATOMIC_SLEEPunsigned long task_state_change;
#endifint pagefault_disabled;
#ifdef CONFIG_MMUstruct task_struct *oom_reaper_list;
#endif
#ifdef CONFIG_VMAP_STACKstruct vm_struct *stack_vm_area;
#endif
#ifdef CONFIG_THREAD_INFO_IN_TASK/* A live task holds one reference: */atomic_t stack_refcount;
#endif
#ifdef CONFIG_LIVEPATCHint patch_state;
#endif
#ifdef CONFIG_SECURITY/* Used by LSM modules for access restriction: */void *security;
#endif#ifdef CONFIG_GCC_PLUGIN_STACKLEAKunsigned long lowest_stack;unsigned long prev_lowest_stack;
#endif/** New fields for task_struct should be added above here, so that* they are included in the randomized portion of task_struct.*/randomized_struct_fields_end/* CPU-specific state of this task: */struct thread_struct thread;/** WARNING: on x86, 'thread_struct' contains a variable-sized* structure. It *MUST* be at the end of 'task_struct'.** Do not put anything below here!*/
};
遍历进程链表,打印pcb相关字段的内核模块相关推荐
- 【Linux 内核】进程管理 - 进程优先级 ① ( 限期进程 | 实时进程 | 普通进程 | 进程优先级相关字段 )
文章目录 一.进程分类 ( 限期进程 | 实时进程 | 普通进程 ) 二.进程优先级相关字段 一.进程分类 ( 限期进程 | 实时进程 | 普通进程 ) Linux 进程 分为 333 种类型 , & ...
- 【Linux 内核】进程管理 task_struct 结构体 ⑤ ( files 字段 | nsproxy 字段 | 信号处理相关字段 | 信号量和共享内存相关字段 )
文章目录 一.task_struct 结构体字段分析 1.files 字段 2.nsproxy 字段 3.信号处理相关字段 4.信号量和共享内存相关字段 在 Linux 内核 中 , " 进 ...
- C语言之单链表打印(遍历),查询,定位,插入,删除,链表长度
生活记录 先聊家常,不想看废话的请直接跳到下面看代码分析. 大半个月没有更新博客了,大半个月也做了很多事情.依旧被困在英国,并且得知下个月的机票又又又被取消了:第二遍参加了某大公司的机试以及再次的失败 ...
- shell实现大批量word转码然后分析相关字段
需求 需要从服务器中的所有附件(2013-2019) 共60G查找相关字段 在linux上面直接打开doc等是乱码的 思路 先全部附件转码为txt, 然后用grep遍历查找字段实现 转码shell # ...
- 进程控制块(PCB)
一.进程控制块 进程控制块是存放进程的管理和控制信息的数据结构称为进程控制块.它是进程管理和控制的最重要的数据结构,没一个进程都有一个PCB,在创建进程时,建立PCB,伴随进程运行的全过程,直到进程撤 ...
- oracle 根据spid查sql,探讨:Oracle数据库查看一个进程是如何执行相关的实际SQL语句...
Oracle数据库查看一个进程是如何执行相关的实际sql语句 代码如下: SELECT b.sql_text,sid,serial#,osuser,machine FROM v$session a,v ...
- oracle dba_hist tablepsace,oracle数据库dba_hist等视图中的Delta相关字段介绍
从10g开始,我们采用awr报告来分析数据库的性能,我们发现增加了很多dba_hist相关的视图,其中基于时间相关的字段delta开始出现,对于我们计算语句的时间消耗很有帮助! 其实Delta 表示第 ...
- oracle数据库dba_hist等视图中的Delta相关字段介绍
从10g开始,我们采用awr报告来分析数据库的性能,我们发现增加了很多dba_hist相关的视图,其中基于时间相关的字段delta开始出现,对于我们计算语句的时间消耗很有帮助! 其实Delta 表示第 ...
- [ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork
文章目录 一.进程 1.1 基本概念 1.2 描述进程 - PCB 1.3 查看进程 1.3.1 第一种方式 1.3.2 第二种方式 1.4 通过系统调用获取进程标识符 1.4.1 获取进程的pid ...
最新文章
- linux改变工作目录命令,linux命令:cd命令,改变当前的工作目录
- jQuery仿淘宝商城天猫鼠标移动过去,透明度降低
- iclone7.8中文版
- C++里如何使用功能键(F1、F2·····)进行操纵?
- android 进度条 代码,Android进度条ProgressBar的实现代码
- 微软.NET 正式劈腿成功,横跨所有平台
- Linux中文件的分类
- 微信中打开网址添加请在在浏览器中打开提示遮罩
- C# 连接本地数据库
- 修改app应用的图标与名字
- 爬虫 - 提高爬虫效率的方法
- php生成字符画,超易用的字符画在线生成器,非常适合制作STEAM展柜。
- 怎么把小米手机通讯录导入苹果手机
- 京东手机电商大数据统计平台搭建
- 为什么现在很多人在用影刀,影刀突然火起来了?
- pbl和sbl_ROKSO、SBL、XBL、PBL、DBL 是什么意思?
- 51 单片机 程序 测量占空比 测量频率 频率计 占空比 proteus
- 获取员工其当前的薪水比其manager当前薪水还高的相关信息
- 红帽与Cloudera结成大数据联盟 释放企业级Hadoop潜能
- 图文并茂——从Kubernetes的诞生背景到什么是Kubernetes, 带你深度解析Kubernetes
热门文章
- MyBatis各个jar包的作用
- 实现Runnable接口
- leetcode题解189-旋转数组
- 「HAOI2018」染色 解题报告
- Java标识符和关键字(static,final,abstract,interface)
- Magento Url重写修改
- Hibernate入门级实例
- 原来 JS 也支持跟 Lua 语意一样的内嵌函数的闭包概念
- php 把图片转换成二进制流,php把图片转换成二进制流的方法
- 学科实践活动感悟50字_“五育并举”的一次生动实践——洪雅实验中学研学活动感悟...