进程管理常见系统调用

2023-05-16

进程管理常见系统调用

  • 1.前言
  • 2.如何提供有许多CPU的假象?
    • 2.1 操作系统是管理多个进程执行的软件
  • 3.操作系统提供哪些系统调用API?
  • 3.1创建(fork)
  • 3.2 直面fork
    • 3.3 execve系统调用
    • 3.4 exit系统调用
  • 4.处理器调度
    • 4.1中断以后选择哪个进程在处理器上执行?
    • 4.2Linux系统中进程的组织方式
    • 4.3实际Linux内核的调度算法是如何取舍的?
      • 4.3.1 就绪队列
      • 4.3.2 调度算法介绍

1.前言

进程的定义非常简单:进程就是运行中的程序。程序本身没有生命周期,它只是存在磁盘上面的一些指令或者静态数据。这些字节需要操作系统的帮助运行起来,发挥其应该有的作用。

我们在使用计算机的时候,会喜欢一边听着音乐,一边玩着游戏,这样是没问题的,我们的操作系统可能有上百个进程在同时运行。但是我们的硬件资源是有限的,假设一个计算机只有一个CPU(实际上也不会太多),成百个进程都在运行着,那CPU应该给谁用呢?操作系统就创造了一个美丽的假象-虚拟化CPU,将一个物理CPU虚拟成多个虚拟CPU分给每一个进程使用,因此每一个进程都以为自己在独占CPU。

2.如何提供有许多CPU的假象?

通过让一个进程只运行一个时间片,然后切换到其他进程,操作系统提供了存在多个虚拟CPU的假象。这叫做时分共享CPU技术。那么操作系统如何完成这一切功能。

2.1 操作系统是管理多个进程执行的软件

简单来看,操作系统就是一组系统调用API。进程执行系统调用,会使用指令(syscall)回到操作系统的代码执行,当然这就属于进程主动的触发中断;除此之外,还有硬件中断(时钟,I/O设备),对于一个进程来说,是被动发生的。
假设以两个程序qq和微信为例,在最开始的时候肯定执行的操作系统程序。将物理内存分别分一部分给微信、qq和内核,我们上面说了进程是运行的程序,其是动态变化的,也就是包含其占有的内存和寄存器,里面存储了当前进程的状态。操作系统代码执行一段时间以后,加载微信程序,也就是恢复它的内存和寄存器状态,让其在CPU上运行,这一过程通过iret中断指令完成。接下来执行权就到了微信进程,也叫做用户态。当然当前进程不能无限执行下去,通过一次中断或者系统调用,会再次执行os代码,os会负责保存微信进行的执行现场也就是内存和寄存器信息。最后再次加载qq程序,一直这样切换执行下去。操作系统始终在内核态运行,而应用程序始终在用户态运行。正是通过这些切换,操作系统实现了对CPU的虚拟化,当某个应用程序运行的时候它感觉到的就是现在它独占了CPU资源。
在这里插入图片描述

3.操作系统提供哪些系统调用API?

上面提到了一个进程可以主动放弃自己的执行权,也是就通过系统调用,将执行权交还给操作系统。那么操作系统内核都提供了哪些系统调用来管理进程?主要有创建(fork)、替换执行(execve)、删除(exit)。

3.1创建(fork)

为什么需要这么一个系统调用呢,因为我们的系统在最开始启动后并不会有太多的进程,需要有一个系统调用能够在接下来的使用中创建更多的进程。例如我们的shell,可以在shell中使用命令创建一个进程。
fork就像一把叉子,主要完成

  • 做一份进程完整的复制(内存、寄存器运行现场)
  • 父进程返回子进程的pid,子进程返回0

在这里插入图片描述
那么创建一个进程为什么要叫做叉子呢?还是以我们刚才的qq进程为例,如果它调用了fork系统调用就会在当前系统中多出一个进程,而这个进程是之前qq进程的拷贝,它包括几乎所有的进程当前的状态(除了返回值pid),包括内存、寄存器。原来的进程也叫父进程和新创建的进程也叫子进程都可以继续向下执行,所以说它像一个叉子,走着走着就分了个叉。父进程返回值是子进程的pid,子进程的返回值是0。
在这里插入图片描述

