13.linux进程基础

2023-10-30

一、进程基础

①基础概念

关于进程和线程的基本概念在操作系统中早已学过,可以概括为一下几点:

根本区别:进程是操作系统资源分配的基本单位,而线程是处理器任务调度和执行的基本单位

资源开销:每个进程都有独立的代码和数据空间(程序上下文),程序之间的切换会有较大的开销;线程可以看做轻量级的进程,同一类线程共享代码和数据空间,每个线程都有自己独立的运行栈和程序计数器(PC),线程之间切换的开销小。

包含关系:如果一个进程内有多个线程,则执行过程不是一条线的,而是多条线(线程)共同完成的;线程是进程的一部分,所以线程也被称为轻权进程或者轻量级进程。

内存分配:同一进程的线程共享本进程的地址空间和资源,而进程之间的地址空间和资源是相互独立的

影响关系:一个进程崩溃后,在保护模式下不会对其他进程产生影响,但是一个线程崩溃整个进程都死掉。所以多进程要比多线程健壮。

执行过程:每个独立的进程有程序运行的入口、顺序执行序列和程序出口。但是线程不能独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制,两者均可并发执行。

② 进程号 

        Linux 系统下的每一个进程都有一个进程号(process ID,简称 PID),进程号是一个正数,用于唯一标识系统中的某一个进程。在 Linux系统下执行 ps 命令可以查到系统中进程相关的一些信息:

        一个进程的进程的进程号也可以通过函数getpid()进行获取,函数原型如下:

getpid()函数:

#include <sys/types.h>
#include <unistd.h>
pid_t getpid(void);

使用该函数需要包含头文件<sys/types.h>和<unistd.h>。
函数返回值为 pid_t 类型变量,便是对应的进程号。

代码测试:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int main(void)
{
    pid_t pid = getpid();
    printf("本进程的进程号为:%d \n",pid);
    return 0;
}

运行结果如下:

除了getpid()函数可以获得进程号之外,getppid()函数可以获取父进程的进程号,其函数使用发方法和getpid()是一样的:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int main(void)
{
    pid_t ppid = getppid();
    printf("父进程号为:%d \n",ppid);
    return 0;
}

运行结果如下:

③ 进程的虚拟地址空间

在 Linux 系统中,采用了虚拟内存管理技术,每一个进程都在自己独立的地址空间中运行,在 32 位系统中,每个进程的逻辑地址空间均为 4GB, 这 4GB 的内存空间按照 3:1 的比例进行分配,其中用户进程享有 3G 的空间,而内核独自享有剩下的 1G 空间,如下所示:

        Linux 系统下,应用程序运行在一个虚拟地址空间中,所以程序中读写的内存地址对应也是虚拟地址,并不是真正的物理地址,譬如应用程序中读写 0x80800000 这个地址,实际上并不对应于硬件的 0x80800000这个物理地址。

为什么需要引入虚拟地址呢?
        计算机物理内存的大小是固定的,就是计算机的实际物理内存, 试想一下,如果操作系统没有虚拟地址机制,所有的应用程序访问的内存地址就是实际的物理地址, 所以要将所有应用程序加载到内存中,但是我们实际的物理内存只有 4G,所以就会出现一些问题:
(1)当多个程序需要运行时,必须保证这些程序用到的内存总量要小于计算机实际的物理内存的大小。
(2)内存使用效率低。内存空间不足时,就需要将其它程序暂时拷贝到硬盘中,然后将新的程序装入内存。然而由于大量的数据装入装出,内存的使用效率就会非常低。
(3)进程地址空间不隔离。由于程序是直接访问物理内存的,所以每一个进程都可以修改其它进程的内存数据, 甚至修改内核地址空间中的数据,所以有些恶意程序可以随意修改别的进程,就会造成一些破坏,系统不安全、不稳定。
(4)无法确定程序的链接地址。 程序运行时,链接地址和运行地址必须一致,否则程序无法运行!因为程序代码加载到内存的地址是由系统随机分配的, 是无法预知的, 所以程序的运行地址在编译程序时是无法确认的。

