目录
一、匿名管道
管道也是一个文件,用于父子间进程通信。父子间一个打开文件写端,一个打开文件读端,即可通过对管道的读写完成父子间的通信。由于父子间通信的管道没有名字因此称为匿名管道。
1.1创建管道
使用pipefd数组表示管道读写端,并作为参数传给系统调用pipe(),即可完成管道创建,创建失败pipe()返回0。
1.2 父子进程通信
fork创建进程后,将父进程读端关闭(close(pipefd[0])),子进程写段关闭。这时通过系统调用write和read对管道读写,读写完毕后父子进程关闭读/写端,完成通信。
1 #include <unistd.h>
2 #include <stdlib.h>
3 #include <stdio.h>
4 #include <errno.h>
5 #include <string.h>
6 int main()
7 {
8 int pipefd[2] = {0};
9 if(pipe(pipefd)!=0)
10 {
11 perror("pipe error\n");
12 }
13 pid_t pid = fork();
14 if (pid == -1)
15 {
16 perror("fork error\n");
17 return 1;
18 }
19 if (pid != 0)
20 {
21 close(pipefd[0]);
22 const char buf[] = "i am father";
23 write(pipefd[1],buf, strlen(buf));
24 close(pipefd[1]);
25 }
26 close(pipefd[1]);
27 char buf[20] = {0};
28 read(pipefd[0], buf, 20);
29 printf("%s\n", buf);
30 close(pipefd[0]);
31 exit(0);
32 return 0;
33 }
1.3 特点
①管道是一个单向通信的信道
②管道面向字节流
③匿名管道仅限于父子间通信
④管道自带同步机制,原子性写入
⑤管道的生命周期与进程相同
1.4 情况
①读端和写段中一端执行地慢,另一端都要进行等待
②读端关闭,写端收到SIGPIPE信号直接终止
③写端关闭,读端读完管道内剩余数据,读到最后返回值为0,表示读取结束
二、命名管道
创建一个命名管道,将两个不相关进程与该管道连接,通过对命名管道读写完成通信。命名管道文件读写不会像一般文件那样去读写磁盘,而是在一个缓冲区进行读写。
1.命名管道创建
//命令行创建
$ mkfifo filename
//调用函数创建
int mkfifo(const char *filename,mode_t mode);
//第一个参数是管道名,第二个是设置权限,成功返回0,失败返回-1
2.通信
管道创建以后,对管道的读写就直接调用open,write,read等系统调用,对管道像文件一样操作读写即可。一个进程写,一个进程读,即可完成不相关进程的通信。只需要一个进程创建管道,另一个进程访问要通信的进程创建的管道。
1: server.c
1 #include <stdio.h>
2 #include <sys/types.h>
3 #include <sys/stat.h>
4 #include <fcntl.h>
5 #include <unistd.h>
6 #include <stdlib.h>
7
8 int main()
9 {
10 umask(0);
11 if(mkfifo("./myfifo",0666)<0)
12 {
13 perror("mkfifo");
14 return 1;
15 }
16 int fd = open("myfifo",O_RDONLY);
17 if(fd<0)
18 {
19 perror("open");
20 return 1;
21 }
22 char buf[64];
23 ssize_t s = read(fd,buf,sizeof(buf)-1);
24 if(s>0)
25 {
26 buf[s] = 0;
27 printf("client say:%s",buf);
28 }
29 else if(s == 0)
30 {
31 printf("client quit\n");
32 }
33 else
34 {
35 perror("read");
36 return 1;
37 }
38 close(fd);
39 return 0;
40 }
2: client.c
1 #include <stdio.h>
2 #include <sys/types.h>
3 #include <sys/stat.h>
4 #include <fcntl.h>
5 #include <unistd.h>
6 #include <stdlib.h>
7 #include <string.h>
8 int main()
9 {
10 int fd = open("myfifo",O_WRONLY);
11 if(fd<0)
12 {
13 perror("open");
14 return 1;
15 }
16 char buf[64];
17 printf("please enter::");
18 fflush(stdout);
19 ssize_t s = read(0,buf,sizeof(buf)-1);
20 if(s<0)
21 {
22 perror("read");
23 return 1;
24 }
25 buf[s] = 0;
26 write(fd,buf,strlen(buf));
27 close(fd);
28 return 0;
29 }
三、共享内存
共享内存区是最快的进程通信形式。内存映射到共享它的进程的地址空间,进程间数据传递不涉及到内核,即进程不再通过执行进入内核的系统调用来传递彼此的数据。
共享内存属于内核,其生命周期与进程无关。
ipcs -m 查看共享内存
ipcrm -m shmid 删除共享内存
3.1共享内存函数
3.1.1shmget函数
功能:用来创建共享内存
原型 int shmget(key_t key, size_t size, int shmflg);
参数
key:这个共享内存段标识符,使用下面的函数得到唯一标识符
size:共享内存大小,建议是4kb的整数倍
shmflg:由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的。ICPC_CREAT表示共享内存存在则返回已存在的共享内存的标识码,否则创建一个新的共享内存。IPC_CREAT | IPC_EXECL,表示必须要一个全新的共享内存空间。
返回值:成功返回一个非负整数,即该共享内存段的标识码;失败返回-1
功能:将共享内存段连接到进程地址空间
终端输入ipcs -m 可查看共享内存
ipcrm -m (shmid) 删除共享内存
key是系统层用来唯一标识共享内存,在用户层使用shmid来管理共享内存。
3.1.2shmat函数
功能::将共享内存段连接到进程地址空间 原型 void *shmat(int shmid, const void *shmaddr, int shmflg);
参数
shmid: 共享内存标识
shmaddr:指定连接的地址
shmflg:它的两个可能取值是SHM_RND和SHM_RDONLY
返回值:成功返回一个指针,指向共享内存地址;失败返回-1
shmaddr为NULL,核心自动选择一个地址
shmaddr不为NULL且shmflg无SHM_RND标记,则以shmaddr为连接地址。
shmaddr不为NULL且shmflg设置了SHM_RND标记,则连接的地址会自动向下调整为SHMLBA的整数倍。公式:shmaddr - (shmaddr % SHMLBA)
shmflg=SHM_RDONLY,表示连接操作用来只读共享内存
3.1.3shmdt函数
功能:将共享内存段与当前进程脱离
原型 int shmdt(const void *shmaddr);
参数
shmaddr: 由shmat所返回的指针
返回值:成功返回0;失败返回-1
注意:将共享内存段与当前进程脱离不等于删除共享内存段
3.1.4shmctl函数
功能:用于控制共享内存
原型 int shmctl(int shmid, int cmd, struct shmid_ds *buf);
参数
shmid:由shmget返回的共享内存标识码
cmd:将要采取的动作(有三个可取值)IPC_STAT(设置关联值)、IPC_SET(有权限设置关联值)、IPC_RMID(删除共享内存)
buf:指向一个保存着共享内存的模式状态和访问权限的数据结构
返回值:成功返回0;失败返回-1
3.2通信
共享内存通信用起来更加简单,在有了基本创建、链接、断开链接、删除共享内存的步骤框架后。在链接完毕后,断开链接前直接对共享内存进行读写。以下是一个共享内存通信示例。
1: comm.h
1 #pragma once
2 #include <stdio.h>
3 #include <sys/ipc.h>
4 #include <sys/shm.h>
5 #define PATH_NAME "./"
6 #define PROJ_ID 0x6666
7 #define SIZE 4097
2: server.c
1 #include "comm.h"
2 #include <unistd.h>
3 int main()
4 {
5 key_t key = ftok(PATH_NAME,PROJ_ID);
6 if(key<0)
7 {
8 perror("ftok");
9 return 1;
10 }
11 int shmid = shmget(key,SIZE,IPC_CREAT|IPC_EXCL|0666);
12 if(shmid<0)
13 {
14 perror("shmget");
15 return 1;
16 }
17 char* mem = (char*)shmat(shmid,NULL,0);
18 if(mem == (void*)-1)
19 {
20 perror("shmat");
21 return 1;
22 }
23 while(1)
24 {
25 sleep(1);
26 printf("client say: %s\n",mem);
27 }
28
29 sleep(1);
30 shmdt(mem);
31
32 shmctl(shmid,IPC_RMID,NULL);
33 return 0;
34 }
3: client.c
1 #include "comm.h"
2 #include <string.h>
3
4 int main()
5 {
6 key_t key = ftok(PATH_NAME,PROJ_ID);
7 if(key<0)
8 {
9 perror("ftok");
10 return 1;
11 }
12 int shmid = shmget(key,SIZE,IPC_CREAT);
13 if(shmid < 0)
14 {
15 perror("shmget");
16 return 1;
17 }
18
19 char* mem = (char*)shmat(shmid,NULL,0);
20
21 const char* buf = "i am a process A";
22 strcpy(mem,buf);
23
24 shmdt(mem);
25 return 0;
26 }
3.3共享内存数据结构
共享内存由操作系统组织管理。使用信号量来保证同步、互斥。
struct shmid_ds {
struct ipc_perm shm_perm; /* operation perms */
int shm_segsz; /* size of segment (bytes) */
__kernel_time_t shm_atime; /* last attach time */
__kernel_time_t shm_dtime; /* last detach time */
__kernel_time_t shm_ctime; /* last change time */
__kernel_ipc_pid_t shm_cpid; /* pid of creator */
__kernel_ipc_pid_t shm_lpid; /* pid of last operator */
unsigned short shm_nattch; /* no. of current attaches */
unsigned short shm_unused; /* compatibility */
void *shm_unused2; /* ditto - used by DIPC */
void *shm_unused3; /* unused */
};