IO进程线程day9(2023.8.7)

一、Xmind整理:

消息队列的原理:

共享内存的原理:

二、课上练习:

练习1:用信号的方式回收僵尸进程(重点!)

1.子进程退出后,父进程会收到17)SIGCHLD信号

2.父进程中捕获17)SIGCHLD信号,给该信号注册新的处理函数。在该新的处理函数中执行waitpid函数,回收僵尸进程。

3.当在信号A的处理函数内部时,再次触发A信号,会导致信号屏蔽,会造成多个子进程短时间内同时退出,父进程只会处理一个17号信号,导致僵尸进程收不干净的问题

4.解决方式:当成功回收到僵尸进程后,再回收一次。直到没有僵尸进程,结束循环。即判断waitpid(-1, NULL, WNOHANG);的返回值

    ①  =0,有子进程,但是没有僵尸进程

    ②  =-1,没有子进程,也没有僵尸进程

小练1:回收一个子进程

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <head.h>
void handler(int sig)
{
    printf("触发%d信号\n",sig);
    pid_t wpid =waitpid(-1,NULL,WNOHANG);
    printf("wpid =%d\n",wpid);
    return;
}
int main(int argc, const char *argv[])
{
    pid_t cpid = fork();

    if(cpid > 0)
    {
        //捕获2号SIGINT信号
        if(signal(17,handler) == SIG_ERR)
        {
            perror("signal");
            return -1;
        }
        printf("捕获17号信号成功\n");
        while(1)                                                                              
        {
            printf("this id parent: %d %d\n",getpid(),cpid);
            sleep(1);
        }
    }
    else if(0 == cpid)
    {
        for(int i = 0; i<3; i++)
        {
            printf("this is cpid %d %d\n",getppid(),getpid());
            sleep(1);
        }
        printf("子进程 %d 准备退出\n",getpid());
        exit(0);
    }
    else
    {
        perror("fork");
        return -1;
    }

    return 0;
}

小练2:多个子进程短时间内同时退出问题

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <head.h>
#include <sys/wait.h>
int count = 0;
void handler(int sig)
{
    while(1)
    {   //当回收成功后,再收一次,直到回收失败
        //=-1,没有僵尸进程,也没有子进程
        //=0,没有僵尸进程,但是有子进程
        pid_t wpid = waitpid(-1,NULL,WNOHANG);
        if(wpid <= 0)
            break;
        printf("[%d] wpid = %d\n",++count,wpid);
    }
       /*
       也可以这样写
       while(waitpid(-1,NULL,WNOHANG) > 0);
       */                                                           

    return;
}
int main(int argc, const char *argv[])
{
    //捕获17号SIGINT信号
    if(signal(17,handler) == SIG_ERR)
    {
        perror("signal");
        return -1;
    }
    printf("捕获17号信号成功\n");

    int i = 0;
    while(i < 100)
    {
        if(fork() == 0)   //若是子进程
        {
            exit(0);      //退出
        }
        i++;              //只有父进程执行i++
    }
    //能运行到当前位置,则代表是父进程
    while(1)
        sleep(1);

    return 0;
}

练习2:kill

功能:给指定进程或者进程组发送一个信号

原型:

#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);

参数:

pid_t pid:指定要发送的进程或者进程组;
pid > 0,  将信号发送给指定的进程,进程号==pid参数;
pid == 0, 将信号发送给当前进程组下的所有进程;
pid == -1, 将信号发送给当前进程权限所能送达的所有进程,除了1号进程;
pid < -1,  将信号发送给指定进程组下的所有进程。进程组ID == -pid参数;
int sig:指定要发送的信号的编号,可以填对应的宏;
sig==0代表没有信号被发送,但是依然会检测对方进程是否存在,或者是否有权限访问。

返回值:

成功,返回0;
失败,返回-1,更新errno;

练习3:alarm

功能:设置一个定时器,当时间到后,给当前进程发送一个14) SIGALRM 信号

原型:

#include <unistd.h>
unsigned int alarm(unsigned int seconds);

参数:

unsigned int seconds:设置定时器时间,以秒为单位;
seconds == 0, 则取消定时器;

返回值:

>0, 返回上一个定时器没有走完的时间;
=0, 没有未走完的定时器;