3.2 直面fork

下面通过两个小实验,来看一下fork究竟是如何工作的。
实验一

  1 #include<unistd.h>                                                                                                             
  2 #include<stdio.h>
  3 
  4 int main(){
  5     pid_t pid1 = fork();
  6     pid_t pid2 = fork();
  7     pid_t pid3 = fork();
  8     printf("Hello world : (%d,%d,%d)\n",pid1,pid2,pid3);
  9     return 0;
 10
 11 
 12 }

根据父进程返回子进程的pid,子进程返回0,我们可以对上面的程序进行画图分析,括号中的内容用于记录三个变量的值和接下来要执行的PC指令,~代表目前未知的变量值,(pid1,pid2,pid3,PC)。程序执行完,一共会有八个进程对printf语句进行输出。红色标出的便是最终的结果。

在这里插入图片描述

下面运行程序,查看实际的运行结果,结果和上面画图分析的结果是一致的,共有八种不同的结果输出。做完该实验就能理解子进程是父进程的一个拷贝,其中黑色标注出的0和FORK2_PID都会一直传递下去,并且fork会返回不同的函数值给父子进程。

在这里插入图片描述

实验2

  1 #include<unistd.h>                                                                                                                      
  2 #include<stdio.h>
  3 #define n 2
  4 int main(){
  5 for(int i = 0; i < n; i++)
  6 {
  7     fork();
  8     printf("Hello world\n");
  9 }
 10 return 0;
 11 }

程序的执行流程如下:
在这里插入图片描述
可以看到程序会打印出六个Hello world,我们执行程序结果也是一样的。
在这里插入图片描述
似乎毫无疑问,现在将输出结果重定向到一个文件中。
在这里插入图片描述
重定向输出结果到一个txt文件后,发现比刚才控制台多了两个输出。这是为什么呢?
原因就在于printf函数,printf函数在标准输出为控制台的时候会直接输出,但是当标准输出是一个文件的时候,printf会将结果暂存在缓冲区中,在第二次fork的时候,毫无疑问,缓冲区中的hello world 也被拷贝了一份,如下图所示,因此最终文件中就包含了8个Hello world。
在这里插入图片描述

3.3 execve系统调用

除了能够对进程进行创建,还需要能够执行别的程序,execve系统调用就是这样一个功能,将当前运行的进程“替换”成另一个程序,从头开始执行。fork以后得到的进程是与父进程相同的,但是跟多的时候我们创建子进程是为了让他执行不同的程序,去做别的工作。execve就是启动一个新程序的系统调用。exec函数一共有六个,其中execve为内核级系统调用,其他(execl,execle,execlp,execv,execvp)都是调用execve的库函数。

execve(filename,argv,enpv)
//执行名称为filename的程序
//分别传入参数v,环境变量e

一个进程调用execve以后,它没有创建新的进程,而是从传入的可执行程序中加载代码和静态数据,并用它覆写自己的代码段、堆、栈,以及其他内存空间也会被重新初始化。然后操作系统就执行该程序。所以对execve的成功调用永远不会返回,只有在发生错误的时候才会返回-1,从原程序的调用点接着往下执行。

arch/i386/kernel/process.c
/*
 * sys_execve() executes a new program.
 */
asmlinkage int sys_execve(struct pt_regs regs)
{
	int error;
	char * filename;
	
	filename = getname((char *) regs.ebx);
	error = PTR_ERR(filename);
	if (IS_ERR(filename))
		goto out;
	error = do_execve(filename, (char **) regs.ecx, (char **) regs.edx, &regs);
	if (error == 0)
		current->ptrace &= ~PT_DTRACE;
	putname(filename);
out:
	return error;
}

regs.ebx存储的是需要调用的程序名称,也就是第一个参数,使用getname 将该字符串从用户空间拷贝到系统空间。接下来就调用do_execve完成其主体功能。