二、fork()创建子进程

        一个现有的进程可以调用 fork()函数创建一个新的进程, 调用 fork()函数的进程称为父进程,由 fork()函数创建出来的进程被称为子进程, fork()函数原如下所示:

#include <unistd.h>
pid_t fork(void);

使用该函数需要包含头文件<unistd.h>

fork()调用成功后,将会在父进程中返回子进程的 PID,而在子进程中返回值是 0;如果调用失败,父进
程返回值-1,不创建子进程,并设置 errno。

        fork()系统调用的关键在于,完成对其调用后将存在两个进程,一个是原进程(父进程)、另一个则是创建出来的子进程,并且每个进程都会从 fork()函数的返回处继续执行,会导致调用fork()返回两次值,子进程返回一个值、父进程返回一个值。在程序代码中,可通过返回值来区分是子进程还是父进程。

        fork()函数调用成功创建的子进程会拷贝父进程的数据段、堆、栈以及继承父进程打开过的文件描述符,因此对于每个进程修改堆栈中的变量值,互不影响。但是父进程和子进程共享同一个代码段

fork()函数用法如下:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int main(void)
{
    pid_t pid;
    pid = fork();

switch (pid) {
    case -1:
    perror("fork error");
    exit(-1);
    case 0:
        printf("这是子进程打印信息<pid: %d, 父进程 pid: %d>\n",
        getpid(), getppid());
        _exit(0); //子进程使用_exit()退出
    default:
        printf("这是父进程打印信息<pid: %d, 子进程 pid: %d>\n",
        getpid(), pid);
        exit(0);
    }
}

运行结果如下:

从图中可以看出分别打印了父进程和子进程的信息,这是因为在fork()函数创建子进程成功以后,会有两个返回值,返回值为0代表子进程,返回值为正整数(子进程的进程号)代表父进程,分别执行switch对应语句。

调用fork()函数以后,子进程会继承父进程所打开的文件描述符,它们间的关系可以用如下图概括:

        意味着父、子进程中对应的文件描述符指向了磁盘中相同的文件,因而这些文件在父、子进程间实现了共享,譬如,如果子进程更新了文件偏移量,那么这个改变也会影响到父进程中相应文件描述符的位置偏移量。

下面验证父子进程维护同一个文件描述符:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main(void)
{
    pid_t pid;
    int fd;
    int i;
    fd = open("./test.txt", O_RDWR | O_CREAT);
    if (0 > fd) {
        perror("open error");
        exit(-1);
    }
    pid = fork();
    switch (pid) {
        case -1:
            perror("fork error");
            close(fd);
            exit(-1);
        case 0:
        /* 子进程 */
        for (i = 0; i < 4; i++) //循环写入 4 次
            write(fd, "1122", 4);
            close(fd);
            _exit(0);
        default:
        /* 父进程 */
        for (i = 0; i < 4; i++) //循环写入 4 次
            write(fd, "AABB", 4);
            close(fd);
            exit(0);
    }
}

运行结果如下:

父、子进程分别对同一个文件进行写入操作,结果是接续写,不管是父进程,还是子进程,在每次写入时都是从文件的末尾写入,这是因为父子进程维护的是同一个文件描述符。


fork()函数有以下两种用法:
        (1)父进程希望子进程复制自己,使父进程和子进程同时执行不同的代码段。这在网络服务进程中是常见的,父进程等待客户端的服务请求,当接收到客户端发送的请求事件后,调用 fork()创建一个子进程,使子进程去处理此请求、而父进程可以继续等待下一个服务请求。
        (2)一个进程要执行不同的程序。 譬如在程序 app1 中调用 fork()函数创建了子进程,此时子进程是要去执行另一个程序 app2,也就是子进程需要执行的代码是 app2 程序对应的代码,需要使用exec函数族。

