C语言:高级并发操作(信号)

news2024/10/6 1:20:41

引言

同步和异步的使用。

异步事件处理的两种方法:查询法、通知法。(单核机器不存在异步)

一、信号

1. 信号的概念

信号是软件中断。信号的响应依赖于中断。中断是底层硬件的机制。

2. signal函数

kill -l命令查看所有的信号。1 -31 属于标准信号 34 - 64属于实时信号


       typedef void (*sighandler_t)(int);

       sighandler_t signal(int signum, sighandler_t handler);
展示形式为: void (* signal(int signum, void (*func)(int) )    ) (int )

信号会打断阻塞的系统调用。

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

void mySig()
{

    write(1,".",1);
}

int main()
{

    signal(SIGINT,SIG_IGN);//运行过程中忽略这个信号
    signal(SIGINT,mySig);
    
    int i;
    for(i = 0;i<10;i++)
    {
        write(1,"*",1);
        sleep(1);
    }
    return 0;
}

重构之前的mycpy.c  对于open  write read 函数增加出现系统调用打断的行为判断

 mycpy.c

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

#define BUF_SIZE    1024*100
int main(int argc,char **argv)
{

    int fps,fpd;
    int ret;
    char buf[BUF_SIZE];
    int len,pos;
    if(argc <3)
    {
        fprintf(stderr,"Usage:%s <src_file> <dest_file>\n",argv[0]);
        exit(1);
    }
    do
    {
        fps = open(argv[1],O_RDONLY);
        if(fps <0)
        {
            if(errno != EINTR)
            {
                perror("open");
                exit(1);
            }
        }
    }while(fps<0);
    do
    {
        fpd = open(argv[2],O_WRONLY|O_CREAT|O_TRUNC,0600 );
        if(fpd <0)
        {
            if(errno != EINTR)
            {
                close(fps);
                perror("open");
                exit(1);
            }
        }
    }while(fpd<0);
    while(1)
    {
        len = read(fps,buf,BUF_SIZE);
        if(len < 0)
        {
            if(errno == EINTR)
                continue;
            perror("read()");
            break;
        }
        if(len ==0)
            break;

        pos = 0;
        while(len > 0)
        {
            ret = write(fpd,buf+pos,len);
            if(ret <0)
            {
                if(errno == EINTR)
                    continue;
                perror("write()");
                exit(1);
            }
            pos += ret;
            len-=ret;
        }
    
    }

    close(fpd);
    close(fps);

    return 0;
}

3. 信号的不可靠

标准的信号会丢失,实时信号不会丢失。指的是信号的行为不可靠。(第一次调用还未结束,第二次调用就开始了)

4. 可重入函数

第一次调用还未结束,第二次调用不会出错叫可重入函数(reentrant)。所有的系统调用都是可重入的,一部分库函数也是可重入的,比如说:memcpy rand_r localtime_r   memove

5. 信号的响应过程

mask(信号屏蔽字,32位一般情况都是1)pending(32位 位图,初始都为0),收到信号后pending位置1,内核在kernal态进入到user态时,会用mask值 & pending 获取发生的信号,将mask与pending全部置为0,执行函数,执行完毕将mask位置1。

信号从收到到响应有一个不可避免的延迟。
思考:
如何忽略掉一个信号?  将mask位置0
标准信号为什么会丢失?  在执行信号时,pending位置多次1,此时mask位是0
标准信号的响应没有严格的顺序。
不能从信号处理函数中随意的往外跳。(sigsetjmp,siglongjmp)

6. 相关常用函数

kill

发送一个信号到一个进程或进程组

raise

给当前进程或者线程发送一个信号

alarm

指定秒数,发送SIGALRM信号。默认alarm会杀掉当前程序。alarm不能实现多任务的计时器,只能执行最后一次设置的计时器。

alarm.c
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <unistd.h>

void mySig()
{

    write(1,".",1);
}

