苏嵌实训——day16
创始人
2024-05-20 17:18:23
0

文章目录

  • 一、进程间通信:
    • 1.传统通信方式:
    • 2. IPC通信方式(第五代操作系统):
      • (1)传统通信之无名管道
      • (2)传统通信方式之有名管道
      • (3)使用有名管道来实现非亲缘间进程之间的通信
      • (4)传统通信方式之信号
    • 3. 信号的相关的函数
      • 1.signal
      • 2. alarm
    • 4. IPC通信linux命令
    • 5. IPC通信之消息队列
      • 5.1消息队列的函数接口
        • 1.ftok
        • 2. msgget
        • 3. msgsnd
        • 4. msgrcv
        • 5. msgctl
    • 6. IPC通信之共享内存
      • 6.1 共享内存的接口
        • 1.shmget
        • 2. shmat
        • 3.shmdt
        • 4.shmctl

一、进程间通信:

主要是利用内核空间,来完成两个进程或者多个进程之间的资源和信息的传递。
进程间通信方式(7大类)

1.传统通信方式:

1.无名管道 ---- 使用的队列
2.有名管道 ---- 使用的队列
3.信号 ---------- 异步的方式

2. IPC通信方式(第五代操作系统):

1.消息队列 ---- 管道的集合
2.共享内存 ---- 地址映射的方式
3.信号灯集 ---- 信号灯的集合
3.网络通信
套接字:socket

(1)传统通信之无名管道

附加:
单工通信方式:任何时间点,只能由一方发送给另一方,方向不允许改变
半双工通信方式:同一个时间内,只允许有一方发送给另一方,具有双方通信的能力
全双工通信方式:任意时间点,双方任意可以给对方发送信息。
无名管道的介绍
无名管道是实现亲缘间进程通信的一种方式,属于半双工通信方式,类似于一个水管,只有两端,一个是数据流入段(写段),数据流出段(读段)。
这两个段都是固定的端口,遵循数据的先进先出,数据拿出来后就消失。管道是有有限长度的64*1024(64K)个字节,无名管道,不在文件系统上体现,
数据存在内存之上,进程结束后,数据就会丢失,管道文件不能使用lseek读写指针偏移。
无名管道的原理图:
在这里插入图片描述

函数接口
创建一个无名管道(pipe)

头文件:#include 原型:int pipe(int pipefd[2]);
功能:创建一个无名管道,会将读写端两个文件描述符分别封装到fd[0]和fd[1]
参数:fd[0] -----rfd[1] -----w
返回值:成功返回0;失败返回-1;

管道注意点

1.如果管道中没有数据,read读取时会阻塞等待数据的到来

#include 
#include 
int main(int argc, char const *argv[])
{int fd[2] = {0};if(pipe(fd) == -1){perror("pipe");return -1;}char buf[123] = {0};ssize_t ret = read(fd[0],buf,sizeof(buf));if(-1 == ret){perror("read");return -1;}printf("读到的数据为%s\n",buf);return 0;
}

2.管道符和先进先出的原则,数据读走后就会消失

#include 
#include int main(int argc, char const *argv[])
{int fd[2] = {0};if(pipe(fd) == -1){perror("pipe");return -1;}write(fd[1],"hello world",11);char buf[123] = {0};ssize_t ret = read(fd[0],buf,5);if(-1 == ret){perror("read");return -1;}printf("读到的数据为%s\n",buf);read(fd[0],buf,6);printf("读到的数据为%s\n",buf);return 0;
}

3.管道的大小是64K,管道写满以后再次进行写入会阻塞等待写入,防止有效数据丢失。

#include 
#include int main(int argc, char const *argv[])
{int fd[2] = {0};if(pipe(fd) == -1){perror("pipe");return -1;}int i = 0;char ch = 'a';for(i = 0; i < 64*1024;i++){write(fd[1],&ch,1);}printf("管道已经写满\n");write(fd[1],&ch,1);   //确定管道的大小,以及确定了管道写满之后再次写入会发生什么return 0;
}

