模仿pstree 打印进程树

NJU操作系统实验一: 打印进程树 (pstree) Linux标准做法getopt (man 3 getopt) 阅读详情

Linux pstree(英文全称:display a tree of processes)) 命令将所有进程以树状图显示,树状图将会以 pid (如果有指定) 或是以 init 这个基本进程为根 (root),如果有指定使用者 id,则树状图会只显示该使用者所拥有的进程。
使用权限:所有使用者。
在这里插入图片描述

首先,我们需要问题分解一下:

  1. 得到命令行的参数,根据要求设置标志变量的数值;
  2. 得到系统中所有进程的编号 (每个进程都会有唯一的编号) 保存到列表里;
  3. 对列表里的每个编号,得到它的的父亲是谁;
  4. 在内存中把树建好,按命令行参数要求排序;
  5. 把树打印到终端上。

因为人的脑容量有限,通常解决问题的办法就是把比较复杂的问题分解成小问题,再把小问题继续分解下去。而在学校里所做的训练就是建立问题分解的思路和培养解决问题的能力。

预备工作

由于在实验中会反复编译运行这个程序,所以编译和测试自动化非常重要。
这里我写了一个脚本利用inotufy命令自动化这个过程:
Inotify 一种强大的、细粒度的、异步文件系统监控机制,它满足各种各样的文件监控需要,可以监控文件系统的访问属性、读写属性、权限属性、删除创建、移动等操作,也就是可以监控文件发生的一切变化。

inotify-tools 是一个C库和一组命令行的工作,提供Linux下inotify的简单接口。inotify-tools安装后会得到inotifywait和inotifywatch这两条命令:

下载inotufy:
在Ubuntu系统下使用命令:
sudo apt-get install inotify-tools
其他Linux发行版安装方法可以参见:https://github.com/rvoicilas/inotify-tools/wiki#wiki-getting

inotifywait使用:

语法:inotifywait [-hcmrq] [-e ] [-t ] [–format ] [–timefmt ] [ … ]

-m:一直监控指定的目录,接收到一个事情而不退出,无限期地执行。默认的行为是接收到一个事情后立即退出。
@:排除不需要监视的文件,可以是相对路径,也可以是绝对路径。
-o, –outfile :输出事情到一个文件而不是标准输出。
-s, –syslog:输出错误信息到系统日志
-d, –daemon:跟–monitor一样,除了是在后台运行,需要指定–outfile把事情输出到一个文件。也意味着使用了–syslog。
–exclude :正则匹配需要排除的文件,大小写敏感。
–excludei :正则匹配需要排除的文件,忽略大小写。
-t , –timeout :设置超时时间,如果为0,则无限期地执行下去。
-r:递归监控所监控目录的子目录。
-q:指定一次,不会输出详细信息,指定二次,除了致命错误,不会输出任何信息。
–timefmt:指定时间格式。
–format:
%w 表示发生事件的目录
%f 表示发生事件的文件
%e 表示发生的事件
%Xe 事件以“X”分隔
%T 使用由–timefmt定义的时间格式
-e:指定监控事件项
示例1:

实现对 /data/web 目录进行监控,监控文件删除,修改,创建和权限相关事件,并且要求将监控信息写入 /var/log/web_watch.log。要求日志条目要清晰明了,能突显文件路径、事件名和时间。

# cat web_watch.sh
#!/bin/bash
inotifywait -mrq --timefmt '%y/%m/%d %H:%M' --format  '%T %w%f %e' --event delete,modify,create,attrib  /data/web | while read  date time file event
  do
      case $event in
          MODIFY|CREATE|MOVE|MODIFY,ISDIR|CREATE,ISDIR|MODIFY,ISDIR)
                  echo $event'-'$file'-'$date'-'$time >> /var/log/web_watch.log
              ;;

          MOVED_FROM|MOVED_FROM,ISDIR|DELETE|DELETE,ISDIR)
                  echo $event'-'$file'-'$date'-'$time /var/log/web_watch.log
              ;;
      esac
  done

