暑假补卷2——进程控制:

2023-10-27

进程地址空间

接进程概念

 

写时拷贝

通常,父子代码共享,父子再不写入时,数据也是共享的,当任意一方试图写入,便以写时拷贝的方式各自一份副本。具体见下图:

fork常规用法

一个父进程希望复制自己,使父子进程同时执行不同的代码段。例如,父进程等待客户端请求,生成子进程来处理请求。

一个进程要执行一个不同的程序。例如子进程从fork返回后,调用exec函数。

fork调用失败的原因

系统中有太多的进程

实际用户的进程数超过了限制

进程终止

进程终止时,操作系统做了什么?

当然要释然进程申请的相关内核数据结构和对应的数据和代码(本质就是释放系统资源)

进程退出场景

  • 代码运行完毕,结果正确(进程退出码为0)

  • 代码运行完毕,结果不正确(......非0)

  • 代码异常终止,程序崩溃了(信号部分,涉及到一点点)

进程常见退出方法

echo $?    //获取最近一个进程执行完毕的退出码

正常终止:

  1. 从main返回

  2. 调用exit

  3. _exit

异常退出:

ctrl + c,信号终止

__exit函数

 #include<unistd.h>
 void _exit(int status);
 ​
 参数: status 定义了进程的终止状态,父进程通过wait来获取该值

说明:虽然status是int,但是只有低8位可以被父进程所用。所以exit(1)时,在终端执行$?发现返回值是255。

exit函数

exit在代码的任何地方调用,都表示直接终止进程,return除了在main函数里表示终止进程,return退出码外,其他表示终止函数

 #include<unistd.h>
 void exit(int status)

exit最后也会调用exit, 但在调用exit之前,还做了其他工作:

  1. 执行用户通过 atexit或on_exit定义的清理函数。

  2. 关闭所有打开的流,所有的缓存数据均被写入

  3. 调用_exit

        return退出

        return是一种更常见的退出进程方法。执行return n 等同于执行exit(n),因为调用main的运行时函数会将main的返回值当做exit的参数

进程等待

进程等待必要性

  • 之前讲过,子进程退出,父进程如果不管不顾,就可能造成‘僵尸进程’的问题,进而造成内存泄漏。

  • 另外,进程一旦变成僵尸状态,那就刀枪不入,“杀人不眨眼”的kill -9 也无能为力,因为谁也没有办法杀死一个已经死去的进程。

  • 最后,父进程派给子进程的任务完成的如何,我们需要知道。如,子进程运行完成,结果对还是不对,或者是否正常退出。

  • 父进程通过进程等待的方式,回收子进程资源,获取子进程退出信息

进程等待的方法

wait方法

 #include<sys/types.h>
 #include<sys/wait.h>
 ​
 pid_t wait(int *status);
 ​
 返回值:
         成功返回被等待进程pid,失败返回-1。
 参数:
     输出型参数,获取子进程退出状态,不关心则可以设置称为NULL

样例:

可以看到父进程先暂停,后执行阻塞等待,在此期间子进程先变成僵尸状态后被回收

回收僵尸进程

往后我们编写多进程,基本写法就是fork+wait/waitpid

获取子进程**status**

  • wait和waitpid,都有一个status参数,该参数是一个输出型参数,由操作系统填充。

  • 如果传递NULL,表示不关心子进程的退出状态信息。

  • 否则,操作系统会根据该参数,将子进程的退出信息反馈给父进程。

  • status不能简单的当作整形来看待,可以当作位图来看待,具体细节如下图(只研究status低16比特位):

测试代码:

(status>>8)&0xFF//status正确传参

思考:

既然父进程退出后僵尸子进程被系统领养后可以回收,干嘛还要用wait呢?

  1. 子进程退出状态不关心,父进程会自动结束可以用

  2. 子进程退出状态关心要用获取

  3. 父进程不会自动退出(用户选择退出),父进程会一直跑,子进程会得不到回收,需要用。

阻塞等待与非阻塞等待:

我们的父进程通过调用waitpid来进行等待,如果子进程没有退出,我们waitpid这个系统调用,立马返回!

 waitpid方法:

pid_t waitpid(pid_t pid,int *status,int options);