4.如果关闭了写入端口,读发生什么情况
1.管道中有数据时,将里面的数据读出来
2.管道中无数据时,管道机制会认为写端关闭,不会再有数据到来,read在做读取时阻塞
没有任何用处,read将不会阻塞等待了,便不会影响进程运行。

#include 
#include int main(int argc, char const *argv[])
{int fd[2] = {0};if(pipe(fd) == -1){perror("pipe");return -1;}char buf[123] = {0};char buf1[123] = {0};write(fd[1],"hello world",11);close(fd[1]);   //关闭写端read(fd[0],buf,sizeof(buf));  //管道有数据,读取管道中的数据返回printf("buf = %s\n",buf);read(fd[0],buf1,sizeof(buf)); //管道无数据,不阻塞,直接返回printf("buf = %s\n",buf1);return 0;
}
  1. 如果读端关闭,在进行写入会发生“管道破裂”,
    是因为:如果读端关闭,写入将没有任何意义了,并且每次调用write函数写入数据都被称为有效数据。如果写入会造成有效数据的丢失,所以在写入时会出现管道破裂的问题,结束进程
#include 
#include 
int main(int argc, char const *argv[])
{int fd[2] = {0};if(pipe(fd) == -1){perror("pipe");return -1;}close(fd[0]);char ch = 'a';write(fd[1],&ch,1);  //关闭读通道,写入会发生管道破裂,结束进程printf("写入成功\n");return 0;
}

使用无名管道实现亲缘间进程通信
因为fork函数创建完子进程后,文件描述符也会被复制过去,相当于父子进程利用相同的文件描述符去操作一个文件指针,进而操作一个文件

#include 
#include 
#include 
#include 
#include 
#include 
int main(int argc, char const *argv[])
{//创建无名管道int fd[2];int fd1[2];if(-1 == pipe(fd))    //管道1用于父进程给子进程发消息{perror("pipe1");return -1;}if(-1 == pipe(fd1))  //管道2用于子进程给父进程发送消息{perror("pipe2");return -1;}pid_t pid = fork();if(-1 == pid){perror("fork");return -1;}if(0 == pid){//关闭无名管道文件描述符close(fd[1]);close(fd1[0]);while(1){char buf[123] = {0};read(fd[0],buf,sizeof(buf));if(strcmp(buf,"quit") == 0){printf("通话结束\n");exit(0);}printf("父进程说:%s\n",buf);//开始回复消息printf("请子进程输入:\n");char buf1[123] = {0};  //接收要发送的数据fgets(buf1,123,stdin);  //必须要去掉\nbuf1[strlen(buf1) -1] = '\0';write(fd1[1],buf1,strlen(buf1));}}else if(pid > 0){//父进程close(fd[0]);close(fd1[1]); while(1){printf("请父进程输入:\n");char buf[123] ={0};fgets(buf,123,stdin);  //必须要去掉\nbuf[strlen(buf) -1] = '\0';write(fd[1],buf,strlen(buf));if(strcmp(buf,"quit") == 0){printf("通话结束\n");wait(NULL);exit(0);}char buf1[123] = {0};read(fd1[0],buf1,sizeof(buf1));printf("收到子进程发过来的数据%s\n",buf1);}}return 0;
}

(2)传统通信方式之有名管道

有名管道是建立在无名管道的基础上,为了完善无名管道只能用于亲缘间进程的缺点来延申出的一种进程间通信的方式,继承无名管道的所有点,有名管道在文件系统中属于一种特殊的管道文件,

虽然在文件系统上有所体现,但是它数据并不存放在磁盘上,而是存储在内存之上,进程结束,数据就丢失了。

有名管道作为一个文件系统上的文件,如果实现非亲缘间进程通信的话,需要open打开这个文件,那么两个进程分别需要以读,写权限打开。如果打开有名管道的释放,不足读写这两个权限。
open会阻塞等待另一个权限的到来。

创建有名管道

