进程间通信---管道通信、命名管道、匿名管道详解

news2024/11/26 15:37:59

什么是通信?
为什么要通信?
如何做到通信?
管道是什么?
管道用来干什么?
管道如何实现通信?
匿名管道是什么?

如何实现?
命名管道是什么?

如何实现?

什么是文件引用计数?
本文,将会回答上述问题,并且带你书写相关代码
 

目录

一、通信

1、进程之间为什么通信?

2、进程如何通信?

3、什么是通信标准?

(1)通信标准

(2)常见通信手段

二、管道

1、管道理解和特征

(1)管道的四种情况

(2)管道5个特征

2、匿名管道

(1)进程以读、写同时打开一个文件

(2)子进程和父进程同时打开一个文件

(3)实现匿名管道通信

a、管道函数说明

b、创建管道示例

3、命名管道

(1)、命名管道理解

(2)实现命名管道

(1)相关函数

1. mkfifo

2. open

3. read

4. write

(2)代码实现命名管道通信


一、通信

1、进程之间为什么通信?

进程需要协同

2、进程如何通信?

a、进程间通信,成本高,因为进程独立
一直可以通信和通信一次是不一样的
b、进程件通信的前提:让不同进程见到同一份(操作系统)资源(内存)
(1)首先是某一个进程需要通信,让OS创建一个共享资源(OS是操作系统的意思)
(2)OS必须提供很多系统调用
因此,OS创建的共享资源的不同,系统调用接口的不同,就说明进程间通信会有不同的种类

3、什么是通信标准?

(1)通信标准

什么是标准?通俗理解,就是一件事情,要怎么做。例如说,一个硬件,要怎么做,如功率多大,尺寸多大、IO接口怎么设计等。那么这个标准,谁规定的?这个行业最叼的公司制定的;凭什么你规定?因为人家叼;你怎么证明你叼?我的发明专利最多,质量最好;我又为什么要听你的?因为你不按照我的做,你卖不出去。例如说,现在有一家手机生产商,手机的充电口是正方形的,你买吗?你不会买。而专利,是需要收钱的;你掌握了标准,你就掌握了市场控制权。所以,这也就是为什么老美跟华为死过不去的原因。一流的企业卖标准,二流的企业卖服务,三流的企业卖产品。
同时,小国家的专利是没有意义的;一个专利的推行,背后必定有一个强大的国家在支撑。有兴趣的同学可以去了解一下日本的半导体产业。再比如华为,如果华为在一个很弱的国家,基本就遭殃了。
两个标准:system、posix。System V 和 POSIX 是两个重要的操作系统标准,它们定义了系统接口和行为,以确保软件的可移植性和一致性。了解即可

(2)常见通信手段

a、信息队列
b、共享内存
c、信息量

二、管道

上述是一个进程打开一个文件的过程。当多个进程同时对一个文件操作时,并不会重新在内存重新加载一份文件,而是两个同时使用一个文件内存,因为数据、属性都是一样的,没有必要再搞一份一模一样的。而为了让两个进程能够通信交流,只能开辟两个进程都能同时访问的空间。而我们发现,当打开同一个文件时,这个文件就能被两个进程同时访问看见。所以,为了直接复用操作系统的特性(降低成本),而不是从头开始开发一套新的通信模式,所以就让两个进程通过打开同一份文件的方式进行通信。所以,这个提供通信的通道也叫做管道文件。

1、管道理解和特征

(1)管道的四种情况

1、管道是空,且write fd没有关闭,则无法读文件,因为没有数据,没有数据我读什么?所以读程序就会被阻塞,此时需要等待读取条件准备,即有数据写入管道,让管道里有数据,即具备了读的条件,此时读取程序开始读取
管道大小是固定的,ubantu大小是64kB
2、管道被写满,且read fd关闭,则无法写数据,因为管道被写满了,再写数据就会被所覆盖,所以写程序就会被阻塞,此时需要等待读条件准备,才能继续写数据,即管道数据被读取
3、管道一直在被读取,但是写入端被关闭,则读端返回值就会读到0,返回值为0意味着没有更多的数据可以读取。程序通常会检查这个返回值,以决定在何时终止读取操作。
4、rfd读端直接关闭,写端wfd一直在写入
这种管道叫做坏的管道,此时写端进程会被操作系统直接杀掉,发送SIGPIPE 13个信号

(2)管道5个特征