返回值:

        当正常返回的时候waitpid返回收集到的子进程的进程ID;

        如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0;

        如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在;

参数:

pid:

       Pid=-1,等待任一个子进程。与wait等效。

       Pid>0.等待其进程ID与pid相等的子进程。

status:

       WIFEXITED(status): 若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出)

        WEXITSTATUS(status): 若WIFEXITED非零,提取子进程退出码。(查看进程的退出码)

options:

        WNOHANG: 若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若正常结束,则返回该子进程的ID。(非阻塞等待)

  • 如果子进程已经退出,调用wait/waitpid时,wait/waitpid会立即返回,并且释放资源,获得子进程退出信息。

  • 如果在任意时刻调用wait/waitpid,子进程存在且正常运行,则进程可能阻塞。

  • 如果不存在该子进程则立即出错返回。

实验代码:

代码引例:

        通常在某些接口中引起进程挂起,比如读写(io类的),包括检测子进程是否退出(检查类的),

        如果子进程没退出,父进程必须在这里挂起等待(检测子进程是否退出),此时代码是操作系统的代码->执行操作系统自己的进程挂起算法和调度算法,然后把该进程切走,此时上层看来就相当于我们卡住了。

        所谓阻塞相当于原进程(PCB)放入等待队列中,把状态设为R状态,如果设置非阻塞状态,此时不再执行操作系统内的进程切换,代码挂起,直接返回0,就可以继续做自己的事情,再过一段时间再检测,再调用waitpid,叫做非阻塞调用。

非阻塞伪代码略了解:

1:myproc.cc
     #include<iostream>
     #include<vector>
     #include<stdio.h>
     #include<stdlib.h>
     #include<unistd.h>
     #include<sys/wait.h>
     
     typedef void (*handler_t)();//函数指针类型
     std::vector<handler_t>handlers;//函数指针数组
 ​
 void fun_one()
 {
     printf("这是一个临时任务1\n");
 }
 void fun_two()
 {
     printf("这是一个临时任务2\n");
 }
 //设置对应的方法回调
 //以后想让父进程闲了执行任何方法的时候,只要向Load里面注册,就可以让父进程执行对应的方法咯~
 void Load()
 {
     handlers.push_back(fun_one);
     handlers.push_back(fun_two);
 }
 ​
 int main()
 {
     pid_t id=fork();
     if(id==0)
     {
         //子进程
         int cnt=5;
         while(cnt)
         {
             printf("我是子进程:%d\n",cnt--);
             sleep(1);
         }
         
         exit(11);//11仅仅用来测试
     }
     else
     {
         int quit=0;
         while(!quit)
         {
             int status=0;
             pid_t res=waitpid(-1,&status,WNOHANG);//以非阻塞方式等待
             if(res>0)
             {
                 //等待成功&&子进程退出
                 printf("等待子进程推出成功,退出码: %d\n",WEXITSTATES(status));
                 quit=1;
             }
             else if(res==0)
             {
                 //等待成功&&但子进程并未退出
                 printf("子进程正在运行中,暂时还没有退出,父进程可以在等一等,处理一下其他事情?");
                 if(handlers,empty()),Load();
                 for(auto iter:handlers)
                 {
                     //执行处理其他任务
                     iter();
                 }
         }
             else
             {
                 //等待失败
                 printf("wait失败!\n");
                 quit=1;
             }
             sleep(1);
     }
         
     }
 }

进程替换:

用fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支),子进程往往要调用一种exec函数以执行另一个程序。当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动

例程开始执行。调用exec并不创建新进程,所以调用exec前后该进程的id并未改变。

替换函数

其实有六种以exec开头的函数,统称exec函数:

  1. #include <unistd.h>`

  2. int execl(const char *path, const char *arg, ...);

  3. int execlp(const char *file, const char *arg, ...);(带p自己会在PATH中查找)

  4. int execle(const char *path, const char *arg, ...,char *const envp[]);

  5. int execv(const char *path, char *const argv[]);

  6. int execvp(const char *file, char *const argv[]);

int execve(const char *path, char *const argv[], char *const envp[]);
//真正的系统接口,以上都是基本封装了这个接口