三、vfork()系统调用

        Linux系统还提供了vfork()函数用于创建子进程,它的使用方法和返回值和fork()函数是一样的,但是在应用场景上有一些不同,vfork()函数原型如下:

#include <sys/types.h>
#include <unistd.h>
pid_t vfork(void);

        从上一节fork()函数的用法中可以知道,fork()函数调用成功创建的子进程会拷贝父进程的数据段、堆、栈以及继承父进程打开过的文件描述符。这就会导致复制过程消耗大量的时间,降低效率。另一个原因是通常会在子进程中调用exec函数去执行新的代码段,这就会导致复制资源的浪费。为了避免这种情况引入了vfork()系统调用。

        vfork()函数和fork()函数主要的区别:

① vfork()与 fork()一样都创建了子进程,但 vfork()函数并不会将父进程的地址空间完全复制到子进程中,因为子进程会立即调用 exec(或_exit) ,于是也就不会引用该地址空间的数据。不过在子进程调用 exec 或_exit 之前,它在父进程的空间中运行、子进程共享父进程的内存。

② vfork()保证子进程先运行, 子进程调用 exec 之后父进程才可能被调度运行。

四、进程的诞生和终止 

        进程的诞生

        Linux系统下的所有进程都是由其父进程创建而来,譬如在 shell 终端通过命令的方式执行一个程序./app,那么 app进程就是由 shell 终端进程创建出来的, shell 终端就是该进程的父进程。进程号为1的init进程是所有进程的父进程。

        进程的终止

进程有两种终止方式:异常终止和正常终止。

正常终止:通常采用return、exit()或者_Exit()函数。
异常终止:调用 abort()函数异常终止进程、当进程接收到某些信号导致异常终止等。

exit()函数会执行的动作如下:
        ① 如果程序中注册了进程终止处理函数,那么会调用终止处理函数。
        ② 刷新 stdio 流缓冲区。
        ③ 执行_exit()系统调用。

五、监视子进程

        有时,父进程需要监视子进程知道子进程是何时被终止,是否是异常终止。因此Linux系统引入了系统调用wait()函数。

wait()函数:

#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status);
使用该函数需要包含头文件<sys/types.h>和<sys/wait.h>。
函数参数和返回值含义如下:
status: 参数 status 用于存放子进程终止时的状态信息,参数 status 可以为 NULL,表示不接收子进程
终止时的状态信息。
返回值: 若成功则返回终止的子进程对应的进程号;失败则返回-1。


系统调用 wait()将执行如下动作:
    调用 wait()函数,如果其所有子进程都还在运行,则 wait()会一直阻塞等待,直到某一个子进程终
止
    如果进程调用 wait(),但是该进程并没有子进程, 也就意味着该进程并没有需要等待的子进程, 那
么 wait()将返回错误,也就是返回-1、并且会将 errno 设置为 ECHILD。
    如果进程调用 wait()之前, 它的子进程当中已经有一个或多个子进程已经终止了,那么调用 wait()
也不会阻塞。 wait()函数的作用除了获取子进程的终止状态信息之外,更重要的一点,就是回收子进程的一些资源,俗称为子进程“收尸” 。

参数 status 不为 NULL 的情况下,则 wait()会将子进程的终止时的状态信息存储在它指向的 int 变量中,
可以通过以下宏来检查 status 参数:
    WIFEXITED(status): 如果子进程正常终止,则返回 true;
    WEXITSTATUS(status): 返回子进程退出状态,是一个数值,其实就是子进程调用_exit()或 exit()
时指定的退出状态; wait()获取得到的 status 参数并不是调用_exit()或 exit()时指定的状态,可通过
WEXITSTATUS 宏转换;
    WIFSIGNALED(status): 如果子进程被信号终止,则返回 true;
    WTERMSIG(status): 返回导致子进程终止的信号编号。如果子进程是被信号所终止,则可以通过
