Linux 进程管理数据结构

文末集赞留言抽奖,我会选出留言点赞数前 3 名送出小米耳机。

别刷赞啊,刷赞被举报无效,相信真的是公众号粉丝的读者,不会做这样的行为,刷赞指的是购买外挂刷,如果是转发到朋友圈和微信群的,不算刷赞行为。

数据结构

Linux 内核使用 task_struct 来表示一个进程,这个结构体里面保存了进程的所有信息,要研究进程的数据结构,我就就需要研究这个结构体里面各个成员的作用。

struct task_struct {/*** 进程状态。*/volatile long state;	/* -1 unrunnable, 0 runnable, >0 stopped *//*** 进程的基本信息。*/struct thread_info *thread_info;atomic_t usage;unsigned long flags;	/* per process flags, defined below */unsigned long ptrace;int lock_depth;		/* Lock depth *//*** 进行的动态优先权和静态优先权*/int prio, static_prio;/*** 进程所在运行队列。每个优先级对应一个运行队列。*/struct list_head run_list;/*** 指向当前运行队列的prio_array_t*/prio_array_t *array;/*** 进程的平均睡眠时间*/unsigned long sleep_avg;/*** timestamp-进程最近插入运行队列的时间。或涉及本进程的最近一次进程切换的时间* last_ran-最近一次替换本进程的进程切换时间。*/unsigned long long timestamp, last_ran;/*** 进程被唤醒时所使用的代码。*     0:进程处于TASK_RUNNING状态。*     1:进程处于TASK_INTERRUPTIBLE或者TASK_STOPPED状态,而且正在被系统调用服务例程或内核线程唤醒。*     2:进程处于TASK_INTERRUPTIBLE或者TASK_STOPPED状态,而且正在被ISR或者可延迟函数唤醒。*     -1:表示从UNINTERRUPTIBLE状态被唤醒*/int activated;/*** 进程的调度类型:sched_normal,sched_rr或者sched_fifo*/unsigned long policy;/*** 能执行进程的CPU的位掩码*/cpumask_t cpus_allowed;/*** time_slice-在进程的时间片中,还剩余的时钟节拍数。* first_time_slice-如果进程肯定不会用完其时间片,就把该标志设置为1.*            xie.baoyou注:原文如此,应该是表示任务是否是第一次执行。这样,如果是第一次执行,并且在开始运行*                         的第一个时间片内就运行完毕,那么就将剩余的时间片还给父进程。主要是考虑到有进程*                         会大量的动态创建子进程时,而子进程会立即退出这种情况。如果不还给父进程时间片,会对这种进程不公平。*/unsigned int time_slice, first_time_slice;#ifdef CONFIG_SCHEDSTATSstruct sched_info sched_info;
#endif/*** 通过此链表把所有进程链接到一个双向链表中。*/struct list_head tasks;/** ptrace_list/ptrace_children forms the list of my children* that were stolen by a ptracer.*//*** 链表的头。该链表包含所有被debugger程序跟踪的P的子进程。*/struct list_head ptrace_children;/*** 指向所跟踪进程其实际父进程链表的前一个下一个元素。*/struct list_head ptrace_list;/*** mm:指向内存区描述符的指针*/struct mm_struct *mm, *active_mm;/* task state */struct linux_binfmt *binfmt;long exit_state;int exit_code, exit_signal;int pdeath_signal;  /*  The signal sent when the parent dies  *//* ??? */unsigned long personality;/*** 进程发出execve系统调用的次数。*/unsigned did_exec:1;/*** 进程PID*/pid_t pid;/*** 线程组领头线程的PID。*/pid_t tgid;/** pointers to (original) parent process, youngest child, younger sibling,* older sibling, respectively.  (p->father can be replaced with* p->parent->pid)*//*** 指向创建进程的进程的描述符。* 如果进程的父进程不再存在,就指向进程1的描述符。* 因此,如果用户运行一个后台进程而且退出了shell,后台进程就会成为init的子进程。*/struct task_struct *real_parent; /* real parent process (when being debugged) *//*** 指向进程的当前父进程。这种进程的子进程终止时,必须向父进程发信号。* 它的值通常与real_parent一致。* 但偶尔也可以不同。例如:当另一个进程发出监控进程的ptrace系统调用请求时。*/struct task_struct *parent;	/* parent process *//** children/sibling forms the list of my children plus the* tasks I'm ptracing.*//*** 链表头部。链表指向的所有元素都是进程创建的子进程。*/struct list_head children;	/* list of my children *//*** 指向兄弟进程链表的下一个元素或前一个元素的指针。*/struct list_head sibling;	/* linkage in my parent's children list *//*** P所在进程组的领头进程的描述符指针。*/struct task_struct *group_leader;	/* threadgroup leader *//* PID/PID hash table linkage. *//*** PID散列表。通过这四个表,可以方便的查找同一线程组的其他线程,同一会话的其他进程等等。*/struct pid pids[PIDTYPE_MAX];struct completion *vfork_done;		/* for vfork() *//*** 子进程在用户态的地址。这些用户态地址的值将被设置或者清除。* 在do_fork时记录这些地址,稍后再设置或者清除它们的值。*/int __user *set_child_tid;		/* CLONE_CHILD_SETTID */int __user *clear_child_tid;		/* CLONE_CHILD_CLEARTID *//*** 进程的实时优先级。*/unsigned long rt_priority;/*** 以下三对值用于用户态的定时器。当定时器到期时,会向用户态进程发送信号。* 每一对值分别存放了两个信号之间以节拍为单位的间隔,及定时器的当前值。*/unsigned long it_real_value, it_real_incr;cputime_t it_virt_value, it_virt_incr;cputime_t it_prof_value, it_prof_incr;/*** 每个进程的动态定时器。用于实现ITIMER_REAL类型的间隔定时器。* 由settimer系统调用初始化。*/struct timer_list real_timer;/*** 进程在用户态和内核态下经过的节拍数*/cputime_t utime, stime;unsigned long nvcsw, nivcsw; /* context switch counts */struct timespec start_time;
/* mm fault and swap info: this can arguably be seen as either mm-specific or thread-specific */unsigned long min_flt, maj_flt;
/* process credentials */uid_t uid,euid,suid,fsuid;gid_t gid,egid,sgid,fsgid;struct group_info *group_info;kernel_cap_t   cap_effective, cap_inheritable, cap_permitted;unsigned keep_capabilities:1;struct user_struct *user;
#ifdef CONFIG_KEYSstruct key *session_keyring;	/* keyring inherited over fork */struct key *process_keyring;	/* keyring private to this process (CLONE_THREAD) */struct key *thread_keyring;	/* keyring private to this thread */
#endifint oomkilladj; /* OOM kill score adjustment (bit shift). */char comm[TASK_COMM_LEN];
/* file system info *//*** 文件系统在查找路径时使用,避免符号链接查找深度过深,导致死循环。* link_count是__do_follow_link递归调用的层次。* total_link_count调用__do_follow_link的总次数。*/int link_count, total_link_count;
/* ipc stuff */struct sysv_sem sysvsem;
/* CPU-specific state of this task */struct thread_struct thread;
/* filesystem information *//*** 与文件系统相关的信息。如当前目录。*/struct fs_struct *fs;
/* open file information *//*** 指向文件描述符的指针*/struct files_struct *files;
/* namespace */struct namespace *namespace;
/* signal handlers *//*** 指向进程的信号描述符的指针*/struct signal_struct *signal;/*** 指向进程的信号处理程序描述符的指针*/struct sighand_struct *sighand;/*** blocked-被阻塞的信号的掩码* real_blocked-被阻塞信号的临时掩码(由rt_sigtimedwait系统调用使用)*/sigset_t blocked, real_blocked;/*** 存放私有挂起信号的数据结构*/struct sigpending pending;/*** 信号处理程序备用堆栈的地址*/unsigned long sas_ss_sp;/*** 信号处理程序备用堆栈的大小*/size_t sas_ss_size;/*** 指向一个函数的指针,设备驱动程序使用这个函数阻塞进程的某些信号*/int (*notifier)(void *priv);/*** 指向notifier函数可能使用的数据*/void *notifier_data;sigset_t *notifier_mask;void *security;struct audit_context *audit_context;/* Thread group tracking */u32 parent_exec_id;u32 self_exec_id;
/* Protection of (de-)allocation: mm, files, fs, tty, keyrings */spinlock_t alloc_lock;
/* Protection of proc_dentry: nesting proc_lock, dcache_lock, write_lock_irq(&tasklist_lock); */spinlock_t proc_lock;
/* context-switch lock */spinlock_t switch_lock;/* journalling filesystem info *//*** 当前活动日志操作处理的地址。* 正在使用的原子操作对象。*/void *journal_info;/* VM state */struct reclaim_state *reclaim_state;struct dentry *proc_dentry;struct backing_dev_info *backing_dev_info;struct io_context *io_context;unsigned long ptrace_message;siginfo_t *last_siginfo; /* For ptrace use.  */
/** current io wait handle: wait queue entry to use for io waits* If this thread is processing aio, this points at the waitqueue* inside the currently handled kiocb. It may be NULL (i.e. default* to a stack based synchronous wait) if its doing sync IO.*/wait_queue_t *io_wait;
/* i/o counters(bytes read/written, #syscalls */u64 rchar, wchar, syscr, syscw;
#if defined(CONFIG_BSD_PROCESS_ACCT)u64 acct_rss_mem1;	/* accumulated rss usage */u64 acct_vm_mem1;	/* accumulated virtual memory usage */clock_t acct_stimexpd;	/* clock_t-converted stime since last update */
#endif
#ifdef CONFIG_NUMAstruct mempolicy *mempolicy;short il_next;
#endif
};