3号函数测试:

 #include<stdio.h>
     2 #include<stdlib.h>
     3 #include<unistd.h>
     4 #include<sys/wait.h>
     5 
     6 #define NUM 16
     7 
     8 int main()
     9 {
    10   pid_t id=fork();
    11   if(id==0)
    12   {
    13     //子进程
    14     ls -a -l
    15     printf("子进程开始运行,pid:%d\n",getpid());
    16     sleep(3);
 W> 17    char *const _argv[NUM]={
    18       (char*)"ls",
    19       (char*)"-a",
    20       (char*)"-l",
    21       NULL
    22     };
    23 
    24     //execl("/usr/bin/ls","ls","-a","-l",NULL);
    25     //execv("/usr/bin/ls",_argv);//和上面的execl只有传参方式的区别
    26     //execv("/usr/bin/ls",_argv);
    27    execlp("ls","ls","-a","-l",NULL);                                             
    28    exit(1);
    29   }
    30   else
    31   {
    32     //父进程
  33     printf("父进程开始运行,pid:%d\n",getpid());
    34     int status=0;
    35     pid_t id=waitpid(-1,&status,0);//阻塞等待,一定是子进程先运行完毕,然后父进程      获取之后
    36     if(id>0)
    37     {
    38       printf("wait success,exit code: %d\n",WEXITSTATUS(status)); 
    39     }
    40   }
    41   return 0;
    42 }    

结果

问题:为什么"ls",这个参数传两遍?

答:

他们俩代表不同的阶段,前面的用来查找,后面的用来做匹配,看你传什么选项怎么执行

 #include<stdio.h>
  2 #include<string.h>
  3 #include<stdlib.h>
  4 
  5 //./mycmd -a/-b/-c...
  6 int main(int argc,char *argv[])//命令行参数
  7 {
  8   if(argc!=2)//必须是有且只有一个参数
  9   {
 10     printf("can not execute!\n");
 11     exit(1);
 12   }
      //MY_105_VAL:目前并不存在
      printf("获取环境变量:MY_105_VAL:%s\n",getenv("MY_105_VAL"));
 13 
 14   if(strcmp(argv[1],"-a")==0)
 15   {
 16     printf("hello a!\n");
 17   }
 18   else if(strcmp(argv[1],"-b")==0)
 19   {
 20     printf("hello b!\n");
 21   }
 22   else{
 23     printf("default!\n");
 24   }
 25 
 26   return 0;
 27 }                 

用makefile执行两个程序

 

 

execl测试:

定义所需执行程序路径:

1.const char *myfile="/home/whb/105/online-code---phase-105/lesson17/mycmd";
2.const char *myfile="./mycmd";

函数替换调用其他程序(python)

execlp("python","python","test.py",NULL);
//二进制程序工具,你想怎么执行,你想执行的脚本是什么,NULL

由此可见exec*系列函数的功能其实就是加载器的底层接口

怎么理解环境变量具有全局属性,可以被子进程继承?

int main(int argc,char*argv[],char *env[])
//创建子进程时可以直接把父进程main函数中的环境变量导给他

函数解释:

​​​​​​
  • 这些函数如果调用成功则加载新的程序从启动代码开始执行,不再返回。

  • 如果调用出错则返回-1

  • 所以exec函数只有出错的返回值而没有成功的返回值。

命名理解

这些函数原型看起来很容易混,但只要掌握了规律就很好记。

  • l(list) : 表示参数采用列表

  • v(vector) : 参数用数组

  • p(path) : 有p自动搜索环境变量PATH

  • e(env) : 表示自己维护环境变量

 exec调用举例如下:

#include<unistd.h>
 ​
 int main()
 {
     char *const argv[]={"ps","-ef",NULL};
     char *const envp[]={"PATH=/bin:/usr/bin","TERM=console",NULL};
     
     execl("/bin/ps","ps","-ef",NULL);
     
     //带p的,可以使用环境变量PATH无需写全路径
     execlp("ps", "ps", "-ef", NULL);
     // 带e的,需要自己组装环境变量
     execle("ps", "ps", "-ef", NULL, envp);
     execv("/bin/ps", argv);
     // 带p的,可以使用环境变量PATH,无需写全路径
     execvp("ps", argv);
     // 带e的,需要自己组装环境变量
     execve("/bin/ps", argv, envp);
     exit(0);
 }