int main()
{
    alarm(5);
    alarm(2);
    alarm(10);
    while(1)
        pause();
#if 0
   signal(SIGALRM,mySig);
    
    int i;
    for(i = 0;i<10;i++)
    {
        write(1,"*",1);
        alarm(1);
        sleep(1);
    }
#endif 
    return 0;
}

5秒进行计数,不用alarm实现

5sec.c
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <time.h>


int main()
{
    time_t end;
    long long  count = 0;
   end = time(NULL)+5;
    while(time(NULL)<= end)
    {
        count++;
    }
    printf("count = %lld \n",count);
    return 0;
}

 使用alarm实现

5sec_signal.c
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <time.h>

static int loop = 1;

void alrm_handler(int num)
{
    loop = 0;
}

int main()
{

    signal(SIGALRM,alrm_handler);
    alarm(5);

    long long  count=0;
    while(loop)
    {
        count++;
    }
    printf("count = %lld \n",count);
    return 0;
}

gcc -S 5sec_signal.c 会产生 5sec_singal.s的汇编文件  

使用-O1优化之后,程序会一直执行下去,因为程序中while中没有用到loop,优化后loop的值会直接从寄存器取程序while会一直成立,volatile关键字告诉程序要去真正的地址取数据,不要从内存获取

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

static volatile int loop = 1;  //从真实的空间去取数据

void alrm_handler(int num)
{
    loop = 0;
}

int main()
{

    signal(SIGALRM,alrm_handler);
    alarm(5);

    long long  count=0;
    while(loop)
    {
        count++;
    }
    printf("count = %lld \n",count);
    return 0;
}
mycat.c
#include <stdlib.h>
#include <stdio.h>
#include <error.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>

#define BUF_SIZE    1024*100
int main(int argc,char **argv)
{

    int fps,fpd=1;
    int ret;
    char buf[BUF_SIZE];
    int len,pos;
    if(argc <2)
    {
        fprintf(stderr,"Usage:%s <src_file> <dest_file>\n",argv[0]);
        exit(1);
    }
    do
    {
        fps = open(argv[1],O_RDONLY);
        if(fps <0)
        {
            if(errno != EINTR)
            {
                perror("open");
                exit(1);
            }
        }
    }while(fps<0);
    
    while(1)
    {
        len = read(fps,buf,BUF_SIZE);
        if(len < 0)
        {
            if(errno == EINTR)
                continue;
            perror("read()");
            break;
        }
        if(len ==0)
            break;

        pos = 0;
        while(len > 0)
        {
            ret = write(fpd,buf+pos,len);
            if(ret <0)
            {
                if(errno == EINTR)
                    continue;
                perror("write()");
                exit(1);
            }
            pos += ret;
            len-=ret;
        }
    
    }

    close(fps);

    return 0;
}

 实例:使用单一计时器,构造一组函数实现,任意数量的计时器。        

pause

等待一个信号来打断他,让程序不在忙等(while函数)

abort

给当前进程发送一个SIGABRT信号,并且产生一个core文件

system

调用shell完成shell命令。           execl("/bin/sh", "sh", "-c", command, (char *) NULL);

如果在信号中使用,需要block 这个SIGCHLD 信号,并且 忽略SIGINT SIGQUIT信号。

sleep

某些平台 用alarm+pause封装 ,当前平台用nanolseep封装,考虑到移植最好不要使用sleep.可以使用usleep,select 

7. 信号集

  int sigemptyset(sigset_t *set);

       int sigfillset(sigset_t *set);

       int sigaddset(sigset_t *set, int signum);

       int sigdelset(sigset_t *set, int signum);

       int sigismember(const sigset_t *set, int signum);
 

8. 信号屏蔽字mask/pending集处理

 /* Prototype for the glibc wrapper function

glibc包装器函数的原型

*/
       int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);

       /* Prototype for the underlying system call

底层系统调用的原型

*/
       int rt_sigprocmask(int how, const kernel_sigset_t *set,
                          kernel_sigset_t *oldset, size_t sigsetsize);

       /* Prototype for the legacy system call (deprecated)

旧系统调用的原型(已弃用)

*/
       int sigprocmask(int how, const old_kernel_sigset_t *set,
                       old_kernel_sigset_t *oldset);

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

