Linux——进程并发控制(系统中的POSIX信息量机制、进程间通信)

news2024/12/25 1:12:47

目录

一、Linux系统中POSIX信号量机制

1、POSIX有名信号量 

(1)常用函数

(2)有名信号量应用于多线程的例子

(3)有名信号量应用于多进程

2、POSIX无名信号量

(1)常用函数

(2) 无名信号量实现一个进程的各个线程间的互斥

(3)无名信号量实现各进程间的互斥

(4) 信息量的共享内存

 二、Linux系统中的进程间通信

1、Signal(信号)机制

2、消息队列

(1)发送进程

(2)参数 

3、共享内存

(1)函数

(2)参数定义

4、管道机制

(1)有名管道

(2)无名管道 

一、Linux系统中POSIX信号量机制

POSIX有两种信号量实现机制:

  • 无名信号量:基于内存操作
  • 有名信号量:基于文件操作

1、POSIX有名信号量 

(1)常用函数

  • sem_open()  //创建并初始化有名信号量
  • sem_close()  //关闭有名信号量
  • sem_unlink()  //从系统中删除信号量
  • sem_getvalue()  //测试信号量
  • sem_wait()/sem_trywait()  //等待共享资源
  • sem_post()  //挂出共享资源
sem_t *sem_open
(
const char *name, //信号量的外部名字
int oflag,  //选择创建或打开一个现有的信号量
mode_t mode,  //权限位
unsigned int value //信号量初始值
);

//Posix有名信号量的值是随内核持续的。也就是说,一个进程创建了一个信号量,这个进程结束后,这个信号量还存在,并且信号量的值也不会改变。
如:
# ./semopen test
# ./semgetvalue test
value=1 //信号的值仍然是1

//当持有某个信号量锁的进程没有释放它就终止时,内核并不给该信号量解锁。
#./semopen test
#./semwait test&
pid 1834 has semaphore,value=0
#./semgetvalue test
value=0  //信号量的值变为0了

(2)有名信号量应用于多线程的例子

#include <semaphore.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <pthread.h>
 
void *thread_function(void *arg); /*线程入口函数*/
void print(pid_t); /*共享资源函数*/
sem_t *sem; /*定义Posix有名信号量*/
int val; /*定义信号量当前值*/

int main(int argc,char *argv[])
{
  int n=0;
  if(argc!=2)
  {
      printf("please input a file name!\n");
      exit(1);
  }
  sem=sem_open(argv[1],O_CREAT,0644,3); /*打开一个信号量*/
  while(n++<5) /*循环创建5个子线程,使它们同步运行*/
  {
    if((pthread_create(&a_thread,NULL,thread_function,NULL))!=0)
        {
             perror("Thread creation failed");
             exit(1);
         }
  }
  pthread_join(a_thread,NULL); //等待线程结束,类似于wait()
  sem_close(sem);
  sem_unlink(argv[1]);
}

void *thread_function(void *arg)
{
    sem_wait(sem); /*申请信号量*/
    print(); /*调用共享代码段*/
    sleep(1); //滞留1s
    sem_post(sem); /*释放信号量*/
    printf("I’m finished,my tid is %d\n",pthread_self());
}
void print() //模拟的共享代码
{
  printf("I get it,my tid is %d\n",pthread_self()); //显示当前线程号
  sem_getvalue(sem,&val);
  printf("Now the value have %d\n",val); //显示信号量值
}

(3)有名信号量应用于多进程

#include <semaphore.h>
#include <unistd.h>
#include <stdio.h>#
#include<stdlib.h>
#include <fcntl.h>
void print(pid_t);
sem_t *sem; /*定义Posix有名信号量*/
int val; /*定义信号量当前值*/