双向链表

Linux 内核使用 list_head 来表示双向链表

struct list_head {struct list_head *next, *prev;
};

我们在上面的数据结构里面,使用了很多双向链表。

任务 ID

pid_t pid;
pid_t tgid;struct task_struct *group_leader;
  • pid:每个进程都有自己的 pid,它在内核中是唯一的,在 Linux 中,我们可以使用 ps -ef 查看所有的进程,其中 PID 就是进程号。pid 可以给用户查看指定进程的信息,可以通过 pid 给指定的进程发送信号
  • tgid:tigd 是 thread group ID,表示线程组 id。thread group 是线程组的意思,所谓的线程组是什么意思呢?内核中不管是线程或者是进程都是使用 task_struct 来表示,一个进程也可以称为主线程,由它创建多个线程,这些线程和进程的主线程就称为一个线程组。每个线程都有自己的 pid,而 tgid 则等于进程的主线程的 pid,这样也就可以区分谁是主线程,谁是被主线程创建出来的
  • group_leader:指向进程组的 task_struct 对象 通过 getpid 返回的是 tgid,也就是说同一个线程组共享也给 pid

信号处理


/* Signal handlers: */
struct signal_struct    *signal;
struct sighand_struct    *sighand;
sigset_t      blocked;
sigset_t      real_blocked;
sigset_t      saved_sigmask;
struct sigpending    pending;
unsigned long      sas_ss_sp;
size_t        sas_ss_size;
unsigned int      sas_ss_flags;
  • blocked:sigset_t 是一个位图,每个位都表示一个信号。blocked 表示的是该进程的哪些信号被阻塞暂不处理
  • pending:表示进程接收到了哪些信号,需要被处理
  • sighand:用户可以定义相应信号的处理方法,其信息保存在这里
  • sas_ss_xxx:信号的处理默认使用的是进程用户空间的函数栈,也可以开辟新的栈专门用于信号处理,这三个变量就是用户维护这个栈信息 在 signal 中,定义了 struct sigpending shared_pending,这个 shared_pending 和 pending 的区别是,pending 表示该 task_struct 收到的信号,而 shared_pending 是整个线程组共享的。也就是说,对于 pending 中接收到的信号,只能由这个 task_struct 来处理,而 shared_pending 中收到的信号,可以由线程组中的任意一个线程处理

