IPC:匿名管道和命名管道

news2025/1/16 13:46:26

一 管道初级测试

写两个小程序,一个负责向管道发数据,一个从管道接收数据;

pipe.cpp

#include <iostream>
using namespace std;

int main()
{
    cout << "hello world" << endl;
    return 0;
}

pipe2.cpp 

#include <iostream>
#include <stdio.h>

using namespace std;
#define DEBUG_INFO(format, ...) printf("%s:%d -- " format "\n", __func__, __LINE__,##__VA_ARGS__)

int main()
{
    string str;
    while(1){
        str.clear();
        cin >> str;
        if(str.length() == 0)break;
        DEBUG_INFO("%s",str.c_str());
    }
    
    return 0;
}

编译生成pipe和pipe2两个可执行文件:

$ls -lsh pipe*
12K -rwxrwxr-x 1 lkmao lkmao 9.1K 5月   6 15:52 pipe
16K -rwxrwxr-x 1 lkmao lkmao  14K 5月   6 15:52 pipe2

执行如下指令:

$ ./pipe | ./pipe2 
main:14 -- hello
main:14 -- world

第二个程序接收到数据了,那么问题来了,代码中怎么没有管道。执行程序那根竖线就是管道。

strace命令跟踪

strace ./pipe | ./pipe2 

 strace ./pipe

strace ./pipe | strace ./pipe2  

 

 如图所示,S_IFIFO和S_IFCHR不一样,这两个宏的含义如下所示

S_IFCHR:文件是一个特殊的字符设备

S_IFIFO:文件是一个FIFO设备

也就是说,如果是S_IFIFO,那么文件描述符1表示,它对应的打开的文件是个管道。如果是S_IFCHR,则表示对应的文件是个字符设备,也就是终端,终端也是个字符设备,所以没毛病,就是常说的标准输出。

在命令./pipe | ./pipe2 中,pipe程序向管道输出"hello world" pipe2从管道中读取数据。

 

二 pipe函数创建匿名管道

#include <unistd.h>

int pipe(int pipefd[2]);

#define _GNU_SOURCE             /* See feature_test_macros(7) */
#include <fcntl.h>              /* Obtain O_* constant definitions */
#include <unistd.h>

int pipe2(int pipefd[2], int flags);

测试,使用pipe创建管道,使用fork创建一个子进程,进程中向向1 写入数据,父进程从0读出数据

测试代码1:传递一个字符串

#include <iostream>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <string.h>
#include <stdio.h>
#define DEBUG_INFO(format, ...) printf("%s:%d -- " format "\n", __func__, __LINE__,##__VA_ARGS__)
using namespace std;

int main()
{
    int fds[2];
    pid_t pid;
    int ret = pipe(fds);
    if(ret != 0){
        perror("pipe");
        exit(0);
    }
    char send_buf[100];
    char read_buf[100];
    memset(read_buf, 0, sizeof(read_buf));
    memset(send_buf, 0, sizeof(send_buf));
    DEBUG_INFO("%d %d",fds[0],fds[1]);
    cout << "create pipe ok" << endl;
    pid = fork();
    if(pid == -1){
        perror("fork");
        exit(-1);
    }
    
    
    if(pid == 0){
        
        int send_len = snprintf(send_buf, sizeof(send_buf),"wo shi child %u",getpid());
        DEBUG_INFO("write:send_len = %d,buf = %s",send_len,send_buf);
        int ret = write(fds[1], send_buf,send_len);
        if(ret == -1){
            perror("write");
            exit(-1);
        }
        DEBUG_INFO("child write finish:ret = %d,buf = %s",ret,send_buf);


        sleep(1);
    }else{
        int read_len = read(fds[0],read_buf,sizeof(read_buf));
        DEBUG_INFO("parent read finish:len = %d,buf = %s",read_len,read_buf);
        sleep(1);
    }
    DEBUG_INFO("bye bye %d",getpid());
    return 0;
}

测试结果:

main:23 -- 3 4
create pipe ok
main:35 -- write:send_len = 18,buf = wo shi child 46834
main:41 -- child write finish:ret = 18,buf = wo shi child 46834
main:47 -- parent read finish:len = 18,buf = wo shi child 46834
main:50 -- bye bye 46834
main:50 -- bye bye 46833

测试代码2:传递一个结构体:

#include <iostream>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <string.h>
#include <stdio.h>
#include <stdint.h>
#define DEBUG_INFO(format, ...) printf("%s:%d -- " format "\n", __func__, __LINE__,##__VA_ARGS__)
using namespace std;
struct mystruct{
    int type;
    int len;
    int frame_size;
    int frame_index;
    int frame_count;
    int offset;
    int length;
    char buf[128];
    uint32_t crc;
};

int main()
{
    int fds[2];
    pid_t pid;
    int ret = pipe(fds);
    if(ret != 0){
        perror("pipe");
        exit(0);
    }
    struct mystruct *ms1 = (struct mystruct *)malloc(sizeof(struct mystruct));
    struct mystruct *ms2 = (struct mystruct *)malloc(sizeof(struct mystruct));
    char send_buf[100];
    char read_buf[100];
    memset(read_buf, 0, sizeof(read_buf));
    memset(send_buf, 0, sizeof(send_buf));
    DEBUG_INFO("%d %d",fds[0],fds[1]);
    cout << "create pipe ok" << endl;
    pid = fork();
    if(pid == -1){
        perror("fork");
        exit(-1);
    }
    
    
    if(pid == 0){
        ms1->type = 1001;
        ms1->crc = 0x1001;

        ms2->type = 1002;
        ms2->crc = 0x1002;
        ret = write(fds[1], ms1,sizeof(struct mystruct));
        if(ret == -1){
            perror("write");
            exit(-1);
        }
        ret = write(fds[1], ms2,sizeof(struct mystruct));
        if(ret == -1){
            perror("write");
            exit(-1);
        }
        DEBUG_INFO("child %u write finish",getpid());


        sleep(1);
    }else{
        sleep(2);
        int read_len_1 = read(fds[0],ms1,sizeof(struct mystruct));
        int read_len_2 = read(fds[0],ms2,sizeof(struct mystruct));

        DEBUG_INFO("parent read finish:len = %d,crc = %04x",read_len_1,ms1->crc);
        DEBUG_INFO("parent read finish:len = %d,crc = %04x",read_len_2,ms2->crc);
        sleep(1);
    }
    DEBUG_INFO("bye bye %d",getpid());
    return 0;
}

在父进程中睡眠两秒,是为了保证让子进程先写完两次。 

测试结果:

main:37 -- 3 4
create pipe ok
main:62 -- child 47519 write finish
main:75 -- bye bye 47519
main:71 -- parent read finish:len = 160,crc = 1001
main:72 -- parent read finish:len = 160,crc = 1002
main:75 -- bye bye 47518

从测试结果可知:

1 管道可以用于传输结构体

2 管道中的传输的数据先写先到达,先被读

例如这些特点,和管道队列满的特点,就可以实现管道的双向通信了。

测试一下PIPE_BUF

#include <iostream>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <string.h>
#include <stdio.h>
#include <stdint.h>
#include <linux/limits.h>

#define DEBUG_INFO(format, ...) printf("%s:%d -- " format "\n", __func__, __LINE__,##__VA_ARGS__)
using namespace std;
struct mystruct{
    int type;
    int len;
    int frame_size;
    int frame_index;
    int frame_count;
    int offset;
    int length;
    char buf[PIPE_BUF];
    uint32_t crc;
};

int main()
{
    int fds[2];
    pid_t pid;
    int ret = pipe(fds);
    if(ret != 0){
        perror("pipe");
        exit(0);
    }
    DEBUG_INFO("PIPE_BUF = %d",PIPE_BUF);
    struct mystruct *ms1 = (struct mystruct *)malloc(sizeof(struct mystruct));
    struct mystruct *ms2 = (struct mystruct *)malloc(sizeof(struct mystruct));
    char send_buf[100];
    char read_buf[100];
    memset(read_buf, 0, sizeof(read_buf));
    memset(send_buf, 0, sizeof(send_buf));
    DEBUG_INFO("%d %d",fds[0],fds[1]);
    cout << "create pipe ok" << endl;
    pid = fork();
    if(pid == -1){
        perror("fork");
        exit(-1);
    }
    
    
    if(pid == 0){
        ms1->type = 1001;
        ms1->crc = 0x1001;

        ms2->type = 1002;
        ms2->crc = 0x1002;
        ret = write(fds[1], ms1,sizeof(struct mystruct));
        if(ret == -1){
            perror("write");
            exit(-1);
        }
        DEBUG_INFO("ret = %d",ret);
        ret = write(fds[1], ms2,sizeof(struct mystruct));
        if(ret == -1){
            perror("write");
            exit(-1);
        }
        DEBUG_INFO("ret = %d",ret);
        DEBUG_INFO("child %u write finish",getpid());

        sleep(1);
    }else{
        sleep(2);
        int read_len_1 = read(fds[0],ms1,sizeof(struct mystruct));
        int read_len_2 = read(fds[0],ms2,sizeof(struct mystruct));

        DEBUG_INFO("parent read finish:len = %d,crc = %04x",read_len_1,ms1->crc);
        DEBUG_INFO("parent read finish:len = %d,crc = %04x",read_len_2,ms2->crc);
        sleep(1);
    }
    DEBUG_INFO("bye bye %d",getpid());
    return 0;
}