1、匿名管道:只能进行具有血缘关系进程之间通信,例如父子、爷孙,这种通信的本质是子进程继承父进程数据(内核级缓存区)
2、管道的同步机制:多执行流执行代码时,具有明显的顺序
例如,子进程向缓冲区写数据,父进程才读数据;子进程没有写数据到缓冲区,父进程要等待子进程写入
3、文件的生命周期事跟随进程的,而管道也是文件,所以生命周期也是跟随进程
4、管道文件在通信的时候,是面向文件字节流的:写入次数和读出的次数不是一致的,例如:写五次,读一次
管道的一端是写入,另一端是读取。管道就像是自来水厂的蓄水池,水厂负责写入;你只管用
当你使用水的时候,你只关心你怎么用水,并不关心水厂那边是怎么往蓄水池里注水和注多少
所以,数据就像是蓄水池里的水一样,从一端流向另一端,因此形象的称为字节流
5、管道的通信模式,是一种特殊的半双工模式(我跟你说话,你得听我说完,你说我听,我说你听;单向)
还有另一种叫全双工(类似于吵架,你问候我,我也问候你,同时进行)

2、匿名管道

(1)进程以读、写同时打开一个文件

当进程同时以读和写两种方式打开文件时:
先用读方式打开:
a、操作系统创建文件属性结构体,从磁盘中加载文件属性
b、添加fd到文件描述符表,建立对应的fd和文件对象映射
c、申请一块文件缓冲区,将磁盘中的文件数据加载进来
再用写方式打开:
并不会重复上述a、b、c步骤
而是仅仅建立对应的文件对象,即一个新的fd对象
在文件描述符中添加新的fd,
但是指向和读文件同一份文件资源
这就避免了重复资源浪费

(2)子进程和父进程同时打开一个文件

当创建子进程时,如果是同时打开一份文件
子进程同样不会重新进行上述a、b、c步骤
而仅仅继承父进程的文件属性结构体
其中继承的文件描述符表指向同一个文件对象
我们说过进程要独立,但是没说过文件也要独立
因此,多个进程就可以管理同一个文件,同时,多个进程都能指向操作系统提供的同一个内存资源
例如文件的缓冲区
这个文件的缓冲区,就叫做管道文件,是父子进程共享的
于是,两个进程就通过共同的缓冲区,可以进行通信
但是,管道只允许单向通信:即子进程->父进程 / 父进程->子进程
所以,父进程读,那么子进程就只能写,因为单向
于是,父进程就要释放写文件对象的fd
子进程就要释放读文件对象的fd
因为是单向,就像是管道一样,只能从一头到另一头
所以叫做管道通信

而在文件对象结构体内部,会有一个类似于引用计数的变量
有多少个进程指向文件,文件引用计数就为几
当引用计数为0时,意味着没有进程使用文件,于是文件资源被释放

(3)实现匿名管道通信

a、管道函数说明

pipe 函数是一个用于在进程间创建管道的系统调用函数。pipe 函数通过创建一个管道来实现这种通信,其中管道有两个端点:一个用于写入数据(写端),另一个用于读取数据(读端)。

int pipe(int pipefd[2]);

参数
pipefd:这是一个整型数组,必须至少包含两个元素。pipefd[0] 是读端文件描述符,pipefd[1] 是写端文件描述符。

返回值
成功时,pipe 返回0。
失败时,返回-1,并且设置 errno 以指示错误原因。

使用方法
创建管道:
调用 pipe(pipefd) 创建一个管道,并将读端和写端的文件描述符存储在 pipefd 数组中。

读写数据:

在写端(pipefd[1])写入数据时,数据会流入管道中。
在读端(pipefd[0])读取数据时,可以从管道中取出之前写入的数据。

关闭文件描述符:
使用完毕后,应关闭文件描述符。通常使用 close(pipefd[0]) 关闭读端,close(pipefd[1]) 关闭写端。

close 函数用于关闭打开的文件描述符,释放与之相关的资源。

int close(int fd);

参数
fd:需要关闭的文件描述符。

返回值
成功时,返回0。
失败时,返回-1,并设置 errno 以指示错误原因。

exit 函数用于终止当前进程,并返回一个退出状态码给操作系统。头文件<stdlib.h>

void exit(int status);

参数
status:一个整数值,表示进程的退出状态。通常用来指示进程是正常退出还是出现了错误。常见的约定是:
0 表示正常退出。
非 0 的值表示异常退出,具体值可以用于表示不同的错误类型(这取决于程序的约定)。