fs/exec.c
/*
* sys_execve() executes a new program.
*/
int do_execve(char * filename, char ** argv, char ** envp, struct pt_regs * regs)
{
   struct linux_binprm bprm;
   struct file *file;
   int retval;
   int i;

   file = open_exec(filename);

   ......
   bprm.file = file;
   bprm.filename = filename;
   bprm.sh_bang = 0;
   bprm.loader = 0;
   bprm.exec = 0;
   if ((bprm.argc = count(argv, bprm.p / sizeof(void *))) < 0) {
   	allow_write_access(file);
   	fput(file);
   	return bprm.argc;
   }

   if ((bprm.envc = count(envp, bprm.p / sizeof(void *))) < 0) {
   	allow_write_access(file);
   	fput(file);
   	return bprm.envc;
   }

   retval = prepare_binprm(&bprm);
   if (retval < 0) 
   	goto out; 

   retval = copy_strings_kernel(1, &bprm.filename, &bprm);
   if (retval < 0) 
   	goto out; 

   bprm.exec = bprm.p;
   retval = copy_strings(bprm.envc, envp, &bprm);
   if (retval < 0) 
   	goto out; 

   retval = copy_strings(bprm.argc, argv, &bprm);
   if (retval < 0) 
   	goto out; 

   retval = search_binary_handler(&bprm,regs);
   if (retval >= 0)
   	/* execve success */
   	return retval;

   ......
}

使用 open_exec找到并打开可执行程序的文件,接下来就需要装载可执行程序了,内核中为可执行程序的装入提供了一个数据结构linux_binprm,以便于存储和组织可执行程序中的信息。

source/include/linux/binfmts.h#L22
/*
* This structure is used to hold the arguments that are used when loading binaries.
*/
struct linux_binprm{
   char buf[BINPRM_BUF_SIZE];
   struct page *page[MAX_ARG_PAGES];
   unsigned long p; /* current top of mem */
   int sh_bang;
   struct file * file;
   int e_uid, e_gid;
   kernel_cap_t cap_inheritable, cap_permitted, cap_effective;
   int argc, envc;
   char * filename;	/* Name of binary */
   unsigned long loader, exec;
};

函数open_exec()返回一个file结构指针,代表着读入可执行文件的上下文,所以将其保存在数据结构 bprm中。
最后的准备工作就是把执行的参数,也就是 argv[],以及运行的环境,也就是envp[],从用户空间拷贝到数据结构 bprm 中。其中的第1个参数 argv[0]就是可执行文件的路径名,已经在 bprm.filename中了,所以用copy_strings_kernel()从系统空间中拷贝,其它的就要用copy_strings()从用户空间拷贝。最后就到了关键的函数 search_binary_handler(),该函数就负责具体的装入和执行。

3.4 exit系统调用

上面介绍了可以使用fork创建一个进程,然后使用execve将该进程替换为任意一个程序,显而易见还缺少一个销毁进程的系统调用,才能完整的运转整个流程。
常用的C库函数void exit(status)

  • 可以传入一个状态码status,0表示正常退出,其他表示非正常退出,一般都用-1或者1,标准C里有EXIT_SUCCESS和EXIT_FAILURE两个宏。
  • 父进程可以使用wait系统调用接受子进程的返回值,从而针对不同的情况进行处理。

除了上面的exit函数,exit还有其他几种形态,_exit(0 ),SYS_exit。
通过一个程序看一下他们的区别:

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <time.h>
#include <sys/syscall.h>
void func() {
	printf( "Goodbye,world!\n");
}
int main(int argc, char *argv[]){
	//当程序正常终止时,调用指定的函数 func。您可以在任何地方注册你的终止函数,但它会在程序终止的时候被调用。
	atexit(func);
	//通过控制台传入不同的参数,调用不同的exit
	if (argc < 2 ) return EXIT_FAILURE;
	if (strcmp( argv[1],"exit")==0)
		exit(0);
	if (strcmp( argv[1], "_exit") ==0)
		_exit(0) ;
	if (strcmp( argv[1], "__exit") ==0)
		syscall(SYS_exit,0);
}