小练: 设置一个3s的定时器

 #include <stdio.h>
 #include <unistd.h>
 #include <signal.h>
 
 void callback(int sig)
 {
     printf("alarm...\n");
     alarm(3);     //设置一个3s的定时器
     return;
 }
 
 int main(int argc, const char *argv[])
 {
     //捕获14号信号
     if(signal(14,callback) == SIG_ERR)              
     {
         perror("signal");
         return -1;
     }
 
     alarm(3);    //设置一个3s的定时器
 
     while(1)
     {
         printf("signal...\n");
         sleep(1);
     }
     return 0;
 }
                                                     

练习4:ftok

功能:该函数通过pathname提供的id,以及proj_id提供的8bit的值,计算key值(键值),给msgget shmget semget函数使用。只要pathname和proj_id一致,则计算的key值就一致。那么通过相同key值找到的IPC对象就是同一个。

原型:

#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id);

参数:

char *pathname:文件的路径以及名字; 该文件必须存在且可访问
int proj_id:传入一个非0参数;

返回值:

成功,返回计算得到的key值;
失败,返回-1,更新errno;

小练: 创建消息队列

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <head.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int main(int argc, const char *argv[])
{
    //创建key值
    key_t key =ftok("/",1);
    if(key < 0)
    {
        perror("ftok");
        return -1;
    }
    printf("key = %#x\n",key);

    //创建消息队列
    int msqid = msgget(key,IPC_CREAT|0664);
    if(msqid < 0)
    {
        perror("msgget");
        return -1;
    }
    printf("msqid = %d\n",msqid);
    system("ipcs -q");               //让代码执行shell指令

    return 0;
}                                                                  

练习5:msgget

功能:通过key值到内核内存中找对应的消息队列,并返回消息队列的id ---> msqid

原型:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t key, int msgflg);

参数:

key_t key:ftok函数返回出来的key值;
int msgflg:
        IPC_CREAT:若消息队列不存在,则创建消息队列。若消息队列存在,则忽略该选项;
        IPC_CREAT|0664:创建的同时指定消息队列的权限。
        IPC_CREAT|IPC_EXCL:若消息队列不存在,则创建消息队列。若消息队列存在,则报错;

返回值:

>=0, 成功返回消息队列的id号  msqid;
=-1, 函数运行失败,更新errno;

练习6:msgsnd

功能:将数据打包后发送到消息队列中

原型:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);

参数:

int msqid:指定要发送到哪个消息队列中;
void *msgp:指定要发送的消息包的首地址; 通用格式如下:
struct msgbuf {
long mtype;       /* message type, must be > 0 */    消息类型,必须大于0;
char mtext[1];    /* message data */  消息内容,类型根据需求修改,想要发什么类型就填什么类型。
                                      大小与下一个参数msgsz指定的一致
              };   
size_t msgsz:消息内容的大小,以字节为单位。   
int msgflg:
0:阻塞方式发送,当消息队列满了,则当前函数阻塞;
IPC_NOWAIT:非阻塞方式,当消息队列满了,该函数不阻塞,且函数运行失败,errno == EAGAIN.

返回值:

=0, 函数运行成功;
=-1, 函数运行失败,更新errno;

小练: 

 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
 #include <head.h>
 #include <sys/ipc.h>
 #include <sys/msg.h>
 
 struct msgbuf
 {
     long mtype;
     char mtext[128];
 };
 
 int main(int argc, const char *argv[])
 {
     //创建key值
     key_t key =ftok("/",1);
     if(key < 0)
     {
         perror("ftok");
         return -1;
     }
     printf("key = %#x\n",key);
 
     //创建消息队列
     int msqid = msgget(key,IPC_CREAT|0664);
     if(msqid < 0)
     {
         perror("msgget");
         return -1;
     }
     printf("msqid = %d\n",msqid);
 
     struct msgbuf sndbuf;
     while(1)
     {
         printf("请输入消息类型:");
         scanf("%ld",&sndbuf.mtype);
         getchar();
 
         if(0 == sndbuf.mtype)   //若终端输入0,则跳出循环
             break;
         printf("请输入消息内容:");
         fgets(sndbuf.mtext,sizeof(sndbuf.mtext),stdin);
         sndbuf.mtext[strlen(sndbuf.mtext)-1] = 0;
 
         //向消息队列中发送数据
         if(msgsnd(msqid,&sndbuf,sizeof(sndbuf.mtext),0)< 0)
         {
             perror("msgsnd");
             return -1;
         }
         printf("发送成功\n");
         system("ipcs -q");               //让代码执行shell指令
     }
     return 0;
 }