waitpid 函数用于在父进程中等待特定的子进程结束,并获取其退出状态。它允许父进程在不阻塞的情况下等待某个特定的子进程或任何子进程的状态变化。

pid_t waitpid(pid_t pid, int *status, int options);

参数
pid:指定要等待的子进程的进程 ID。

可以是以下值:

正整数:等待指定的子进程。
-1:等待任何子进程。
0:等待与调用进程具有相同进程组 ID 的任何子进程。
小于 -1:等待具有指定进程组 ID 的任何子进程。

status:一个指向整数的指针用于存储子进程的退出状态。如果为 NULL,则不保存状态。

options:指定等待行为的选项。常用的选项有:

0:默认行为,阻塞,直到子进程结束。
WNOHANG:非阻塞模式,如果没有子进程退出,则立即返回。
WUNTRACED:即使子进程被停止(例如由于信号),也返回其状态。

返回值
成功:返回结束的子进程的进程 ID。
失败:返回 -1,并设置 errno 来指示错误原因。

进程退出状态:

位数

描述

说明

31

终止信号标志

1 位,用于指示进程是否因信号终止。

30

保留位

1 位,通常为保留位。

29-23

停止信号编号

7 位,表示导致进程停止的信号编号(如 SIGSTOP)。

22-16

保留位

7 位,通常为保留位。

15-8

正常退出状态码

8 位,表示进程正常退出时的状态码。

7-0

信号编号

8 位,表示进程因信号终止时的信号编号。

read 函数是 Unix 和类 Unix 操作系统中用于从文件描述符中读取数据的系统调用。它的功能是从指定的文件描述符中读取数据到缓冲区中。

ssize_t read(int fd, void *buf, size_t count);

参数

  1. fd: 文件描述符。这个文件描述符通常是通过 opensocketpipe 函数获得的,指定了要读取的文件或设备。

  2. buf: 指向一个缓冲区的指针。read 函数会将读取到的数据存储在这个缓冲区中。

  3. count: 要读取的字节数。read 函数会尽可能读取 count 字节的数据到缓冲区 buf 中。

返回值

  • 成功: 返回实际读取的字节数。如果返回值小于 count,可能是因为到达了文件末尾或文件描述符的缓冲区数据不足。

  • 失败: 返回 -1,并设置 errno 以指示错误原因。常见的错误包括:

    • EAGAIN 或 EWOULDBLOCK: 文件描述符设置为非阻塞模式,并且在该操作无法立即完成。

    • EBADF: 文件描述符无效。

    • EFAULTbuf 指针无效(指向不可访问的内存区域)。

    • EINTR: 操作被信号中断。

b、创建管道示例
#include <iostream>
#include <cstdio>
#include <unistd.h>
#include <string.h>
#include <string>
#include <sys/types.h>
#include <sys/wait.h>

void SubProcessWrite(int wfd)
{
    char ch = 'A';
    int pipesize = 1;
    while (1)
    {
        write(wfd, &ch, 1); // 往管道里写内容
        ch++;
        if(ch == 'G')
            break;
    }
    std::cout << "child quit ..." << std::endl;
}

void FatherProcessRead(int rfd)
{
    char inbuffer[1024];
    while (1)
    {
        std::cout << "------------------------" << std::endl;
        ssize_t n = read(rfd, inbuffer, sizeof inbuffer - 1);
        if (n > 0) // 读取成功
        {
            inbuffer[n] = 0;
            std::cout << "father get info is : " << inbuffer << std::endl;
        }
        else if (n == 0)
        {
            // 返回值为0,表示写端口关闭,读到文件末尾
            std::cout << "client quit, father get return val : " << n << std::endl;
            break;
        }
        else if (n < 0) // 读取错误
        {
            std::cout << "read error " << std::endl;
            break;
        }
    }
}