测试结果:

main:33 -- PIPE_BUF = 4096
main:40 -- 3 4
create pipe ok
main:60 -- ret = 4128
main:66 -- ret = 4128
main:67 -- child 47924 write finish
main:79 -- bye bye 47924
main:75 -- parent read finish:len = 4128,crc = 1001
main:76 -- parent read finish:len = 4128,crc = 1002
main:79 -- bye bye 47923

PIPE_BUF的值是4096

写一个测试程序,

测试看看写入多少时,会写满出错。在此例中,将管道描述符设置为非阻塞模式:

1 子进程循环向管道写数据,直到出错返回-1

2 父进程循环从管道读数据,直到出错返回-1

#include <iostream>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <string.h>
#include <stdio.h>
#include <stdint.h>
#include <linux/limits.h>
#include <fcntl.h>

#define DEBUG_INFO(format, ...) printf("%s:%d -- " format "\n", __func__, __LINE__,##__VA_ARGS__)
using namespace std;
struct mystruct{
    int type;
    int len;
    int frame_size;
    int frame_index;
    int frame_count;
    int offset;
    int length;
    char buf[PIPE_BUF];
    uint32_t crc;
};

int main()
{
    int fds[2];
    pid_t pid;
    int ret = pipe(fds);
    if(ret != 0){
        perror("pipe");
        exit(0);
    }
    DEBUG_INFO("PIPE_BUF = %d",PIPE_BUF);
    struct mystruct *ms1 = (struct mystruct *)malloc(sizeof(struct mystruct));
    char send_buf[100];
    char read_buf[100];
    memset(read_buf, 0, sizeof(read_buf));
    memset(send_buf, 0, sizeof(send_buf));
    DEBUG_INFO("%d %d",fds[0],fds[1]);
    cout << "create pipe ok" << endl;
    pid = fork();
    if(pid == -1){
        perror("fork");
        exit(-1);
    }
    
    
    if(pid == 0){
        
        int flag = fcntl(fds[1], F_GETFL, 0);
        flag |= O_NONBLOCK;
        fcntl(fds[1], F_SETFL, flag);
        int count = 0;
        while(1){
            ms1->type = 1001 + count;
            ms1->crc = 0x1001 + count;
            ret = write(fds[1], ms1,sizeof(struct mystruct));
            if(ret == -1){
                perror("write");
                break;
            }
            DEBUG_INFO("ret = %d",ret);
            count++; 
        }
        DEBUG_INFO("write %d cuccess",count * sizeof(struct mystruct));
        DEBUG_INFO("child %u write finish",getpid());

        sleep(1);
    }else{
        sleep(20);
        int flag = fcntl(fds[0], F_GETFL, 0);
        flag |= O_NONBLOCK;
        fcntl(fds[0], F_SETFL, flag);
        while(1){
            ret = read(fds[0],ms1,sizeof(struct mystruct));
            if(ret == -1){
                perror("read");
                break;
            }
            DEBUG_INFO("ret = %d,%d,%04x",ret,ms1->type,ms1->crc);

        }
        DEBUG_INFO("parent");
    }
    DEBUG_INFO("bye bye %d",getpid());
    return 0;
}

 测试结果:

main:34 -- PIPE_BUF = 4096
main:40 -- 3 4
create pipe ok
main:63 -- ret = 4128
main:63 -- ret = 4128
main:63 -- ret = 4128
main:63 -- ret = 4128
main:63 -- ret = 4128
main:63 -- ret = 4128
main:63 -- ret = 4128
main:63 -- ret = 4128
main:63 -- ret = 4128
main:63 -- ret = 4128
main:63 -- ret = 4096
write: Resource temporarily unavailable
main:66 -- write 45408 cuccess
main:67 -- child 48554 write finish
main:86 -- bye bye 48554
main:81 -- ret = 4128,1001,1001
main:81 -- ret = 4128,1002,1002
main:81 -- ret = 4128,1003,1003
main:81 -- ret = 4128,1004,1004
main:81 -- ret = 4128,1005,1005
main:81 -- ret = 4128,1006,1006
main:81 -- ret = 4128,1007,1007
main:81 -- ret = 4128,1008,1008
main:81 -- ret = 4128,1009,1009
main:81 -- ret = 4128,1010,100a
main:81 -- ret = 4096,1011,100a
read: Resource temporarily unavailable
main:84 -- parent
main:86 -- bye bye 48553

从结果可知,write返回结果的前一次,返回结果是4096,小于结构体的大小。这里需要判断,这个返回的4096,是不是表示写了4096个字节。正式项目时,这里还要剩下的没写成功的数据写完。

父进程读数据,最后读回了4096个字节,也是要判断数据的完整性。防止误判。

至少,管道中到底能存多少数据。这个要不同的操作系统,在使用之前测试一下。大部分情况下,只要满足需要就可以了。

测试代码修改为阻塞模式:就是注释掉下图中的两段代码

 一不小心,就出来了一大堆,总之阻塞模式下是不会出现写一半的情况的。

main:81 -- ret = 4128,75163,131b3
main:63 -- ret = 4128
main:63 -- ret = 4128
main:63 -- ret = 4128
main:81 -- ret = 4128,75164,131b4
main:63 -- ret = 4128
main:81 -- ret = 4128,75165,131b5
main:63 -- ret = 4128
main:81 -- ret = 4128,75166,131b6
main:63 -- ret = 4128
main:81 -- ret = 4128,75167,131b7
main:63 -- ret = 4128
main:81 -- ret = 4128,75168,131b8
main:63 -- ret = 4128
main:81 -- ret = 4128,75169,131b9
main:81 -- ret = 4128,75170,131ba
main:81 -- ret = 4128,75171,131bb
main:63 -- ret = 4128
main:63 -- ret = 4128
main:81 -- ret = 4128,75172,131b

 所以,如果代码相对简单,设置为阻塞模式。就会使代码更简单。减少出错的机会。尽量不要为了使用IO多路复用而使用IO多路复用。

 三 命名管道

#include <sys/types.h>
#include <sys/stat.h>

int mkfifo(const char *pathname, mode_t mode);

#include <fcntl.h>           /* Definition of AT_* constants */
#include <sys/stat.h>

int mkfifoat(int dirfd, const char *pathname, mode_t mode);

删除一个命名管道:

    #include <unistd.h>

    int unlink(const char *pathname);

 测试一:创建一个命名管道:子进程写,父进程读

#include <iostream>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <stdio.h>
#include <stdint.h>
#include <linux/limits.h>
#include <fcntl.h>

#define DEBUG_INFO(format, ...) printf("%s:%d -- " format "\n", __func__, __LINE__,##__VA_ARGS__)
using namespace std;

#define FIFO_NAME   "myfifo"

void create_fifo(const char *file_name){
    int ret = unlink(file_name);
    if(ret == -1){
        perror("unlink");
    }

    ret = mkfifo(file_name,0666);
    if(ret < 0){
        perror("mkfifo");
        exit(1);
    }
    DEBUG_INFO("mkfifo ok ret = %d",ret);
    system("ls -lsh myfifo");
}

int main(int argc, char** argv){
    create_fifo(FIFO_NAME);
    pid_t pid = fork();
    if(pid == 0){
        int fd = open(FIFO_NAME,O_WRONLY);
        if(fd < 0){
            perror("open");
            exit(1);
        }
        write(fd, "hello world",sizeof("hello world"));
        close(fd);
        sleep(1);
    }
    if(pid > 0){
        char buf[100] = {0};
        int fd = open(FIFO_NAME,O_RDONLY);
        if(fd < 0){
            perror("open");
            exit(1);
        }
        int len = read(fd, buf,sizeof(buf));
        DEBUG_INFO("read len = %d,buf = %s",len,buf);
        close(fd);
    }
    if(pid == -1){
        perror("fork");
        exit(-1);
    }
    
    sleep(10);
    
    return 0;
}

测试结果:

create_fifo:28 -- mkfifo ok ret = 0
0 prw-rw-r-- 1 lkmao lkmao 0 5月   8 12:49 myfifo
main:53 -- read len = 12,buf = hello world

代码中设置的mode是0666,但是文件实际上是664,为什么呢,这个和系统本身的掩码mask有关:

执行umask:

umask
0002

所以设置的真正值是(mode & ~umask) = 0666 &~0002 = 0664

 

小结 

更多细节还需根据具体情况严谨测试,切记相当然的认为,它一定会使这样的,以事实为依据,以理论为准绳。

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

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

相关文章

【LeetCode】环形链表+结论证明

题目链接&#xff1a;环形链表 题目&#xff1a;给你一个链表的头节点 head &#xff0c;判断链表中是否有环。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链表中存在环。 为了表示给定链表中的环&#xff0c;评测系统内部使用整数 p…

中级软件设计师备考--解答题--数据流图

目录 基本概念基本元素数据流图的分层平衡原则 数据字典 基本概念 数据流图也称为数据流程图&#xff08;DFD&#xff09;&#xff0c;它摆脱了系统的物理内容&#xff0c;精确地在逻辑上描述系统的功能、输入、输出和数据存储&#xff0c;是系统逻辑模型的重要组成部分。 基…

《走进对象村3》找对象送孩子之特殊的构造方法

文章目录 &#x1f680;文章导读1. 构造方法1.1 构造方法的分类1.1.1 非默认的静态方法1.1.2 默认的构造方法1.1.3 带参数的构造方法 构造方法的特性&#xff1a; &#x1f680;文章导读 在本篇文章中&#xff0c;对构造方法进行了一些总结&#xff0c;可能也有不足的地方&…

Golang每日一练(leetDay0059) 两数之和II、Excel表列名称

目录 167. 两数之和 II 输入有序数组 Two-sum-ii-input-array-is-sorted &#x1f31f;&#x1f31f; 168. Excel表列名称 Excel Sheet Column Title &#x1f31f; &#x1f31f; 每日一练刷题专栏 &#x1f31f; Golang每日一练 专栏 Python每日一练 专栏 C/C每日一练…

MySQL多表查询之连接查询

