当前位置: 首页 > news >正文

【linux】重定向+缓冲区

重定向+缓冲区

  • 1.重定向
    • 1.1重定向本质
    • 1.2重定向接口
    • 1.3重定向分类
      • 1.3.1>输出重定向
      • 1.3.2>>追加重定向
      • 1.3.3<输入重定向
  • 2.理解 >, >>, <
  • 3.如何理解linux下一切皆文件
  • 4.缓冲区
    • 4.1理解缓冲区问题
      • 4.1.1为什么要有缓冲区
      • 4.1.2缓冲区刷新策略的问题
      • 4.1.3缓冲区在哪里,指的是什么缓冲区
      • 4.1.4如何解释fork问题
  • 5.缓冲区该如何理解
    • 5.1myStdio.h
    • 5.2myStdio.c
      • 5.2.1_fopen
      • 5.2.2_fwrite
      • 5.2.3_fflush
      • 5.2.4_fclose
    • 5.3main.c
  • 6.缓冲区和OS的关系

自我名言只有努力,才能追逐梦想,只有努力,才不会欺骗自己。在这里插入图片描述
喜欢的点赞,收藏,关注一下把!在这里插入图片描述

1.重定向

  1 #include<stdio.h>2 #include<string.h>3 #include<sys/types.h>4 #include<sys/stat.h>5 #include<fcntl.h>6 #include<unistd.h>7 #include<stdlib.h>8 9 int main()10 {11    // close(0);12    // close(2);13     close(1);                                                                                                                                                    14     umask(0);                                                         15     int fd=open("log.txt",O_WRONLY|O_CREAT|O_TRUNC,0666);             16     if(fd == -1)                                                      17     {                                                                 18         perror("open");                                               19         exit(1);                                                      20     }                                                                 21     printf("fd:%d\n",fd);                                             22                                                                       23     close(fd);                                                        24                                                                       25     return 0;                                                         26 }  

在这里插入图片描述

close(1),为什么没有打印新建文件fd呢?

printf(“%d\n”,fd); printf会把内容打印到stdout文件中。
但是close(1)关闭标准输出stdout—>显示器,int fd=open();新打开的文件fd是1。
stdout–>fd–>1,虽然我们手动关闭了stdout,但是系统并不知道,还以为fd为1的位置是stdout,但是这个位置现在已经被新打开的文件占用了,所以打印到了新打开的文件里。

  1 #include<stdio.h>2 #include<string.h>3 #include<sys/types.h>4 #include<sys/stat.h>5 #include<fcntl.h>6 #include<unistd.h>7 #include<stdlib.h>8 9 int main()10 {11    // close(0);12    // close(2);13     close(1);14     umask(0);15     int fd=open("log.txt",O_WRONLY|O_CREAT|O_TRUNC,0666);16     if(fd == -1)17     {18         perror("open");19         exit(1);20     }21     printf("fd:%d\n",fd);22     23     //这里必须刷新一下,不然log.txt里面没有内容,这里和缓冲区有关,下面讲                                                                                                                  24     fflush(stdout);      25     close(fd);           26                          27     return 0;            28 } 

在这里插入图片描述

本来应该打印到显示器文件中,但是却写到文件里了。------>重定向

1.1重定向本质

在这里插入图片描述

1.2重定向接口

在这里插入图片描述
这个主要介绍dup2函数。

int dup2(int oldfd, int newfd);

在这里插入图片描述
那怎么使用dup2来实现刚才的效果,把打印到显示器内容,写入到"log.txt"
在这里插入图片描述
是上面那样写的,还是下面那样写的,我们分析分析。
在这里插入图片描述
画图分析
在这里插入图片描述
在这里插入图片描述
因此正确写法如下
在这里插入图片描述

  4 #include<sys/stat.h>5 #include<fcntl.h>6 #include<unistd.h>7 #include<stdlib.h>8 9 int main()10 {11    // close(0);12    // close(2);13    // close(1);14     umask(0);15     int fd=open("log.txt",O_WRONLY|O_CREAT|O_TRUNC,0666);16     if(fd == -1)17     {18         perror("open");19         exit(1);20     }21     //重定向                                                                                                                                                     22     dup2(fd,1);                                                                                                               23     printf("fd:%d\n",fd);                                                                                                     24                                                                                                                               25     //这里必须刷新一下,不然log.txt里面没有内容                                                                               26     fflush(stdout);                                                                                                           27     close(fd);                                                                                                                28                                                                                                                               29     return 0;                                                                                                                 30 }  