int main(int argc,char *argv[])
{
  int n=0;
  if(argc!=2)
  {
      printf("please input a file name!\n");
      exit(1);  
  }
  sem=sem_open(argv[1],O_CREAT,0644,2); /*打开一个信号量, 初值设为2*/
  while(n++<5) /*循环创建5个子进程,使它们同步运行*/
  {
    if(fork()==0) 
    {
        sem_wait(sem); /*申请信号量*/
        print(getpid()); /*调用共享代码段*/
        sleep(1); 
        sem_post(sem); /*释放信号量*/
        printf("I’m finished,my pid is %d\n",getpid());
        return 0; 
    }
  }
  wait(); /*等待所有子进程结束*/
  sem_close(sem);
  sem_unlink(argv[1]);
  exit(0);
}

void print(pid_t pid)
{
  printf("I get it,my pid is %d\n",pid);
  sem_getvalue(sem,&val);
  printf("Now the value have %d\n",val);
}

运行结果:

2、POSIX无名信号量

 Posix提供的无名信号量是基于内存的信号量,它们由应用程序分配信号量的内存空间,然后由系统初始化它们的值。

(1)常用函数

int sem_init(sem_t *sem,int shared,unsigned int value);
int sem_getvalue(sem_t *sem);
基于内存的信号量是由sem_init初始化的。sem参数指向必须由应用程序分配的sem_t变量。
shared为0,待初始化的信号量是在同一进程的各个线程共享的,否则该信号量是在进程间共享的。
shared为非0,该信号量必须存放在即将使用它的所有进程都能访问的某种类型的共享内存中。跟sem_open一样,value参数是该信号量的初始值。
 使用完一个基于内存的信号量后,调用sem_destroy关闭它。
除了sem_open和sem_close外,其它的poisx有名信号量函数都可以用于基于内存的信号量。 

(2) 无名信号量实现一个进程的各个线程间的互斥

#include <semaphore.h>
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <pthread.h>
#include <stdlib.h>
void *thread_function(void *arg); /*线程入口函数*/
void print(void); /*共享资源函数*/
sem_t bin_sem; /*定义信号量*/
int value; /*定义信号量的灯*/

int main()
{
  int n=0;
  pthread_t a_thread; 
  if((sem_init(&bin_sem,0,2))!=0) /*初始化信号量,初始值为2*/
  {
      perror("sem_init");
      exit(1);
  }
  while(n++<5) /*循环创建5个线程*/
  {
    if((pthread_create(&a_thread,NULL,thread_function,NULL))!=0) 
    {
      perror("Thread creation failed");
      exit(1);
    }
  }
  pthread_join(a_thread,NULL);/*等待子线程返回*/
}

void *thread_function(void *arg)
{
  sem_wait(&bin_sem); /*等待信号量*/
  print();
  sleep(1);
  sem_post(&bin_sem); /*唤起信号量*/
  printf("I finished,my pid is %d\n",pthread_self());
  pthread_exit(arg);
}
void print()
{
  printf("I get it,my tid is %d\n",pthread_self());
  sem_getvalue(&bin_sem,&value); /*获取信号量的值*/
  printf("Now the value have %d\n",value);
}

运行结果:

 

(3)无名信号量实现各进程间的互斥

#include <semaphore.h>
#include <unistd.h>
#include<stdlib.h>
#include <stdio.h>
void print(pid_t);
sem_t bin_sem; /*定义Posix信号量*/
int val; /*定义信号量当前值*/


int main() 
{
  int n=0;
  if((sem_init(&bin_sem,0,3))!=0) /*初始化信号量,初始值为3*/
  {
    perror("sem_init");
    exit(1);
  }
  sem_getvalue(&bin_sem,&val); /*查看信号量的值*/
  printf("The value have %d\n",val);
  while(n++<5) /*循环创建5个子进程,使它们同步运行*/
  {
    if(fork()==0) 
    {
        sem_wait(&bin_sem); /*申请信号量*/
        print(getpid()); /*调用共享代码段*/
        sleep(1); 
        sem_post(&bin_sem); /*释放信号量*/
        printf("I’m finished,my pid is %d\n",getpid());
        return 0; 
    }
  }
  wait(); /*等待所有子进程结束*/
  return 0;
}