inotify 优点:

1)监控文件系统事件变化,通过同步工具实现实时数据同步。

inotify 缺点

1)并发如果大于200个文件(10-100k),同步就会有延迟
2)我们前面写的脚本,每次都是全部推送一次,但确实是增量的。也可以只同步变化的文件,不变化的不理。
3)监控到事件后,调用rsync同步是单进程的,而sersync为多进程同步。既然有了inotify-tools,为什么还要开发sersync?

serysync功能多:(inotify+rsync命令)

1)支持通过配置文件管理
2)真正的守护进程socket
3)可以对失败文件定时重传(定时任务功能)
4)第三方的HTTP接口(例如:更新cdn缓存)
5)默认多进程rsync同步

在了解inotigy命令相关知识后,就可以利用脚本auto_test.sh每次在文件改动以后自动运行所有测试,代码如下:

#!/bin/bash

projectDir=/home/acs/os_test

while inotifywait $projectDir --timefmt '%d/%m/%y %H:%M' --format "%T %f %e" -e MODIFY --exclude '^.*.swp$'
do
	cd $projectDir && gcc test.c -o test1 && ./test1
done

运行这个脚本就可以了:
./auto_test.sh
在这里插入图片描述

step 1: 得到命令行的参数,根据要求设置标志变量的数值

在c语言主函数中,int argc, char *argv[]就是从命令行获取的参数,这是操作系统与 C 程序之间的约定。其中argc是参数的个数,argv是参数字符串。
这里我们使用getopt()函数来处理命令行参数:
getopt()函数的出处是unistd.h头文件,可以参考这篇博客Linux下getopt()函数的简单使用
代码:

int main(int argc, char *argv[]) {
    int ch; 
    while((ch = getopt(argc, argv, "pnv")) != -1) 
    {   
        printf("optind: %d\n", optind);
        switch(ch){
            case 'p':
                printf("HAVE option: -p \n\n");
                break;
            case 'n':
                printf("HAVE option: -n \n\n");
                break;
            case 'v':
                printf("HAVE option: -v \n\n");
                break;
            case '?':
                printf("Unknown option: %c\n",(char)optopt);
                break;
        }
    }
    assert(!argv[argc]);
    return 0;                                                                       
}

step 2: 得到系统中进程的编号

Everything is a file,进程信息当然也可以是 “一切” 的一部分

首先我们要知道的是在Linux中,所有进程的信息都以文件的形式存储在/proc目录中,在/proc中存在由数字命名的文件夹,这些数字就是相应进程的pid,进入各个进程的目录中(如进入1号进程的目录中),有一个stat文件,该文件中第四列即为其父进程的pid。
输入命令:cd /proc && ls可以发现查看这些文件夹
在这里插入图片描述
1号进程的stat文件信息:
在这里插入图片描述

思路:

这里我用一个结构体数组先将每一个进程的ppid和pid存起来,打印出来查看一下效果

代码:
#include <stdio.h>
#include <assert.h>
#include <unistd.h>
#include <dirent.h>
#include <string.h>
#include <stdlib.h>

#define PATH "/proc"                                                                                                                                          
#define BUFFSIZE_INFO 50

// pid ppid
struct pinfo{
    int pid;
    int ppid;
}processInfos[500];

int number_process = 0;// 进程个数


int getPpid(char *filename);
void setPid_Ppid();

int main(int argc, char *argv[]) {
    setPid_Ppid();

    for(int i = 0;i<number_process;i++){
       // printf("%d %d \n",processInfos[i].pid,processInfos[i].ppid);
    }
    int ch;
    while((ch = getopt(argc, argv, "pnv")) != -1)
    {
        printf("optind: %d\n", optind);
        switch(ch){
            case 'p':
                printf("HAVE option: -p \n\n");
                break;
            case 'n':
                printf("HAVE option: -n \n\n");
                break;
            case 'v':
                printf("HAVE option: -v \n\n");
                break;
            case '?':
                printf("Unknown option: %c\n",(char)optopt);
                break;
        }
    }
    assert(!argv[argc]);
    return 0;
}