结果如下
在这里插入图片描述
不传入参数调用exit(0),会执行func输出字符串,其余两个都不会执行atexit注册的func,没有任何输出。因为前者是libc的库函数,后两者是系统调用,看不到libc的atexit函数。
使用strace分别跟踪后两个程序,发现_exit最终调用了exit_group(0),会终止进程中的所有线程。
在这里插入图片描述
SYS_exit最终调用了exit(0),只会终止当前的线程。

在这里插入图片描述

exit的几种形态

  • exit(0) - stdlib.h中声明的 libc函数,会调用atexit,上面介绍的函数。
  • _exit(0) - glibc的syscall wrapper
    • 执行“exit_group”系统调用终止整个进程(所有线程)
    • 不会调用atexit
  • syscall(SYS_exit,0)
    • 执行“exit”系统调用终止当前线程,不会调用atexit

4.处理器调度

通过前面已经了解到了,操作系统通过分时,实现了CPU的虚拟化。在中断发生以后,返回之前,操作系统一定要选择一个进程进行执行,那么这里就有了一个问题。

4.1中断以后选择哪个进程在处理器上执行?

选择哪个进程在处理器上运行,就是调度的概念,调度的实质就是资源的分配。操作系统考虑了不同的算法来实现资源的分配,而且一个好的调度算法应该考虑以下几个方面:

  • 公平:保证每个进程得到合理的CPU时间
  • 高效:使CPU保持忙碌状态
  • 响应时间:使交互响应时间尽可能短
  • 周转时间:等待输出的时间尽可能短
  • 吞吐量:单位时间内处理的进程数量尽可能多
    显然不可能有算法同时达到这五个目标,不同的操作系统会有不同的取舍。

常见的调度算法有:

  • 时间片轮转调度算法
  • 优先权调度算法
  • 多级反馈队列调度算法
  • 实时调度
  • 完全公平调度算法

4.2Linux系统中进程的组织方式

一个系统中通常有成百上千的进程,相应的就有这么多的PCB,为了有效的进行管理,就必须用适当的方式将他们组织起来。
组织方式主要包括:

  • 进程链表
  • 哈希表
  • 就绪队列
  • 等待队列

4.3实际Linux内核的调度算法是如何取舍的?

Linux 2.4中使用goodness()函数,给每个处于可运行状态的进程赋予一个权值(Weight),使用这个权值衡量一个处于可运行状态的进程值得运行的程度。调度程序以这个权值作为选择进程的唯一依据。

虽然Linux2.4进程调度程序简单有效,但是也有其缺点。

  • 单个就绪队列问题,时间片耗尽的进程依然会放进同一个就绪队列中,在不可被调度的情况下,还会参与调度。
  • 多处理器问题,多个处理器上的进程放在同一个就绪队列中,因而调度器对它的所有操作都会因全局自旋锁而导致系统各个处理机之间的等待,使得就绪队列成为一个明显的瓶颈。
  • 内核不可抢占问题,如果某个进程,一旦进了内核态那么再高优先级的进程都无法剥夺,只有等进程返回内核态的时候才可以进行调度。缺乏对实时进程的支持。

针对以上问题,Linux 2.6做了较大的改进。针对多处理器问题,为每个CPU设置一个就绪队列。针对单就绪队列问题,设置两个队列组,即active队列组和expired队列组。借助以上队列实现了时间复杂度为O(1)的调度算法。直到Linxu 2.6.23内核版本中,O(1)调度算法才真正替代为CFS(完全公平)调度算法。

4.3.1 就绪队列

O(1)调度器是以进程的动态优先级prio为调度依据的,它总是选择目前就绪队列中优先
级最高的进程作为候选进程 next。由于实时进程的优先级总是比普通进程的优先级高,故能
保证实时进程总是比普通进程先被调度。

Linux2.6 中,优先级 prio 的计算不再集中在调度器选择 next 进程时,而是分散在进程
状态改变的任何时候,这些时机有:

  • 进程被创建时;
  • 休眠进程被唤醒时;
  • 从TASK_INTERRUPTIBLE 状态中被唤醒的进程被调度时;
  • 因时间片耗尽或时间片过长而分段被剥夺 CPU 时;