void mySig()
{

    write(1,".",1);
}

int main()
{

    signal(SIGINT,mySig);
    
    int i;
    sigset_t set,old_set;
    sigemptyset(&set);
    sigaddset(&set,SIGINT);
    for(i = 0;i<1000;i++)
    {

        sigprocmask(SIG_BLOCK,&set,NULL); //堵塞 SIGINT信号
        for(int j=0;j<5;j++)
        {
        write(1,"*",1);
        sleep(1);  // 信号可以打断堵塞 的系统调用
        }
        write(1,"\n",1);
        sigprocmask(SIG_UNBLOCK,&set,NULL); //解除堵塞信号

    }
    return 0;
}
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <unistd.h>

void mySig()
{

    write(1,".",1);
}

int main()
{

    signal(SIGINT,mySig);
    
    int i;
    sigset_t set,old_set;
    sigemptyset(&set);
    sigaddset(&set,SIGINT);
    for(i = 0;i<1000;i++)
    {

        sigprocmask(SIG_BLOCK,&set,&old_set); // 保存之前的状态
        for(int j=0;j<5;j++)
        {
        write(1,"*",1);
        sleep(1);  // 信号可以打断堵塞 的系统调用
        }
        write(1,"\n",1);
        sigprocmask(SIG_SETMASK,&old_set,NULL); //回复当前的状态

    }
    return 0;
}
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <unistd.h>

void mySig()
{

    write(1,".",1);
}

int main()
{

    signal(SIGINT,mySig);
    
    int i;
    sigset_t set,old_set,save_set;
    sigemptyset(&set);
    sigaddset(&set,SIGINT);
    sigprocmask(SIG_UNBLOCK,&set,&save_set);// 模块话编程,先保存之前的状态。


    for(i = 0;i<1000;i++)
    {

        sigprocmask(SIG_BLOCK,&set,NULL);
        for(int j=0;j<5;j++)
        {
        write(1,"*",1);
        sleep(1);  // 信号可以打断堵塞 的系统调用
        }
        write(1,"\n",1);
        sigprocmask(SIG_UNBLOCK,&old_set,NULL); 

    }
    sigprocmask(SIG_SETMASK,&save_set,NULL); //结束后,恢复之前保存的状态。
    return 0;
}

9. 扩展

sigsuspend()

实现信号驱动程序。

susp.c
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <unistd.h>

void mySig()
{

    write(1,".",1);
}

int main()
{

    signal(SIGINT,mySig);
    
    int i;
    sigset_t set,old_set,save_set;
    sigemptyset(&set);
    sigaddset(&set,SIGINT);
    sigprocmask(SIG_UNBLOCK,&set,&save_set);// 模块话编程,先保存之前的状态。

    sigprocmask(SIG_BLOCK,&set,&old_set);

    for(i = 0;i<1000;i++)
    {

   //     sigprocmask(SIG_BLOCK,&set,&old_set);
        for(int j=0;j<5;j++)
        {
        write(1,"*",1);
        sleep(1);  // 信号可以打断堵塞 的系统调用
        }
        write(1,"\n",1);
        sigsuspend(&old_set);
/* sigsuspend的原子操作
        sigset_t tmpset;
        sigprocmask(SIG_SETMASK,&old_set,&tmpset); //解除阻塞 
        pause(); //响应信号
        sigprocmask(SIG_SETMASK,&tmpset,NULL); //重新恢复信号。

*/
    }
    sigprocmask(SIG_SETMASK,&save_set,NULL); //结束后,恢复之前保存的状态。
    return 0;
}

sigaction()

用于替换signal,signal有使用上的缺陷。
       int sigaction(int signum, const struct sigaction *act,
                     struct sigaction *oldact);

    struct sigaction {
               void     (*sa_handler)(int);
               void     (*sa_sigaction)(int, siginfo_t *, void *);
               sigset_t   sa_mask;
               int        sa_flags;
               void     (*sa_restorer)(void);
           };