int getPpid(char* filename){
    int ppid = -100;
    char *right=NULL;

    FILE *fp = fopen(filename,"r");                                                                                                                           
    char info[BUFFSIZE_INFO+1];
    info[BUFFSIZE_INFO] = '\0';

    if(fp==NULL){
        fprintf(stderr,"open file %s error!\n", filename);
        return -1;
    }

    if(fgets(info,BUFFSIZE_INFO,fp)==NULL){
        puts("fgets error!");
        exit(0);
    }

    right=strrchr(info,')');
    if(right == NULL){
        printf("cannot find )\n");
    }
    right+=3;
    sscanf(right, "%d", &ppid);
    return ppid;
}
void setPid_Ppid(){
    DIR * dir_ptr;  // 目录中的句柄                                                                                                                           
    struct dirent * direntp; //文件结构体
    int pid,ppid;
    char process_path[51] = "/proc/";
    char stat[6] = "/stat";
    char pidStr[20];


    dir_ptr = opendir(PATH); //打开目录,并返回句柄
    if(dir_ptr==NULL){
        fprintf(stderr,"can not open /proc\n");
        exit(0);
    }

    while(direntp = readdir(dir_ptr)){ //读取句柄,返回dirent结构体
        // printf("%s  %d\n",direntp->d_name,atoi(direntp->d_name));
        pid = atoi(direntp->d_name);
        if(pid){
           //拼接字符串process_path
           //examples: /proc/pid/stat
           //itoa(pid,pidStr,10);
           processInfos[number_process].pid = pid;

           sprintf(pidStr,"%d",pid);
           strcat(process_path,pidStr);
           strcat(process_path,stat);
           int ppid = getPpid(process_path); // 返回-1表示出错                                                                                                
           if(~ppid){
              processInfos[number_process++].ppid = ppid;
           }else{
              number_process++;
           }

           // 重置process_path
            process_path[6] = 0;

        }
    }
}

打印结果:
在这里插入图片描述

step 3: 根据进程编号及父进程编号建树

在获得进程编号的基础上,我们还需要得到每一个进程的name,因此增加一段获取pname的代码:

// 获取pname                                                                                 
			char ch[100];
            strcpy(ch, "/proc/");
            strcat(ch, pidStr);
            strcat(ch, "/status");
            FILE* fp = fopen(ch,"r");                                                                    
            char pname[100];
            while((fscanf(fp, "%s", ch)) != EOF) {
                if(strcmp(ch, "Name:") == 0){
                    fscanf(fp, "%s", pname);
                    break;
                }
            }
            printf("%s\n",pname);

首先,我们需要构建一颗多叉树,保存进程的父子节点信息,以及相应的操作函数:

int build_tree(const char* path, process* head, int dep) {
    const int size = 1024;
    FILE* fp; 
    char ch[size];
    strcpy(ch, "/proc/");
    strcat(ch, path);
    strcat(ch, "/status");
    fp = fopen(ch, "r");
    char pid[size], ppid[size], name[size];
    while((fscanf(fp, "%s", ch)) != EOF) {
        if (strcmp(ch, "Pid:") == 0) {
            fscanf(fp, "%s", pid);
        }
        if (strcmp(ch, "PPid:") == 0) {
            fscanf(fp, "%s", ppid);
        }
        if (strcmp(ch, "Name:") == 0) {
            fscanf(fp, "%s", name);
        }
    }   
    ch[0] = 0;
    if (dep) strcpy(ch, "{");
    strcat(ch, name);
    if (dep) strcat(ch, "}");
    insert(head, create_p(ch, pid), ppid);
    fclose(fp);                                                                                          
    return 0;
}