进程状态

在 task_struct 中,定义了这样几个变量,与进程的状态有关

 volatile long state;    /* -1 unrunnable, 0 runnable, >0 stopped */int exit_state;unsigned int flags;

state 和 exit_state 的定义如下

/* Used in tsk->state: */
#define TASK_RUNNING                    0
#define TASK_INTERRUPTIBLE              1
#define TASK_UNINTERRUPTIBLE            2
#define __TASK_STOPPED                  4
#define __TASK_TRACED                   8
/* Used in tsk->exit_state: */
#define EXIT_DEAD                       16
#define EXIT_ZOMBIE                     32
#define EXIT_TRACE                      (EXIT_ZOMBIE | EXIT_DEAD)

state 相关

  • TASK_RUNNING:运行态或就绪态,表示进程正在运行,或者进程处于就绪态位于运行队列中
  • TASK_INTERRUPTIBLE:可中断的睡眠态,表示进程正在睡眠等待,睡眠过程中可以被信号唤醒
  • TASK_UNINTERRUPTIBLE:不可中断的睡眠态,表示进程正在睡眠等待,并且睡眠过程中不可被信号唤醒。这就意味着,如果有一个进程一直处于这种状态,我们无法使用信号将其杀死,唯一的办法就是重启,所以这个状态较少使用
  • __TASK_STOPPED:在进程收到 SIGSTOP、SIGTTIN、SIGTSTP 或者 SIGTTO 等信号的时候,进入该状态
  • __TASK_TRACED:进程被另一个进程跟踪的时候,进入此状态