此宏获取终止子进程的信号;
    WCOREDUMP(status): 如果子进程终止时产生了核心转储文件,则返回 true;

测试代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>

int main(void)
{
    int status;
    int ret;
    int i;
    /* 循环创建 3 个子进程 */
    for (i = 1; i <= 3; i++) {
    switch (fork()) {
        case -1:
            perror("fork error");
            exit(-1);
        case 0:
        /* 子进程 */
            printf("子进程<%d>被创建\n", getpid());
            sleep(i);
            _exit(i);
        default:
        /* 父进程 */
        break;
    }
}
    sleep(1);
    printf("~~~~~~~~~~~~~~\n");
    for (i = 1; i <= 3; i++) {
        ret = wait(&status);
    printf("回收子进程<%d>, 终止状态<%d>\n", ret,
    WEXITSTATUS(status));
    }
    exit(0);
}

运行结果如下:

        通过 for 循环创建了 3 个子进程,父进程中循环调用 wait()函数等待回收子进程,并将本
次回收的子进程进程号以及终止状态打印出来。
 

waitpid()函数: 

        wait()函数有一定的局限性,它无法等待某一个特定的进程结束,只能按照顺序谁先终止就去处理谁,因此引入了waitpid()函数:

#include <sys/types.h>
#include <sys/wait.h>
pid_t waitpid(pid_t pid, int *status, int options);

pid: 参数 pid 用于表示需要等待的某个具体子进程,关于参数 pid 的取值范围如下:
    如果 pid 大于 0,表示等待进程号为 pid 的子进程;
    如果 pid 等于 0,则等待与调用进程(父进程)同一个进程组的所有子进程;
    如果 pid 小于-1,则会等待进程组标识符与 pid 绝对值相等的所有子进程;
    如果 pid 等于-1,则等待任意子进程。 wait(&status)与 waitpid(-1, &status, 0)等价。
status: 与 wait()函数的 status 参数意义相同。
options: 通常为0
返回值: 返回值与 wait()函数的返回值意义基本相同,在参数 options 包含了 WNOHANG 标志的情况
下,返回值会出现 0,稍后介绍

僵尸进程与孤儿进程

        当一个进程创建子进程之后,它们俩就成为父子进程关系,父进程与子进程的生命周期往往是不相同的,这里就会出现两个问题:

 ①父进程先于子进程结束。

父进程先于子进程结束,也就是意味着,此时子进程变成了一个“孤儿”,我们把这种进程就称为孤儿进程。 在 Linux 系统当中,所有的孤儿进程都自动成为 init 进程(进程号为 1)的子进程, 换言之, 某一子进程的父进程结束后,该子进程调用 getppid()将返回 1。


②子进程先于父进程结束。

如果子进程先于父进程结束,此时父进程还未来得及给子进程“收尸”,那么此时子进程就变成了一个僵尸进程。父进程调用 wait()(或其变体,下文不再强调)为子进程“收尸”后,僵尸进程就会被内核彻底删除。另外一种情况,如果父进程并没有调用 wait()函数然后就退出了,那么此时 init 进程将会接管它的子进程并自动调用 wait(), 故而从系统中移除僵尸进程。

六、执行新程序

        父进程通过fork()函数创建完子进程以后,子进程可以通过 exec 函数来实现运行另一个新的程序。

 语法定义如下:

所需头文件 #include <unistd.h>
函数原型 int execl(const char *path, const char *arg, ...);
int execv(const char *path, char *const argv[]);
int execle(const char *path, const char *arg, ..., char *const envp[]);
int execve(const char *path, char *const argv[], char *const envp[]);
int execlp(const char *file, const char *arg, ...);
int execvp(const char *file, char *const argv[]);
函数返回值 -1:出错