step 4: 打印信息

int print_tree(process* head, int len, int* buf, int flag) {
    if (head == NULL) return -1;

    while (head) {
        printf("%s", head->pname);
        if (flag) printf("(%s)", head->pid);
        if (head->son) {
            if(!head->son->bro) printf("\u2500\u2500\u2500");
            else printf("\u2500\u252c\u2500");
            int size = strlen(head->pname) + len + 3 + (flag * (strlen(head->pid) + 2));
            if (head->son->bro) buf[size - 2] = 1;
            print_tree(head->son, size, buf, flag);
        }
        head = head->bro;
        if (head) {
            printf("\n");
            if (!head->bro) buf[len - 2] = 0;                                                            
            for (int i = 0; i < len - 2; ++ i)-
                if (buf[i]) printf("\u2502");
                else printf(" ");
            if (!head->bro) printf("\u2514\u2500");
            else printf("\u251c\u2500");
        }
    }
    return 0;
}

完整代码:

#include <stdio.h>
#include <assert.h>
#include <unistd.h>
#include <dirent.h>
#include <string.h>
#include <stdlib.h>

#define PATH "/proc"                                                                                                                                          
#define BUFFSIZE_INFO 50


typedef struct process {
    char *pname;
    char *pid;
    struct process *bro;
    struct process *son;
}process;

process* create_p(const char*,const char*);
int insert(process*, process*,const char*);
int print_tree(process*, int, int*, int);
void listdir(const char*,const int father, process** head,int dep,int flag);


    int number_process = 0;// 进程个数


    int getPpid(char *filename);
    void create_print_pro(int op);  //创建树
    process* create_p(const char*, const char*);
    int build_tree(const char*, process*, int dep);    
    int main(int argc, char *argv[]) {
        int ch;
        while((ch = getopt(argc, argv, "pnv")) != -1)
        {
            printf("optind: %d\n", optind);
            switch(ch){
                case 'p':
                    printf("HAVE option: -p \n\n");
                    create_print_pro(1);
                    break;
                case 'n':
                    printf("HAVE option: -n \n\n");
                    create_print_pro(2);
                    break;
                case 'v':
                    printf("HAVE option: -v \n\n");
                    create_print_pro(3);
                    break;
                case '?':
                    printf("Unknown option: %c\n",(char)optopt);
                    break;
            }
        }
        assert(!argv[argc]);
        return 0;
    }



void create_print_pro(int op){

    process** head = (process**)malloc(sizeof(process*));
    char buff[1024] = {0};
    listdir(buff," ",head,0,op);
    printf("\n");
    int buf[1024] = {0};
    int state_n = 0;
    if(op == 2)state_n = 1;
    print_tree((*head), 0, buf, state_n);
    printf("\n");
}





void listdir(const char* path, int father, process** head,int dep,int flag){
    DIR * dir_ptr;  // 目录中的句柄
    struct dirent * direntp; //文件结构体
    int pid,ppid;


    dir_ptr = opendir(PATH); //打开目录,并返回句柄
    if(dir_ptr==NULL){
        fprintf(stderr,"can not open /proc\n");
        exit(0);
    }
    if(!dep) *head = create_p("", "0");  // 初始化

    while(direntp = readdir(dir_ptr)){ //读取句柄,返回dirent结构体
        // printf("%s  %d\n",direntp->d_name,atoi(direntp->d_name));
        pid = atoi(direntp->d_name);
        if(pid){
            if(dep == 1 && pid != father)continue;
            build_tree(direntp->d_name, *head,dep);
            if(0 == dep && flag){
                char buf[1024];
                strcpy(buf, "/proc/");
                strcat(buf, direntp->d_name);
                strcat(buf, "/task");
                listdir(buf, pid, head, 1, flag);
            }
        }
    }
    closedir(dir_ptr);
}