exit_state 相关

  • EXIT_ZOMBIE:僵尸态,如果一个进程已经死亡,但在内核中的 task_struct 还未被父进程回收,那么进程就会变成僵尸进程
  • EXIT_DEAD:最终态,进程的资源被父进程回收后,会从 EXIT_ZOMBIE 变成 EXIT_DEAD

进程的状态转换如下640?wx_fmt=png

flags 的某些定义如下

#define PF_EXITING    0x00000004
#define PF_VCPU      0x00000010
#define PF_FORKNOEXEC    0x00000040
  • PF_EXITING:表示正在退出
  • PF_VCPU:表示运行在虚拟 CPU 上
  • PF_FORKNOEXEC:表示 fork 完,还没有调用 exec

进程调度

进程调度相关的变量如下


//是否在运行队列上
int        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;
struct sched_dl_entity    dl;
//调度策略
unsigned int      policy;
//可以使用哪些CPU
int        nr_cpus_allowed;
cpumask_t      cpus_allowed;
struct sched_info    sched_info;
  • on_rq:表明进程是否在运行队列上
  • prio、static_prio、normal_prio、rt_priority:优先级相关的变量
  • sched_class:调度类,也就是这个进程采用的调度策略
  • se、rt、dl:调度实体,调度类操作的单位
  • policy:调度策略,与 sched_class 对应
  • nr_cpus_allowed、cpus_allowed:表明进程可以在哪些 CPU 上运行

运行统计信息

u64        utime;//用户态消耗的CPU时间
u64        stime;//内核态消耗的CPU时间
unsigned long      nvcsw;//自愿(voluntary)上下文切换计数
unsigned long      nivcsw;//非自愿(involuntary)上下文切换计数
u64        start_time;//进程启动时间,不包含睡眠时间
u64        real_start_time;//进程启动时间,包含睡眠时间

进程亲缘关系

struct task_struct __rcu *real_parent; /* real parent process */
struct task_struct __rcu *parent; /* recipient of SIGCHLD, wait4() reports */
struct list_head children;      /* list of my children */
struct list_head sibling;       /* linkage in my parent's children list */

进程之间有亲缘关系,所以所有的进程实际上是一棵树,上面说过,进程组成一个双向循环链表,这并不冲突,因为既是双向循环链表,又是一棵树

  • parent:指向父进程
  • children:所有子进程的组成的链表的链表头
  • sibling:兄弟链表,又相当于父进程的 children 链表中的一个节点 所有进程组成的关系如下640?wx_fmt=png

real_parent 和 parent 在大多数情况下是一样的,只有在某些特殊情况下才会不一样

内存管理

struct mm_struct                *mm;
struct mm_struct                *active_mm;

每个进程都有自己独立的地址空间,内核使用了 mm_struct 结构体来管理进程的地址空间

文件与文件系统

/* Filesystem information: */
struct fs_struct                *fs;
/* Open file information: */
struct files_struct             *files;

每个进程都有一个文件系统的数据结构,就是 fs_struct 每个进程还要维护它所打开的文件,这些信息在 files_struct 中

进程内核栈

栈结构

上一篇文章讲解了 task_struct 的大部分成员,这一篇文章来讲解进程的内核栈

程序的运行需要使用到栈,所以不管进程是在内核态运行还是在用户态运行都需要用到栈

Linux 将进程地址空间分为内核空间和用户空间,它们之间是不能直接访问的,而一个进程某些时候可能在用户态运行,某些时候可能在内核态运行(发生系统调用时),所以一个进程既需要用户栈又需要内核栈