在这些情况下,内核都会调用 effective_prio()重新计算进程的动态优先级 prio并根据计算结果调整它在就绪队列中的位置。

Linux 2.6为每个cpu定义了一个struck runqueue数据结构,每个就绪队列都有一个自旋锁,从而解决了 2.4 中因只有一个就绪队列而造成的瓶颈。

 struct runqueue {
     ...
     prio_array_t *active, *expired, array[2];
     ...
     }

active 是指向活动进程队列的指针;expired 是指向过期进程队列的指针;array[2]是实际的优先级进程队列,其中一个是活跃的一个是过期的,过期数组存放时间片耗完的进程。

每个处理器的就绪队列都有两个优先级数组,它们是 prio_array 类型的结构体。Linux2.6
内核正是因为使用了优先级数组,才实现了 O(1)调度算法。该结构定义在 kernel/sched.c 中:

     struct prio_array{
        ...
        unsigned int nr_active;
        //相应 runqueue 中的进程数
        
        unsigned long bitmap[BITMAP_SIZE];
        /*索引位图,BITMAP_SIZE 默认值为 5,5个long(32位)类型,每位代表一个优先级,可以代表160个优先级,但实际中只有140。与下面的queue[]对应。分布0-99对应为实时进程,100-140对应为普通的进程*/
        
        struct list_head queue[MAX_PRIO];
        /*每个优先级的进程队列,MAX_PRIO 是系统允许的最大优先级数,默认值为 140,数值越小优先级越高
        bitmap每一位都与 queue[i]相对应,当 queue[i]的进程队列不为空时,bitmap 相应位为 1,否则就为 0。*/
     }

4.3.2 调度算法介绍

选择并运行候选进程,确定next,下一个应该占有 CPU 并运行的进程,schedule()函数是完成进程调度的主要函数,并完成进程切换的工作。schedule()用于确定最高优先级进程的代码非常快捷高效,其性能的好坏对系统性能有着直接影响,它在/kernel/sched.c 中的定义如下:

     ...
     int idx;
     ...
     preempt_disable();
     ...
     idx = sched_find_first_bit( array -> bitmap);
     queue = array -> queue + idx;
     next = list_entry( queue -> next, task_t, run_list);
     ...
     prev = context_switch( rq, prev, next);
     ...
     }

其中,sched_find_first_bit()能快速定位优先级最高的非空就绪进程链表,运行时间和就绪队列中的进程数无关,是实现O(1)调度算法的一个关键所在。
schedule()的执行流程:
首先,调用 pre_empt_disable(),关闭内核抢占,因为此时要对内核的一些重要数据结构进行操作,所以必须将内核抢占关闭;其次,调用sched_find_first_bit()找到位图中的第1个置1的位,该位正好对应于就绪队列中的最高优先级进程链表;
再者,调用 context_switch()执行进程切换,选择在最高优先级链表中的第 1个进程投入运行;

在这里插入图片描述

图中的网格为 140 位优先级数组,queue[7]为优先级为 7 的就绪进程链表。此种算法保证了调度器运行的时间上限,加速了候选进程的定位过程。

Linux2.4 调度系统在所有就绪进程的时间片都耗完以后在调度器中一次性重新计算,其中重算是用for循环相当耗时。

Linux2.6 为每个 CPU 保留 active 和 expired 两个优先级数组, active 数组中包含了有剩余时间片的任务,expired 数组中包含了所有用完时间片的任务。

当一个任务的时间片用完了就会重新计算其时间片,并插入到 expired 队列中,当 active 队列中所有进程用完时间片时,只需交换指向 active 和 expired 队列的指针即可。此交换是实现 O(1)算法的核心,由 schedule()中以下程序来实现:

    array = rq ->active;
    if (unlikely(!array->nr_active)) {
             rq -> active = rq -> expired;
             rq -> expired = array;
             array = rq ->active;
    ...
    }

更多精彩内容见 https://szp2016.github.io/

更多精彩内容关注微信公众号"知书码迹"!
在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

