欢迎光临
我们一直在努力

Linux系统编程(3)——进程间通信

1. 进程通信的方式:

同一主机内:

   1. 传统进程间通信方式:

  • 管道—-无名管道 (1) 、有名管道 (2)
  • 信号 (3)

   2. 系统V的进程间通信方式:

  • 共享内存 (4) —通信效率最高的通信方式
  • 消息队列 (5) —专门应用场景
  • 信号量 (6) —用于多进程间的信号量

不同主机间:

   3.网络的通信 :—— socket (7)

2. 进程间通信原理(本质):

进程拥有独立的4G内容空间(3G用户空间,1G内核空间)

共享了内核空间 — 可以借助内核空间完成通信。

一、管道

1. 无名管道

1). 创建无名管道

头文件:#include <unistd.h>

语法:int pipe(int pipefd[2]);

功能: 在内核创建一条管道。

参数:

  • @pipefd — 获得管道得读端和写端
    pipefd[0] —读端 — 文件描述符
    pipefd[1] —写端 — 文件描述符

返回值: 成功返回0,失败返回-1 && errno 被设置。

int fd[2];
int ret=pipe(fd)
if(ret<0)
{
perror("pipe fail");
return -1;
}

管道读写:
write(fd[1],buf,10);
read(fd[0],buf,10);

管道关闭:
close(fd[0]);

2). 无名管道特性

工作模式 :半双工(同一时刻只能单向传输)。

数据存储 :FIFO队列(先进先出),读走即删除。

文件属性 :特殊文件,不支持`lseek`定位。

IO操作 :使用文件IO:`/read/write/close`。

容量限制 :建议值4K,实际测试64K(65536字节)。

管道有三种工作模式:

  • 单工    —  固定读端和写端,eg:广播
  • 半双工 — 同一时刻只能单向传输,eg:对讲机
  • 全双工 — 可以同时读和写,eg:手机

3). 管道读写规则

写端存在 读端存在时 管道空 → 可写
管道满 → 写阻塞
读端关闭时 继续写 → 触发SIGPIPE(管道破裂)信号,进程异常终止
读端存在 写端存在时 管道有数据 → 读取
管道空 → 读阻塞
写端关闭时 管道有数据 → 读完数据
管道空 → read()立即返回0,不阻塞,继续执行代码

注意:无名管道只能用于亲缘关系进程间通信。

2.有名管道

有名管道,可以用于任意两个进程间(亲缘和非亲缘都可以–只要用的是同一个管道)。

特性:

  • 1.特殊文件
  • 2.不占用磁盘空间,因为它得空间在内核(内存空间)
  • 3.使用思路:1.打开 –fp / fd ;2.读/写 ;3.关闭

1). 创建有名管道

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

功能: 创建管道文件。

参数:

  • @pathname — 管道文件得名字
  • @mode — 创建得权限,0666

返回值: 成功返回0,失败返回-1 && errno被设置 。

2). 删除管道

语法:int unlink(const char *path);

功能:删除一个管道。

返回值:成功返回0,失败返回-1且errno被设置(如果两个进程都有删管道操作,那么会失败,提示管道不存在)

3). 阻塞特性

open()时程序会阻塞,直到另一端以相对方式打开:

  • A以O_RDONLY打开 → 阻塞等待B以O_WRONLY打开
  • B以O_WRONLY打开 → 阻塞等待A以O_RDONLY打开
  • 如果不想阻塞,以O_RDWR打开方式

二、信号通信——软中断

1.信号基础:

信号的本质是软中断,异步通信机制。

2. 信号函数

1). 注册信号处理函数——signal()

语法:sighandler_t signal(int signum, sighandler_t handler);

功能: 用来注册一个信号的处理函数,函数执行收到信号后的处理。

参数:

  • @signum — 要处理的信号
  • @handler — 指向信号对应的处理函数的函数指针