void print(pid_t pid)
{
  printf("I get it,my pid is %d\n",pid);
  sem_getvalue(&bin_sem,&val);
  printf("Now the value have %d\n",val);
}

 运行结果:

(4) 信息量的共享内存

问题在于sem信号量不在共享内存区中。fork出来的子进程通常不共享父进程的内存空间。子进程是在父进程内存空间的拷贝上启动的,它跟共享内存不是一回事。

  • sem_open不需要shared参数,有名信号量总是可以在不同进程间共享的。
  • sem_init不使用任何类似于O_CREAT标志的东西,也就是说,sem_init总是初始化信号量的值。因此,对于一个给定的信号量,我们必须小心保证只调用一次sem_init。
  • sem_open返回一个指向某个sem_t变量的指针,该变量由函数本身分配并初始化。但sem_init的第一个参数是一个指向某个sem_t变量的指针,该变量由调用者分配,然后由sem_init函数初始化。
  • posix有名信号量是通过内核持续的,一个进程创建一个信号量,另外的进程可以通过该信号量的外部名(创建信号量使用的文件名)来访问它。 posix基于内存的信号量的持续性却是不定的,如果基于内存的信号量是由单个进程内的各个线程共享的,那么该信号量就是随进程持续的,当该进程终止时它也会消失。 如果某个基于内存的信号量是在不同进程间同步的,该信号量必须存放在共享内存区中,只要该共享内存区存在,该信号量就存在。
  • 基于内存的信号量应用于进程很麻烦,而有名信号量却很方便,基于内存的信号量比较适合应用于一个进程的多个线程。

 二、Linux系统中的进程间通信

通信方法:

  • 信号量---semaphore
  • 信号---signal
  • 共享内存---share memory
  • 消息队列---message queque
  • 管道---pipe
  • 套接字---socket 

①信号量(semaphore):主要作为进程间以及同一进程不同线程之间的同步手段。

②信号(Signal):信号是比较复杂的通信方式,用于通知接受进程有某种事件发生,除了用于进程间通信外,进程还可以发送信号给进程本身;linux除了支持Unix早期信号语义函数signal外,还支持语义符合Posix.1标准的信号函数sigaction;

 ③管道(Pipe)及有名管道(FIFO):管道可用于具有亲缘关系进程间的通信,有名管道克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允许无亲缘关系进程间的通信;FIFO是全双工的管道,管道是半双工的。

④消息队列(message queue):消息队列是消息的链接表,包括Posix消息队列和system V消息队列。有足够权限的进程可以向队列中添加消息,被赋予读权限的进程则可以读走队列中的消息。消息队列克服了信号承载信息量少,管道只能承载无格式字节流以及缓冲区大小受限等缺点。

⑤共享内存(Share memory):使得多个进程可以访问同一块内存空间,是最快的可用IPC形式。是针对其他通信机制运行效率较低而设计的。往往与其它通信机制,如信号量结合使用,来达到进程间的同步及互斥。

⑥套接口(Socket):更为一般的进程间通信机制,可用于不同机器之间的进程间通信。起初是由Unix系统的BSD分支开发出来的,但现在一般可以移植到其它类Unix系统上:Linux和System V的变种都支持套接字。

拓展:

  • 最初Unix IPC包括:管道、FIFO、信号;
  • System V IPC包括:System V消息队列、System V信号量、System V共享内存区;
  • Posix IPC包括: Posix消息队列、Posix信号量、Posix共享内存区。

1、Signal(信号)机制

        Signal机制可以被理解成进程的软中断,因此,在实时性方面还是相对比较高的。进程控制块中设计了一个signal的位图信息(在signal.h中定义),其中的每位与具体的signal相对应,这与中断机制是保持一致的。

       当系统中一个进程A通过signal系统调用向进程B发送signal时,设置进程B的对应signal位图,类似于触发了signal对应中断。发送signal只是“中断”触发的一个过程,具体执行会在两个阶段发生:

1、  system call返回。进程B由于调用了system call后,从内核返回用户态时需要检查他拥有的signal位图信息表,此时是一个执行点。

2、  中断返回。进程被系统中断打断之后,系统将CPU交给进程时,需要检查即将执行进程所拥有的signal位图信息表,此时也是一个执行点。

2、消息队列

       消息队列为进程提供了一种异步传递消息的方法。在使用msgget()建立了一条消息队列之后,发送进程和接收进程就可以通过这条消息队列交换消息。

(1)发送进程

发送进程将消息发送到指定的消息队列,而接收者试图从指定的消息队列中获取消息。如果该队列中没有消息的话,则接收者根据自己是否要等待的意愿而阻塞或返回某个标志。

key_t ftok (char*pathname, char proj_id); 

该函数用于为消息队列、信号量、共享存储等IPC机制创建键值。

pathname是一个存在的文件名。

proj_id是子序号,

在实现中,是将文件的索引节点号取出,前面加上子序号得到key_t的返回值。

int msgget(key_t key, int msgflg)

(2)参数 

参数key是一个键值,由ftok获得;

msgflg参数是一些标志位。该调用返回与健值key相对应的消息队列描述字。

在以下两种情况下,该调用将创建一个新的消息队列:

  • 1.如果没有消息队列与健值key相对应,并且msgflg中包含了IPC_CREAT标志位;
  • 2. key参数为IPC_PRIVATE;

 参数msgflg可以为以下:IPC_CREAT、IPC_EXCL、IPC_NOWAIT或三者的或结果;为0则不创建,只查找已有的消息队列。

调用返回:成功返回消息队列描述字,否则返回-1。

注:参数key设置成常数IPC_PRIVATE并不意味着其他进程不能访问该消息队列,只意味着即将按照随机数创建新的消息队列。

int msgrcv(int msgqid, struct msgbuf *msgp, int msgsz, long msgtype, int msgflg);

该系统调用从msgqid代表的消息队列中读取一个消息,并把消息存储在msgp指向的msgbuf结构中。

msgqid为消息队列描述字

消息返回后存储在msgp指向的地址

msgsz指定msgbuf的mtext成员的长度(即消息内容的长度)

msgtype为请求读取的消息类型;

3、共享内存

     共享内存提供了一种在进程间高效共享大量数据的方法。共享内存是IPC 机制提供的最重要资源之一,广泛用在许多数据库应用之中。

      System V 的共享内存段由shmget()系统调用创建。在创建共享内存段之后,进程通过shmat()系统调用将自身附加到该共享内存段上,然后就可以对其执行读写操作。进程还可以通过shmdt()系统调用将自身脱离内存段。

 由于共享内存为多个进程提供了一种公共资源,它经常和信号量一起使用以防止发生碰撞。

shmid=shmget(key,size,flag)  //创建、获得一个共享存储区。

  • key是共享存储区的名字;
  • size是其大小(以字节计);
  • flag是用户设置的标志,如IPC_CREAT。

(1)函数

  • shmat

共享存储区的附接。从逻辑上将一个共享存储区附接到进程的虚拟地址空间上。

系统调用格式:   virtaddr=shmat(shmid,addr,flag)

•shmid是共享存储区的标识符;

•addr是用户给定的,将共享存储区附接到进程的虚地址空间;

•flag规定共享存储区的读、写权限,以及系统是否应对用户规定的地址做舍入操作。其值为SHM_RDONLY时,表示只能读;其值为0时,表示可读、可写;其值为SHM_RND(取整)时,表示操作系统在必要时舍去这个地址。v该系统调用的返回值是共享存储区所附接到的进程虚地址virtaddr。

  • shmdt