int build_tree(const char* path, process* head, int dep) {
    const int size = 1024;
    FILE* fp;
    char ch[size];
    strcpy(ch, "/proc/");
    strcat(ch, path);
    strcat(ch, "/status");
    fp = fopen(ch, "r");
    char pid[size], ppid[size], name[size];
    while((fscanf(fp, "%s", ch)) != EOF) {
        if (strcmp(ch, "Pid:") == 0) {
            fscanf(fp, "%s", pid);
        }
        if (strcmp(ch, "PPid:") == 0) {
            fscanf(fp, "%s", ppid);
        }
        if (strcmp(ch, "Name:") == 0) {
            fscanf(fp, "%s", name);
        }
    }
    ch[0] = 0;
    if (dep) strcpy(ch, "{");
    strcat(ch, name);
    if (dep) strcat(ch, "}");
    insert(head, create_p(ch, pid), ppid);
    fclose(fp);
    return 0;
}

void check_memory(void* p) {
    if (p == NULL) {
        perror("malloc error");
        exit(0);
    }
}

process* create_p(const char* pname, const char* pid) {
    process* p = (process*)malloc(sizeof(process));
    check_memory(p);
    p->pname = (char*)malloc(sizeof(char) * (strlen(pname) + 1));
    p->pid  = (char*)malloc(sizeof(char) * (strlen(pid)  + 1));
    check_memory(p->pname);
    check_memory(p->pid);
    strcpy(p->pname, pname);
    strcpy(p->pid,  pid);
    p->bro = NULL;
    p->son = NULL;
    return p;
}

// tools
int cmp(const char* a, const char *b) {
    int tempa = atoi(a), tempb = atoi(b);
    if (a == b) return 0;
    if (a > b) return 1;
    return -1;
}
void insert_node(process* head, process* node) { 
    /*help insert node function*/
    process* cur = head->bro;
    process* prv = head;
    while (cur) {
        int state = strcmp(head->pname, node->pname);
        if ( 1 == state || 
                (0 == state && -1 == cmp(head->pid, node->pname))
           ) {
            prv->bro = node;
            node->bro = cur;
            return;
        }
        prv = cur;
        cur = cur->bro;
    }
    prv->bro = node;
    return;
}

int insert(process* head, process* node, const char* fpid) {
    if (node == NULL) return -1;
    if (head == NULL) return -1;
    while (head) {
        if (0 == strcmp(head->pid, fpid)) {
            if (head->son)
                insert_node(head->son, node);
            else 
                head->son = node;
            return 0;
        } else {
            if (0 == insert(head->son, node, fpid))
                return 0;
        }
        head = head->bro;
    }
    return -1;
}

int print_tree(process* head, int len, int* buf, int flag) {
    if (head == NULL) return -1;

    while (head) {
        printf("%s", head->pname);
        if (flag) printf("(%s)", head->pid);
        if (head->son) {
            if(!head->son->bro) printf("\u2500\u2500\u2500");
            else printf("\u2500\u252c\u2500");
            int size = strlen(head->pname) + len + 3 + (flag * (strlen(head->pid) + 2));
            if (head->son->bro) buf[size - 2] = 1;
            print_tree(head->son, size, buf, flag);
        }
        head = head->bro;
        if (head) {
            printf("\n");
            if (!head->bro) buf[len - 2] = 0;
            for (int i = 0; i < len - 2; ++ i) 
                if (buf[i]) printf("\u2502");
                else printf(" ");
            if (!head->bro) printf("\u2514\u2500");
            else printf("\u251c\u2500");
        }
    }
    return 0;
}


实现pstree命令的程序 #include#include#includetypedef struct pro_info{ int pid; int ppid; char name[100]; int flag;//标志是否打印 int rec;//计算总父进程个数}info;//返回所有数字目录int filter(const struct d 阅读详情

相关推荐

Linux应急响应速查表:5分钟快速定位服务器入侵与异常