int main()
{
    // 创建管道
    int pipefd[2];
    int n = pipe(pipefd); // 创建成功,返回0;失败返回-1,并设置出错码
    if (n != 0)
    {
        std::cout << "error: " << errno << ":"
                  << "erroring : " << strerror(errno) << std::endl; // 用strerror函数获取错误码信息
        return 1;
    }

    std::cout << "pipe[0]: " << pipefd[0] << "  pipe[1]: " << pipefd[1] << std::endl;
    std::cout << "pipe create sucessed!" << std::endl;

    // 2、创建子进程
    pid_t id = fork();
    if (id == 0) // 返回0创建成功
    {
        // 关闭不需要的端口,加入让子进程写,读端口就要关闭
        close(pipefd[0]);
        std::cout << "child close read done, Get ready to write!" << std::endl;
        // 子进程写数据
        SubProcessWrite(pipefd[1]);
        close(pipefd[1]);
        std::cout << "child close wtite done!" << std::endl;
        exit(0); // 子进程正常退出
    }

    // 3、父子进程读取,并关闭不需要的管道
    sleep(2);
    close(pipefd[1]);
    std::cout << "Father close write fd done, Receiving information: " << std::endl;
    FatherProcessRead(pipefd[0]);
    close(pipefd[0]);
    std::cout << "Father close done" << std::endl;

    // 父进程等待子进程退出并回收
    sleep(1);
    int status = 0;
    pid_t rid = waitpid(id, &status, 0);
    if (rid > 0) // 如果返回值大于0,返回成功(status退出状态是以32位图标识)
    {
        std::cout << "wait child process done, exit sigstop: " << (status & 0x7f) << std::endl;
        std::cout << "wait child process done, exit cod(ign) :" << ((status >> 8) & 0xff) << std::endl;
    }

    return 0;
}

3、命名管道

(1)、命名管道理解

解决的是两个毫无相关进程的通信问题
当一个进程打开一个文件时,首先把文件加入到进程结构体中的文件描述符表(数组),下标就是文件fd,即inode编号
这个inode编号指向文件的结构体,这个结构体有文件的属性信息、操作集、文件内核缓冲区等
此时如果另一个进程也同时打开这个文件,也要创建文件结构体,但是文件结构体没有必要再创建
而是指向同一个文件结构体,此时,两个进程就有了共同的资源
此时一个进程读,一个进程写,就实现了通信
如何确保两个进程打开同一个文件?
有父子关系的进程,通过继承打开同一个文件
每一个文件的路径具有唯一性,所以通过路径找到同一个文件
这个文件数据没有必要同步到磁盘
因为只需要两个进程间数据交流即可,其他部分没必要拿到数据
所以,这个文件是一个特殊的文件,叫做命名管道
以p开头的文件是管道文件
读端关闭,写端会直接关闭,因为人读取,写了也没有用。

(2)实现命名管道

(1)相关函数

命名管道(Named Pipes),也称为 FIFOs(First In, First Out),是 Unix 和类 Unix 操作系统中用于进程间通信(IPC)的机制。它允许不同进程之间通过文件系统中的特殊文件进行数据交换。以下是与命名管道相关的主要函数及其用法:

1. mkfifo

函数原型:

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

参数:

pathname: 命名管道的路径。

mode: 管道的权限位(通常与 chmod 中使用的权限位类似)。

返回值:

成功:返回 0。
失败:返回 -1,并设置 errno。

说明:
mkfifo 用于创建一个命名管道。如果管道已经存在,mkfifo 将失败。创建的管道在文件系统中具有指定的 pathname,可以用于不同进程之间的通信。

2. open

函数原型:

int open(const char *pathname, int flags, ...);

参数:

pathname: 命名管道的路径。

flags: 文件访问模式(例如 O_RDONLY、O_WRONLY、O_RDWR)。
...: 可选的模式位(仅在创建新文件时使用,如 O_CREAT)。

返回值:

成功:返回文件描述符。
失败:返回 -1,并设置 errno。

说明:
使用 open 函数打开命名管道时,可以指定不同的访问模式。如果以 O_WRONLY 打开管道,表示写入;如果以 O_RDONLY 打开管道,表示读取。

3. read

函数原型:

ssize_t read(int fd, void *buf, size_t count);


参数:

fd: 命名管道的文件描述符。
buf: 指向缓冲区的指针,用于存放读取的数据。
count: 读取的字节数。

返回值:

成功:返回实际读取的字节数。
失败:返回 -1,并设置 errno。

说明:
从命名管道中读取数据时,read 函数会阻塞直到有数据可读或管道被关闭。

4. write

函数原型:

ssize_t write(int fd, const void *buf, size_t count);

参数:

fd: 命名管道的文件描述符。
buf: 指向包含要写入数据的缓冲区的指针。
count: 写入的字节数。

返回值:

成功:返回实际写入的字节数。
失败:返回 -1,并设置 errno。

说明:
向命名管道中写入数据时,write 函数会阻塞直到有进程读取这些数据,或者管道被关闭。