① 第一个参数是带路径的执行码(execlp、execvp函数第一个参数是无路径的,系统会根据PATH自动查找然后合成带路径的执行码);
      ② 第二个是不带路径的执行码,执行码可以是二进制执行码和Shell脚本。

调用举例如下:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
 
 int main(int argc, char *argv[])
 {
   //以NULL结尾的字符串数组的指针,适合包含v的exec函数参数
   char *arg[] = {"ls", "-a", NULL};
   
   /**
    * 创建子进程并调用函数execl
    * execl 中希望接收以逗号分隔的参数列表,并以NULL指针为结束标志
    */
   if( fork() == 0 )
   {
     // in clild 
     printf( "1------------execl------------\n" );
     if( execl( "/bin/ls", "ls","-a", NULL ) == -1 )
     {
       perror( "execl error " );
       exit(1);
     }
   }
   
   /**
    *创建子进程并调用函数execv
    *execv中希望接收一个以NULL结尾的字符串数组的指针
    */
   if( fork() == 0 )
   {
     // in child 
     printf("2------------execv------------\n");
     if( execv( "/bin/ls",arg) < 0)
     {
       perror("execv error ");
       exit(1);
     }
   }
   
   /**
    *创建子进程并调用 execlp
    *execlp中
    *l希望接收以逗号分隔的参数列表,列表以NULL指针作为结束标志
    *p是一个以NULL结尾的字符串数组指针,函数可以DOS的PATH变量查找子程序文件
    */
   if( fork() == 0 )
   {
     // in clhild 
     printf("3------------execlp------------\n");
     if( execlp( "ls", "ls", "-a", NULL ) < 0 )
     {
       perror( "execlp error " );
       exit(1);
     }
   }
   
   /**
    *创建子里程并调用execvp
    *v 望接收到一个以NULL结尾的字符串数组的指针
    *p 是一个以NULL结尾的字符串数组指针,函数可以DOS的PATH变量查找子程序文件
    */
   if( fork() == 0 )
   {
     printf("4------------execvp------------\n");
     if( execvp( "ls", arg ) < 0 )
     {
       perror( "execvp error " );
       exit( 1 );
     }
   }
   
   /**
    *创建子进程并调用execle
    *l 希望接收以逗号分隔的参数列表,列表以NULL指针作为结束标志
    *e 函数传递指定参数envp,允许改变子进程的环境,无后缀e时,子进程使用当前程序的环境
    */
   if( fork() == 0 )
   {
     printf("5------------execle------------\n");
     if( execle("/bin/ls", "ls", "-a", NULL, NULL) == -1 )
     {
       perror("execle error ");
       exit(1);
     }
   }
   
   /**
    *创建子进程并调用execve
    * v 希望接收到一个以NULL结尾的字符串数组的指针
    * e 函数传递指定参数envp,允许改变子进程的环境,无后缀e时,子进程使用当前程序的环境
    */
   if( fork() == 0 )
   {
     printf("6------------execve-----------\n");
     if( execve( "/bin/ls", arg, NULL ) == 0)
     {
       perror("execve error ");
       exit(1);
     }
   }
   return 0;
 }

运行结果如下:

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