事实上,只有execve是真正的系统调用,其它五个函数最终都调用 execve,所以execve在man手册第2节,其它函数在man手册第3节。这些函数之间的关系如下图所示。

下图exec函数族一个完整的例子:

做一个简易的shell

用下图的时间轴来表示事件的发生次序。

在继续学习新知识前,我们来思考函数和进程之间的相似性

exec/exit就像call/return

一个C程序有很多函数组成。一个函数可以调用另外一个函数,同时传递给它一些参数。被调用的函数执行一定的操作,然后返回一个值。每个函数都有他的局部变量,不同的函数通过call/return系统进行通信。

这种通过参数和返回值在拥有私有数据的函数间通信的模式是结构化程序设计的基础。Linux鼓励将这种应用于程序之内的模式扩展到程序之间。如下图

一个C程序可以fork/exec另一个程序,并传给它一些参数。这个被调用的程序执行一定的操作,然后通过exit(n)来返回值。调用它的进程可以通过wait(&ret)来获取exit的返回值。

shell程序实现:

 #include<stdio.h>                             
 #include<unistd.h>
 #include<stdlib.h>
 #include<sys/wait.h>
 #include<string.h>
 #include<sys/types.h>
 ​
 #define SIZE 32
 #define NUM 1024
 ​
 //保存完整的命令行                                                               
 char cmd_line[NUM];
 //保存打散之后的命令行字符串
 char *g_argv[SIZE];
    
 ​
    int main()
  {
    //0.命令行解释器,一定是一个常驻内存的进程,不退出
   while(1)
    {
 ​
     //1.打印出提示信息
     printf("[root@localhost myshell]#");
    fflush(stdout);//刷新缓冲区,显示提示(上一行)并且光标与其在同一行内;
    memset(cmd_line,'\0',sizeof cmd_line);
    //2.获取用户的键盘输入[输入的是各种指令和选项:"ls -a -l -i"]
     if(fgets(cmd_line,sizeof cmd_line,stdin)==NULL)
     {
         continue;//读入命令行失败
      }
      cmd_line[strlen(cmd_line-1)]='\0';//替换掉输入缓冲区的换行(enter键)
      //"ls -a -l -i\n\0"
     //printf("echo:%s\n",cmd_line);
       //3.命令行字符串解析
//用来将一个字符串打散成多个(C语言)
     g_argv[0]=strtok(cmd_line," ");//获取分隔符之前的字符串地址保存到g_argv[]中
      //char *strtok(char *str,const char*delim);//第一次调用,要传入原始字符串
    
        int index=1;
          //修改命令行颜色
        if(strcmp(g_argv[0],"ls")==0)
       {
       g_argv[index++]="--color=auto";
      }
     while(g_argv[index++]=strtok(NULL," "));//第二次,如果还要解析原始字符
      //for debug
      //for(index=0;g_argv[index];index++)
     //printf("g_argv[%d]:%s\n",index,g_argv[index]);
      //4.TODO内置命令,让父进程(shell)自己执行的命令,我们叫做内置命令,内建命令
        //内建命令本质其实就是(shell)中的一个函数调用
       if(strcmp(g_argv[0],"cd")==0)
        {
       if(g_argv[1]!=NULL) chdir(g_argv[1]);//cd path,cd ..
          
        continue;//跳出循环,不执行下面的代码
       }
        //5.fork()
       pid_t id=fork();
       if(id==0)//child
      {
         printf("下面功能让子进程进行的\n");
        execvp(g_argv[0],g_argv);
       exit(1);
       }
     //father
       int status=0;
       pid_t ret=waitpid(id ,&status,0);
        if(ret>0) printf("exit code:%d\n",WEXITSTATUS(status));
    }
   }         

想要学习更完整的shell,请持续关注Linux学习系列

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

