文章目录
一、无名管道
1.概念
无名管道有一定的局限性。
第一:它是属于半双工的通信方式;
第二:只有具有“亲缘关系”的的进程才能使用这种通信方式,也就是父进程和子进程之间。
2.定义
pipe
int pipe(int pipefd[2])
参数 pipefd[0]:用于读管道。
参数 pipefd[1]:用于写管道。
返回值:执行成功返回 0,失败返回-1。
3.示例代码
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
//进程读函数
void read_data(int *);
//进程写函数
void write_data(int *);
int main(int argc,char *argv[])
{
int pipes[2],rc;
pid_t pid;
rc = pipe(pipes); //创建管道
if(rc == -1){ // 失败
perror("\npipes\n");
exit(1);
}
pid = fork(); //创建进程
switch(pid){
case -1: // 失败
perror("\nfork\n");
exit(1);
case 0: // 子进程
read_data(pipes); //相同的pipes
default: // 父进程
write_data(pipes); //相同的pipes
}
return 0;
}
//进程读函数
void read_data(int pipes[])
{
int c,rc;
//由于此函数只负责读,因此将写描述关闭(资源宝贵)
close(pipes[1]);
//阻塞,等待从管道读取数据
//int 转为 unsiged char 输出到终端
while( (rc = read(pipes[0],&c,1)) > 0 ){
putchar(c);
}
exit(0);
}
//进程写函数
void write_data(int pipes[])
{
int c,rc;
//关闭读描述字
close(pipes[0]);
while( (c=getchar()) > 0 ){ // 输入数据
rc = write( pipes[1], &c, 1); //写入管道
if( rc == -1 ){
perror("Parent: write");
close(pipes[1]);
exit(1);
}
}
close( pipes[1] );
exit(0);
}
4.测试结果
当往管道写入数据的时候,子进程都会从管道读出来。
二、有名管道
1.概念
无名管道只能用于有亲缘进程之间的通信,有名管道可以实现无亲缘关系的通信。有名管道 fifo 给文件系统提供一个路径,这个路径和管道关联,只要知道这个管道路径,就可以进行文件访问,fifo 是指先进先出,也就是先写入的数据,先读出来。
2.定义
mkfifo
int mkfifo(const char *pathname, mode_t mode);
参数*pathname:路径名,管道名称。
参数 mode:管道的权限。
返回值:成功返回 0,错误返回-1
3.示例代码
往date文件中写入数据
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
void filecopy(FILE *,char *);
int main(void)
{
FILE *fp1;
long int i = 100000;
char buf[] = "I am luatao ,I want to study Linux!\n";
char *file1 = "data.txt";
printf("begin!\n");
if((fp1 = fopen(file1,"a+")) == NULL ){ // 打开这个文件 没有则创建
printf("can't open %s\n",file1);
}
while(i--)
filecopy(fp1,buf); // 把buf数据写入到fp1文件中
fclose(fp1);
printf("over!\n");
return 0;
}
void filecopy(FILE *ifp,char *buf)
{
char c;
int i,j;
j = 0;
i = strlen(buf)-1;
while(i--){
putc(buf[j],ifp);
j++;
}
putc('\n',ifp);
}
从date文件读取数据往管道里写
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <string.h>
int main()
{
const char *fifo_name = "my_fifo"; // 管道名
char *file1 = "data.txt";
int pipe_fd = -1;
int data_fd = -1;
int res = 0;
const int open_mode = O_WRONLY; // 以只写方式打开文件
int bytes_sent = 0;
char buffer[PIPE_BUF + 1];
if(access(fifo_name, F_OK) == -1) // 访问管道
{
//管道文件不存在
//创建命名管道
res = mkfifo(fifo_name, 0777);
if(res != 0)
{
fprintf(stderr, "Could not create fifo %s\n", fifo_name);
exit(EXIT_FAILURE);
}
}
printf("Process %d opening FIFO O_WRONLY\n", getpid());
//以只写阻塞方式打开FIFO文件,以只读方式打开数据文件
pipe_fd = open(fifo_name, open_mode); // 以只写方式打开管道
data_fd = open(file1, O_RDONLY); // 以只读方式打开文件
printf("Process %d result %d\n", getpid(), pipe_fd);
if(pipe_fd != -1) // 打开管道成功
{
int bytes_read = 0;
//向数据文件读取数据
bytes_read = read(data_fd, buffer, PIPE_BUF);
buffer[bytes_read] = '\0';
while(bytes_read > 0)
{
//向FIFO文件写数据
res = write(pipe_fd, buffer, bytes_read);
if(res == -1)
{
fprintf(stderr, "Write error on pipe\n");
exit(EXIT_FAILURE);
}
//累加写的字节数,并继续读取数据
bytes_sent += res;
bytes_read = read(data_fd, buffer, PIPE_BUF);
buffer[bytes_read] = '\0';
}
close(pipe_fd);
close(data_fd);
}
else
exit(EXIT_FAILURE);
printf("Process %d finished\n", getpid());
exit(EXIT_SUCCESS);
}
从管道里读取数据并写入到DataFormFIFO文件里
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <limits.h>
#include <string.h>
int main()
{
const char *fifo_name = "my_fifo";
int pipe_fd = -1;
int data_fd = -1;
int res = 0;
int open_mode = O_RDONLY;
char buffer[PIPE_BUF + 1];
int bytes_read = 0;
int bytes_write = 0;
//清空缓冲数组
memset(buffer, '\0', sizeof(buffer));
printf("Process %d opening FIFO O_RDONLY\n", getpid());
//以只读阻塞方式打开管道文件,注意与fifowrite.c文件中的FIFO同名
pipe_fd = open(fifo_name, open_mode);
//以只写方式创建保存数据的文件
data_fd = open("DataFormFIFO.txt", O_WRONLY|O_CREAT, 0644);
printf("Process %d result %d\n",getpid(), pipe_fd);
if(pipe_fd != -1)
{
do
{
//读取FIFO中的数据,并把它保存在文件DataFormFIFO.txt文件中
res = read(pipe_fd, buffer, PIPE_BUF);
bytes_write = write(data_fd, buffer, res);
bytes_read += res;
}while(res > 0);
close(pipe_fd);
close(data_fd);
}
else
exit(EXIT_FAILURE);
printf("Process %d finished, %d bytes read\n", getpid(), bytes_read);
exit(EXIT_SUCCESS);
}
4.测试结果
依次运行下面程序,顺序不能变
运行creatc程序
运行writepipe程序
运行readpipe程序
三、消息队列
1.概念
消息队列就是一个消息的链表。可以把消息看作一个记录,具有特定的格式以及特定的优先级。对消息队列有写权限的进程可以向其中按照一定的规则添加新消息;对消息队列有读权限的进程则可以从消息队列中读走消息。
2.定义
结构体 msgp
struct msgstru{
long mtype; //大于 0
char mtext[nbyte];
}
msgrcv
函数 ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
参数 msqid:消息队列的标识码。
参数*msgp:指向消息缓冲区的指针。
参数 msgsz:消息的长短
参数 msgflg:标志位。
返回值:成功返回数据长度,错误返回-1。
msgsnd
函数 int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
参数 msqid:消息队列的标识码。
参数*msgp:指向消息缓冲区的指针,此位置用来暂时存储发送和接收的消息,是一个用
户可定义的通用结构。
参数 msgsz:消息的长短。
参数 msgflg:标志位。
返回值:成功返回 0,错误返回-1
msgget
还有一个函数 msgget 需要介绍,用来获取与某个键“key”关联的消息队列标识。
函数 int msgget(key_t key, int msgflg):
参数“key”:消息队列关联的键。
参数“msgflg”:消息队列的建立标志和存取权限。IPC_CREAT 如果内核中没有此队列,
则创建它;IPC_EXCL 当和 IPC_CREAT 一起使用时,如果队列已经存在,则失败。
返回值:执行成功则返回消息队列的标识符,否则返回-1。
3.示例代码
msgsend
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/msg.h>
#include <errno.h>
#define MAX_TEXT 512
struct msg_st
{
long int msg_type;
char text[MAX_TEXT];
};
int main()
{
int running = 1;
struct msg_st data;
char buffer[BUFSIZ];
int msgid = -1;
//建立消息队列
msgid = msgget((key_t)1234, 0666 | IPC_CREAT);
if(msgid == -1)
{
fprintf(stderr, "msgget failed with error: %d\n", errno);
exit(EXIT_FAILURE);
}
//向消息队列中写消息,直到写入end
while(running)
{
//输入数据
printf("Enter some text: ");
fgets(buffer, BUFSIZ, stdin);
data.msg_type = 1; //注意2
strcpy(data.text, buffer);
//向队列发送数据
if(msgsnd(msgid, (void*)&data, MAX_TEXT, 0) == -1)
{
fprintf(stderr, "msgsnd failed\n");
exit(EXIT_FAILURE);
}
//输入end结束输入
if(strncmp(buffer, "end", 3) == 0)
running = 0;
sleep(1);
}
exit(EXIT_SUCCESS);
}
msgreceive
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/msg.h>
struct msg_st
{
long int msg_type;
char text[BUFSIZ];
};
int main()
{
int running = 1;
int msgid = -1;
struct msg_st data;
long int msgtype = 0; //注意1
//建立消息队列
msgid = msgget((key_t)1234, 0666 | IPC_CREAT);
if(msgid == -1)
{
fprintf(stderr, "msgget failed with error: %d\n", errno);
exit(EXIT_FAILURE);
}
//从队列中获取消息,直到遇到end消息为止
while(running)
{
if(msgrcv(msgid, (void*)&data, BUFSIZ, msgtype, 0) == -1)
{
fprintf(stderr, "msgrcv failed with errno: %d\n", errno);
exit(EXIT_FAILURE);
}
printf("You wrote: %s\n",data.text);
//遇到end结束
if(strncmp(data.text, "end", 3) == 0)
running = 0;
}
//删除消息队列
if(msgctl(msgid, IPC_RMID, 0) == -1)
{
fprintf(stderr, "msgctl(IPC_RMID) failed\n");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
4.测试结果
四、信号
1.概念
信号用于处理异步事件,信号的通信方式理解起来还是有一定难度的。它既可以在一个进程内进行通信,发送信号给进程,又可以用于不同进程间的通信。
2.定义
alarm
unsigned int alarm(unsigned int seconds);
参数 seconds:闹钟的时间,单位为秒。
返回值:成功返回 0 或者返回剩余时间;错误返回-1。
signal
sighandler_t signal(int signum, sighandler_t handler);
参数 signum:等待的信号。
参数 handler:信号到来之后,触发的处理方式。
返回值:成功返回 0,错误返回-1。
3.示例代码
产生信号 alarm和signal
#include<unistd.h>
#include<stdio.h>
#include<signal.h>
void handler()
{
printf("hello\n");
}
int main(void)
{
int i;
signal(SIGALRM, handler); // 产生信号
alarm(5); // 等待5s触发信号
// 上面等待的过程中下面还在执行
for(i=1;i<7;i++){
printf("sleep %d....\n",i);
sleep(1);
}
return 0;
}
除了闹钟可以产生信号之外,在终端输入“Ctrl+c”也会产生信号。
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <stdlib.h>
#include <stdio.h>
void handler(int sig)
{
printf("Handler the signal %d\n", sig);
}
int main(void)
{
sigset_t sigset;//用于记录屏蔽字
sigset_t ign;//用于记录被阻塞的信号集
struct sigaction act;
//清空信号集
sigemptyset(&sigset); //初始化信号集
sigemptyset(&ign);
//向信号集中添加信号SIGINT
sigaddset(&sigset, SIGINT);
//设置处理函数和信号集
act.sa_handler = handler;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
sigaction(SIGINT, &act, 0);
printf("Wait the signal SIGINT...\n");
pause();//挂起进程,等待信号
//设置进程屏蔽字,在本例中为屏蔽SIGINT
sigprocmask(SIG_SETMASK, &sigset, 0);
printf("Please press Ctrl+c in 10 seconds...\n");
sleep(10);
//测试SIGINT是否被屏蔽
sigpending(&ign);
if(sigismember(&ign, SIGINT))
printf("The SIGINT signal has ignored\n");
//在信号集中删除信号SIGINT
sigdelset(&sigset, SIGINT);
printf("Wait the signal SIGINT...\n");
//将进程的屏蔽字重新设置,即取消对SIGINT的屏蔽
//并挂起进程
sigsuspend(&sigset);
printf("The app will exit in 5 seconds!\n");
sleep(5);
exit(0);
}
4.测试结果
alarm和signal
除了闹钟可以产生信号之外,在终端输入“Ctrl+c”也会产生信号。
五、信号量
1.概念
前面介绍的进程通信方式中,有一个问题,就是可能有其它多个进程访问同一个资源,为了提供一种排他性的通信,使用信号量可以解决这个问题。
信号与信号量是不同的两种事物。
信号量是用来调协进程对共享资源的访问的。为了防止出现因多个程序同时访问一个共享资源而引发的一系列问题,通过生成并使用令牌来授权,在任一时刻只能有一个执行线程访问代码的临界区域。临界区域是指执行数据更新的代码需要独占式地执行,而信号量就可以提供这样的一种访问机制,让一个临界区域统一时间只有一个线程在访问它。
2.定义
semun结构体
Union semun
-- union semun{int val;struct_semid_ds *buf;unsigned short *arry;}
-- 以一种数据类型存储数据,以另一种数据类型来读取数据
semget
int semget(key_t key, int nsems, int semflg);
参数 key:不想管的进程可以通过key访问一个信号量,只有semget函数才直接使用信号量键,所有其他的信号量函数使用由semget函数返回的信号量标识符
参数 nsems:指定需要的信号量数目,它的值几乎总是1
参数 semflg:sem_flags是一组标志,权限+创建(如果不存在,就创建,存在则打开)
返回值:成功返回标识符,用于其它信号函数,错误返回-1。
sembuf
struct sembuf{short sem_num;short sem_op;short sem_flg;}
-- sem_num: 除非使用一组信号量,否则它为0
-- sem_op:操作时需要改变的数据,-1即P等待,+1即V发送信号
-- sem_flg:通常为SEM_UNDO,使操作系统跟踪信号
semop
Int semop(int semid,struct sembuf *sops,size_t nsops)
-- semop(sem_id,&sem_b,1) PV操作通过调用semop函数实现
-- semid:信号集的识别码
-- sops:指向存储信号操作结构的数组指针
--nsops: 信号操作结构的数量,恒大于或等于1
semctl
Int semctl(int semid,int semnum,int cmd,/*union semun arg*/)
- semctl(sem_id,0, SETVAL,sem_union) 系统调用semctl用来执行在信号量集上的控制操作。这和在消息队列中的系统调用msgctl是十分相似的。但这两个系统调用的参数略有不同。
-- semid: 信号量集IPC标识符
--semnum:操作系统在信号集中的编号
--cmd: 指出要操作的具体命令,SETVAL设置信号量集中的一个单独的信号量的值,IPC_RMID将信号量集从内存中删除
-- sem_union: 指向内核中使用的数据结构的指针
rand
rand() 函数是产生随机数的一个随机函数
fflush
-- fflush() 清除读写缓冲器,需要立即把输出缓冲区的数据进行物理写入时
-- 缓冲区有stdin 和stdout
Seml中主要有下面几种操作,建立信号量,初始化信号量,进入p(sv)等待状态,发送信号V(sv),删除信号量
Seml是两个代码同样的程序运行。第一次运行的时候,因为不存在该信号量所以要初始化信号量,这样我们输入的信息,将会在第一次运行的程序中打印出来;第二次运行的时候,因为已经存在该信号量,所以打印出来的是默认的‘x’字符
3.示例代码
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/sem.h>
union semun // 信号量结构体
{
int val;
struct semid_ds *buf;
unsigned short *arry;
};
static int sem_id = 0;
static int set_semvalue();
static void del_semvalue();
static int semaphore_p();
static int semaphore_v();
int main(int argc, char *argv[])
{
char message = 'X';
int i = 0;
//创建信号量
sem_id = semget((key_t)1234, 1, 0666 | IPC_CREAT);
if(argc > 1)
{
//程序第一次被调用,初始化信号量
if(!set_semvalue())
{
fprintf(stderr, "Failed to initialize semaphore\n");
exit(EXIT_FAILURE);
}
//设置要输出到屏幕中的信息,即其参数的第一个字符
message = argv[1][0];
sleep(2);
}
for(i = 0; i < 10; ++i)
{
//进入临界区
if(!semaphore_p())
exit(EXIT_FAILURE);
//向屏幕中输出数据
printf("%c", message);
//清理缓冲区,然后休眠随机时间
fflush(stdout);
sleep(rand() % 3);
//离开临界区前再一次向屏幕输出数据
printf("%c", message);
fflush(stdout);
//离开临界区,休眠随机时间后继续循环
if(!semaphore_v())
exit(EXIT_FAILURE);
sleep(rand() % 2);
}
sleep(10);
printf("\n%d - finished\n", getpid());
if(argc > 1)
{
//如果程序是第一次被调用,则在退出前删除信号量
sleep(3);
del_semvalue();
}
exit(EXIT_SUCCESS);
}
static int set_semvalue()
{
//用于初始化信号量,在使用信号量前必须这样做
union semun sem_union;
sem_union.val = 1;
if(semctl(sem_id, 0, SETVAL, sem_union) == -1)
return 0;
return 1;
}
static void del_semvalue()
{
//删除信号量
union semun sem_union;
if(semctl(sem_id, 0, IPC_RMID, sem_union) == -1)
fprintf(stderr, "Failed to delete semaphore\n");
}
static int semaphore_p()
{
//对信号量做减1操作,即等待P(sv)
struct sembuf sem_b;
sem_b.sem_num = 0;
sem_b.sem_op = -1;//P()
sem_b.sem_flg = SEM_UNDO;
if(semop(sem_id, &sem_b, 1) == -1)
{
fprintf(stderr, "semaphore_p failed\n");
return 0;
}
return 1;
}
static int semaphore_v()
{
//这是一个释放操作,它使信号量变为可用,即发送信号V(sv)
struct sembuf sem_b;
sem_b.sem_num = 0;
sem_b.sem_op = 1;//V()
sem_b.sem_flg = SEM_UNDO;
if(semop(sem_id, &sem_b, 1) == -1)
{
fprintf(stderr, "semaphore_v failed\n");
return 0;
}
return 1;
}
4.测试结果
六、共享内存
1.概念
共享内存是进程间通信中最简单的方式之一。共享内存在各种进程间通信方式中具有最高的效率。因为系统内核没有对访问共享内存进行同步,您必须提供自己的同步措施。解决这些问题的常用方法是通过使用信号量进行同步。
2.定义
shmget
函数 int shmget(key_t key, size_t size, int shmflg);
参数 key:建立新的共享内存对象
参数 size:新建立的内存大小
参数 shmflg:标识符
返回值:成功 shmget 返回一个共享内存标识符或创建一个共享内存对象,错误返回-1。
shmat
void *shmat(int shmid, const void *shmaddr, int shmflg)
参数 shmid:共享内存标识符
参数 shmaddr:指定共享内存出现在进程内存地址的什么位置,直接指定为 NULL 让内核
自己决定一个合适的地址位置
参数 shmflg :SHM_RDONLY,为只读模式,其他为读写模式
返回值:成功返回共享的内存地址,否则返回-1。
shmdt
int shmdt(const void *shmaddr)
参数 shmaddr:连接的共享内存的起始地址。
返回值:成功返回 0,错误返回-1。
shmctl
int shmctl(int shmid, int cmd, struct shmid_ds *buf)
参数 shmid:共享内存标识符
参数 cmd IPC_RMID:删除这片共享内存
参数 buf:共享内存管理结构体
返回值:成功返回 0,错误返回-1。
3.示例代码
shmdata.h
先编写简单的 shmdata.h,内存读和写都需要用到的机构。
#ifndef _SHMDATA_H_HEADER
#define _SHMDATA_H_HEADER
#define TEXT_SZ 2048
struct shared_use_st
{
//作为一个标志,非0:表示可读,0表示可写
int written;
//记录写入和读取的文本
char text[TEXT_SZ];
};
#endif
shmread
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/shm.h>
#include "shmdata.h"
int main(void)
{
int running = 1;//程序是否继续运行的标志
void *shm = NULL;//分配的共享内存的原始首地址
struct shared_use_st *shared;//指向shm
int shmid;//共享内存标识符
//创建共享内存
shmid = shmget((key_t)1234, sizeof(struct shared_use_st), 0666|IPC_CREAT);
if(shmid == -1)
{
fprintf(stderr, "shmget failed\n");
exit(EXIT_FAILURE);
}
//将共享内存连接到当前进程的地址空间
shm = shmat(shmid, 0, 0);
if(shm == (void*)-1)
{
fprintf(stderr, "shmat failed\n");
exit(EXIT_FAILURE);
}
printf("\nMemory attached at %p\n", shm);
//设置共享内存
shared = (struct shared_use_st*)shm;
shared->written = 0;
while(running)//读取共享内存中的数据
{
//没有进程向共享内存定数据有数据可读取
if(shared->written != 0)
{
printf("You wrote: %s", shared->text);
sleep(rand() % 3);
//读取完数据,设置written使共享内存段可写
shared->written = 0;
//输入了end,退出循环(程序)
if(strncmp(shared->text, "end", 3) == 0)
running = 0;
}
else//有其他进程在写数据,不能读取数据
sleep(1);
}
//把共享内存从当前进程中分离
if(shmdt(shm) == -1)
{
fprintf(stderr, "shmdt failed\n");
exit(EXIT_FAILURE);
}
//删除共享内存
if(shmctl(shmid, IPC_RMID, 0) == -1)
{
fprintf(stderr, "shmctl(IPC_RMID) failed\n");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
shmwrite
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/shm.h>
#include "shmdata.h"
int main(void)
{
int running = 1;
void *shm = NULL;
struct shared_use_st *shared = NULL;
char buffer[BUFSIZ + 1];//用于保存输入的文本
int shmid;
//创建共享内存
shmid = shmget((key_t)1234, sizeof(struct shared_use_st), 0666|IPC_CREAT);
if(shmid == -1)
{
fprintf(stderr, "shmget failed\n");
exit(EXIT_FAILURE);
}
//将共享内存连接到当前进程的地址空间
shm = shmat(shmid, (void*)0, 0);
if(shm == (void*)-1)
{
fprintf(stderr, "shmat failed\n");
exit(EXIT_FAILURE);
}
printf("Memory attached at %p\n", shm);
//设置共享内存
shared = (struct shared_use_st*)shm;
while(running)//向共享内存中写数据
{
//数据还没有被读取,则等待数据被读取,不能向共享内存中写入文本
while(shared->written == 1)
{
sleep(1);
printf("Waiting...\n");
}
//向共享内存中写入数据
printf("Enter some text: ");
fgets(buffer, BUFSIZ, stdin);
strncpy(shared->text, buffer, TEXT_SZ);
//写完数据,设置written使共享内存段可读
shared->written = 1;
//输入了end,退出循环(程序)
if(strncmp(buffer, "end", 3) == 0)
running = 0;
}
//把共享内存从当前进程中分离
if(shmdt(shm) == -1)
{
fprintf(stderr, "shmdt failed\n");
exit(EXIT_FAILURE);
}
sleep(2);
exit(EXIT_SUCCESS);
}
4.测试结果
shmread
测试结果不理想
shmwrite
测试结果也不理想
以后用到再来研究。