第三个参数来源于:

       #include <ucontext.h>

       int getcontext(ucontext_t *ucp);
       int setcontext(const ucontext_t *ucp);
 

struct sigaction sa;
      sa.sa_handler = daemon_exit; //指定要执行的函数
      sigemptyset(&sa.sa_mask);
      sigaddset(&sa.sa_mask,SIGINT)
      sigaddset(&sa.sa_mask,SIGQUIT)
      sigaddset(&sa.sa_mask,SIGTERM)
      sa.sa_flag = 0;
      sigaction(SIGINT,&sa,NULL);

while true ;do kill -ALRM 121812;done;SHELL脚本

重新更新mytbf,不响应终端的信号。

mytbf.c
#include <stdlib.h>
#include <stdio.h>
#include <error.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <signal.h>
#include <sys/time.h>
#include "mytbf.h"

struct mytbf_st
{
    int cps;
    int burst;
    int token;
    int pos;

};
static struct mytbf_st* job[MYTBF_MAX];
static int inited = 0;

typedef void (*sighandler_t)(int);

struct sigaction alrm_sa_save;

static int get_free_pos(void)
{
    for(int i=0;i< MYTBF_MAX;i++)
    {
        if(job[i]==NULL)
            return i;
    }
    return -1;
}

static void alrm_action(int num,siginfo_t*infop,void *unsed)
{
    if(!infop->si_code == SI_KERNEL) //响应kernal来的信号,一般从程序中发出就是kernal来的。
        return;
    for(int i=0;i<MYTBF_MAX;i++)
    {
        if(job[i] != NULL)
        {
            job[i]->token += job[i]->cps;
            if(job[i]->token >job[i]->burst )
            {
                job[i]->token = job[i]->burst;
            }
        }
    }

}

static void module_unload()
{
    sigaction(SIGALRM,&alrm_sa_save,NULL);
    
    struct itimerval itv;
    itv.it_interval.tv_sec = 0;
    itv.it_interval.tv_usec = 0;
    itv.it_value.tv_sec = 0;
    itv.it_value.tv_usec = 0;
    setitimer(ITIMER_REAL,&itv,NULL);

    for(int i=0;i<MYTBF_MAX;i++)
        free(job[i]);
}

static void module_load()
{
    struct sigaction sa;
    
    sa.sa_sigaction  = alrm_action; 
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = SA_SIGINFO; //表示传入的是三参的形式
    sigaction(SIGALRM,&sa,&alrm_sa_save);
    
    struct itimerval itv;
    itv.it_interval.tv_sec = 1;
    itv.it_interval.tv_usec = 0;
    itv.it_value.tv_sec = 1;
    itv.it_value.tv_usec = 0;

    
    setitimer(ITIMER_REAL,&itv,NULL);

    atexit(module_unload);
}

mytbf_t* mytbf_init(int cps ,int burst)  //C语言中,void*可以赋值给任何类型的指针,任何类型的指针也都可以赋值给void*
{
    struct mytbf_st*me;
    int pos;
    if(inited == 0)
    {
        module_load();
        inited = 1;
    }

    pos = get_free_pos();
    if(pos < 0)
        return NULL;

    me = malloc(sizeof(*me));
    if(me == NULL)
        return NULL;

    me->cps = cps;
    me->burst = burst;
    me->token = 0;
    me->pos = pos;
    job[pos] = me;

    return me;

}
static int min(int token,int size)
{
	if(token> size)
		return size;
	return token;
}
int mytbf_fetchtoken(mytbf_t*ptr,int size)  //获取token
{
    if(size <= 0)
        return -EINVAL;  //参数非法
    struct mytbf_st*me = ptr;
    while(me->token <= 0 )  //token为空就等待
        pause();
    
   //  int n = min(me->token,size);
    int n = (me->token>size?size:me->token);
    me->token -= n;
    return n;
}