在系统运维与安全领域,应急响应是处理服务器突发安全事件的核心能力。其原理在于通过系统化的信息收集与分析流程,快速定位异常源头,从而有效控制事态发展。这项技术的核心价值在于将个人经验转化为可复用的操作清单,极大提升在高压环境下的排查效率与准确性。典型的应用场景包括服务器CPU异常飙高、网络流量激增、文件被篡改或疑似入侵事件。本文聚焦于Linux应急响应,提供一份高度提炼的速查命令表,涵盖系统资源快照、进程网络深度排查、持久化机制检查等关键模块,旨在帮助工程师在黄金时间窗口内,通过精准的命令组合,例如使用`to

weixin_34279579的博客 346

南大OS2023-M1:打印进程树

linux系统中,在/proc文件系统中,每个运行中的进程都有一个相应的目录,其名称是进程的PID。在这个目录下,有一个名为status的文件,其中包含了有关该进程的各种信息,包括进程号。status文件中包含的信息(部分截选):本人只完成了基础的打印功能,按进程号排序打印没有实现。

xiaoxTai的博客 1229

Ubuntu服务器挖矿病毒排查与清除实战:从Python进程异常到系统加固

在Linux服务器运维中,进程管理与系统安全监控是保障服务稳定的基础。当服务器出现CPU使用率异常飙高、系统响应变慢时,往往意味着资源被恶意占用,其背后原理常与未授权进程或恶意软件有关。这类问题不仅消耗计算资源,导致电费与云成本激增,更暴露了系统存在的安全漏洞。从技术价值看,掌握服务器入侵检测与应急响应能力,是运维工程师保障业务连续性的核心技能。典型的应用场景包括Web服务器性能骤降排查、云计算实例资源异常消耗分析等。本文聚焦于一种伪装成Python进程的挖矿病毒,通过分析其利用计划任务(Cron)实现持久

weixin_34241036的博客 330

linux 进程树id,Linux中使用pstree命令:查看进程树

pstree 命令是以树形结构显示程序和进程之间的关系,此命令的基本格式如下:[root@localhost ~]# pstree [选项] [PID或用户名]表 1 罗列出了 pstree 命令常用选项以及各自的含义。表 1 pstree命令常用选项及含义选项含义-a显示启动每个进程对应的完整指令,包括启动进程的路径、参数等。-c不使用精简法显示进程信息,即显示的进程中包含子进程和父进程。-n根...

weixin_39848347的博客 722

/proc打印进程树

在应用层打印/proc文件系统的进程信息,并统计进程数

使用proc目录打印linux进程树

在Linux环境下,用C++语言编写一个程序,以树状结构(即体现父子关系)输出系统当前所有进程。 通过/proc目录获得各进程的父进程ppid,获得进程树的父亲表示,再将进程树的父亲表示转换成左孩子与右孩子,最后中序遍历二叉树,输出进程信息即可。

Linux系统权限维持与痕迹清理:从账户隐藏到日志清除的完整攻防解析

在Linux系统安全领域,权限维持与痕迹清理是攻防对抗的核心技术栈。其原理在于攻击者在获得系统访问权限后,通过创建隐蔽账户、植入持久化后门等方式维持控制,并在操作后清理系统日志以规避检测。这项技术的价值不仅体现在渗透测试的权限维持阶段,更是系统管理员和安全工程师必须掌握的防御知识,只有深入理解攻击手法,才能有效构建检测与响应能力。在实际应用场景中,它常见于红蓝对抗演练、应急响应中的溯源分析以及服务器安全加固。本文聚焦于**账户隐藏**与**日志清除**两大关键技术,通过剖析PAM模块、SSH后门等实现方式,

weixin_30307921的博客 484

Linux打印进程树_linux打印_

Linux打印,显示需要内容,属于控制台程序内容

Linux服务器应急响应实战:从入侵检测到安全加固的完整指南

在Linux系统运维中,服务器安全是保障业务连续性的基石。当系统遭遇入侵时,应急响应成为遏制损害、恢复业务的关键技术手段。其核心原理在于通过系统化的流程,快速收集证据、分析入侵路径并消除威胁,从而最大限度减少损失。从技术价值看,掌握应急响应不仅能提升系统可用性,更是构建纵深防御体系的重要实践。常见的应用场景包括挖矿病毒处置、Webshell后门清理、异常进程排查等安全事件。本文将聚焦Linux应急响应,深入解析PDCERF模型下的实战流程,结合进程分析、文件排查、日志溯源等核心操作,并探讨如何通过自动化脚本

weixin_30781107的博客 414

Verilog——反相器

反相器代码: module inv( A, Y );//A,Y是端口 //定义端口属性 input A; output Y; //定义输入输出关系 assign Y=~A;//Y等于A的反 endmodule //inv是module的名字

weixin_41788560的博客 9501

Django 小实例S1 简易学生选课管理系统 —— 〇、初步介绍与演示

python Django实现的一个简易的教务选课系统。 介绍与演示的视频版本已发到我的b站: https://www.bilibili.com/video/BV1er4y1w7ty。 项目已上传到我的github: https://github.com/BigShuang/SimpleStudentCourseManagementSystem。 S1总目录: 〇、初步介绍与演示 一、项目流程梳理与数据库设计 ======================= 大爽歌作,made by big shuang.

大爽老师的博客 5945

Linux服务器挖矿病毒排查与清除实战:从pamdicks病毒到系统加固

在Linux服务器运维中,进程管理与系统安全是保障服务稳定性的核心基础。其原理在于操作系统通过进程调度分配CPU资源,而恶意软件常通过伪装进程、劫持系统调用等方式隐匿自身。掌握这些技术对于维护服务器健康、抵御资源滥用攻击具有关键价值,尤其在云计算与虚拟化普及的当下,服务器安全已成为运维人员的必备技能。典型的应用场景包括应对服务器CPU异常满载、响应迟缓等性能问题,这往往与挖矿病毒等恶意进程的入侵有关。本文以处理顽固的pamdicks挖矿病毒为例,深入剖析了从利用top、netstat等命令进行异常诊断,到清

weixin_30425949的博客 385

Quuppa 如何引领蓝牙 ® 定位服务的全新发展方向

我们最优质的客户,是那些对其他实时定位系统(RTLS)的承诺感到失望、转而寻求 “能覆盖全业务场景的更广泛赋能技术” 的客户 —— 前文提到的消防车制造商就是典型案例:他们尝试并通过 Quuppa 的蓝牙方案取得了成功。我们希望业务合作伙伴(如系统集成商、解决方案提供商)能充分利用 Quuppa 智能定位系统的功能,为客户提供专属解决方案:无论是追踪微型标签、实现未来移动设备的 AoD 功能,还是基于 RSSI 的简单信标(信标作为物联网网关),这些功能都能让同一套 Quuppa 系统支持广泛的用例。

专业开发者的博客,一个从事Android WiFi和蓝牙的工程师 833

代码还原之 pstree (一)

代码还原之 pstree (一)

xiaozhi 1171

【C/C++ 性能优化】了解cpu 从而进行C++ 高效编程

在当代软件开发领域,CPU(Central Processing Unit,中央处理器)的性能优化一直是一个核心议题。尤其是对于C++开发者而言,无论是在Linux平台还是其他操作系统中,都需要深刻理解CPU的工作原理以及如何充分利用CPU资源,以确保软件运行的高效性和稳定性。正如计算机科学家Donald Knuth在《计算机程序设计的艺术》中所说:“过早的优化是万恶之源”,然而,这并不意味着我们应该忽视性能优化——相反,我们需要在正确的时间以正确的方式进行优化。

探索C++编程的奥秘,分享深入的技术见解和实践,旨在激发读者创造力与解决问题的思维。 4751

在ARM平台上使用Anbox和LXC进行ARM开发调试

在ARM平台上使用Anbox和LXC进行ARM开发调试是一种强大的工具组合。通过安装和配置Anbox和LXC,以及在LXC容器中运行ARM应用程序,开发人员可以方便地进行ARM开发和调试。LXC(Linux容器)是一种轻量级虚拟化技术,它提供了一种在单个主机上运行多个独立的Linux容器的方法。我们将讨论安装和配置Anbox和LXC,以及如何在容器中运行ARM应用程序。通过上述步骤,你现在可以在LXC容器中运行ARM应用程序,并使用Anbox在ARM平台上进行开发和调试。步骤2:启动LXC容器。

KwmGroovy的博客 739

【Shell 命令集合 系统管理 】Linux 显示进程之间的关系 pstree命令 使用指南

pstree命令用于以树状结构显示进程之间的关系。它可以帮助用户更清晰地了解系统中运行的进程以及它们之间的父子关系。

探索C++编程的奥秘,分享深入的技术见解和实践,旨在激发读者创造力与解决问题的思维。 1876

海康Visionmaster-通讯管理:ModBus 通信发送非整型 数据的方法

海康Visionmaster-通讯管理:ModBus 通信发送非整型 数据的方法

视觉人机器视觉的博客 1744

Linux实用命令

Linux常用命令

藏知阁 7万+

深度学习笔记——前向传播与反向传播、神经网络(前馈神经网络与反馈神经网络)、常见算法概要汇总

本文简要介绍深度学习的前向传播与反向传播,以及前馈神经网络与反馈神经网络。

haopinglianlian的博客 3044

机器学习的低代码与无代码平台:便捷入门之路

机器学习领域正涌现出大量低代码和无代码平台,使非技术人员也能轻松应用AI技术。低代码平台(如PyCaret、H2O AutoML)需少量编程但灵活性强,而无代码工具(如Google Cloud AutoML、Teachable Machine)则完全无需编程基础。这些平台覆盖图像识别、文本处理、业务预测等多种场景,适合快速部署简单项目。但专业人士仍需注意,这些工具无法替代复杂场景下的定制化开发。随着技术进步,低门槛AI工具正在改变各行业的创新方式。

2501_90323865的博客 154

2021-04-26 Linux系统下的进程树打印实验

Linux系统下通过内核模块显示进程控制块信息 前言 本机为微软Surface pro4,为64位,所用操作系统为Windos 10。本机虚拟机版本为Oracle VM VirtualBox 6.1.8,所用操作系统是使用Ubuntu18.04,。Ubuntu的虚拟硬盘设置为200G,显存为128MB,内存为4G,CPU2个,所用镜像源为清华大学软件镜像源。所使用linux内核为linux-5.11.8。 注意事项 (1)使用本博客中的指令时一定要采取对应自己电脑安装的Ubuntu配置。 (2)内核模块在.

APPDREAMER的博客 1691

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

零基础小白速通北邮操作系统课设(三)判断进程关系及打印进程树

是实现了给定`pid`序列判断其中进程关系和打印Linux进程树两个功能

qq_58400270的博客 406

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

Linux学习之打印进程树

前言 继续Linux的学习,操作系统学到了Linux系统下的进程结构,布置了一个作业是打印进程树,来加深一下对Linux进程的理解。 虚拟主机 主机:联想Y7000P;64位windows10;CPU:i7-9750H;显卡:GTX 1660 Ti;内存:16G 虚拟机:Ubuntu 18.04 LTS;硬盘100G;内存4G;64位;4核心 Linux内核:5.11.8 本博客原创,转载请注明!!! 基础知识补充: 问题解决需要分析两个问题: 如何得到系统进程树1号进程信息 如何由1号进程开始获取所有进

RongLin02的博客 4666
上一篇: Linux使用笔记
下一篇: 将医学图像.dcm格式与.nii.gz格式进行一些处理
Andy in boots
博客等级 码龄7年 73粉丝 69原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值