把一个共享存储区从指定进程的虚地址空间断开。

系统调用格式:     shmdt(virtaddr)

virtaddr是要断开连接的虚地址,亦即以前由连接的系统调用shmat( )所返回的虚地址。

调用成功时,

返回0值,调用不成功,返回-1。

  • shmctl

共享存储区的控制,对其状态信息进行读取和修改。

   shmctl(int shmid,int cmd,struct shmid_ds *buf)

(2)参数定义

         参数定义,buf是用户缓冲区地址,cmd是操作命令。命令可分为多种类型:

(1)IPC_STAT用于查询有关共享存储区的情况。如其长度、当前连接的进程数、共享区的创建者标识符等;

(2)IPC_SET 用于设置或改变共享存储区的属性。如共享存储区的许可权、当前连接的进程计数等;

(3)SHM_LOCK /SHM_UNLOCK对共享存储区的加锁和解锁命令;

(4)IPC_RMID 删除共享存储区标识符等。

状态查询是将shmid所指示的数据结构中的有关成员,放入所指示的缓冲区中;

设置是用由buf所指示的缓冲区内容来设置由shmid所指示的数据结构中的相应成员。

4、管道机制

      所谓管道,是指能够连接一个写进程和一个读进程的、并允许它们以生产者—消费者方式进行通信的一个共享文件,又称为pipe文件。由写进程从管道的写入端(句柄1)将数据写入管道,而读进程则从管道的读出端(句柄0)读出数据。

(1)有名管道

     有名管道是一个可以在文件系统中长期存在的、具有路径名的文件。

     用系统调用mknod建立。可让更多的进程也能利用管道进行通信。其它进程可以知道它的存在,并能利用路径名来访问该文件。对有名管道的访问方式与访问其他文件一样,需先用open( )打开。

(2)无名管道 

     无名管道是一个临时文件。利用pipe建立起来的无名文件(无路径名)。

     只用该系统调用所返回的文件描述符来标识该文件,故只有调用pipe的进程及其子孙进程才能识别此文件描述符,才能利用该文件(管道)进行通信。

当这些进程不再使用此管道时,核心收回其索引结点。

两种管道的读写方式是相同的。

pipe文件的建立:

  • 分配磁盘和内存索引结点
  • 为读进程分配文件表项
  • 为写进程分配文件表项
  • 分配用户文件描述符
  • 读/写进程互斥
  • 内核为地址设置一个读指针和一个写指针,按先进先出顺序读、写。

      为使读、写进程互斥地访问pipe文件,需使各进程互斥地访问pipe文件索引结点中的直接地址项。

       因此,每次进程在访问pipe文件前,都需检查该索引文件是否已被上锁。若是,进程便睡眠等待,否则,将其上锁,进行读/写。操作结束后解锁,并唤醒因该索引结点上锁而睡眠的进程。

1、pipe 建立一无名管道。

    系统调用格式      int pipe(int filedes[2])

    参数定义,filedes[1]是写入端,filedes[0]是读出端。

 2、read

  系统调用格式     int read(int fd,char *buf, unsigned int nbyte)

  功能:从fd所指示的文件中读出nbyte个字节的数据,并将它们送至由指针buf所指示的缓冲区中。如该文件被加锁,等待,直到锁打开为止。

 3、write

系统调用格式       int write(int fd,char *buf,unsigned int nbyte)

功能:把nbyte 个字节的数据,从buf所指向的缓冲区写到由fd所指向的文件中。如文件加锁,暂停写入,直至开锁。参数定义同read。

如有错误,敬请指正。

您的收藏与点赞都是对我最大的鼓励和支持!

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

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

相关文章

基于jsp+mysql+ssm大学生社交平台-计算机毕业设计