练习7:msgrcv

功能:从消息队列中读取数据

原型:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);

参数:

int msqid: 指定要从哪个消息队列中读取消息;
void *msgp:指定将读取到的数据存储到什么位置,填对应空间的地址; 以什么形式写入,就以什么形式读取。通用格式如下:
struct msgbuf {
long mtype;       /* message type, must be > 0 */    消息类型,必须大于0;
char mtext[1];    /* message data */  消息内容,类型根据需求修改,想要发什么类型就填什么类型。
                                      大小与下一个参数msgsz指定的一致
              };   
size_t msgsz:指定要读取多少个字节大小的消息内容,以字节为单位。   
long msgtyp:指定要读取的消息类型; 
msgtyp == 0, 读取消息队列中的第一条消息; 先进先出;
msgtyp > 0,  指定消息类型读取,读取消息队列中第一条消息类型为 msgtyp参数指定的消息;
msgflg指定了MSG_EXCEPT,读取消息队列中第一条消息类型 不等于 msgtyp参数指定的消息;
vi -t MSG_EXCEPT;   #define MSG_EXCEPT      020000 
msgtyp < 0,  读取消息队列中第一条最小的,且类型小于等于 msgtyp参数绝对值的消息。                  
int msgflg:
0:阻塞方式,当消息队列中没有消息了,该函数阻塞;
IPC_NOWAIT:非阻塞方式运行,当消息队列中没有消息了,该函数不阻塞,运行失败,errno == ENOMSG;

返回值:

>0, 成功读取到的字节数;
=-1, 函数运行失败,更新errno;

代码示例: 

若消息队列中有消息:

                                   mtype     100      101      99      100      101

                                   mtext       aaa     bbb     ccc     ddd     eee

 i. msgtyp == 0

while(1)                                                                   
    {
        //阻塞方式读取消息队列中第一条消息,先进先出的原则
        //res = msgrcv(msqid, &rcv, sizeof(rcv.mtext), 0, 0);
        
        //非阻塞方式读取消息队列中第一条消息,先进先出的原则
        res = msgrcv(msqid, &rcv, sizeof(rcv.mtext), 0, IPC_NOWAIT);
        if(res < 0)
        {
            perror("msgrcv");
            return -1;
        }
        printf("res=%ld : %ld %s\n", res, rcv.mtype, rcv.mtext);
    }
​
输出顺序:
    100 aaa   101 bbb   99 ccc   100 ddd    101 eee

ii. msgtyp > 0 

while(1)
    {
        //1.阻塞方式读取消息队列中第一条消息类型 == 101 的消息
        //res = msgrcv(msqid, &rcv, sizeof(rcv.mtext), 101, 0);
​
        //2.非阻塞方式读取消息队列中第一条消息 == 101 的消息
        //res = msgrcv(msqid, &rcv, sizeof(rcv.mtext), 101, IPC_NOWAIT);
                                                                                  
        //3.非阻塞方式读取消息队列中第一条消息类型 != 101的消息 
        res = msgrcv(msqid, &rcv, sizeof(rcv.mtext), 101, IPC_NOWAIT|020000);
        if(res < 0)
        {
            perror("msgrcv");
            return -1;
        }
        printf("res=%ld : %ld %s\n", res, rcv.mtype, rcv.mtext);
    }
注释1,2的现象:
    101 bbb     101 eee
第3个的现象:
    100 aaa    99 ccc   100 ddd    

iii.msgtyp < 0

 while(1)
    {
        //阻塞方式读取消息队列中<=msgtyp参数指定的消息类型中最小的那条消息;
        res = msgrcv(msqid, &rcv, sizeof(rcv.mtext), -100, 0);
       
        //非阻塞方式读取消息队列中<=msgtyp参数指定的消息类型中最小的那条消息;
        //res = msgrcv(msqid, &rcv, sizeof(rcv.mtext), -100, IPC_NOWAIT);
​
        if(res < 0)
        {
            perror("msgrcv");
            return -1;
        }
        printf("res=%ld : %ld %s\n", res, rcv.mtype, rcv.mtext);
    }
​
现象:
res=128 : 99 ccc
res=128 : 100 aaa
res=128 : 100 ddd

练习8:msgctl

功能:控制消息队列,常用于删除消息队列