int mytbf_returntoken(mytbf_t*ptr,int size)  //返还token
{
    if(size<=0)
        return -EINVAL;
    struct mytbf_st*me = ptr;

    me->token+= size;
    if(me->token > me->burst)
        me->token  = me->burst;
    return size;
}

int mytbf_destroy(mytbf_t*ptr)
{
    struct mytbf_st *me;
    me = ptr;
    job[me->pos] = NULL;
    free(ptr);
    return 0;
    
}

setitimer()

 #include <sys/time.h>

       int getitimer(int which, struct itimerval *curr_value);
       int setitimer(int which, const struct itimerval *new_value,
                     struct itimerval *old_value)

比alarm时间更加的准确

           struct itimerval {
               struct timeval it_interval; /* Interval for periodic timer */
               struct timeval it_value;    /* Time until next expiration */
           };

           struct timeval {
               time_t      tv_sec;         /* seconds */
               suseconds_t tv_usec;        /* microseconds */
           };


slowcat.c
#include <stdlib.h>
#include <stdio.h>
#include <error.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <fcntl.h>
#include <errno.h>
#include <signal.h>


#define CPS 100
#define BUF_SIZE    CPS

static volatile int loop = 0;

static void alrm_handler(int num)
{
//     alarm(1);
    loop = 1;
}

int main(int argc,char **argv)
{

    int fps,fpd=1;
    int ret;
    char buf[BUF_SIZE];
    int len,pos;
    if(argc <2)
    {
        fprintf(stderr,"Usage:%s \n",argv[0]);
        exit(1);
    }
    signal(SIGALRM,alrm_handler);
//    alarm(1);
    struct itimerval itv;
    itv.it_interval.tv_sec = 1;
    itv.it_interval.tv_usec = 0;
    itv.it_value.tv_sec = 1;
    itv.it_value.tv_usec = 1;

    if(setitimer(ITIMER_REAL,&itv,NULL) <0)
    {
        fprintf(stderr,"setitimer:%s",strerror(errno));
    }

    do
    {
        fps = open(argv[1],O_RDONLY);
        if(fps <0)
        {
            if(errno != EINTR)
            {
                perror("open");
                exit(1);
            }
        }
    }while(fps<0);
    
    while(1)
    {
        //while(!loop);
        while(loop == 0)
            pause();   //用于等待一个打断的到来。不加则是忙等。

        loop = 0;
        while(1)
        {
        len = read(fps,buf,BUF_SIZE);
        if(len < 0)
        {
            if(errno == EINTR)
                continue;
            perror("read()");
            break;
        }
        break;
        }
        if(len ==0)
            break;

        pos = 0;
        while(len > 0)
        {
            ret = write(fpd,buf+pos,len);
            if(ret <0)
            {
                if(errno == EINTR)
                    continue;
                perror("write()");
                exit(1);
            }
            pos += ret;
            len-=ret;
        }
    
    }

    close(fps);

    return 0;
}

  int sigpending(sigset_t *set); 不清楚使用场景。
 

10. 实时信号

标准信号会丢失,实时信号不会丢失,需要排队,响应有一个顺序。

SIGUSR1 SIGUSR2是预留的信号。

susp_rt.c

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

#define MYRTSIG (SIGRTMIN+6)


void mySig()
{

    write(1,".",1);
}

int main()
{

    signal(MYRTSIG,mySig);
    
    int i;
    sigset_t set,old_set,save_set;
    sigemptyset(&set);

    sigaddset(&set,MYRTSIG);

    sigprocmask(SIG_UNBLOCK,&set,&save_set);// 模块话编程,先保存之前的状态。

    sigprocmask(SIG_BLOCK,&set,&old_set);

    for(i = 0;i<1000;i++)
    {

   //     sigprocmask(SIG_BLOCK,&set,&old_set);
        for(int j=0;j<5;j++)
        {
        write(1,"*",1);
        sleep(1);  // 信号可以打断堵塞 的系统调用
        }
        write(1,"\n",1);
        sigsuspend(&old_set);
    }
    sigprocmask(SIG_SETMASK,&save_set,NULL); //结束后,恢复之前保存的状态。
    return 0;
}