项目介绍 本系统需要满足校园网上社交方面的基本需要。需要实现用户所要求的功能&#xff0c;方便他们进行交流。在界面上力求做到美观、操作方面尽量避免由于会员操作不当带来系统的出错现象。对数据库操作的性能需要做到优化&#xff0c;数据库过大将会影响运行速度。编程过…

(四) Docker镜像

Docker镜像一、概述二、镜像加载原理三、镜像注意点四、Docker镜像commit操作五、总结一、概述 书面解释 是一种轻量级、可执行的独立软件包&#xff0c;它包含运行某个软件所需的所有内容&#xff0c;我们把应用程序和配置依赖打包好形成一个可交付的运行环境(包括代码、运行时…

开关电源环路稳定性分析(04)-电压控制模式

大家好&#xff0c;这里是大话硬件。 在前3节分析了一个开环电源是如何工作的&#xff0c;开环电源的弊端也很明显&#xff0c;无法维持输出的稳定&#xff0c;不能抗扰动&#xff0c;无法得到我们想要的电压等等。因此&#xff0c;开关电源的闭环环路对稳定性来说非常重要。 …

LeetCode简单题之统计共同度过的日子数

题目 Alice 和 Bob 计划分别去罗马开会。 给你四个字符串 arriveAlice &#xff0c;leaveAlice &#xff0c;arriveBob 和 leaveBob 。Alice 会在日期 arriveAlice 到 leaveAlice 之间在城市里&#xff08;日期为闭区间&#xff09;&#xff0c;而 Bob 在日期 arriveBob 到 l…

大数据:Storm和流处理简介

一、Storm 1.1 简介 Storm 是一个开源的分布式实时计算框架&#xff0c;可以以简单、可靠的方式进行大数据流的处理。通常用于实时分析&#xff0c;在线机器学习、持续计算、分布式 RPC、ETL 等场景。Storm 具有以下特点&#xff1a; 支持水平横向扩展&#xff1b;具有高容错…

信息安全技术 信息安全风险评估方法 汇总

概述 风险评估应贯穿于评估对象生命周期 各阶段中。评估对象生命周期各阶段中涉及的风险评估原则和方法昆一致的&#xff0c;但由干各阶段实施内容对象、安全需求不同.使得风险评估的对象、目的、要求等各方面也有所不同。在规划设计阶段&#xff0c;通过风险评估以确定评估对…

(推荐阅读)H264, H265硬件编解码基础及码流分析

需求 在移动端做音视频开发不同于基本的UI业务逻辑工作,音视频开发需要你懂得音视频中一些基本概念,针对编解码而言,我们必须提前懂得编解码器的一些特性,码流的结构,码流中一些重要信息如sps,pps,vps,start code以及基本的工作原理,而大多同学都只是一知半解,所以导致代码中的…

JAVA-元注解和注解

故事背景&#xff1a;罗芭是一名正在学习java的妹子&#xff0c;最近看甲骨文的官方文档&#xff0c;学到了注解Annotation这里&#xff0c;发现注解我可以自定义&#xff0c;但罗芭不会诶。但是布洛特 亨德尔已经学习过了java注解。 罗芭&#xff0c;help me~ 唰唰唰&#xff…

Redis05:Redis高级部分

Redis高级部分SpringBoot整合Redis整合测试序列化配置解决乱码问题redis自定义RedisTemplateSpringBoot整合Redis 说明&#xff1a;在SpringBoot2.x之后&#xff0c;原来使用jedis被替换成了letttuce! jedis:采用的时直连&#xff0c;多个线程操作的话&#xff0c;是不安全的&a…

MySQL下载和安装(Windows)

前言&#xff1a;刚换了一台电脑&#xff0c;里面所有东西都需要重新配置&#xff0c;习惯了所有东西都配好的装配&#xff0c;突然需要自己从头来配才发现不知道如何下手&#xff0c;所以决定将这些步骤都做个记录&#xff0c;以便后续查看。仅限没有安装过的人使用&#xff0…