原型:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgctl(int msqid, int cmd, struct msqid_ds *buf);

参数:

int msqid:指定要控制的消息队列的id号;
int cmd:
        IPC_STAT:获取消息队列的属性,属性存储在第三个参数中;
        IPC_SET:设置消息队列属性,属性存储在第三个参数中;
        IPC_RMID:删除消息队列,第三个参数无效,填NULL即可;

返回值:

成功,返回0;
失败,返回-1,更新errno;
//删除消息队列
    if(msgctl(msqid, IPC_RMID, NULL) < 0)
    {
        perror("msgctl");
        return -1;
    }
    printf("删除消息队列成功\n");

练习9:共享内存函数   ftok

功能:该函数通过pathname提供的id,以及proj_id提供的8bit的值,计算key值(键值),给msgget shmget semget函数使用。只要pathname和proj_id一致,则计算的key值就一致。那么通过相同key值找到的IPC对象就是同一个。

原型:

#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id);

参数:

char *pathname:文件的路径以及名字; 该文件必须存在且可访问
int proj_id:传入一个非0参数;

返回值:

成功,返回计算得到的key值;
失败,返回-1,更新errno;

练习10:shmget

功能:通过key值到内核内存中找对应的共享内存,并返回共享内存的id ---> shmid

原型:

#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);

参数:

key_t key:ftok函数返回出来的key值;
size_t size:指定要申请多少个字节的共享内存;
int shmflg:
        IPC_CREAT:若消息队列不存在,则创建共享内存。若共享内存存在,则忽略该选项;
        IPC_CREAT|0664:创建的同时指定共享内存的权限。
        IPC_CREAT|IPC_EXCL:若共享内存不存在,则创建共享内存。若共享内存存在,则报错;

返回值:

>=0, 成功返回共享内存的id号  shmid;
=-1, 函数运行失败,更新errno;

练习11:shmat

功能:将共享内存映射到用户空间中

原型:

#include <sys/types.h>
#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr, int shmflg);

参数:

int shmid:指定要映射的共享内存id号 , 
const void *shmaddr:指定共享内存要映射到用户空间的位置,填对应空间的首地址; 例如:(void*)0x10
                     填NULL,代表让操作系统自动映射;
int shmflg :
        0:默认方式映射,进程对共享内存可读可写;
        SHM_RDONLY:只读,进程对共享内存只读;

返回值:

成功,返回共享内存映射到用户空间的首地址;
失败,返回 (void *) -1,更新errno;
注意:获取到的映射空间的首地址的指向不允许修改,若修改后会导致首地址找不到,导致内存泄漏,与堆空间首地址不能改变的概念一致

小练: 

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/shm.h>
#include <head.h>
#include <sys/ipc.h>
int main(int argc, const char *argv[])
{
    //创建key值
    key_t key = ftok("./",10);
    if(key < 0)
    {
        perror("ftok");
        return -1;
    }
    printf("key = %#x\n",key);

    //创建共享内存,获得shmid号
    int shmid =shmget(key, 32, IPC_CREAT|0664);
    if(shmid < 0)
    {
        perror("shmget");
        return -1;                                 
    }
    printf("shmid = %d\n",shmid);

    //映射共享内存到用户空间
    void* addr = shmat(shmid,NULL,0);
    if((void*)-1 == addr)
    {
        perror("shmat");
        return -1;
    }
    printf("addr = %p\n",addr);
    system("ipcs -m");

    return 0;
}

练习12:shmdt

功能:将共享内存与进程的用户空间断开映射; 当进程不想操作共享内存的时候,就可以断开映射

原型:

#include <sys/types.h>
#include <sys/shm.h>
int shmdt(const void *shmaddr);

参数:

void *shmaddr:指定要断开映射的用户空间的首地址

返回值:

成功,返回0;
失败,返回-1,更新errno;

练习13:shmctl

功能:控制共享内存,常用于删除共享内存

原型:

#include <sys/ipc.h>
#include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_ds *buf);

参数:

int shmid:指定要控制的共享内存;
int cmd:
        IPC_STAT:获取共享内存的属性,属性存储在第三个参数中;
        IPC_SET:设置共享内存属性,属性存储在第三个参数中;
        IPC_RMID:删除共享内存,第三个参数无效,填NULL即可;

返回值:

成功,返回0;
失败,返回-1,更新errno;
//删除共享内存
    if(shmctl(shmid, IPC_RMID, NULL) < 0)
    {
        perror("shmctl");
        return -1;
    }
    printf("删除共享内存成功\n");

三、课后作业:

1.要求用消息队列实现AB进程对话

   A进程先发送一句话给B进程,B进程接收后打印

   B进程再回复一句话给A进程,A进程接收后打印

   重复1.2步骤,当收到quit后,要结束AB进程

   实现随时收发:用多进程 多线程。

A进程:

#include <stdio.h>
#include <string.h>
#include <head.h>

struct msgbuf
{
    long mtype;
    char mtext[128];
};
int main(int argc, const char *argv[])
{
    //创建key值
    key_t key =ftok("./",1);

    if(key < 0)
    {
        perror("ftok");
        return -1;
    }
    printf("key = %#x\n",key);

    //创建消息队列                                                   
    int msqid = msgget(key,IPC_CREAT|0664);
    if(msqid < 0)
    {
        perror("msgget");
        return -1;
    }
    printf("msqid = %d\n",msqid);

    struct msgbuf sndbuf;
    //创建一个子进程
    pid_t cpid = fork();
    if(cpid > 0)
    {
        while(1)
        {
            sndbuf.mtype = 1;
            scanf("%s",sndbuf.mtext);
            msgsnd(msqid,&sndbuf,sizeof(sndbuf.mtype),0);
            if(strcmp(sndbuf.mtext,"quit") == 0)
                break;
        }
    }
    else if(0 == cpid)
    {
        while(1)
        {
            msgrcv(msqid,&sndbuf,sizeof(sndbuf.mtext),2,0);
            if(strcmp(sndbuf.mtext,"quit") == 0)
                break;
            printf("%s\n",sndbuf.mtext);
        }
        kill(getppid(),2);
    }
    else
    {
        perror("fork");
        return -1;
    }
    return 0;
}

B进程:

#include <stdio.h>
#include <string.h>
#include <head.h>
struct msgbuf
{
    long mtype;
    char mtext[128];
};
int main(int argc, const char *argv[])
{
    key_t key = ftok("./",1);
    if(ftok < 0)
    {
        perror("ftok");
        return -1;                                                       
    }
    printf("key = %#x\n",key);

    int msqid = msgget(key,IPC_CREAT|0664);
    if(msqid < 0)
    {
        perror("msgget");
        return -1;
    }

    struct msgbuf sndbuf;
    pid_t cpid = fork();
    if(cpid > 0)
    {
        while(1)
        {
            sndbuf.mtype = 2;
            scanf("%s",sndbuf.mtext);
            msgsnd(msqid,&sndbuf,sizeof(sndbuf.mtype),0);
            if(strcmp(sndbuf.mtext,"quit") == 0)
                break;
        }
    }

    else if(0 == cpid)
    {
        while(1)
        {
            msgrcv(msqid,&sndbuf,sizeof(sndbuf.mtext),1,0);
            if(strcmp(sndbuf.mtext,"quit") == 0)
                break;
            printf("%s\n",sndbuf.mtext);
        }
        kill(getppid(),2);
    }
    else
    {
        perror("fork");
        return -1;
    }

    return 0;
}

2.要求在共享内存中存入字符串 “1234567”。A进程循环打印字符串,B进程循环倒置字符串,要求结果不允许出现乱序:

提示:共享内存中存储 flag + string.

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <head.h>
#include <sys/shm.h>
int main(int argc, const char *argv[])
{
    //创建key值
    key_t key = ftok("./", 3);
    if(key < 0)
    {
        perror("ftok");
        return -1;
    }
    printf("key = %#x\n", key);

    //通过key值获取shmid号
    int shmid = shmget(key, 32, IPC_CREAT|0777);
    if(shmid < 0)
    {
        perror("shmget");
        return -1;
    }
    printf("shmid = %d\n", shmid);

    //将共享内存映射到用户空间
    void* addr = shmat(shmid, NULL, 0);
    if((void*)-1 == addr)
    {
        perror("shmat");
        return -1;
    }                                                                            
    printf("addr = %p\n", addr);

    //存储一个str
    *(int *)addr = 0;
    char *str = (char *)addr+4;
    //存储一个字符串
    strcpy(str,"1234567");
    pid_t cpid = fork();
    if(cpid > 0)
    {
        while(1)
        {
            if(*(int *)addr == 0)
            {
                printf("%s\n",(char *)addr+4);
                *(int *)addr = 1;
            }
        }
    }
    else if(0 == cpid)
    {
        while(1)
        {
            if(*(int*)addr == 1)
            {
                char *star = (char *)addr+4;
                char *end  = (char*)addr+4+strlen(str)-1;
                while(star<end)
                {
                    char temp = *star;
                    *star = *end;
                    *end  = temp;
                    star++;
                    end--;
                }
                *(int*)addr = 0;
            }
        }
    }
    else
    {
        perror("fork");
        return -1;
    }

    system("ipcs -m");

    return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

java版直播商城平台规划及常见的营销模式 电商源码/小程序/三级分销+商城免费搭建 bbcbbc

​ Java版工程项目管理系统 Spring CloudSpring BootMybatisVueElementUI前后端分离 1. 涉及平台 平台管理、商家端&#xff08;PC端、手机端&#xff09;、买家平台&#xff08;H5/公众号、小程序、APP端&#xff08;IOS/Android&#xff09;、微服务平台&#xff08;业务服务…

3.1 计算机网络和网络设备

数据参考&#xff1a;CISP官方 目录 计算机网络基础网络互联设备网络传输介质 一、计算机网络基础 1、ENIAC&#xff1a;世界上第一台计算机的诞生 1946年2月14日&#xff0c;宾夕法尼亚大学诞生了世界上第一台计算机&#xff0c;名为电子数字积分计算机&#xff08;ENIAC…

leetcode1-两数之和

vector知识回顾 C基础——STL——Vector_52Tiramisu的博客-CSDN博客 自己写的初始代码 class Solution { public:vector<int> twoSum(vector<int>& nums, int target) {int i 0, j 1;for(i 0; i < nums.size(); i){for(j 1; j < nums.size(); j){…

Python-OpenCV 图像的基础操作

图像的基础操作 获取图像的像素值并修改获取图像的属性信息图像的ROI区域图像通道的拆分及合并图像扩边填充图像上的算术运算图像的加法图像的混合图像的位运算 获取图像的像素值并修改 首先读入一副图像&#xff1a; import numpy as np import cv2# 1.获取并修改像素值 # 读…

blender 毛发粒子

新建平面&#xff0c;点击右侧粒子系统&#xff0c;选择毛发&#xff0c;调整毛发长度&#xff0c;数量&#xff08;Number&#xff09;&#xff0c;调整数量是为了避免电脑卡顿&#xff1b; 上面设置的每一根柱子都可以变成一个物体&#xff0c;点击渲染&#xff0c;渲染为选择…

微信云托管(本地调试)⑥:nginx、vue刷新404问题

一、nginx默认路径 1.1、默认配置文件路径&#xff1a;/etc/nginx/nginx.conf 1.2、默认资源路径&#xff1a;/usr/share/nginx/html/index.html 二、修改nginx.conf配置 &#xff08;注意配置中的&#xff1a;include /etc/nginx/conf.d/*.conf; 里面包了一个server配置文件…

【Rust】Rust学习 第六章枚举和模式匹配

本章介绍 枚举&#xff08;enumerations&#xff09;&#xff0c;也被称作 enums。枚举允许你通过列举可能的 成员&#xff08;variants&#xff09; 来定义一个类型。首先&#xff0c;我们会定义并使用一个枚举来展示它是如何连同数据一起编码信息的。接下来&#xff0c;我们会…

爬虫011_元组高级操作_以及字符串的切片操作---python工作笔记030

获取元组的下标对应的值 注意元组是不可以修改值的,只能获取不能修改 但是列表是可以修改值的对吧

NSS [MoeCTF 2022]baby_file

NSS [MoeCTF 2022]baby_file 题目源码直接给了 使用data伪协议发现被ban了。 那就换一种伪协议php://filter&#xff0c;猜测flag在同目录下flag.php中或根目录下/flag中 php://filter/readconvert.base64-encode/resourceflag.php读取文件源码&#xff08;针对php文件需要ba…

RISC-V公测平台发布:如何在SG2042上玩转OpenMPI

About HS-2 HS-2 RISC-V通用主板是澎峰科技与合作伙伴共同研发的一款专为开发者设计的标准mATX主板&#xff0c;它预装了澎峰科技为RISC-V高性能服务器定制开发的软件包&#xff0c;包括各种标准bencmark、支持V扩展的GCC编译器、计算库、中间件以及多种典型服务器应用程序。…

龙架构 Arch Linux 发行版发布

导读近日&#xff0c;龙架构 Arch Linux 发行版官方网站宣布结束 beta 状态&#xff0c;正式支持龙架构 (LoongArch)。 Arch Linux 是一种轻量级、可定制、灵活的 Linux 操作系统。作为一款简单、现代、开放的操作系统&#xff0c;Arch Linux 旨在基于 “KISS 原则”&#xff0…

fabric.js里toDataURL后,画布内容展示不全?

复现场景&#xff1a; 用fabric生成画布后&#xff0c;转成图片&#xff0c;然后直接在浏览器里打开&#xff0c;画布展示内容缺失 画布原图&#xff1a; toDataURL后链接在浏览器打开&#xff1a; 原因解析&#xff1a; base64链接太长&#xff0c;输入浏览器链接被截断&…

C语言每日一题:14《数据结构》复制带随机指针的链表

题目一&#xff1a; 题目链接&#xff1a; 思路一&#xff1a; 找相对位置暴力求解的方法&#xff1a; 1.复制一个新的链表出来遍历老的节点给新的节点赋值&#xff0c;random这个时候不去值。 2.两个链表同时遍历&#xff0c;遍历老链表的时候去寻找相对位置&#xff0c;在遍…

gma 2 教程(二)数据操作:5. 多维科学数据

多维科学数据定义 如下图所示&#xff0c;gma将多维栅格定义为N&#xff08;>1&#xff09;个普通栅格数据集&#xff08;DataSet&#xff09;1组&#xff08;记录多维数据信息的&#xff09;元数据组成的多数据集&#xff08;MultiDataSets&#xff09;。   注&#xff1…

【搜索框的匹配功能】

功能需求&#xff1a; 1. 输入关键字的同时&#xff0c;以下拉列表的形式显示匹配的内容&#xff1b; 2. 点击下拉列表的选项&#xff0c;跳转到对应的新的页面 注意&#xff1a;这里读取data.txt&#xff08;检索的文件对象&#xff09;&#xff0c;会存在跨域的问题&#x…

网络编程——深入理解TCP/IP协议——OSI模型和TCP/IP模型:构建网络通信的基石

TCP/IP协议— 一、简介 TCP/IP协议&#xff0c;即传输控制协议/互联网协议&#xff0c;是一组用于在计算机网络中实现通信的协议。它由两个主要的协议组成&#xff1a;TCP&#xff08;传输控制协议&#xff09;和IP&#xff08;互联网协议&#xff09;。TCP负责确保数据的可靠…

本地化部署自建类ChatGPT服务远程访问

本地化部署自建类ChatGPT服务远程访问 文章目录 本地化部署自建类ChatGPT服务远程访问前言系统环境1. 安装Text generation web UI2.安装依赖3. 安装语言模型4. 启动5. 安装cpolar 内网穿透6. 创建公网地址7. 公网访问8. 固定公网地址 &#x1f340;小结&#x1f340; 前言 Te…

VBA技术资料MF41:VBA_将常规数字转换为文本数字

【分享成果&#xff0c;随喜正能量】时有落花至&#xff0c;远随流水香。人生漫长&#xff0c;不攀缘&#xff0c;不强求&#xff0c;按照自己喜欢的方式生活&#xff0c;不必太过在意&#xff0c;顺其自然就好。路再长也有终点&#xff0c;夜再黑也有尽头。 我给VBA的定义&am…

7 个最佳Node.js日志记录库和聚合器

日志记录是软件测试的重要组成部分。当我们知道错误是什么以及代码中出现问题的确切行时&#xff0c;调试应用程序要容易得多。 在本文中&#xff0c;我们将探讨与 Node.js 中的日志记录相关的各种概念&#xff0c;包括七个流行的日志记录库和聚合器&#xff0c;您可以使用它们…

成品短视频App源码,开启你的创意视频之旅!

短视频App如今已成为人们记录和分享生活的热门方式。你是否想过自己拥有一款属于自己的短视频App呢?有了短视频App源码&#xff0c;就能轻松实现这一愿望。本文将介绍短视频App源码的优势、开发流程和功能特点&#xff0c;助你快速创建个性化短视频App&#xff0c;开启你的创意…