进程管理常见系统调用 的相关文章

  • Gradle-Could not determine java version from ‘11‘

    问题描述 因为换工作 xff0c 需要新配置的环境 xff0c 所以遇到了各种问题 xff0c 例如下面这个 xff1a 发生错误的相关环境配置 JDK 11Gradle 4 4 如果不知道自己安装的 Java 版本 xff0c 可以根据一
  • 重装双系统中的 Ubuntu

    我的电脑是 Windows 43 Ubuntu 双系统 xff08 UEFI启动 xff09 xff0c 最近 Ubuntu 被我玩坏了 xff0c 正好 15 04 alpha 2 发布了嘛 xff0c 我就想干脆重装 Ubuntu 算了
  • Markdown:插入图片和图注

    文章目录 在Markdown中插入图片 xff0c 调节位置 xff0c 大小 xff0c 以及插入图注 并排插入图片并排插入图片但无缝隙简单的插入可调整大小图片使用图床 xff08 腾讯云cos 43 picgo xff09 在markd
  • Vmware Esxi在线管理虚拟机

    目录 特征 占地面积小 可靠的性能 增强的安全性 生态系统卓越 用户友好的体验 管理截图 登录系统 主机虚拟机 虚拟机操作 What is ESXI Bare Metal Hypervisor ESX VMware 特征 通过将多台服务器整
  • 孤立森林(Isolation Forest)

    最近正好在做孤立森林的项目 xff0c 写下这篇推文记录下项目进程 xff0c 也给需要做这个项目的同学一点参考 1 什么叫异常点 图中红色的点偏离大部分的数据 xff0c 可以发现这些点分布稀疏且离密度高的群体较远 xff0c 这样的点称
  • 修改docker容器端口映射

    大家都知道docker run可以指定端口映射 xff0c 但是容器一旦生成 xff0c 就没有一个命令可以直接修改 通常间接的办法是 xff0c 保存镜像 xff0c 再创建一个新的容器 xff0c 在创建时指定新的端口映射 有没有办法不
  • MaxWelll常用命令

    文章目录 1 启动maxwell xff1a 1 1 控制台为输出 xff1a 1 2 kafka为输出 xff1a 1 3 配置指定某些表输出 xff1a 1 4 配置输出到kafka 分区规则为表的主键 xff1a 1 5 配置文件启动
  • Linux系统--进程与线程的区别

    看了一遍排在前面的答案 xff0c 类似 进程是资源分配的最小单位 xff0c 线程是CPU调度的最小单位 这样的回答感觉太抽象 xff0c 都不太容易让人理解 做个简单的比喻 xff1a 进程 61 火车 xff0c 线程 61 车厢 线
  • 关于锁的理解

    本文打算写一些和锁有关的东西 xff0c 谈一谈我对锁的原理和实现的理解 xff0c 主要包含以下方面 信号量互斥量条件变量 同步与互斥 其实同步与互斥都是计算机科学里面概念性的东西 xff0c 它们和什么编程语言 操作系统其实都没什么关系
  • Android 适配ipv6

    Android8 0开始支持ipv6 okhttp支持ipv6 glide支持ipv6方法 ijkplayer
  • java自我学习第一阶段的收获和感受

    1 首先对于编程的学习第一步都是基本语法的学习 xff0c 语法学习的特点有 xff1a 所有编程语言的语法定义都大体相似 xff0c 大概的是编程发展的延续 xff0c 没有人语言设计人员会标新立异 xff0c 只有站在别人的肩上不断完善
  • docker常用命令及配置

    最近使用了docker xff0c 像是发现了新大陆 真是简洁好用的东西 xff0c 为啥原来没发现 docker的安装不再赘述 xff0c 在此记录一下常用的命令 xff0c 以便于将来查询 查看docker容器版本 docker ver
  • 数字图像处理读后感

    数字图像处理读后感 前言 初步写完比赛代码后 看了机器学习看了数字图像处理还有其他的一些乱七八糟的东西 然后又觉得如果只是看完没有总结回顾那就是很失败的一件事情 所以就想写一写自己的读后感 我自己看得是B站的视频 这是西安电子科技大学的任老
  • 第七天 树莓派+计算棒配置过程

    一 xff1a 安装过程参考 xff1a https blog csdn net qqqzmy article details 85213414 1 材料硬件 树莓派3B 43 intel movidius 神经元计算棒2代显示器 鼠标键盘
  • Docker镜像推送(push)到Docker Hub

    镜像构建成功后 xff0c 只要有docker环境就可以使用 xff0c 但必须将镜像推送到Docker Hub上去 我们之前创建的镜像不符合Docker Hub的tag要求 xff0c 因为 在Docker Hub注册的用户名是boony
  • Altera FPGA 远程更新

    文章目录 1 概述2 如何生成factory jic xff1f 3 如何使用ASMI IP4 如何使用Remote updata IP xff1f 5 UART命令 以下内容均参考自官方手册 手册下载地址 xff1a ug altremo
  • 将ros的深度图像 转换成 opencv的图像 并求距离

    将ros的图像 转换成 opencv的图像并求距离 参考的博客概念将ROS图像消息转换为OpenCV图像示例计算距离 参考的博客 ROS学习笔记47 xff08 ROS图像和OpenCV图像之间的转换 xff08 C 43 43 xff09
  • ROS入门总结(二):Python定义发布器Publisher与订阅器Subscriber

    节点ROS通信系统中就是一个可执行的程序 xff0c 这边博客主要记录如何在Python代码节点中定义发布器和订阅器 发布器 xff1a import rospy from std msgs msg import String 载入Stri
  • 计算机视觉中的传统特征提取方法总结(转载)

    前言 本文对计算机视觉传统方法中的一些特征提取方法进行了总结 xff0c 主要包括有 xff1a SIFT 尺度不变特征变换 HOG 方向梯度直方图 SURF ORB LBP HAAR 目录 1 SIFT xff08 尺度不变特征变换 xf