关于海量级存储用户标签体系架构

项目场景&#xff1a; 对于我们运营来说&#xff0c;需要给用户打上不同的身份标签。比如用户是否偏重&#xff0c;身高范围&#xff0c;是不是我们的会员。。。等等一些标签。 比如我们有100W用户。我们需要来给100W用户打上接近200个不同身份的标签应该如何去做&#xff1f…

【数据集NO.4】遥感图像数据集汇总

文章目录前言一、NWPU VHR-10卫星图像数据集二、RSOD三、DIOR四、DOTA五、HRSC2016六、UCAS AOD七、HRRSD八、SSDD九、DLR 3K Vehicle前言 数据集对应应用场景&#xff0c;不同的应用场景有不同的检测难点以及对应改进方法&#xff0c;本系列整理汇总领域内的数据集&#xff0…

《MySQL实战45讲》——学习笔记20 “幻读、全表扫描的加锁方式、间隙锁、next-key lock“

本篇介绍MySQL在可重复度RR隔离级别下&#xff0c;引入的一种锁机制&#xff1a;间隙锁 (Gap Lock)&#xff1b;间隙锁与事务相关的表锁、行锁不同&#xff0c;它锁的是“往这个间隙中插入一个记录”这个操作&#xff0c;除此之外间隙锁之间都不存在冲突关系&#xff08;因而有…

Spring @Autowire注解源码详解

目录 一&#xff1a;触发方式&#xff1a; 二&#xff1a;源码解析 2.1 扫描注入点 2.2 属性赋值 一&#xff1a;触发方式&#xff1a; 1.Spring容器在每个Bean实例化之后&#xff0c;调用AutowireAnnotationBeanPostProcessor的postProcessMergedBeanDefinition方法进行扫…

[附源码]计算机毕业设计美发店会员管理系统Springboot程序

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

七周成为数据分析师 | 数据可视化

一.常见的初级图表 维度&#xff1a;描述分析的角度和属性&#xff0c;分类数据&#xff08;时间&#xff0c;地理位置&#xff0c;产品类型等&#xff09; 度量&#xff1a;具体的参考数值&#xff0c;数值数据&#xff08;元&#xff0c;销量&#xff0c;销售金额等&#x…

windows监控linux服务器资源grafana+prometheus+node_exporter

Windows环境监控Linux服务器资源grafanaprometheusnode_exporter 1.安装包下载 链接&#xff1a;https://pan.baidu.com/s/1xqdIYNtadt2tRSN-XlELUw 提取码&#xff1a;12342.安装grafana &#xff08;1&#xff09;将压缩包解压后&#xff0c;在bin文件目录下&#xff0c;点…

【Linux】shell命令以及运行原理和Linux权限详解

本期主题&#xff1a;Linux权限详解博客主页&#xff1a;小峰同学分享小编的在Linux中学习到的知识和遇到的问题小编的能力有限&#xff0c;出现错误希望大家不吝赐 目录 &#x1f341;1.shell命令以及运行结果 &#x1f341;2. Linux用户管理 &#x1f341;3. Linux权限管理 …

Python进阶学习之阅读代码

起因 在这个过程中&#xff0c;学习到了一些东西&#xff0c;同时整理了自己以前的一些收获&#xff0c;然后分享给大家&#xff0c;有不对的地方还望海涵、指正。 阅读代码有助于处理bug 阅读代码是一项更重要的技能&#xff0c;在大学编程语言的考试中也有相关的考察——代…

Paper reading:Fine-Grained Head Pose Estimation Without Keypoints (CVPR2018)

Paper reading&#xff1a;Fine-Grained Head Pose Estimation Without Keypoints &#xff08;CVPR2018&#xff09; 一、 背景 为什么要读这篇论文&#xff0c;因为LZ之前要做头部姿态估计&#xff0c;看到一些传统的方法&#xff0c;都是先进行人脸检测&#xff0c;然后再…