返回值: 成功返回上一次的处理函数的地址,失败 SIG_ERR 。

2). 信号处理函数

信号处理函数:void (*sighandler_t)(int);

参数 :@signo — 表示信号的编号。

信号处理函数可为系统宏定义的SIG_DFL,表终止处理,SIG_IGN ,表忽略信号,也可以自己定义一个函数。

可以通过SIGCHLD信号(子进程改变状态时,父进程会受到这个信号),自定义信号处理函数,实现异步回收资源处理。

3). 发信号——kill()

语法:int kill(pid_t pid,int sig);

功能:给进程发信号。

参数:

  • @pid — 对应的进程pid
  • @sig — 要发的信号

4). 等待信号——pause()

语法:int pause(void);

功能:阻塞等待,直到一个信号的输入。

eg:10)SIGUSR1—用户信号1,默认行为:让进程结束。

三、系统V——共享内存

共享内存是通信效率最高的进程通信方式。

1.原理:

通过同一个key值找到同一个共享内存(IPC对象)。

进程都是通过IPC对象唯一的名字(称为key)找到IPC对象,但内核是通过IP(对象的ID)来找。所以不同进程只要获得同一IP,就可以实现操作同一IPC对象,实现进程间通信。

2.函数:

1). 获得key——ftok()

头文件:#include <sys/types.h>    #include <sys/ipc.h>

语法:key_t ftok(const char *pathname, int proj_id);

功能:获得key值。

参数:

  • @pathname — 一个已存在的文件路径(避免".",用"/"表示当前路径)
  • @proj_id — 只会使用低八位的工程id

返回值:成功返回id,失败返回-1且errno被设置。

key_t key=ftok("/",'A');
if(key<0)
{
perror("ftok fail");
return -1;
}
printf("key = %#x\\n",key);

2).申请共享内存——shmget()

头文件:#include <sys/ipc.h>    #include <sys/shm.h>

语法:int shmget(key_t key, size_t size, int shmflg);

功能:申请一块指定大小的共享内存。

参数:

  • @key — 获得key方式:1.IPC_PRIVATE:在亲缘间进程通信使用;2.通过ftok获得的key值:可在任意进程间使用。
  • @shmflg:
    IPC_CREAT|0666:对应的共享内存段空间不存在则创建,存在则返回id号。
    IPC_CREAT|IPC_EXCL|0666:对应的共享内存空间不存在则创建,存在则报错,错误码为EEXIST
  • @size— 申请的大小,实际分配的内存空间为4K的倍数,可用大小为申请的大小。

返回值:成功返回共享内存id,失败返回-1且errno被设置。

int shmid = 0;
if((shmid=shmget(key,1024,IPC_CREAT|0666))<0)
{
perror("shmget fail");
return -1;
}
printf("id = %d\\n",shmid);

3). 共享内存映射——shmat()

语法:void *shmat(int shmid, const void *shmaddr, int shmflg);

功能:映射共享内存到进程的私有地址空间。

参数:

  • @shmid — 共享内存id
  • @shmadder — NULL,系统自动映射到进程。
  • @shmflg — 0,表示映射可以读写;SHM_RDONLY,映射后只能读。

返回值:成功返回映射所在的地址,失败返回(void*)-1且errno被设置。

注意:进程结束,映射自动撤销。
 

4). 撤销映射共享内存——shmdt()

语法:int shmdt(const void* shmaddr);

功能:撤销映射共享内存。

参数:

  • @shmaddr — 必须是从shmdt获得的地址。

5). 删除共享内存

语法:int shmctl(int shmid, int cmd, struct shmid_ds *buf);

功能:对共享内存进行控制。

参数:

  • @cmd — 设置为IPC_RMID,可实现对共享内存的删除。
  • @buf — 共享内存的信息。

shmctl(shmid,IPC_RMID,NULL);

 

赞(0)
未经允许不得转载:171主机测评 » Linux系统编程(3)——进程间通信
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址