暑假补卷2——进程控制: 的相关文章

  • 主动\被动模式下 FTP 服务器的适当 iptables 规则

    我在 CentOS6 上安装了 ProFTPD 服务器 如果我使 ftp 本地主机 我可以正确连接 但如果我从外部尝试 我会收到消息 没有到主机的路由 但有一条到主机的路由 因为我是通过 SSH 连接的 我尝试添加以下 iptable 规则
  • 为什么 ls -l 中的“总计”加起来不等于列出的总文件大小? [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 为什么是total在输出中ls l打印为64并不是26078列出的所有文件的总数是多少 ls l test ls total 64 rw
  • copy_from_user() 错误:目标大小太小

    我正在为内核模块编写 ioctl 处理程序 我想从用户空间复制数据 当我编译禁用优化的代码时 O0 gflags 编译器返回以下错误 include linux thread info h 136 17 error call to bad
  • Linux 内核中是否使用了扩展指令集(SSE、MMX)?

    好吧 它们带来 至少应该带来 性能的巨大提升 不是吗 所以 我还没有看到任何 Linux 内核源代码 但很想问 它们是否以某种方式被使用 在这种情况下 对于没有此类指令的系统 必须有一些特殊的 代码上限 SSE 和 MMX 指令集在音频 视
  • 使用 SED 将单词的第一个字母大写

    如何将单词的第一个字母替换为大写字母 例如 Trouble me Gold rush brides into Trouble Me Gold Rush Brides 这一行应该做到这一点 sed e s b u 1 g
  • 使用 awk 处理多个文件

    我必须使用 awk 处理大量 txt 文件 每个文件 1600 万行 我必须阅读例如十个文件 File 1 en sample 1 200 en n sample 2 10 en sample 3 10 File 2 en sample 1
  • 如何在 Linux 中隐藏 wget 输出? [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 我不想在使用时看到任何消息wget 我想抑制它通常在屏幕上产生的所有输出 我该怎么做 你为什么不使用 q From man wget q
  • Laravel:调用未定义的函数 Symfony\Component\Console\mb_convert_variables()?

    我正在运行普通 Amazon EC2 服务器 我自己安装了 PHP 5 5 21 我的 Web 应用程序是基于 Laravel 构建的 几个星期以来 一切都进展顺利 然而 现在 当运行 Laravel 命令来更新数据库架构时 php 工匠迁
  • 为 Python 终端提供持久的历史记录

    有没有办法告诉交互式 Python shell 保留会话之间执行命令的历史记录 当会话运行时 在执行命令后 我可以向上箭头并访问所述命令 我只是想知道是否有某种方法可以保存一定数量的这些命令 直到下次使用 Python shell 时 这将
  • 真实设备中的 Android strace

    我有以下情况 我想监控Android手机上的系统调用 所以 我编写了一个脚本来做到这一点 使用 Android 模拟器可以完美地工作 将应用程序的痕迹写入我的 Ubuntu 上的特定文件中 问题是当我连接一个真实的手机来分析它时 它在结果文
  • 具有多个文件扩展名的查找命令

    我正在查看许多子目录 找到所有以 JPG jpg 和 png 结尾的文件 并将它们复制到一个单独的目录中 但是现在只找到 JPG 有人可以解释我做错了什么吗 find root TEST Images name png o name jpg
  • 在 Linux 内核中使用断言

    我有一个问题assert 在Linux中 我可以在内核中使用它吗 如果不是 例如 如果我不想输入 NULL 指针 您通常会使用什么技术 对应的内核宏是BUG ON and WARN ON 前者适用于当您想让内核恐慌并使系统崩溃 即不可恢复的
  • rusage 进程/线程的 Cpu 时间不可能

    我正在计算进程 线程正在使用的 cpu 时间之间的增量 以及某个函数的调用之间的增量 我得到了进程 cpu 时间不可能的值 有时下一次调用会返回较小的 cpu 时间 这是不可能的 这是我用来计算CPU时间的代码 u64 CpuTime ca
  • 如何将域名附加到我的服务器? [关闭]

    Closed 这个问题是无关 help closed questions 目前不接受答案 我有一个小问题 查看了这里和其他论坛 但找不到解决方案 我的网站位于 my domain com 网站位于共享主机上 在我的域名注册中 我添加了当我为
  • Docker:如何创建堆栈、多个映像或一个基础映像?

    I am new using Docker and I got the doubt of using one image base for my stack or I have to define each image depending
  • 是否可以在 python shebang 中包含命令行选项?

    我的 python 脚本顶部有规范的 shebang usr bin env python 但是 当我运行脚本时 我仍然经常希望将未缓冲的输出导出到日志文件 因此我最终调用 python u myscript py gt myscript
  • 无法从 Windows GUI 工具连接到远程 Linux 服务器上的 MySql 数据库

    我已经在 Amazon EC2 上的 Linux 服务器上设置了 mysql 数据库 这在本地效果很好 我可以登录 linux 盒子并管理 mysql 数据库 我正在尝试将本地 GUI 客户端连接到远程 mysql 但连接失败 我更新了 e
  • 如何在bash中使用kill SIGUSR2?

    我在linux中使用iptraf来监控网络流量 shell命令是 让iptraf在后台运行 iptraf s eth0 f B L traffic dat 如果我想得到结果 我必须先停止 iptraf 所以我使用 shell 命令 kill
  • 使用 tac 和 sed 反转文件

    我有一个用例 我需要搜索并替换文件中最后一次出现的字符串并将更改写回文件 下面的案例是该用例的简化版本 我正在尝试反转该文件 进行一些更改 再次将其反转并写入该文件 我为此尝试了以下代码片段 tac test sed s a b sed i
  • 如果文件没有行尾字符,则 wc -l 不计算文件的最后一个

    我需要计算 unix 文件的所有行数 该文件有 3 行 但是wc l仅给出 2 个计数 我知道它不计算最后一行 因为它没有行尾字符 任何人都可以告诉我如何计算这一行吗 grep c返回匹配行的数量 只需使用一个空字符串 作为您的匹配表达式

随机推荐

  • ubuntu设置网卡默认启动_ubuntu 网络配置

    检查网络配置命令 ifconfig 一 通过配置文件配置 新手没怎么用过Ubuntu 所以走了不少弯路 网上找了很多方法 大都没对我起到帮助作用 所以把自己的配置方法写一写 Ubuntu上连了两块网卡 eth0为外网的eth1为内网的 VP
  • Docker下载包的时候显示Connection failed [IP: 91.189.88.142 80]

    之前使用Docker生成镜像的时候 有一些包因为网络问题出现Connection failed 和 failed to fetch 这种时候我们可以通过修改Dockerfile中的下载源来防止网络连接失败 具体操作为在Dockerfile
  • 读取Excel数据

    该工具类是可以用的 注意别导错包 还有Excel是有版本控制的 我这里就没有那么详细了 工具类 import java io File import java io FileInputStream import java util Arra
  • Dubbo序列化问题排查

    h1 现象 为一个dubbbo接口新增了一个方法 code DomainObject
  • 动态建立Vxlan实现隧道跨子网互访实验配置(集中式网关场景)

    目录 基础配置 配置E V P N 在CE1 CE2 CE3开启E V P N功能 建立CE1 CE2 CE3之间的E V P N对等体 创建BD域并配置EVPN实例 选择报文进入Vxlan隧道 配置发送Type3路由 创建三层网关的Vbd
  • 【 Linux 网络编程 】Linux 下 select 的使用

    一 相关函数 NAME select FD CLR FD ISSET FD SET FD ZERO synchronous I O multiplexing 同步多路IO转接 SYNOPSIS According to POSIX 1 20
  • 简单的实现app界面劫持和后台监控

    如今越来越多的手机玩家都为了折腾一些稀奇古怪的东西 或出于好奇 或出于贪心 往往会root手机去尝试安装一些外挂 刷分 刷粉 修改版等等乱七八糟的东西 正因为此类用户的存在 一些小人就盯上了这方面的市场 去开发一些盗取财产 密码 锁机等等
  • Java面试必问的HashMap,javaweb开发的框架

    准备好套路 自我介绍 千万不能筐瓢 一定要牢记 自然流畅地介绍自己的学习经历 工作经历 项目经历 个人优势等等 抽象概念 当面试官问你是如何理解多线程的时候 你要知道从定义 来源 实现 问题 优化 应用方面系统性地回答 项目强化 必须针对简
  • Java学生信息管理系统_毕业设计项目实例(附源码)

    一 项目介绍 1 面向人群 学校的学生信息管理人员以及老师和学生 2 功能描述 1 基本功能 该学生成绩管理系统 涉及学生基本信息 姓名 学号 专业 班级 所属院系 高等数学 英语 数据结构 计算机组成原理 面向对象程序设计 系统可以完成对
  • 【重庆邮电大学协办】2023年第六届数据挖掘与知识发现国际会议(DMKD 2023)

    重庆邮电大学协办 2023年第六届数据挖掘与知识发现国际会议 DMKD 2023 重要信息 会议网址 www icdmkd org 会议时间 2023年6月24 26日 召开地点 中国 重庆 截稿时间 2023年5月24日 录用通知 投稿后
  • OPC服务器简介和入门介绍

    什么是OPC OPC代表OLE 对象链接和嵌入 过程控制 OPC是最流行的数据连接标准 用于在控制器 设备 应用程序和其他基于服务器的系统之间进行通信 而无需进入数据传输的自定义驱动程序 工厂自动化系统或过程由来自不同供应商或供应商的不同协
  • 动态主机配置协议DHCP

    文章目录 一 DHCP介绍 二 DHCP工作原理 三 DHCP实验 eNSP 四 总结 一 DHCP介绍 DHCP 全称动态主机配置协议 Dynamic Host Configuration Protocol 在大型企业网络中 会有大量的主
  • python hook android_[原创]初识Frida--Android逆向之Java层hook (一)

    博客同步 访问 0x00 文中用到的工具Frida jadx gui 一个强大的android反编译工具 genymotion模拟器 Python2 7以及frida python库 radare2 反汇编器 pycharm 0x01 ho
  • 安卓APP_ 布局(1)—— LinearLayout

    摘自 安卓APP 布局 1 LinearLayout 作者 丶PURSUING 发布时间 2021 04 05 16 46 22 网址 https blog csdn net weixin 44742824 article details
  • python决策树算法代码_Python3.0 实现决策树算法的流程

    决策树的一般流程 检测数据集中的每个子项是否属于同一个分类 if so return 类标签 Else 寻找划分数据集的最好特征 划分数据集 创建分支 节点 from math import log import operator 生成样本
  • ASP.NET微信公众号开发实记之一 接入配置

    第一步 填写服务器配置 登录微信公众平台官网后 在公众平台后台管理页面 开发者中心页 点击 修改配置 按钮 填写服务器地址 URL Token和EncodingAESKey 其中URL是开发者用来接收微信消息和事件的接口URL Token可
  • 【Unity 3D】图形界面GUI的讲解及在C#中实现用户登录界面的实战(附源码)

    需要源码请点赞关注收藏后评论区留言并且私信 在游戏开发过程中 游戏界面占据了非常重要的地位 玩家启动游戏的时候 首先看到的就是游戏的UI 其中包含图片 按钮和高级控件等等 UGUI和GUI是Unity 3D中最常用的两个UI系统 一 GUI
  • VMware workstation搭建华为FusionCompute实验环境(五)保姆级安装教程,可运行虚拟机

    VMware安装VRM 1 打开VMware 点击文件 选择新建虚拟机 2 选择 自定义 3 硬件兼容性保持默认 4 选择稍后安装操作系统 5 操作系统选择 ESXI 6 输入虚拟机名称及虚拟机保存位置 7 配置处理器 最少4个 8 配置内
  • 低功耗设计及其UPF实现第一节

    第一节 低功耗设计的基本单元和概念 大家好 最近疫情宅家 工作没有那么忙 所以写一下之前从事过的低功耗设计并和大家分享 希望大家多多提出不足 本次总结一共分为若干小节 还没定 我会从最最基础的低功耗的概念和器件开始分享 尽量全部说干货 不过
  • 暑假补卷2——进程控制:

    进程地址空间 接进程概念 写时拷贝 通常 父子代码共享 父子再不写入时 数据也是共享的 当任意一方试图写入 便以写时拷贝的方式各自一份副本 具体见下图 fork常规用法 一个父进程希望复制自己 使父子进程同时执行不同的代码段 例如 父进程等