在这里插入图片描述

1.3重定向分类

1.3.1>输出重定向

上面内容就是输出重定向,把新打开文件的fd重定向到fd为1(默认为显示器)的位置。

1.3.2>>追加重定向

    1 #include<stdio.h>2 #include<string.h>3 #include<sys/types.h>4 #include<sys/stat.h>5 #include<fcntl.h>6 #include<unistd.h>7 #include<stdlib.h>8 9 int main()10 {11    // close(0);12    // close(2);13    // close(1);14     umask(0);15    // int fd=open("log.txt",O_WRONLY|O_CREAT|O_TRUNC,0666);16     int fd=open("log.txt",O_WRONLY|O_CREAT|O_APPEND,0666);17     if(fd == -1)18     {19         perror("open");20         exit(1);21     }22     //重定向23     dup2(fd,1);24     printf("你好\n");25     printf("吃了吗\n");                                                                                                                                           26                                                                                                                           27     //这里必须刷新一下,不然log.txt里面没有内容                                                                           28     fflush(stdout);                                                                                                       29     close(fd);                                                                                                            30                                                                                                                           31     return 0;                                                                                                             32 } 

在这里插入图片描述

1.3.3<输入重定向

#include<stdio.h>
#include<string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<stdlib.h>int main()    
{    // close(0);    // close(2);    // close(1);    umask(0);    // int fd=open("log.txt",O_WRONLY|O_CREAT|O_TRUNC,0666);    // int fd=open("log.txt",O_WRONLY|O_CREAT|O_APPEND,0666);    int fd=open("log.txt",O_RDONLY);    if(fd == -1)    {      perror("open");      exit(1);      }      //输入重定向    dup2(fd,0);                                                                                                                                                      char outbuffer[64];        while(1)                   {                          printf("<");                                                     if(fgets(outbuffer,sizeof(outbuffer),stdin) == NULL) break;      printf("%s",outbuffer);                                                                                                                        }return 0;}   

在这里插入图片描述

2.理解 >, >>, <

在前面实现过一个自己的shell,现在我们给这个shell增加重定向功能来理解重定向。
在这里插入图片描述

//增加一个分割指令和文件名的函数                                                                                                                               
commandstrtok(lineCommand);

分割的时候,我们需要知道重定向是什么类型,文件是什么名字,因此增加两个全局变量来记录。

  //重定向类型    //第一个为初始重定向     #define DEFAULT_REDIR 0    #define INPUT_REDIR 1  #define OUTPUT_REDIR 2    #define ERROR_EDIRR 3    //重定向类型+文件名    int redirType=DEFAULT_REDIR;    char* redirFile=NULL;    

分割函数

  //这里找文件名没有写成函数,而写个宏#define trimSpace(start) do{\while(isspace(*start)) ++start;\}while(0)void commandstrtok(char* cmd){assert(cmd);char* start=cmd;char* end=cmd+strlen(cmd);while(start < end){if(*start == '<'){*start=0;++start;//这里可能 ls -a -l >      log.txt,trimSpace(start);redirType=INPUT_REDIR;redirFile=start;break;}else if(*start == '>')                                                                                                                                 {                                                                                                                                                      *start=0;                                                                                                                                          ++start;                                                                                                                                           if(*start == '>')                                                                                                                                  {                                                                                                                                                  redirType=APPEND_REDIR;                                                                                                                        ++start;                                                                                                                                       }                                                                                                                                                  else                                                                                                                                               {                                                                                                                                                  redirType=OUTPUT_REDIR;                                                                                                                        }                                                                                                                                                  trimSpace(start);                                                                                                                                  redirFile=start;                                                                                                                                       break;                                                                                                                                             }                                                                                                                                                      else{                                                                                                                                                      ++start;}}

因为命令是子进程执行的,真正重定向的功能一定是由子进程来完成的,
但是如何重定向是父进程要告知给子进程的。

//创建子进程
pid_t id = fork();
assert(id != -1);
if(id == 0)
{             switch(redirType){case INPUT_REDIR:{int fd=open("log.txt",O_RDONLY);if(fd <0){perror("open");return 1;}//重定向文件已经打开了dup2(fd,0);}break;case OUTPUT_REDIR:case APPEND_REDIR:{umask(0);int flags=O_WRONLY|O_CREAT;if(redirType == OUTPUT_REDIR) flags|=O_CREAT;else flags|=O_APPEND;                                                                                                                     int fd=open("log.txt",flags,0666);if(fd < 0){perror("open");return 1;}dup2(fd,1);     }break;default:printf("bug?\n");break;}//程序替换execvp(myargv[0],myargv);exit(1);
}

在这里插入图片描述

shell完整代码

问:子进程重定向会影响父进程吗?

不会
在这里插入图片描述

问:指向程序替换的时候,会不会影响曾经进程打开的重定向文件呢?

不会
在这里插入图片描述

3.如何理解linux下一切皆文件

Linux下一切皆文件,那键盘,显示器,磁盘,网卡等硬件在linux下都是文件吗?
可以这样说的。
在这里插入图片描述
在这里插入图片描述

站在struct file上层看来,所有的设备和文件,统一都是struct file--------->Linux下一切皆文件。

在这里插入图片描述

这里可能有这样一个问题,如果同一个文件被多个指针指向,但是某个进程把这个文件关了,会不会影响其他进程对这个文件的操作呢?

其实并不会,一个被打开的文件有引用计数
在这里插入图片描述
表明有几个指针指向这个文件,这样做是因为一个文件可能被多个指针指向。如果某个进程关闭文件,影响到其他进程,就不能保证进程的独立性。close关闭文件,其实并不是真正关闭文件,只是把引用计数减1,当只有一个指针指向这个文件,close关闭文件才是真正关闭这个文件。

4.缓冲区

先看一种现象。

  1 #include<stdio.h>  2 #include<string.h>                                                        3 #include<sys/types.h>  4 #include<sys/stat.h>  5 #include<fcntl.h>  6 #include<unistd.h>  7 #include<stdlib.h>  8   9 int main()  10 {  11     printf("hello printf\n");  12     fprintf(stdout,"%s\n","hello fprintf");  13   14   15     const char* output="hello write\n";  16     write(1,output,strlen(output));  17   18                                                                                                                                                                  19     return 0;  20 }     

在这里插入图片描述

  1 #include<stdio.h>2 #include<string.h>3 #include<sys/types.h>4 #include<sys/stat.h>5 #include<fcntl.h>6 #include<unistd.h>7 #include<stdlib.h>8 9 int main()10 {11     printf("hello printf\n");12     fprintf(stdout,"%s\n","hello fprintf");13 14 15     const char* output="hello write\n";16     write(1,output,strlen(output));17 18     fork();                                                                                                                                                      19     return 0;                                                                                                                           20 }    

在这里插入图片描述
对比两张图片,第二段代码补充fork(创建子进程),重定向之后,C接口的函数,被打印了两次,系统调用的接口前面都只是打印了一次。

这是什么原因?现在只是知道肯定是fork函数有关。
其实这里也和缓冲区有关。

4.1理解缓冲区问题

缓冲区本质就是一段内存!!!

4.1.1为什么要有缓冲区

这里讲一个小故事,帮助理解。

在四川的张三要给远在北京的李四送一个键盘。
在这里插入图片描述
张三为了节省时间,选择2;
现实中快递行业的意义?
节省发送者的时间。

进程把数据打包给磁盘,是一个很慢的过程,如果让进程一直等着显然不太好。因此我们也需要一个快速的方式----->缓冲区

在这里插入图片描述
缓冲区的意义是什么呢?

节省进程进行数据IO的时间。

但是我们在用文件写的接口时,并没有专门拷贝数据啊?(以fwrite为例)

其实与其理解fwrite是写入到文件的函数,倒不如理解fwrie是拷贝函数,将数据从进程拷贝到"缓冲区"或者“外设”中。

4.1.2缓冲区刷新策略的问题

把东西给顺丰之后,那顺丰什么时候发货呢?

张三第二天在想寄一个鼠标给李四,当走到快递站点发现自己昨天的快递还没有发走,就问快递人员,快递人员说,我们的快递都是用大货车和飞机发送的,就这一个快递不可能马上就发走,等到满足发送件货才发。

缓冲区的刷新策略:
在这里插入图片描述
不同外设IO访问速度是不同的。
缓冲区结合具体的设备,定制自己的刷新策略。(3策略,2特殊)

3策略
a.立即刷新 ----> 无缓冲
b.行刷新 ----> 行缓存 ----> 显示器 (显示器是给人看的,如果一次给很多信息,人看着不舒服)
c.缓冲区满 ----> 全缓存 ----> 磁盘文件(显示器除外)

2特例
1:用户强制刷新 (fflush)
2:进程退出 (进程退出要刷新缓冲区)

4.1.3缓冲区在哪里,指的是什么缓冲区

C接口打印了两次,系统调用接口打印了一次,这种现象一定和缓冲区有关。
虽然现在不知道缓冲区在哪里,但是我们知道缓冲区一定不在内核中
因为C接口底层调用的是系统调用接口,如果缓冲区在内核中,write也应该打印两次。

那缓冲区到底在哪?

我们之前谈论的所有缓冲区,都指的是用户级语言层面给我们提供的缓冲区
还记得C程序默认给我们打开stdin,stdout,stderr都是------->FILE*------>指向FILE结构体,结构体包含-------> fd,其实还包含一个缓冲区

因此,缓冲区在FILE结构体中。

所以当我们自己要强制刷新:fflush(文件指针),关闭:fclose(文件指针)传的都是FILE*。因为缓存区在FILE结构体中。

4.1.4如何解释fork问题

在这里插入图片描述
代码结束之前,创建子进程。

1.我们没有进行>输出重定向,看到了三条信息。

stdout默认使用的是行刷新,在fork之前,两条C函数已经将数据打印输出到到显示器上,你的FILE内部,进程内部不存在对应的数据了。这时创建子进程,等到子进程,父进程退出的时,都要刷新缓冲区,但是这时缓冲区已经没有内容可以刷新了。因此C函数打印两条信息。

2.>输入重定向之后,C接口打印两次

当我们进行>,写入文件就不再是显示器,而是普通文件,采用的刷新策略是全缓存,之前2条C打印函数,虽然带了\n,但是不足与将stdout缓冲区写满,数据并没有被刷新。
fork的时候,stdout属于父进程,创建子进程紧跟着就是进程退出,谁先退出,一定要进行缓冲区刷新(缓存区刷新---->就是修改)
这时就有了写时拷贝!! 因此C接口,数据最终会显示两份。

3.write为什么前后只打印一次

上面过程都是write无关,write没有FILE,而用的是fd,就没有C提供的缓冲区。

5.缓冲区该如何理解

自己写一个简易的缓冲区,来帮助理解"数据刷新策略"+“数据如何缓存”

5.1myStdio.h

  1 #pragma once  2 #include<errno.h> 3 #include<stdio.h>      4 #include<string.h>      5 #include<sys/types.h>      6 #include<sys/stat.h>      7 #include<fcntl.h>      8 #include<unistd.h>      9 #include<stdlib.h> 10   11 #define SIZE 1024    12 #define SYNC_NOW 1    13 #define SYNC_LINE 2      14 #define SYNC_FULL 3     15                      16 typedef struct _FILE{  17     int flags;//刷新方式  18     int fileno;  //文件描述符19     int capacity;//buffer容量  20     int size;//buffer当前使用量  21     char buffer[SIZE]; //缓冲区22 }_FILE;              23   24                      25 _FILE* _fopen(const char* path_name,const char* mode);  26 void _fwrite(const void* ptr,int num,_FILE* fp);                                                                                                                 27 void _fclose(_FILE* fp);  28 void _fflush(_FILE* fp);  

5.2myStdio.c

5.2.1_fopen

 _FILE* _fopen(const char* path_name,const char* mode)
{assert(path_name);int flags=0;int defaultmode=0666;if(strcmp(mode,"r") == 0){flags|=O_RDONLY;}else if(strcmp(mode,"w") == 0){flags|=(O_WRONLY|O_CREAT|O_TRUNC);}else if(strcmp(mode,"a") == 0){flags|=(O_WRONLY|O_CREAT|O_APPEND);}int fd=0;if(flags & O_RDONLY){fd=open(path_name,flags);}else{fd=open(path_name,flags,defaultmode);                                                                                                                        }if(fd < 0){const char*err=strerror(errno);write(2,err,strlen(err));return NULL;//这就是为什么创建文件失败,返回NULL}_FILE* fp=(_FILE*)malloc(sizeof(_FILE));assert(fp);fp->flags=SYNC_LINE;fp->fileno=fd;fp->capacity=SIZE;fp->size=0;memset(fp->buffer,0,SIZE);return fp;
}

5.2.2_fwrite

void _fwrite(const void* ptr,int num,_FILE* fp)                                                                                       
{                                                                                   //写到缓冲区里                                                                                                                  memcpy(fp->buffer+fp->size,ptr,num); //这里没有考虑缓冲区溢出问题                                                                                           fp->size+=num;                                                                                                                  //判断是否要刷新                                                                                                                if(fp->flags & SYNC_NOW)                                                                                                        {                                                                                                                               write(fp->fileno,fp->buffer,fp->size);                                                                                       fp->size=0;//清空缓冲区                                                                                                     }                                                                                                                               else if(fp->flags & SYNC_LINE)                                                                                                  {                                                                                                                               if(fp->buffer[fp->size-1] == '\n')  //这里也没有考虑abc\ndef这种形式,如果是这样的可以用for循环                                                                                        {                                                                                                                            write(fp->fileno,fp->buffer,fp->size);                                                                                       fp->size=0;                                                                                                             }                                                                                                                           }                                                                                                                               else if(fp->flags & SYNC_FULL)                                                                                                  {                                                                                                                               if(fp->size == fp->capacity)                                                                                                {                                                                                                                            write(fp->fileno,fp->buffer,fp->size);                                                                                       fp->size=0;                                                                                   }                                                                                                                           }                                                                                                                               
}     

5.2.3_fflush

void _fflush(_FILE* fp)                                                                                       
{                                                                                       if(fp->size > 0) write(fp->fileno,fp->buffer,fp->size);                                                                                       fsync(fp->fileno); //把数据强制从内核缓冲区刷新到磁盘                                                                                     fp->size=0;                                                                                                                                                      
} 

这里引入了内核缓冲区,下面解释。

5.2.4_fclose

void _fclose(_FILE* fp)                                                                                       
{                                                                                       _fflush(fp);  close(fp->fileno);                                                                                                                                               
}    

5.3main.c

行刷新

  1 #include"myStdio.h"2 3 4 int main()5 {6     _FILE* fp=_fopen("log.txt","w");7     if(fp == NULL)8     {9         perror("_fopen");10         return 1;11     }12 13     int cnt=10;14     const char* msg="hello linux\n";15     while(1)16     {17         _fwrite(msg,strlen(msg),fp);18         sleep(1);19         printf("count:%d\n",cnt--);20         if(cnt == 0) break;21     }                                                                       22     _fclose(fp);23 24     return 0;25 }

在这里插入图片描述

退出刷新

    1 #include"myStdio.h"2 3 4 int main()5 {6     _FILE* fp=_fopen("log.txt","w");7     if(fp == NULL)8     {9         perror("_fopen");10         return 1;11     }12 13     int cnt=10;14     const char* msg="hello linux";15     while(1)16     {17         _fwrite(msg,strlen(msg),fp);18         sleep(1);19         printf("count:%d\n",cnt--);                                       20         if(cnt == 0) break;21     }                    22     _fclose(fp);         23                          24     return 0;            25 }  

在这里插入图片描述
立即刷新

  1 #include"myStdio.h"2 3 4 int main()5 {6     _FILE* fp=_fopen("log.txt","w");7     if(fp == NULL)8     {9         perror("_fopen");10         return 1;11     }12 13     int cnt=10;14     const char* msg="hello linux";15     while(1)16     {17         _fwrite(msg,strlen(msg),fp);18         _fflush(fp);                                                        19         sleep(1);20         printf("count:%d\n",cnt--);21         if(cnt == 0) break;22     }          23     _fclose(fp);24                25     return 0;  26 }   

在这里插入图片描述

6.缓冲区和OS的关系

在这里插入图片描述
这里是由write函数,直接把数据写到磁盘上的文件中吗?
其实并不是这样的。
在这里插入图片描述
在这里插入图片描述
write并不是直接把缓冲区里的内容刷新到文件中,在打开文件对应的struct file{}结构体中其他有一个方法指针,还有一个指向内核缓冲区的指针,系统会把FILE结构体里面缓冲区内容通过struct file{}找到内核缓冲区,再由write()拷贝到内核缓冲区,然后再由这个内核缓冲区把内容刷新到文件中区,至于如何刷新和用户毫无关系,我们所知道的行刷新等,这是由语言层面所分类的,而内核缓冲区刷新由OS自主决定。

那么我们该如何证明有这个内核缓冲区呢?
超级大佬可以证明,这里证明不了,但是我们可以看到接口。
在这里插入图片描述
还有一个问题,如果OS突然宕机了会发生什么情况?
数据肯定会丢失。如果还是按照OS规定的内核缓冲区刷新策略肯定是不行的。
我们希望可以在用户层就告知OS,内核缓冲区不要再给我缓存了,赶紧把数据刷新到磁盘中。

这里介绍fsync函数

在这里插入图片描述
强制性把该文件对应的内核缓冲区数据持久到磁盘。就是OS不要给我缓存了,赶紧把数据给我更新到磁盘上。

相关文章:

【linux】重定向+缓冲区

重定向缓冲区 1.重定向1.1重定向本质1.2重定向接口1.3重定向分类1.3.1>输出重定向1.3.2>>追加重定向1.3.3<输入重定向 2.理解 >&#xff0c; >>&#xff0c; <3.如何理解linux下一切皆文件4.缓冲区4.1理解缓冲区问题4.1.1为什么要有缓冲区4.1.2缓冲区刷…...

【vim 学习系列文章 10 -- vim 将代码中空格高亮显示方法】

文章目录 vim 高亮空格使用背景如何配置vim 可以自动显示空格呢&#xff1f;vim highlight 命令使用介绍vim 空白行的处理vim match 命令详细介绍 vim 高亮空格使用背景 开发人员在编写代码之后&#xff0c;在review通过之后会将代码推到服务器然后merge&#xff0c;但是有些代…...

吴恩达深度学习笔记

B站看的视频&#xff0c;课太长了&#xff0c;180多节&#xff0c;但搬运的没有作业练习&#xff0c;最好找个能练习的 1&#xff0c;假设模型时&#xff0c;以前(2011版机器学习)用西塔代表参数组成的向量&#xff0c;现在用w代表参数组成的向量&#xff0c;b代表西塔0&#x…...

基于SpringBoot的医疗预约服务管理系统

基于SpringBootVue的医疗预约服务管理系统的设计与实现~ 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringBootMyBatisVue工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 主页 登陆界面 管理员界面 医生界面 会员界面 摘要 基于SpringBoot的…...

Java本地缓存的使用

在项目开发中&#xff0c;经常会碰到一些数据量小、但使用频率极高的数据。比如&#xff0c;团队到BU的映射表。此类数据通常不会变动&#xff0c;使用频率高且数据量较小&#xff0c;如果每次使用都去数据库查询&#xff0c;代价比较大&#xff0c;可以考虑在项目启动时将此类…...

华为数通方向HCIP-DataCom H12-831题库(单选题:281-300)

第281题 如图所示,某工程师利用4台路由器进行网络互通测试,其中R1、R2、R3部署OSPF (Area0)实现网络互通,R2、R3、R4部署IS-IS(均部署为Level-2路由器)实现网络互通,现在该工程师在R1的OSPF进程中引入直连路由,在R2的IS-IS进程中引入OSPF路由,则以下关于该场景的描述,正…...

【分享Python代码】图片转化为素描画

哈喽&#xff0c;大家好&#xff0c;我是木易巷~ 代码生成效果图 原图&#xff1a; 生成图&#xff1a; 原图&#xff1a; 生成图&#xff1a; 准备工作 Python编程首先需要安装环境&#xff0c;下面是详细步骤&#xff1a; 会的小伙伴可自行跳过&#xff0c;代码在最后 1…...

汇川Easy521PLC与压力传感器485通讯实例

本例是汇川Easy521PLC与支持485通讯的压力传感器进行通讯的实例记录。对于初次使用汇川PLC的朋友,可能有借鉴的意义。 配置: 1、汇川Easy521PLC 2、美控压力变送器 3、汇川Autoshop编程软件 将压力变送器的485线与PLC本体的485端子一一连接: 485+:A+ 485-:B- 一般485的标…...

创意作品管理软件 Bridge 2024 mac中文版 br2024功能特色

Bridge 2024 mac旨在连接不同的Ad obe应用程序&#xff0c;帮助用户更轻松地管理和编辑他们的创意作品。 Bridge 2024 mac软件特色和功能介绍 一致的用户体验&#xff1a;Bridge 2024现在具有增强的用户界面&#xff0c;可提供与其他Creative Cloud应用程序类似的体验。用户还…...

【分享】教你加速访问GitHub,进来学!

哈喽&#xff0c;大家好&#xff0c;木易巷来啦&#xff01; 众所周知&#xff0c;Github是一款程序猿必备的代码托管平台&#xff0c;上面已经存在了无数前辈的心血&#xff01;经常需要在上面查看大佬写的一些好用的开源项目&#xff0c;无赖国外网站的速度实在让人难以接受。…...

利用在线培训系统提升员工技能,助力企业发展

近年来&#xff0c;随着互联网技术的发展&#xff0c;在线培训系统逐渐成为企业提升员工技能的利器。这种新型的培训方式打破了时间和空间的限制&#xff0c;为企业提供了更加灵活和高效的培训解决方案。下面&#xff0c;我们将详细介绍如何利用在线培训系统提升员工技能&#…...

深度学习小工具:Linux 环境下的用户命令和脚本顺序执行器

前言 深度学习跑代码的时候&#xff0c;需要跑很多个对比实验&#xff0c;要么开多个窗口并行执行代码&#xff0c;要么就写在一个 .sh 文件里面顺序执行&#xff0c;前面一种并行执行多个任务出结果很慢&#xff0c;而后一种如果想添加任务或者删除某个任务就得全部停止&…...

c# WPF 应用程序在屏幕上居中显示

xaml<Window ... WindowStartupLocation"CenterScreen">...

JVM之class文件结构剖析

文章目录 0.前言1. 引言1.1 Java编译原理基础1.2 Class文件在Java编译过程中的角色 2. Class文件的整体结构2.1 Class 文件组成 3. Class文件的详细解析3.1 魔数与版本号的作用和意义3.2 常量池的结构和作用3.3 访问标志的含义和可能的值3.4 类索引、父类索引和接口索引集合的作…...

TDengine 签约中石化,支撑八大油田 PCS 系统

近日&#xff0c;TDengine 成功签约中国石化 PCS 一级部署时序数据库项目。未来&#xff0c;TDengine 将作为中国石化 PCS 系统的时序数据库&#xff0c;为石化总部、胜利油田、西北油田、中原油田、河南油田、华北油田、江苏油田、华东油田、江汉油田等油田 PCS 系统提供高效、…...

win11 定时计划任务

控制面板 任务计划 添加任务计划 &#xff0c;选按步骤添加。...

C++入门之引用与内联函数

一、引用 1、初步理解 引用在语法上的理解就是起别名&#xff0c;用法就是在类型后面加&&#xff0c;例子&#xff1a;int a 1; int& b a; 上例所示&#xff0c;执行后&#xff0c;b就是a的别名&#xff0c;它们代表同一块空间&#xff0c;a的改变会影响b&#xff0…...

浅谈智能照明控制系统应用在城市轨道交通

叶根胜 江苏安科瑞电器制造有限公司 江苏江阴 214405 摘要&#xff1a;在传统的城市轨道交通设计方面&#xff0c;照明设计方案具有一定的弊端。随着计算机技术的发展&#xff0c;智能化技术渐渐步入人们的生活并成为主流&#xff0c;故在城市轨道交通中应用新型的照明控制设…...

macos 中ios系统升级,但是macos还是老系统,在手机上无法安装ios软件

https://github.com/filsv/iOSDeviceSupport 构建项目出现 解决的方法&#xff1a; 就可以了&#xff0c;...

Pytest+Allure生成可添加附件的测试报告

#测试套件层级 allure.feature("测试PecExplorer") #重试次数&#xff0c;粒度为用例&#xff0c;建议用例设计可重复性高 pytest.mark.flaky(reruns3) class TestPecExplorer:#功能模块层级allure.story("登录界面")#测试用例层级allure.title("Test…...

变量 varablie 声明- Rust 变量 let mut 声明与 C/C++ 变量声明对比分析

一、变量声明设计&#xff1a;let 与 mut 的哲学解析 Rust 采用 let 声明变量并通过 mut 显式标记可变性&#xff0c;这种设计体现了语言的核心哲学。以下是深度解析&#xff1a; 1.1 设计理念剖析 安全优先原则&#xff1a;默认不可变强制开发者明确声明意图 let x 5; …...

MPNet:旋转机械轻量化故障诊断模型详解python代码复现

目录 一、问题背景与挑战 二、MPNet核心架构 2.1 多分支特征融合模块(MBFM) 2.2 残差注意力金字塔模块(RAPM) 2.2.1 空间金字塔注意力(SPA) 2.2.2 金字塔残差块(PRBlock) 2.3 分类器设计 三、关键技术突破 3.1 多尺度特征融合 3.2 轻量化设计策略 3.3 抗噪声…...

基于大模型的 UI 自动化系统

基于大模型的 UI 自动化系统 下面是一个完整的 Python 系统,利用大模型实现智能 UI 自动化,结合计算机视觉和自然语言处理技术,实现"看屏操作"的能力。 系统架构设计 #mermaid-svg-2gn2GRvh5WCP2ktF {font-family:"trebuchet ms",verdana,arial,sans-…...

突破不可导策略的训练难题:零阶优化与强化学习的深度嵌合

强化学习&#xff08;Reinforcement Learning, RL&#xff09;是工业领域智能控制的重要方法。它的基本原理是将最优控制问题建模为马尔可夫决策过程&#xff0c;然后使用强化学习的Actor-Critic机制&#xff08;中文译作“知行互动”机制&#xff09;&#xff0c;逐步迭代求解…...

聊聊 Pulsar:Producer 源码解析

一、前言 Apache Pulsar 是一个企业级的开源分布式消息传递平台&#xff0c;以其高性能、可扩展性和存储计算分离架构在消息队列和流处理领域独树一帜。在 Pulsar 的核心架构中&#xff0c;Producer&#xff08;生产者&#xff09; 是连接客户端应用与消息队列的第一步。生产者…...

HTML 列表、表格、表单

1 列表标签 作用&#xff1a;布局内容排列整齐的区域 列表分类&#xff1a;无序列表、有序列表、定义列表。 例如&#xff1a; 1.1 无序列表 标签&#xff1a;ul 嵌套 li&#xff0c;ul是无序列表&#xff0c;li是列表条目。 注意事项&#xff1a; ul 标签里面只能包裹 li…...

五年级数学知识边界总结思考-下册

目录 一、背景二、过程1.观察物体小学五年级下册“观察物体”知识点详解&#xff1a;由来、作用与意义**一、知识点核心内容****二、知识点的由来&#xff1a;从生活实践到数学抽象****三、知识的作用&#xff1a;解决实际问题的工具****四、学习的意义&#xff1a;培养核心素养…...

CocosCreator 之 JavaScript/TypeScript和Java的相互交互

引擎版本&#xff1a; 3.8.1 语言&#xff1a; JavaScript/TypeScript、C、Java 环境&#xff1a;Window 参考&#xff1a;Java原生反射机制 您好&#xff0c;我是鹤九日&#xff01; 回顾 在上篇文章中&#xff1a;CocosCreator Android项目接入UnityAds 广告SDK。 我们简单讲…...

AirSim/Cosys-AirSim 游戏开发(四)外部固定位置监控相机

这个博客介绍了如何通过 settings.json 文件添加一个无人机外的 固定位置监控相机&#xff0c;因为在使用过程中发现 Airsim 对外部监控相机的描述模糊&#xff0c;而 Cosys-Airsim 在官方文档中没有提供外部监控相机设置&#xff0c;最后在源码示例中找到了&#xff0c;所以感…...

「全栈技术解析」推客小程序系统开发:从架构设计到裂变增长的完整解决方案

在移动互联网营销竞争白热化的当下&#xff0c;推客小程序系统凭借其裂变传播、精准营销等特性&#xff0c;成为企业抢占市场的利器。本文将深度解析推客小程序系统开发的核心技术与实现路径&#xff0c;助力开发者打造具有市场竞争力的营销工具。​ 一、系统核心功能架构&…...