第一种方式:linux命令
mkfifo + 有名管道名字
第二种方式:c语言函数接口
头文件:#include #include 原型:int mkfifo(const char *pathname, mode_t mode);
功能:创建一个有名管道
参数:pathname:目标路径及名称mode: 权限 例如:0666
返回值:成功返回 0失败返回 -1
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
int main(int argc, char const *argv[])
{//创建有名管道,不具备去检测文件存在则打开文件的功能if(-1 == mkfifo("./myfifo",0664)){if(errno == EEXIST){printf("文件已经存在,直接打开!\n");}else{perror("mkfifo");return -1;           }}//打开有名管道int fd = open("./myfifo",O_WRONLY);if(-1 == fd){perror("open");return -1;}printf("打开文件成功!\n");return 0;
}

(3)使用有名管道来实现非亲缘间进程之间的通信

//read.c

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
int main(int argc, char const *argv[])
{//创建有名管道,不具备去检测文件存在则打开文件的功能if(-1 == mkfifo("./myfifo",0664)){if(errno == EEXIST){printf("文件已经存在,直接打开!\n");}else{perror("mkfifo");return -1;           }}if(-1 == mkfifo("./myfifo1",0664)){if(errno == EEXIST){printf("文件已经存在,直接打开!\n");}else{perror("mkfifo1");return -1;           }}//如果进行两个进程的双方通信,还需要两个有名管道//myfifo作为该进程的读取端 myfifo1作为写入端//打开有名管道int fd = open("./myfifo",O_RDONLY);if(-1 == fd){perror("open");return -1;}int fd1 = open("./myfifo1",O_WRONLY);if(-1 == fd1){perror("open1");return -1;}printf("打开两个管道成功!\n");while(1){char buf[123] = {0};read(fd,buf,sizeof(buf));if(strcmp(buf,"quit") == 0){printf("通话结束\n");exit(0);}printf("buf = %s\n",buf);//开始回复消息printf("请输入:\n");char buf1[123] = {0};  //接收要发送的数据fgets(buf1,123,stdin);  //必须要去掉\nbuf1[strlen(buf1) -1] = '\0';write(fd1,buf1,strlen(buf1));    if(strcmp(buf1,"quit") == 0){printf("通话结束\n");exit(0);}    }return 0;
}

//write.c

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
int main(int argc, char const *argv[])
{//创建有名管道,不具备去检测文件存在则打开文件的功能if(-1 == mkfifo("./myfifo",0664)){if(errno == EEXIST){printf("文件已经存在,直接打开!\n");}else{perror("mkfifo");return -1;           }}if(-1 == mkfifo("./myfifo1",0664)){if(errno == EEXIST){printf("文件已经存在,直接打开!\n");}else{perror("mkfifo1");return -1;           }}//如果进行两个进程的双方通信,还需要两个有名管道//myfifo作为该进程的读取端 myfifo1作为写入端//打开有名管道int fd = open("./myfifo",O_WRONLY);if(-1 == fd){perror("open");return -1;}int fd1 = open("./myfifo1",O_RDONLY);if(-1 == fd1){perror("open1");return -1;}printf("打开两个管道成功!\n");while(1){//开始发送消息printf("请输入:\n");char buf1[123] = {0};  //接收要发送的数据fgets(buf1,123,stdin);  //必须要去掉\nbuf1[strlen(buf1) -1] = '\0';write(fd,buf1,strlen(buf1));    if(strcmp(buf1,"quit") == 0){printf("通话结束\n");exit(0);}    //开始接收另一个进程发过来的消息char buf[123] = {0};read(fd1,buf,sizeof(buf));if(strcmp(buf,"quit") == 0){printf("通话结束\n");exit(0);}printf("接收发送过来的数据为 %s\n",buf); }return 0;
}

(4)传统通信方式之信号

信号是什么:
信号在软件层对硬件层中断的一种模拟,是一个异步信号
中断:是一种优先级高的代码事件
在这里插入图片描述

linux所提供的信号:
查看所有信号:kill -l
发送信号给进程:kill + -信号码 + 进程PID
在这里插入图片描述

信号的原理:
在进程创建初期,会为进程创建一个信号函数表:
在这里插入图片描述

信号的处理方式:

1.忽略:指的是信号到来了,不采取热呢措施,如:SIGCHLDSIGCHLD:子进程结束后给父进程发送一个信号SIGKILL和SIGSTOP不能被忽略
2.捕捉:指的是信号到来之前,将信号函数表中信号所对应的默认函数指针修改成指向自己定义的函数----为我所用SIGKILL和SIGSTOP不能被捕捉
3.默认:指定的是信号到来之后,去执行进程创建初期信号函数表中的默认操作

3. 信号的相关的函数

1.signal

头文件:#include

原型:typedef void (*sighandler_t)(int);sighandler_t signal(int signum, sighandler_t handler);
功能:    注册一个信号函数,一般在进程刚开始的时候
参数:signum:信号号handler:信号的处理方式1.忽略:SIG_IGN2.默认:SIG_DFL3.捕捉:指向自定义函数的指针,函数指针,指向一个返回值:void,参数:int类型的函数指针signal函数会将该signum信号号和函数指针相绑定
返回值:
成功:返回一个函数指针指向上一次所执行的函数,保留下一个失败:返回 SIG_ERR
#include 
#include 
#include 
void myfun(int signum)
{printf("哈哈,关不掉我吧!\n");
}
int main(int argc, char const *argv[])
{//注册信号函数//进行信号捕捉,将SIGINT信号的处理方式改成自己的处理方式//去执行我自己的功能if(signal(SIGINT,myfun) == SIG_ERR){perror("signal");return -1;}while(1){printf("主线程在干自己的事情\n");sleep(1);}return 0;
}
练习:利用signal函数去完成最佳回收僵尸进程的方式
#include 
#include 
#include 
#include 
#include 
#include void myfun(int signum)
{pid_t pid = wait(NULL);printf("回收成功,进程ID为 %d\n",pid);
}
int main(int argc, char const *argv[])
{//注册信号if(signal(SIGCHLD,myfun) == SIG_ERR){perror("signal");return -1;}//创建一个进程pid_t pid = fork();if(-1 == pid){perror("fork");return -1;}else if(0 == pid){//子进程sleep(4);exit(0);}else if(pid > 0){while(1){printf("父进程在做自己的事情\n");sleep(1);}}return 0;
}

2. alarm

头文件:#include 原型:unsigned int alarm(unsigned int seconds);
功能:给自己发送一个闹钟信号 ----》SIGALRM 默认终止进程
参数:定时seconds秒后发送信号unsigned int ret = alarm(5); //5秒之后发送一个信号,代码正常向下执行
返回值:
成功返回上一次alarm剩余的秒数
0代表定时器时间到
注意:如果调用alarm后再次调用alarm函数会刷新定时器的事件,打断了上一次alarm的定时,上一次的alarm不会再发送闹钟信号,会将上一次alarm剩余的描述返回回来。
#include 
#include 
#include 
#include 
#include 
#include 
void myfun(int signum)
{printf("自动出牌了\n");alarm(5);
}
int main(int argc, char const *argv[])
{if(SIG_ERR == signal(SIGALRM,myfun)){perror("signal");return -1;}//开始出牌alarm(5);while(1){char ch;printf("请出牌\n");scanf("%c",&ch);  //用它来模拟出牌getchar();alarm(5);}return 0;
}

4. IPC通信linux命令

ipc通信共有三个:消息队列,共享内存,信号灯集
linux查看所有IPC对象的命令: ipcs
linux删除ipc对象的命令:
ipcrm -s ID号 :信号灯集
ipcrm -q ID号: 消息队列
ipcrm -m ID号:共享内存

5. IPC通信之消息队列

消息队列:是实现进程间通信的一种方式,是利用内核空间完成的,并且是一种全双工的通信方式,实质就是管道在内核空间的集合。

5.1消息队列的函数接口

1.ftok

头文件:#include #include 原型:key_t ftok(const char *pathname, int proj_id);
功能:根据pathname和proj_id这两个参数生成一个key_t类型的数据,按照内部自己的算法,pathname和proj_id这两个参数给的一样,生成的key_t类型的数据就是一样的。
参数:pathname:文件路径,路径必须存在proj_id:只用这个int类型的数据的低八位,所以说我们一把在使用时都给其字符型
返回值:成功返回生成好的键值失败: -1