kill -40 pid 杀掉信号,可以使程序继续运行,切发送多次信号都会执行。排队次数用ulimit -i查看。 

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

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

相关文章

ThreadPoolExecutor - 管理线程池的核心类

下面是使用给定的初始参数创建一个新的 ThreadPoolExecutor &#xff08;构造方法&#xff09;。 public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,…

缓冲器的重要性,谈谈PostgreSQL

目录 一、PostgreSQL是什么二、缓冲区管理器介绍三、缓冲区管理器的应用场景四、如何定义缓冲区管理器 一、PostgreSQL是什么 PostgreSQL是一种高级的开源关系型数据库管理系统&#xff08;RDBMS&#xff09;&#xff0c;它以其稳定性、可靠性和高度可扩展性而闻名。它最初由加…

Static关键字的用法详解

Static关键字的用法详解 1、Static修饰内部类2、Static修饰方法3、Static修饰变量4、Static修饰代码块5、总结 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 在Java编程语言中&#xff0c;static是一个关键字&#xff0c;它可以用于多种上…

【Java探索之旅】初识多态_概念_实现条件

文章目录 &#x1f4d1;前言一、多态1.1 概念1.2 多态的实现条件 &#x1f324;️全篇总结 &#x1f4d1;前言 多态作为面向对象编程中的重要概念&#xff0c;为我们提供了一种灵活而强大的编程方式。通过多态&#xff0c;同一种操作可以应用于不同的对象&#xff0c;并根据对象…

数据库系统原理 | 查询作业2

整理自博主本科《数据库系统原理》专业课自己完成的实验课查询作业&#xff0c;以便各位学习数据库系统概论的小伙伴们参考、学习。 *文中若存在书写不合理的地方&#xff0c;欢迎各位斧正。 专业课本&#xff1a; ​ ​ ———— 本次实验使用到的图形化工具&#xff1a;Heidi…

深入分析SSL/TLS服务器的证书(C/C++代码实现)

SSL&#xff08;Secure Sockets Layer&#xff09;和TLS&#xff08;Transport Layer Security&#xff09;是网络安全领域的重要协议&#xff0c;它们在保护网络通信中发挥着至关重要的作用。这些协议通过加密和身份验证机制&#xff0c;确保数据在传输过程中的机密性和完整性…

cs231n作业1——SVM

参考文章&#xff1a;cs231n assignment1——SVM SVM 训练阶段&#xff0c;我们的目的是为了得到合适的 &#x1d44a; 和 &#x1d44f; &#xff0c;为实现这一目的&#xff0c;我们需要引进损失函数&#xff0c;然后再通过梯度下降来训练模型。 def svm_loss_naive(W, …

python基础篇(8):异常处理

在Python编程中&#xff0c;异常是程序运行时发生的错误&#xff0c;它会中断程序的正常执行流程。异常处理机制使得程序能够捕获这些错误&#xff0c;并进行适当的处理&#xff0c;从而避免程序崩溃。 1 错误类型 代码的错误一般会有语法错误和异常错误两种&#xff0c;语法错…

最新整理的机器人相关数据合集(1993-2022年不等 具体看数据类型)

机器人安装数据是指记录全球或特定区域内工业机器人新安装数量的信息&#xff0c;这一数据由国际机器人联合会(IFR)等权威机构定期发布。这些数据不仅揭示了机器人技术的市场需求趋势&#xff0c;还反映了各国和地区自动化水平及产业升级的步伐。例如&#xff0c;数据显示中国在…

nginx相关概念(反向代理、负载均衡)

1 Nginx 是什么 Nginx是一款轻量级的Web 服务器&#xff0c;其特点是占有内存少&#xff0c;并发能力强 2 Nginx 反向代理 正向代理代替客户端去发送请求反向代理代替服务端接受请求 2.1 正向代理 若客户端无法直接访问到目标服务器 server 则客户端需要配置代理服务器 pr…

云渲染技术对电影24帧和游戏60帧渲染需求及时间效率的影响