随机推荐

  • docker源码分析(1)(持续更新)

    docker源码分析 xff08 1 xff09 xff08 持续更新 xff09 daemondocker DaemonClient的启动内部函数分析newAPIServerConfigloadListenersinitContainer
  • JS实现图片切割

    lt DOCTYPE html gt lt html lang 61 34 en 34 gt lt head gt lt meta charset 61 34 UTF 8 34 gt lt title gt Title lt title g
  • GPS、RTK、PPK三种定位技术的原理及应用

    一 GPS技术 1 原理 之前做过集成GPS功能的产品 xff0c 对这种不以定位为主要功能的产品 xff0c 精度是没有要求的 xff0c 例如我只是用它来得到当前社区的位置 xff0c 一般的GPS模块都能满足要求 理论上 xff0c
  • 【STM32】RS485 Modbus协议 采集传感器数据

    源码链接 xff1a Modbus传感器采集协议 一 硬件 1 传感器 xff1a 为液压传感器 xff0c 12vDC xff0c RS485数据输出 xff0c 采用Modbus协议通信 2 电路 xff1a 根据传感器属性 xff0c
  • 【FPGA】QuartusII13.0全编译Error (119013): Current license file does not support the EP4CE10F17C8 device

    Quartuls II全编译时 xff0c 出现如下错误 网上很多人说找到32位的exe可以编译成功 xff0c 但是凭啥64位的就不行了 xff1f 而且我32位的也不行 这种情况就是没破解好 xff0c 但Licence Setup 显
  • 【FPGA】QuartusII13.0实现组合逻辑 3/8 译码器

    3 8译码器一般用于资源扩展 xff0c 如驱动led阵列时 xff0c 即使是FPGA xff0c IO资源也明显不足 xff0c 借此可以扩展出更多的IO口 xff0c 通过实现 3 8译码器 xff0c 记录一下FPGA开发的基本步骤
  • SIFT3D(3D尺度不变特征变换)算法

    转载自医学图像处理案例 xff08 十 xff09 SIFT3D xff08 3D尺度不变特征变换 xff09 算法 腾讯云开发者社区 腾讯云 一 SIFT3D算子 上述SIFT算子用于二维图像关键点检测 xff0c 医学影像一般都是三维图
  • 【Qt】opencv源码&官方编译好的opencv在windows下使用的区别

    之前用过很多次OpenCV xff0c 但是感觉每次情况都不一样 xff0c 看了之前的文章 QT 搭载OpenCV实现简单的图像处理 xff0c 用的是官方编译好的OpenCV 43 msvc2017 43 Qt Creator xff0
  • 【Pixhawk】Ubuntu下编译原生固件PX4

    环境 xff1a 编译主机 xff1a Ubuntu 20 0 4 xff08 官方推荐18 0 4 xff09 源码版本 xff1a PX4 1 12 0 xff08 目前最新 xff0c 截至2021 7 22 xff09 建议 尽量以
  • 【Pixhawk】PX4源码控制逻辑详解(以UGV小车为例)

    PX4启动流程 主体控制逻辑 commander xff1a 飞行模式控制stickmapper xff1a 摇杆映射position estimator xff1a 从GPS获得的位置估计navigator xff1a 读取航点 xff0
  • 【Pixhawk】PX4添加麦克纳姆轮车架

    PX4中提供了很多机架 xff0c 但要开发PX4不支持的机架类型 xff0c 需要我们自定义 比如麦克纳姆轮 1 在ROMFS px4fmu common init d airframes 下添加 hil机架文件 参考其他 命名为7000
  • 【BMS】锂电池充放电倍率C

    1 1C表示电池一小时完全充放电时电流强度 那这个电流强度是多少呢 xff1f 不同电芯肯定不一样 xff0c 要参考它的电池容量 xff08 Ah或者mAh xff09 下面是一节18650电池的容量参数 xff1a 它的标称容量是255
  • 【BMS】电池包低温加热技术

    1 目的 xff1a 提高锂电池低温充放电性能 xff08 如电动车在寒冷天气续航变短 启动困难 充电难以充满等一些列问题 xff09 2 分类 2 1 外部加热 2 1 1 电热元件加热 电热原件加热为最常见的低温加热方式 xff0c 主
  • 【BMS】电池能量管理:充电管理

    x1f50b 电池包能量管理 xff1a 充电管理 x1f50b 一 通信 充电是一个过程 xff0c 需要电池包和充电器间进行频繁的通信 xff0c 以保证电池安全和寿命 由于通信速率要求不高 xff0c 以及精简接口的考虑 xff0c
  • 【QT】QByteArray的使用

    1 QByteArray可以用来存储原始二进制字节和8 bits字符 xff0c 一般在需要传输原始数据和内存资源短缺时使用 xff08 嵌入式linux Qt xff09 2 QByteArray存储的是char型字符 xff0c 继承自
  • 【工具】搭建TCP公网服务器(用于日常调试)

    1 Openluat TCP Lab xff1a xff08 1 xff09 在MCU上调试GPRS等模块时 xff0c 为了验证和平台TCP通信是否顺畅 xff0c 很多时候需要一个公网的IP环境作为服务器 xff0c 能够直接和GPRS
  • 9.patroni中文手册—Using Patroni with Kubernetes

    9 Using Patroni with Kubernetes Patroni可以使用Kubernetes对象来存储集群的状态并管理领导者密钥 这样一来 它就可以在Kubernetes环境中操作PostgreSQL 而无需任何一致性存储 也
  • PX4飞控学习(一)

    一 linux下环境搭建 用户需要 dialout 组 来访问usb cat gt span class hljs variable HOME span rule tmp lt lt EOF span class hljs comment
  • 阿里云-轻量应用服务器-Ubuntu-图形界面-xfce-VNC远程连接

    1 在阿里云上申请一个 轻量应用服务器 阿里云 xff1a https www aliyun com 配置选好 系统选Ubuntu xff0c 下单付款 然后就去管理控制台 我当初是选了CentOS 但想着想着又把系统换回Ubuntu了 x
  • 进程管理常见系统调用

    进程管理常见系统调用 1 前言2 如何提供有许多CPU的假象 xff1f 2 1 操作系统是管理多个进程执行的软件 3 操作系统提供哪些系统调用API 3 1创建 xff08 fork xff09 3 2 直面fork3 3 execve系