这篇文章就来讲解 Linux 内核给进程分配的栈结构

在 task_struct 中,有一个变量指向该进程的内核栈,如下

struct task_struct {...void *stack;...
};

内核栈的大小在内核中的定义如下

#define THREAD_SIZE_ORDER  1
#define THREAD_SIZE    (PAGE_SIZE << THREAD_SIZE_ORDER)

一个 PAGE_SIZE 是 4K,左移一位就是乘以 2,所以 THREAD_SIZE 就是 8K,所以大体 j 结构如下640?wx_fmt=png

接下来我们看这 8K 的空间的结构分布在这段空间的最底部,存放着一个 struct thread_info 结构体,何以证明呢?在 Linux 中有一个 union thread_union 共用体,其定义如下

union thread_union {
#ifndef CONFIG_THREAD_INFO_IN_TASKstruct thread_info thread_info;
#endifunsigned long stack[THREAD_SIZE/sizeof(long)];
};

其中的 stack 表示栈空间,大小为 THREAD_SIZE 个字节 union 表示是一个共用体,可以看出,thread_info 在位于这个栈的最底部,如下图所示640?wx_fmt=pngLinux 中发生系统调用时,会从用户态变成内核态,然后执行内核代码,当内核代码执行完之后,又会回到用户态执行用户代码

在进程从用户态变成内核态的时候,内核需要将用户态运行时寄存器的值保存下来,然后修改寄存器,当内核代码执行完之后,又将寄存器的值恢复,这些寄存器的值保存在哪里呢?

在内核栈的最高端,存放着一个 pt_regs 结构,这个结构包含了相关寄存器的定义,用户态寄存器的值就保存在这里,对于 X86 32 位其定义如下

struct pt_regs {unsigned long bx;unsigned long cx;unsigned long dx;unsigned long si;unsigned long di;unsigned long bp;unsigned long ax;unsigned long ds;unsigned long es;unsigned long fs;unsigned long gs;unsigned long orig_ax;unsigned long ip;unsigned long cs;unsigned long flags;unsigned long sp;unsigned long ss;
};

此外剩余的空间才是用作函数栈,栈是向下生长的,所以进程的内核栈就变成下面这个样子640?wx_fmt=png

接下来看 thread_info 的定义,如下

struct thread_info {struct task_struct  *task;    /* main task structure */__u32      flags;    /* low level flags */__u32      status;    /* thread synchronous flags */__u32      cpu;    /* current CPU */mm_segment_t    addr_limit;unsigned int    sig_on_uaccess_error:1;unsigned int    uaccess_err:1;  /* uaccess failed */
};

thread_info 中有一个变量 task_struct,指向拥有这个内核栈的进程,如下所示640?wx_fmt=png

current 宏

Linux 内核中可以通过 current 宏来获取当前正在运行的进程,它的实现十分巧妙,下面我们一起来看一看

#define get_current() (current_thread_info()->task)
#define current get_current()

current 通过 get_current(),进而调用 current_thread_info()->task 我们看一看 current_thread_info 的定义

static inline struct thread_info *current_thread_info(void)
{return (struct thread_info *)(current_stack_pointer & ~(THREAD_SIZE - 1));
}

current_stack_pointer 表示当前栈顶寄存器的值,对于 X86 就是 esp,在内核态的时候,current_stack_pointer 表示内核栈中的某一个位置

THREAD_SIZE 我们上面说过是 8K,THREAD_SIZE - 1 就是 8K 剩下的所有位,如下640?wx_fmt=png

那么这个操作是什么意思呢?