13.linux进程基础 的相关文章

  • 完整的 C++ i18n gettext()“hello world”示例

    我正在寻找完整的 i18ngettext 你好世界的例子 我已经开始了一个基于的脚本使用 GNU gettext 的本机语言支持教程 https web archive org web 20130330233819 http oriya s
  • 运行 shell 命令并将输出发送到文件?

    我需要能够通过 php 脚本修改我的 openvpn 身份验证文件 我已将我的 http 用户设置为免通 sudoer 因为这台机器仅在我的家庭网络中可用 我目前有以下命令 echo shell exec sudo echo usernam
  • 如何在特定的Java版本上运行应用程序?

    如何运行具有特定 Java 版本的应用程序 我安装了三个 Java 版本 myuser mysystem sudo update alternatives config java There are 3 choices for the al
  • 用于时间线数据的类似 gnuplot 的程序

    我正在寻找一个类似 gnuplot用于在时间轴中绘制数据图表的程序 类似 gnuplot 在 Linux 上运行 命令行功能 GUI 对我帮助不大 可编写脚本的语法 输出为 jpg png svg 或 gif 输出应该是这样的 set5 s
  • 在用户程序中使用 或在驱动程序模块代码中使用 ...这有关系吗?

    我正在开发一个设备驱动程序模块和关联的用户库来处理ioctl 来电 该库获取相关信息并将其放入一个结构中 该结构被传递到驱动程序模块中并在那里解压 然后进行处理 我省略了很多步骤 但这就是总体思路 一些数据通过结构体传递ioctl is u
  • 如何回忆上一个 bash 命令的参数?

    Bash 有没有办法回忆上一个命令的参数 我通常这样做vi file c其次是gcc file c Bash 有没有办法回忆上一个命令的参数 您可以使用 or 调用上一个命令的最后一个参数 Also Alt can be used to r
  • 如何指定配置脚本的包含目录

    我的工作场所有一个 Linux 系统 其中包含相当旧的软件包 并且没有 root 访问权限 我正在从源代码编译我需要的包 prefix somewhere in homedir 我的问题是我只是不知道如何说服配置在特定目录中查找头文件 源码
  • Unix 中的访问时间是多少

    我想知道访问时间是多少 我在网上搜索但得到了相同的定义 读 被改变 我知道与touch我们可以改变它 谁能用一个例子来解释一下它是如何改变的 有没有办法在unix中获取创建日期 时间 stat结构 The stat 2 结构跟踪所有文件日期
  • 在 vimrc 中切换匹配

    我的 vimrc 文件中有以下几行 hi ExtraWhitespace cterm NONE ctermbg green ctermfg green guibg green guifg green match ExtraWhitespac
  • 无法连接到 Azure Ubuntu VM - 公钥被拒绝

    我们在 Azure 上使用 Ubuntu VM 一段时间了 很少遇到任何问题 然而 其中一台虚拟机最近出现了问题 出乎意料的是 Ubuntu VM 开始拒绝公钥 ssh i azure key email protected cdn cgi
  • 如何在 Linux 中向热敏打印机发送 ESC/POS 命令

    我正在尝试在热敏打印机上发送 ESC POS 命令 但每当我发送它们时 热敏打印机都会将它们打印为文本 而不是作为命令执行它们 我在 prn 文件中编写这些命令 每当我执行 lp 命令来打印文件时 这些 prn 文件也会被打印 但作为文本
  • 如何将命令输出作为多个参数传递给另一个命令

    我想将命令的每个输出作为多个参数传递给第二个命令 例如 grep pattern input returns file1 file2 file3 我想复制这些输出 例如 cp file1 file1 bac cp file2 file2 b
  • Linux 中有没有一种轻量级的方法来获取当前进程数?

    我希望我的 基于 C C 的 程序显示一个数字指示器 指示本地系统上当前有多少个进程 将经常查询正在运行的进程数值 例如每秒一次 以更新我的显示 有没有一种轻量级的方法来获取该数字 显然我可以调用 ps ax wc l 但我不想强迫计算机生
  • 通过名称获取进程ID

    我想在 Linux 下获得一个给定其名称的进程 ID 有没有一种简单的方法可以做到这一点 我还没有在 C 上找到任何可以轻松使用的东西 如果追求 易于使用 char buf 512 FILE cmd pipe popen pidof s p
  • EULA 接受 Bash 脚本

    我有一个尝试安装垃圾箱的脚本 除了 bin 在 more 中打开 EULA 之外 一切正常 在脚本再次开始并自行完成安装之前 您必须手动 ctrl c 退出此 more 实例 因为这更多的是逃离 shell 所以脚本在打开后不知道要运行什么
  • 删除 Python 中某些操作的 root 权限

    在我的 Python 脚本中 我执行了一些需要 root 权限的操作 我还创建并写入文件 我不想由 root 独占所有 而是由运行我的脚本的用户独占所有 通常 我使用以下命令运行脚本sudo 有办法做到上述吗 您可以使用以下方式在 uid
  • Flex 的远程版本误解了我的规则

    我使用 flex 和 bison 编写了一个小汇编程序 可以在我的机器 ubuntu 10 10 上构建并运行正常 现在其他人正在尝试在 arch linux 上构建它 并且他们安装的 flex 产生了不同的 lex yy c 这是不匹配的
  • “./somescript.sh”和“. ./somescript.sh”有什么区别

    今天我按照一些说明在 Linux 中安装软件 有一个需要首先运行的脚本 它设置一些环境变量 指令告诉我执行 setup sh 但是我执行时犯了一个错误 setup sh 所以环境没有设置 最后我注意到了这一点并继续进行 我想知道这两种调用脚
  • 在Linux中创建可执行文件

    我计划做的一件事是编写 非常简单的 Perl 脚本 并且我希望能够在不从终端显式调用 Perl 的情况下运行它们 我明白 要做到这一点 我需要授予他们执行权限 使用 chmod 执行此操作非常简单 但它似乎也是一个稍微费力的额外步骤 我想要
  • ARM 的内核 Oops 页面错误错误代码

    Oops 之后的错误代码给出了有关 ARM EX 中的恐慌的信息 Oops 17 1 PREEMPT SMP在这种情况下 17 给出了信息 在 x86 中它代表 bit 0 0 no page found 1 protection faul