2. msgget

头文件:#include #include #include 
原型:int msgget(key_t key, int msgflg);
功能:打开或者创建一个消息队列
参数:
KEY:通过秘钥键值创建一个消息队列返回一个msgid消息队列的ID号,同一个key值生成的ID号一样IPC_PRIVATE:直接用于亲缘间关系自定义KEY值:ftok:生成一个key_t类型的变量---键值msgflg:打开消息队列的方式IPC_CREAT:如果消息队列不存在,则创建消息队列如果消息队列存在,则打开消息队列例如:IPC_CREAT | 0664IPC_EXCL:如果文件存在则报错返回IPC_CREAT|IPC_EXCL | 0664    //如果文件不存在则创建文件
返回值:成功:返回一个消息对垒ID号(非负数)失败 :-1
#include 
#include 
#include 
#include 
#include int main(int argc, char const *argv[])
{//创建一个key_t的键值key_t mykey = ftok("/home/jsetc",'a');if(-1 == mykey){printf("生成键值失败\n");return -1;}//打开或者创建消息队列int msgid = 0;msgid = msgget(mykey,IPC_CREAT | 0664);if(-1 == msgid){perror("msgget");return -1;}printf("创建消息队列成功!\n");return 0;
}

3. msgsnd

头文件:#include #include #include 原型:int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
功能:向消息队列放入数据
参数: msqid:目标消息队列的ID号msgp:要发送消息的地址,必须是一个结构体类型struct msgbuf {long mtype;       /* message type, must be > 0 */char mtext[1];    /* message data */};msgsz:要发送消息正文的大小msgflg:发送消息的方式阻塞发送:0非阻塞发送:IPC_NOWAIT       
返回值:成功:返回 0失败 :-1
#include 
#include 
#include 
#include 
#include 
#include 
struct msgbuf {long mtype;       /* message type, must be > 0 */char mtext[1];    /* message data */};
struct msgbuf mybuf;
int main(int argc, char const *argv[])
{//创建一个key_t的键值key_t mykey = ftok("/home/jsetc",'a');if(-1 == mykey){printf("生成键值失败\n");return -1;}//打开或者创建消息队列int msgid = 0;msgid = msgget(mykey,IPC_CREAT | 0664);if(-1 == msgid){perror("msgget");return -1;}printf("创建消息队列成功!\n");//开始发送数据while(1){//发送数据fgets(mybuf.mtext,1024,stdin);mybuf.mtext[strlen(mybuf.mtext) - 1] = '\0';if(-1 == msgsnd(msgid,&mybuf,strlen(mybuf.mtext),0)){perror("msgsnd");return -1;}}return 0;
} 

4. msgrcv

头文件:#include #include #include 原型:int ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,
int msgflg);
功能:从消息队列中读取数据
参数: msqid:目标消息队列的ID号msgp:存放消息的地址,结构体要求和发送端一致struct msgbuf {long mtype;       /* message type, must be > 0 */char mtext[1];    /* message data */};msgsz:要发送消息正文的大小msgtyp:消息类型,可以等于0,如果写0,从消息队列开头读取msgflg:发送消息的方式阻塞发送:0非阻塞发送:IPC_NOWAIT       
返回值:成功:成功接收到正文的大小失败 :-1
#include 
#include 
#include 
#include 
#include 
#include 
struct msgbuf {long mtype;       /* message type, must be > 0 */char mtext[1024];    /* message data */};
struct msgbuf mybuf;
int main(int argc, char const *argv[])
{//创建一个key_t的键值key_t mykey = ftok("/home/jsetc",'a');if(-1 == mykey){printf("生成键值失败\n");return -1;}//打开或者创建消息队列int msgid = 0;msgid = msgget(mykey,IPC_CREAT | 0664);if(-1 == msgid){perror("msgget");return -1;}printf("创建消息队列成功!\n");//开始接收数据ssize_t ret = msgrcv(msgid,&mybuf,sizeof(mybuf.mtext),1,0);if(-1 == ret){perror("msgrcv");return -1; }printf("mybuf.text = %s\n",mybuf.mtext);return 0;
} 