(current_stack_pointer & ~(THREAD_SIZE - 1)

意思就是将 current_stack_pointer 的低 12 位清空

我们从这个 current_thread_info 函数可以看出,通过这个操作就可以获得 thread_info 对象,这是为什么呢?

这是因为,内核栈在申请的时候,总是 8K 对齐的,也就是说地址的低 12 位肯定为 0

当进程在内核态运行的时候,栈顶指针总是指向这块申请的内核栈中的某一个区域,内核栈的大小最大也就 8K,所以将当前栈顶指针的低 12 位置零就可以得到内核栈的基址

而 thread_info 存在于内核栈的栈底处,所以也就获取到了该进程对应的 thread_info 结构640?wx_fmt=png

thread_info 结构中有一个 task_struct* 成员,指向该进程的 task_struct,所以也就可以获得该进程的 task_struct 结构

不禁感叹,Linux 内核的实现真是巧妙啊

好了,关于 Linux 进程的数据结构就介绍到这里了,后面的文章将讲解 Linux 的进程调度

640?wx_fmt=png

640?wx_fmt=jpeg
扫码或长按关注
回复「加群 」进入技术群聊

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/468258.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何安装python3.8_python3.8下载及安装步骤详解

1.操作系统&#xff1a;Windows7 64bit executable installer 2.安装步骤&#xff1a; 双击安装文件python-3.8.0-amd64.exe 勾选下方“Add Python 3.8 to PATH”&#xff0c;并选择“Customize installation”3.把Optional Features全部勾选上&#xff0c;点击“Next"4.A…

队列,C语言实现

什么是队列&#xff1f;上一篇文章写了什么是栈&#xff0c;用C语言实现了栈&#xff0c;既然说了栈&#xff0c;不说队列&#xff0c;感觉总是少了点什么&#xff0c;所以就顺手写一个队列&#xff0c;而且最近做项目也用到这个队列的代码。栈的特点是先进后出&#xff0c;队列…

华为hr,我尽力了

最近&#xff0c;一则新闻很火但是突然&#xff0c;又不火了&#xff0c;火于不火之间&#xff0c;时间有点短&#xff0c;其中猜测很大部分是gongguan原因以下为正文截图~以上为事件原文&#xff0c;这个是一个热点新闻&#xff0c;之前发了一个热点新闻&#xff0c;瞬间就火爆…

idea,eclipse创建多模块项目

新建一个maven项目 iead,新建是不选择archetype,新建好之后,pom中的 <packaging>pom</packaging>节点是默认的,如果不是要改成这这样子 然后选中这个项目,新建一个module,之后就和建立普通项目一样了. eclipse 是这样的 建一个普通的maven项目就可以了 这个是建好之…

python画图程序代码_少儿python编程(7)海龟画图(拓展1)

我们继续用Python的海龟库来画图吧&#xff01;上图是画一朵花的程序&#xff0c;重点是6-12行&#xff0c;使用了函数来定义drawleaf:每一掰叶子由两条弧线组成&#xff0c;每一条弧线重复画15次&#xff0c;每次前进5步&#xff0c;右转6度。看图形化代码就很清楚了&#xff…

opencv matlab三维点云,点云采样的三种方法 - 小白学视觉的个人空间 - OSCHINA - 中文开源技术交流社区...

点击上方“小白学视觉”&#xff0c;选择“星标”公众号重磅干货&#xff0c;第一时间送达编辑&#xff1a;3D视觉工坊本文由知乎作者GeometryHub授权转载&#xff0c;不得擅自二次转载。原文链接&#xff1a;https://zhuanlan.zhihu.com/p/86044055点云采样分类点云采样的方法…

C语言数组越界导致无限循环

大奖也是你们的&#xff0c;感谢支持&#xff0c;不喜欢的请轻拍。过年抽奖我还是很期待的&#xff0c;每年公司的年会抽奖的时候&#xff0c;我总是能小中一把&#xff0c;有一年我还中了个一等奖&#xff0c;不知道大家对一等奖什么概念&#xff0c;中一等奖的概率非常低&…

七月流水账

7.1-7.3 >< 7.4 看了一会儿李老大给的Caffe的教程 重配python qt vtk&#xff0c;大概是原来装了一个xxx&#xff08;忘记名字&#xff09;&#xff0c;然后位数有些不对&#xff0c;搞了好一阵 果然还是卸载重装好>< 然后又看了会儿Matlab神经网络工具箱怎么用 …

python绘制拟合回归散点图_机器学习之利用Python进行简单线性回归分析

前言&#xff1a;在利用机器学习方法进行数据分析时经常要了解变量的相关性&#xff0c;有时还需要对变量进行回归分析。本文首先对人工智能/机器学习/深度学习、相关分析/因果分析/回归分析等易混淆的概念进行区分&#xff0c;最后结合案例介绍如何利用Python进行简单线性回归…

十年经验教你如何学习嵌入式系统

一、如何学习嵌入式系统- - 嵌入式系统的概念着重理解“嵌入”的概念 &#xff0c;主要从三个方面上来理解。1、从硬件上&#xff0c;“嵌入”将基于CPU的处围器件&#xff0c;整合到CPU芯片内部&#xff0c;比如早期基于X86体系结构下的计算机&#xff0c;CPU只是有运算器和累…

php博客文章修改,wordpress博客如何快速修改文章阅读数

WordPress修改文章阅读次数可以通过插件来完成&#xff0c;下面我们以WP-PostViews插件演示如何修改文章阅读次数。WP-PostViews是一个非常常用的wordpress插件&#xff0c;主要用于文章阅读数访问量的统计&#xff0c;插件使用简单&#xff0c;直接安装启用即可&#xff0c;并…

ionic拍照,从相册选择功能

在介绍插件的使用之前&#xff0c;我们有必要先大致了解下插件的原理 一、插件工作原理分析 一个插件是如何正确地在IONIC框架下运行的呢? 前台的HTML/js代码又是如何与后面具体平台做数据交互的呢? 这个就需要对插件的工作原理及各个模块之间的流程关系有一个基本的了解。 为…

python爬虫爬图片教程_python爬虫实战之爬取京东商城实例教程

前言 本文主要介绍的是利用python爬取京东商城的方法&#xff0c;文中介绍的非常详细&#xff0c;下面话不多说了&#xff0c;来看看详细的介绍吧。 主要工具 scrapy BeautifulSoup requests 分析步骤 1、打开京东首页&#xff0c;输入裤子将会看到页面跳转到了这里&#xff0c…

安卓camera总体框架

今天是周五了&#xff0c;时间过得真快&#xff0c;通常这个时候&#xff0c;我都还沉醉了上班的状态中&#xff0c;说到上班&#xff0c;我是认真的&#xff0c;我非常喜欢上班&#xff0c;特别是今天&#xff0c;我会听到一声优美的声音&#xff0c;我的银行卡会多出一些钱&a…

matlab可以连接阻抗分析仪么,阻抗分析仪的工作原理与测试方法

阻抗分析仪和LCR表是非常通用的测量器件的电子仪器。根据阻抗范围和频率范围的不同&#xff0c;有一系列不同原理的仪器来满足测试要求&#xff0c;图1是不同阻抗范围和不同频率范围的阻抗测量方法。图1 阻抗测量方法图2是自动平衡电桥法的原理框图。通过精确测量加载到被测件D…

分享几个有意思的视频

最近微信加我的好友有点多&#xff0c;然后问问题的也不少&#xff0c;有的问题我不知道&#xff0c;有的问题我觉得百度一下应该很多答案&#xff0c;有的问题我觉得我可以知道&#xff0c;但是需要自己去寻找答案&#xff0c;也需要花费我一些时间&#xff0c;我有时候非常不…

php+redis+设置前缀,spring使用Redis自定义前缀后缀名(去掉SimpleKey []+自定义)

标签&#xff1a;spring中自动加上 SimpleKey [] 解决方案一、自定义后缀名1、定义类实现KeyGenerator接口MyKeyGeneratorpackage com.wbg.springRedis.service.impl;import org.springframework.cache.interceptor.KeyGenerator;import org.springframework.stereotype.Compon…

北京的林书豪,像一把小李飞刀

从《我要打篮球》节目开始&#xff0c;就一直在关注书豪的比赛&#xff0c;我喜欢书豪&#xff0c;书豪选择来CBA打球&#xff0c;也满足了很多球迷的愿望&#xff0c;近距离感受球星的那种状态&#xff0c;今天晚上&#xff0c;北京跟青岛的比赛&#xff0c;我是一分钟没有拉下…

I2C和SPI注定要打一架

最近微信群里好几天聊到I2C&#xff0c;既然说到了I2C&#xff0c;那就有必要说SPI&#xff0c;那就一起说了算了&#xff0c;大家有更好见解的&#xff0c;留言回复下&#xff0c;让更多的人知道你的观点。整理与网络&#xff0c;侵删I2C和SPI应该要打一架&#xff1f;现今&am…

第1章 ssh命令和SSH服务详解

基础服务类系列文章&#xff1a;http://www.cnblogs.com/f-ck-need-u/p/7048359.html 本文对SSH连接验证机制进行了非常详细的分析&#xff0c;还详细介绍了ssh客户端工具的各种功能&#xff0c;相信能让各位对ssh有个全方位较透彻的了解&#xff0c;而不是仅仅只会用它来连接远…