随机推荐

  • Weblogic 12c 负载均衡和session复制

    在上一篇 我们介绍了weblogic集群的部署和session的复制 如何将请求负载均衡到这个三个服务器上呢 这里提供两种方式 1 weblogic自带的proxy代理 2 nginx实现负载均衡 一 通过proxy实现负载均衡 1 创建p
  • PyTorch动态神经网络

    PyTorch 是 PyTorch 在 Python 上的衍生 因为 PyTorch 是一个使用 PyTorch 语言的神经网络库 Torch 很好用 但是 Lua 又不是特别流行 所有开发团队将 Lua 的 Torch 移植到了更流行的语
  • 在linux下启动tomcat命令

    操作步骤 第一步 进入tomcat的bin目录 cd usr local tomcat bin 第二步 使用tomcat关闭命令 shutdown sh 第三步 查看tomcat是否关闭 ps ef grep java如果显示以下信息 则说
  • cherry-pick的定义和使用方法

    1 定义 使用遴选 cherry pick 命令 Git 可以让你将任何分支中的个别提交合并到你当前的 Git HEAD 分支中 当执行 git merge 或者 git rebase 时 一个分支的所有提交都会被合并 cherry pic
  • 使用jdbc连接oracle数据库

    1 导入架包ojdbc14 2 编写工具类 package utils import java sql Connection import java sql DriverManager import java sql SQLExceptio
  • Axure RP 9最新版的授权码(专业版)亲测可用

    分享AxureRP9授权码和密钥 Axure RP9分为专业版 Pro 团队版 Team 和企业版 Enterprise 其中企业版功能最为强大 下面分享几个Axure RP 9最新版的授权码 专业版 亲测可用 被授权人 License A
  • Gof23设计模式之简单工厂/静态工厂模式

    在java中 万物皆对象 这些对象都需要创建 如果创建的时候直接new该对象 就会对该对象耦合严重 假如我们要更换对象 所有new对象的地方都需要修改一遍 这显然违背了软件设计的开闭原则 如果我们使用工厂来生产对象 我们就只和工厂打交道就可
  • JavaScript 检查变量是否为 null 或 undefined

    当它的左侧操作数为null 或 undefined时 返回右侧的操作数 否则返回其左侧的操作数 用法 想检查一个变量是否为 null 或 undefined 时 操作符很有用 const foo null Hello console log
  • python小游戏 推箱子小游戏设计与实现

    文章目录 0 项目简介 1 游戏介绍 2 实现效果 3 开发工具 3 1 环境配置 3 2 Pygame介绍 4 具体实现 5 最后 0 项目简介 Hi 各位同学好呀 这里是L学长 今天向大家分享一个今年 2022 最新完成的毕业设计项目作
  • Spring之@AutoWired注解

    开发中依赖注入一般用 AutoWired 首先需要bean xml文件中需要配置
  • C语言字符串必备练习题

    1 作业标题 633 字符串的结束标志是 作业内容 A 是 0 B 是EOF C 是 0 D 是空格 答案解析 C语言规定 以 0 作为有效字符串的结尾标记 A 错误 是 0 不是字符0 B EOF一般用来作为检测文本文件的末尾 C 正确
  • 机器学习笔试题汇总

    文章目录 树 特征工程 样本处理 K近邻 聚类 深度学习 分类 距离 相似度 指标性能评价 HMM 数学 为什么将回归问题转化成分类问题 解空间变小 softmax和sigmoid的区别 为什么用多项分布 多项分布能天然刻画值域变化趋势的
  • 演讲文档和视频《元宇宙与区块链IT基础设施》下载

    当今科技迅猛发展 知识爆炸的时代 有些知识 非公司保密信息 及那些不需要申请知识产权 构建护城河的信息 不及时分享 就过期了 重要的是 构建个人知识壁垒的根本是 快速迭代学习和交流碰撞 另外 我希望能遇到更多有共鸣 相互启发的朋友 我的QQ
  • RESETLOGS

    使用resetlogs选项 会把当前的日志序号 log sequence number 重设为1 并抛弃所有日志信息 在以下条件时需要使用resetlogs选项 在不完全恢复 介质恢复 使用备份控制文件 使用resetlogs打开数据库后
  • AttributeError: module ‘networkx‘ has no attribute ‘from_numpy_matrix‘解决方法

    在我学习louvain算法时 运行了这样一段代码 from communities algorithms import louvain method from communities visualization import draw co
  • 初次使用QT5串口类QSerialPort

    因为工作需要 现在正在学习Qt的串口通信 Qt4的话需要使用第三方类 使用起来也非常简单 只需要把对应的文件添加到自己的项目中就可以了 我参考的是Qt论坛上的demo 刘大师和yafei的demo都非常详细 网上都可以下载到 不过 Qt5添
  • Elementui设置样式不起效

    在使用ElementUI时 其渲染的dom元素有时是在模板外的 虽然代码写在了vue文件对用的模板内 但elementui在渲染的时候可能会渲染到和body一级 如这种弹窗 有时候想给这种el dialog加个样式 发现是不起效的 原因是
  • VCS命令行CTRL+C后dump完整的fsdb波形

    UCLI命令行CTRL C后dump完整的fsdb波形 1 ucli fsdbDumpFinish 2 ucli fsdbDumpvars 0 harness mda struct 如果仿真过程中直接CTRL C会调到UCLI接口 此时如果
  • 工作不好找,普通打工人如何破局

    大家好 我是苍何 我的一位阿里朋友被裁后 找工作找了一个月都没结果 很多到最后一面被pass了 不由得做一下感慨 即使是大厂背景又如何 面对经济环境和大环境市场 每个人都不容易 我身边很多都是程序员群体 最近也在在编程导航 收到了很多小伙伴
  • 13.linux进程基础

    一 进程基础 基础概念 关于进程和线程的基本概念在操作系统中早已学过 可以概括为一下几点 根本区别 进程是操作系统资源分配的基本单位 而线程是处理器任务调度和执行的基本单位 资源开销 每个进程都有独立的代码和数据空间 程序上下文 程序之间的