5. msgctl

头文件:#include #include #include 
原型:int msgctl(int msqid,int cmd,struct msqid_ds *buf)
功能:控制消息队列
参数:msqid:目标消息队列cmd:如何控制,控制的方式IPC_STAT:获取消息队列的信息,可以获取到所有msqid_ds的信息IPC_SET:设置消息队列信息,只能设置msgid_ds里面的msg_perm结构体IPC_RMID:删除消息队列,第三个参数直接填写NULLbuf:struct msqid_ds {struct ipc_perm msg_perm;     /* Ownership and permissions */time_t          msg_stime;    /* Time of last msgsnd(2) */time_t          msg_rtime;    /* Time of last msgrcv(2) */time_t          msg_ctime;    /* Time of last change */unsigned long   __msg_cbytes; /* Current number of bytes inqueue (nonstandard) */msgqnum_t       msg_qnum;     /* Current number of messagesin queue */msglen_t        msg_qbytes;   /* Maximum number of bytesallowed in queue */pid_t           msg_lspid;    /* PID of last msgsnd(2) */pid_t           msg_lrpid;    /* PID of last msgrcv(2) */};The ipc_perm structure is defined as follows (the highlighted fields are settable using IPC_SET):struct ipc_perm {key_t          __key;       /* Key supplied to msgget(2) */uid_t          uid;         /* Effective UID of owner */gid_t          gid;         /* Effective GID of owner */uid_t          cuid;        /* Effective UID of creator */gid_t          cgid;        /* Effective GID of creator */unsigned short mode;        /* Permissions */unsigned short __seq;       /* Sequence number */};返回值:

6. IPC通信之共享内存

共享内存是利用地址映射的方式完成进程间通信,实质在内核建立一个共享的区域,然后将这片区域的地址,映射到用户空间,用户空间就有了一个映射出来的地址,
操作用户空间,就相当于直接操作了内核空间。共享内存相对来说简单一些。还有就是共享内存是IPC通信中能够效率最高的一种也是速度最快的一种。但是共享内存数据拿走不会消失。并且写入数据时会覆盖掉之间的数据。
共享内存一般用于数据的实时采集上传,本身不具备进程同步的功能。

6.1 共享内存的接口

1.shmget

头文件:#include #include 原型:int shmget(key_t key, size_t size, int shmflg);
功能:打开或者创建一个共享内存
参数:key:两种方式打开或者创建共享内存IPC_PRIVATE:用于亲缘间进程自定义key使用ftok函数size:创建共享内存的阿晓,如果已经存在了则无效注意:size一般采取4K的倍数,如果不采取4K的倍数,在真实的共享内存中,会向上近似等于4K的倍数,但是IPCS不会显示出来shmflg:打开的方式IPC_CREAT:如果共享内存存在,则打开,不存在则创建例如:IPC_CREAT | 0664IPC_EXCL:如果存在则报错返回,如果不存在配合IPC_CREAT创建
返回值:成功返回共享内存的ID号失败返回-1

2. shmat

头文件:#include #include 原型:void *shmat(int shmid, const void *shmaddr, int shmflg);
功能:地址映射,将shmid所对应的共享内存的首地址映射到用户空间shmaddr地址上
参数:shmid:目标共享内存shmaddr:映射到用户空间的地址默认入口:如果填写NULL。操作系统会自动分配地址会通过返回值提供给用户shmflg:操作映射完的地址的权限0:默认操作 读写权限SHM_RDONLY:只读权限操作内存
返回值:成功:会返回用户空间映射的地址失败:返回(void *)-1

3.shmdt

头文件:#include #include 原型:int shmdt(const void *shmaddr);
功能:取消地址映射
参数:shmaddr:映射到用户空间的首地址
返回值:成功:返回0失败:返回-1

4.shmctl