0. 数据源 /*Navicat Premium Data TransferSource Server : localhost_3306Source Server Type : MySQLSource Server Version : 80016Source Host : localhost:3306Source Schema : tempdbTarget Server Type : MySQLTarget Server Version…

《走进对象村6》面向对象的第三大特性——多态

文章目录 &#x1f680;文章导读1.1 多态的概念1.2 多态的实现条件1.3 向上转型和向下转型1.4 重写 **面试问题&#xff1a;重写和重载的区别**多态的实现 &#x1f680;文章导读 在本篇文章中&#xff0c;将会有很多的干货和知识点掌握&#xff0c;希望读者慢慢耐心阅读 在本篇…

如何设定项目中的里程碑?

项目管理中非常重要的就是合理设置阶段性的里程碑&#xff0c;在项目实施过程中&#xff0c;根据里程碑来灵活控制项目进度和节奏。那么一个IT项目该如何合理地安排里程碑呢&#xff1f; 在IT项目管理中&#xff0c;里程碑是一种非常重要的工具&#xff0c;它能够帮助项目经理和…

谈谈架构分层

大家好&#xff0c;我是易安&#xff01; 在系统从0到1的阶段&#xff0c;为了让系统快速上线&#xff0c;我们通常是不考虑分层的。但是随着业务越来越复杂&#xff0c;大量的代码纠缠在一起&#xff0c;会出现逻辑不清晰、各模块相互依赖、代码扩展性差、改动一处就牵一发而动…

IIS6.0和网络管理软件SNMPc

一.IIS6.0 IIS 6.0提供了更为方便的安装/管理功能和增强的应用环境、基于标准的分布协议、改进的性能表现和扩展性&#xff0c;以及更好的稳定性和易用性。其服务组件包括&#xff1a; ①WWW服务。WWW是图形最为丰富的Internet服务。Web具有很强的链接能力&#xff0c;支持协…

软考中级——系统集成项目管理工程师(20天冲刺)

PV:计划价值(计划要成的价值) EV:挣值(实际做了的事儿的价值) AC:实际成本(实际花出去多少钱) SV:进度偏差EV-PV(项目提前或者落后的进度)>0项目进度超前<0项目进度落后 CV:成本偏差EV-AC(项目预算的号空成者盈利)>0成本节约<0成本超支 SPI:进度绩效指数EV/PV(挣值…

创建VUE2 前端以及后端的交互

创建vue2项目 1.javascript–>vue(不要勾选)–>安装element-ui(组件 | Element)–>执行指令&#xff08;npm i element-ui -S&#xff09;–>在main.js中引入&#xff08;import ElementUI from ‘element-ui’; ​ import ‘element-ui/lib/theme-chalk/index.c…

JavaWeb:Web 的基本概念、Tomcat 服务器、Http 详解、Maven 的下载安装步骤、模仿一个 Servlet

文章目录 JavaWeb - 01一、基本概念1、静态 Web2、动态 Web3、Web 应用程序4、三个技术 二、Web 服务器三、Tomcat 详解四、发布一个 Web 网站五、Http 详解1. Http 请求&#xff08;1&#xff09;请求行&#xff08;2&#xff09;消息头 2. Http 响应&#xff08;1&#xff09…

sourceTree离线环境部署

目录 1、下载sourceTree安装包&#xff0c;打开之后弹出注册界面&#xff08;需要去国外网站注册&#xff09;2、使用技术手段跳过注册步骤3、打开安装包进行安装 注&#xff1a;建议提前安装好git 1、下载sourceTree安装包&#xff0c;打开之后弹出注册界面&#xff08;需要去…

27 - 两数、三数、四数问题

文章目录 1. 两数之和2. 三数之和3. 最接近的三数之和4. 四数之和5. 四数相加 1. 两数之和 在遍历数组的时候只需要在map中去查询是否有个目前元素target - numbers[i]匹配的数值&#xff0c;如果有&#xff0c;就找到匹配对&#xff0c;如果没有就把目前遍历的元素放入map中&a…

融合有序,创造无限——解密力扣“合并两个有序数组”

本篇博客会讲解力扣“88. 合并两个有序数组”这道题&#xff0c;这是题目链接。 其实&#xff0c;有经验的朋友一看到这个题目的名字&#xff0c;应该就明白了&#xff0c;这玩意和归并排序脱不了干系。下面我们来审题&#xff1a; 输出示例如下&#xff1a; 以下是一些提…

3.是人就能学会的Spring源码教学-IOC容器的核心实现原理

是人就能学会的Spring源码教学-IOC容器的核心实现原理 我们学习Spring源码的动力&#xff0c;估计大部分人都是因为面试中会问到吧。 那么我们今天就以面试问Spring来开头。 关于Spring&#xff0c;在面试的时候一般会问到的两个最基础的问题。 第一个什么是IOC&#xff1f…

技术控,看这里,一款支持断点调试的数据科学工具

数据科学是一门利用统计学、机器学习、数据挖掘、数据可视化等技术和方法&#xff0c;从数据中提取知识和信息的交叉学科。自上世纪60年代&#xff0c;统计学家John W.Tukey首次提出“数据分析”&#xff08;Data Analysis&#xff09;的概念起&#xff0c;数据科学已历经了几十…

ASEMI代理ADUM131E1BRWZ-RL原装ADI车规级ADUM131E1BRWZ-RL

编辑&#xff1a;ll ASEMI代理ADUM131E1BRWZ-RL原装ADI车规级ADUM131E1BRWZ-RL 型号&#xff1a;ADUM131E1BRWZ-RL 品牌&#xff1a;ADI /亚德诺 封装&#xff1a;SOIC-16-300mil 批号&#xff1a;2023 安装类型&#xff1a;表面贴装型 引脚数量&#xff1a;16 工作温度…

基于springboot的“智慧食堂”设计与实现(源码等)

摘要 随着Internet的发展&#xff0c;人们的日常生活已经离不开网络。未来人们的生活与工作将变得越来越数字化&#xff0c;网络化和电子化。网上管理&#xff0c;它将是直接管理“智慧食堂”系统的最新形式。本xx是以构建“智慧食堂”系统为目标&#xff0c;使用java技术制作…

智能仿写软件-智能伪原创改写软件

智能仿写工具&#xff1a;营销创意的必备利器 在当今快节奏和不断发展的商业环境中&#xff0c;企业营销人员需要在短时间内产生大量有创意和高质量的内容。因此&#xff0c;智能仿写工具作为营销策略的一种创新方法而出现&#xff0c;可以帮助企业的写作团队更快速地生成文章…