函数原型:


int unlink(const char *pathname);

参数:

pathname: 要删除的命名管道的路径。

返回值:

成功:返回 0。
失败:返回 -1,并设置 errno。

说明:
unlink 用于删除命名管道。即使管道文件被删除,只要仍有进程打开这个管道,管道仍然存在,直到所有进程关闭它为止。

(2)代码实现命名管道通信

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

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

相关文章

探究 Element Plus Menu 横向多层级展开组件的 Bug 及解决方案

文章目录 1 ellipsis 是否省略多余的子项&#xff08;仅在横向模式生效&#xff09;问题描述解决方案 2 多个级别的子菜单位置错乱或默认直接展开问题描述解决方案1 index没有设置2 通用策略 1 ellipsis 是否省略多余的子项&#xff08;仅在横向模式生效&#xff09; 问题描述…

Global Structure-from-Motion Revisited golmap论文翻译

Global Structure-from-Motion Revisited Paper&#xff1a;https://demuc.de/papers/pan2024glomap.pdf Code&#xff1a;https://github.com/colmap/glomap 摘要- 从图像中恢复三维结构和摄像机运动一直是计算机视觉研究的热点&#xff0c;被称为运动结构恢复(SfM)。这个问…

【RAG提升技巧】查询改写HyDE

简介 提高 RAG 推理能力的一个好方法是添加查询理解层 ——在实际查询向量存储之前添加查询转换。以下是四种不同的查询转换&#xff1a; 路由&#xff1a;保留初始查询&#xff0c;同时查明其所属的适当工具子集。然后&#xff0c;将这些工具指定为合适的选项。查询重写&…

[论文笔记]ZeRO: Memory Optimizations Toward Training Trillion Parameter Models

引言 今天带来ZeRO: Memory Optimizations Toward Training Trillion Parameter Models的论文笔记。 大型深度模型提供了显著的准确性提升&#xff0c;但训练数十亿到数万亿个参数是具有挑战性的。现有的解决方案&#xff0c;如数据并行和模型并行&#xff0c;存在基本的局限…

汇昌联信做拼多多电商如何提升浏览量?

在拼多多电商平台上&#xff0c;如何提升商品的浏览量是每个卖家都关注的问题。高浏览量不仅意味着更多的潜在客户&#xff0c;还能间接提升销量和店铺的知名度。汇昌联信作为电商运营者&#xff0c;需要采取有效策略来吸引消费者的注意力。 一、优化商品标题和描述 商品标题是…

LDR6328Q:重塑电源管理新境界的取电芯片

在电子设备日益普及的今天&#xff0c;高效、智能的电源管理成为了提升用户体验的关键因素之一。而LDR6328Q作为一款专为设备端设计的取电芯片&#xff0c;凭借其出色的性能和创新的技术&#xff0c;正逐步成为电源管理领域的一颗璀璨新星。本文将深入探讨LDR6328Q取电芯片的特…

大模型面经,不要到处找了,收藏我这一篇就够了

在当前技术快速发展的背景下&#xff0c;大模型领域的职位成为了许多求职者的热门选择。为了帮助大家更好地准备面试&#xff0c;这里整理了一份大模型面试经验分享&#xff0c;涵盖了一些常见的面试流程、可能遇到的技术问题以及面试官可能会问到的行为问题等。 大模型面试经…

什么是实时数据仓库? 优势与最佳实践

在当今数据驱动的世界中&#xff0c;许多企业使用实时数据仓库来满足其分析和商业智能 (BI) 需求。这使他们能够做出更好的决策、推动增长并为客户提供价值。 数据仓库是一种数据存储和管理系统&#xff0c;其设计目标只有一个&#xff1a;管理和分析数据&#xff0c;以实现商…

链表求和问题(面试题)

目录 一题目&#xff1a; 二思路汇总&#xff1a; 三解答代码&#xff1a; 一题目&#xff1a; leetcode题目链接 &#xff1a;面试题 02.05. 链表求和 - 力扣&#xff08;LeetCode&#xff09; 二思路汇总&#xff1a; 想到这道题&#xff0c;可能看起来第一思路就是把它们…

【C++二分查找】2563. 统计公平数对的数目

本文涉及的基础知识点 C二分查找 LeetCode2563. 统计公平数对的数目 给你一个下标从 0 开始、长度为 n 的整数数组 nums &#xff0c;和两个整数 lower 和 upper &#xff0c;返回 公平数对的数目 。 如果 (i, j) 数对满足以下情况&#xff0c;则认为它是一个 公平数对 &…