头文件:#include #include 原型:int shmctl(int shmid, int cmd, struct shmid_ds *buf);
功能:控制共享内存
参数:shmid:目标共享内存ID号cmd:如何控制,控制的方式IPC_STAT:获取共享内存属性IPC_SET:设置共享内存属性IPC_RMID:删除共享内存,如果说有进程正在使用,它会记录一下,等该进程断开连接后删除buf:The buf argument is a pointer to a shmid_ds structure, defined in  as follows:struct shmid_ds {struct ipc_perm shm_perm;    /* Ownership and permissions */size_t          shm_segsz;   /* Size of segment (bytes) */time_t          shm_atime;   /* Last attach time */time_t          shm_dtime;   /* Last detach time */time_t          shm_ctime;   /* Last change time */pid_t           shm_cpid;    /* PID of creator */pid_t           shm_lpid;    /* PID of last shmat(2)/shmdt(2) */shmatt_t        shm_nattch;  /* No. of current attaches */...};The ipc_perm structure is defined as follows (the highlighted fields are settable using IPC_SET):struct ipc_perm {key_t          __key;    /* Key supplied to shmget(2) */uid_t          uid;      /* Effective UID of owner */gid_t          gid;      /* Effective GID of owner */uid_t          cuid;     /* Effective UID of creator */gid_t          cgid;     /* Effective GID of creator */unsigned short mode;     /* Permissions + SHM_DEST andSHM_LOCKED flags */unsigned short __seq;    /* Sequence number */};返回值:IPC_STAT:成功返回ID号其它情况:返回0失败返回-1
//read.c
#include 
#include 
#include 
#include 
#include 
int main(int argc, char const *argv[])
{//生成一个共享内存使用的key值key_t mykey = ftok("/home/jsetc/jsetc/208/",'a');if(-1 == mykey){perror("生成键值失败");return -1;}//创建共享内存int shmid = shmget(mykey,4096,IPC_CREAT | 0664);if(-1 == shmid){perror("shmget");return -1;}printf("创建或者打开共享内存成功\n");//地址映射char *buf = (char *)shmat(shmid,NULL,0);if((char *)-1 == buf){perror("shmat");return -1;}//开始操作共享内存while(1){printf("buf = %s\n",buf);sleep(1);}return 0;
}
#include 
#include 
#include 
#include 
int main(int argc, char const *argv[])
{//生成一个共享内存使用的key值key_t mykey = ftok("/home/jsetc/jsetc/208/",'a');if(-1 == mykey){perror("生成键值失败");return -1;}//创建共享内存int shmid = shmget(mykey,4096,IPC_CREAT | 0664);if(-1 == shmid){perror("shmget");return -1;}printf("创建或者打开共享内存成功\n");//地址映射char *buf = (char *)shmat(shmid,NULL,0);if((char *)-1 == buf){perror("shmat");return -1;}//开始操作共享内存scanf("%s",buf);//取消地址映射,断开与共享内存的连接,避免误操作shmdt(buf);//删除共享内存shmctl(shmid,IPC_RMID,NULL);return 0;
}

相关内容

热门资讯

监控摄像头接入GB28181平... 流程简介将监控摄像头的视频在网站和APP中直播,要解决的几个问题是:1&...
Windows10添加群晖磁盘... 在使用群晖NAS时,我们需要通过本地映射的方式把NAS映射成本地的一块磁盘使用。 通过...
protocol buffer... 目录 目录 什么是protocol buffer 1.protobuf 1.1安装  1.2使用...
在Word、WPS中插入AxM... 引言 我最近需要写一些文章,在排版时发现AxMath插入的公式竟然会导致行间距异常&#...
【PdgCntEditor】解... 一、问题背景 大部分的图书对应的PDF,目录中的页码并非PDF中直接索引的页码...
Fluent中创建监测点 1 概述某些仿真问题,需要创建监测点,用于获取空间定点的数据࿰...
educoder数据结构与算法...                                                   ...
MySQL下载和安装(Wind... 前言:刚换了一台电脑,里面所有东西都需要重新配置,习惯了所...
修复 爱普生 EPSON L4... L4151 L4153 L4156 L4158 L4163 L4165 L4166 L4168 L4...
MFC文件操作  MFC提供了一个文件操作的基类CFile,这个类提供了一个没有缓存的二进制格式的磁盘...