随着云计算技术的飞速发展&#xff0c;云渲染正重塑着影视和游戏产业的制作流程。它如何影响传统电影24帧和现代游戏60帧的渲染需求与时间效率&#xff1f;本文将深入探讨云渲染带来的变革。 一、电影24帧和游戏60帧作用 电影通常以24帧每秒&#xff08;fps&#xff09;的标准…

数字化精益生产系统--IFS财务管理系统

IFS财务管理系统是一款功能丰富、高效且灵活的企业财务管理软件&#xff0c;广泛应用于多个行业和不同规模的企业中。以下是对IFS财务管理系统的功能设计&#xff1a;

Linux shell编程学习笔记63:free命令 获取内存使用信息

0 前言 在系统安全检查中&#xff0c;内存使用情况也是一块可以关注的内容。Linux提供了多个获取内存信息的命令很多。今天我们先研究free命令。 1 free命令的功能、用法和选项说明 1.1 free命令的功能 free 命令可以显示系统内存的使用情况&#xff0c;包括物理内存、交换…

SSM高校教师教学质量评估系统-计算机毕业设计源码03344

摘要 在高等教育中&#xff0c;教学质量是培养优秀人才的关键。为了提高教学质量&#xff0c;高校需要建立一套科学、有效的教师教学质量评估系统。本研究采用 SSM技术框架&#xff0c;旨在开发一款高校教师教学质量评估系统。 SSM框架作为一种成熟的Java开发框架&#xff0c;具…

入门PHP就来我这(高级)11 ~ MySQL

有胆量你就来跟着路老师卷起来&#xff01; -- 纯干货&#xff0c;技术知识分享 路老师给大家分享PHP语言的知识了&#xff0c;旨在想让大家入门PHP&#xff0c;并深入了解PHP语言。 1 PHP操作MySQL数据库的方法 PHP操作数据库现在用的多的是mysqli拓展库&#xff0c;mysqli扩…

【Python机器学习】模型评估与改进——多分类指标

多分类问题的所有指标基本是上都来自于二分类问题&#xff0c;但是要对所有类别进行平均。多分类的精度被定义为正确分类的样本所占的比例。同样&#xff0c;如果类别是不平衡的&#xff0c;精度并不是很好的评估度量。 想象一个三分类问题&#xff0c;其中85%的数据点属于类别…

可视化作品集(07):网格化管理领域

网格化管理可视化大屏是指利用大屏幕显示设备&#xff0c;通过数据可视化的方式展示网格化管理的相关信息和指标。网格化管理是一种以网格为基础的城市管理模式&#xff0c;通过将城市划分为不同的网格单元&#xff0c;实现对城市各项管理工作的全覆盖、全时空监控和全过程管理…

中国星坤X1224系列线对板连接器:小巧稳定,助力物联网终端高效运行

在物联网、电器和消防等领域&#xff0c;终端设备的安全稳定运行至关重要。为了满足这些领域对连接器高可靠性、小巧轻便和耐高温的需求&#xff0c;X1224系列线对板连接器应运而生。这款连接器以其独特的设计和卓越的性能&#xff0c;成为了终端设备中不可或缺的一部分。 一、…

中英双语介绍加拿大(Canada)

加拿大国家简介 中文版 加拿大简介 加拿大是位于北美洲北部的一个国家&#xff0c;以其广袤的土地、多样的文化和自然美景著称。以下是对加拿大的详细介绍&#xff0c;包括其地理位置、人口、经济、特色、高等教育、著名景点、国家历史和交通条件。 地理位置 加拿大是世界…

Day2用 rustlings 练习 Rust 语言-Move Semantics

大家好 今天 完成 2024年自动驾驶OS开发训练营-初阶营第四期-导学 Day2用 rustlings 练习 Rust 语言 -Move Semantics https://doc.rust-lang.org/stable/book/ch04-00-understanding-ownership.html 提交代码时候 提示 没有权限怎么出来 aciton 参考开发环境配置 https://rcor…