利用住宅代理优化媒体监控,全面提升品牌管理与市场竞争力

引言 什么是媒体监控&#xff1f;主要用于哪里&#xff1f; 媒体监控面临的主要挑战 住宅代理在媒体监控中的作用 如何利用住宅代理进行媒体监控 总结 引言 在信息化时代&#xff0c;媒体监控&#xff08;media monitoring&#xff09;已成为企业进行品牌声誉管理、市场研…

喵喵蓝牙热敏打印机(下)

目录 前言一、电量、温度、缺纸检测1.电量检测2.针头温度检测3.缺纸检测 二、蓝牙APP通信打印1.蓝牙初始化2.APP通信打印 三、FreeRTOS任务整合 前言 喵喵蓝牙热敏打印机&#xff08;上&#xff09; 内容有点多&#xff0c;就分为了上下两篇。 一、电量、温度、缺纸检测 先启…

Codigger视频会议(Meeting):科技行业软件开发团队的协作革新

在数字化时代&#xff0c;软件开发团队的协作方式正在经历一场革命。远程工作已成为新常态&#xff0c;而Codigger视频会议&#xff08;Meeting&#xff09;作为这场变革的催化剂&#xff0c;正在帮助科技行业的团队突破地理限制&#xff0c;实现无缝协作。 随着科技行业的快速…

Adobe Audition AU 2023-23.6.6.1 解锁版下载和安装教程(专业的音频处理工具)

前言 Audition是Adobe旗下一款非常好用的音频处理工具&#xff0c;软件为用户们提供了功能强大的音频编辑功能和一个相对完善的工作流程&#xff0c;用户们无论是录制音乐、无线电广播还是视频配音&#xff0c;多音频合成&#xff0c;这款软件都能够给你足够的创作动力。audit…

MR400D工业级带网口4G DTU:RS232/RS485 TO LTE深度测评

在物联网技术日新月异的今天&#xff0c;数据传输的效率和稳定性成为了各行各业关注的焦点。作为一款集先进性与实用性于一身的物联网设备&#xff0c;工业级带网口的4G DTU&#xff08;数据传输单元&#xff09;以其强大的功能特性和广泛的应用场景&#xff0c;赢得了市场的广…

整合nacos遇到的小问题

1.nacos创建配置中心文件时&#xff0c;创建不了&#xff0c;原因是新版本nacos数据表有字段加了安全校验 2.bootStrap.yaml文件创建时。需注意文件名称和后缀

simulink种deadzone死区模块的理解和使用,使用simulink开发pid的时候经常添加deadzone(重要)

Dead Zone模块产生指定范围&#xff08;称为截止区&#xff09;内的零输出。输入输出的关系如下&#xff1a; 1、输入落入截止区&#xff0c;输出0 2、输入大于上限值&#xff0c;输出上限值 3、输入小于等于下限值&#xff0c;输出下限值 使用simulink开发pid的时候经常添…

人工智能在子宫内膜癌领域的研究进展|顶刊速递·24-08-12

小罗碎碎念 本期推文主题&#xff1a;人工智能在子宫内膜癌领域中的研究进展 昨天的推文主要介绍的是卵巢癌&#xff0c;有一小部分涉及到了子宫内膜癌&#xff0c;按照最新的规划&#xff0c;今天的推文是与子宫内膜癌相关的。 从事妇科肿瘤研究的老师/同学&#xff0c;可以好…

探索数字媒体产业园区的未来之路

随着科技的飞速发展和数字经济的崛起&#xff0c;数字媒体产业园区正迎来前所未有的机遇和挑战。未来&#xff0c;数字媒体产业园区的发展将主要体现在以下几个方面&#xff1a; 1. 技术驱动的创新 数字媒体产业园区将继续受到前沿技术的推动。人工智能、大数据、虚拟现实和增…

中小企业文档管理最佳方案:7个热门工具

本文将分享7大优质中小企业文档管理工具&#xff1a;PingCode&#xff1b;2. Worktile&#xff1b;3. 飞书文档&#xff1b;4. 语雀&#xff1b;5. 有道云笔记&#xff1b;6. 石墨文档&#xff1b;7. DocuWare。 在管理中小企业时&#xff0c;文档混乱和信息难以追踪是一个常见…