UNIX网络编程卷2:进程间通信 阅读记录

简介

第一章

1、IPC对象的持续性(声明周期)

随进程持续的IPC:一直存在直到最后一个使用IPC对象的进程关闭为止,如管道和FIFO;
随内核持续的IPC:一直存在到内核重新自举或者显示删除,如System V 消息队列、信号量…;
随文件系统持续的IPC:一直存在到显示删除IPC对象;
使用IPC时要注意IPC对象的持续性。

2、errno值

Linux中系统调用的错误都存储于 errno中,errno由操作系统维护,每个进程都有,存储就近发生的错误,即下一次的错误码会覆盖掉上一次的错误。

第二章:Posix IPC

posix ipc 包括: posix消息队列(mqueue.h)、posix信号量(semaphore.h)、posix共享内存(sys/mman.h),并以路径名作为标识,创建或打开xxopen

1、IPC权限

第三章:System V IPC

System V IPC 包括:System V消息队列(sys/msg.h)、System V信号量(sys/sem.h)、System V共享内存(sys/shm.h),创建或打开xxxget

1、key_t键 和 ftok 函数

#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id);

ftok函数把一个已经存在的路径名(参数1)和 一个整数标识(参数2 id)转换成一个key_t值,称为IPC键(至少为32位)。
使用IPC的进程将给定的pathname和id转化成一个IPC键。
IPC键的组成:pathname指向文件的stat->st_dev + stat->st_ino + id[7:0].
id的[7:0]位不能全为0,否则ftok函数行为无效
若pathname或进程无访问权限ftok返回-1.

2、System V IPC 对象结构体 ipc_perm

/* Obsolete, used only for backwards compatibility and libc5 compiles */
<sys/ipc.h>
struct ipc_perm
{
    __kernel_key_t  key;//键
    __kernel_uid_t  uid;//用户id
    __kernel_gid_t  gid;//用户组id
    __kernel_uid_t  cuid;//创建者用户id
    __kernel_gid_t  cgid;//创建者组id
    __kernel_mode_t mode;//读写权限
    unsigned short  seq;//槽位使用情况序列
};

3、IPC权限

4、标识符重用

5、ipcs 和 ipcrm

共享内存

IPC中最快的方式

mmap munmap msync

mmap将一个文件或者posix共享内存区对象映射到调用进程的地址空间。使用该函数的目的有:
1)对普通文件提供内存映射IO
2)对特殊文件提供匿名内存映射
3)使用shm_open以提供无亲缘关系的进程之间的posix共享内存

#include <sys/mman.h>

       void *mmap(void *addr, size_t length, int prot, int flags,
                  int fd, off_t offset);
       int munmap(void *addr, size_t length);

#include <sys/mman.h>

       int msync(void *addr, size_t length, int flags);


参数:
mmap
addr:映射到进程中的内存首地址,NULL,由内核指定
length:映射空间大小
prot:映射内存的保护参数,读、写、执行、不可访问
flags:共享、私有、FIXED
fd:文件描述符
offset:文件偏移位置
return:成功返回映射空间首地址,失败MAP_FAILED设置errno
mmap成功后可以关闭文件描述符
munmap
return:成功返回0,失败返回-1

msync
flags:同步方式,同步写、异步写、使高速缓存失效
return:成功返回0,失败返回-1

匿名空间映射

不使用文件进行空间映射,匿名空间映射可以在父子进程之间实现共享内存,且不需要打开文件。
mmap flags参数添加MAP_ANON,fd 为-1.

Posix共享内存

无亲源关系的进程间共享内存区
1)内存映射文件
2)共享内存区对象
这两种方法都需要使用mmap函数,不同之处在于文件描述符的不同一个由open得到,一个由shm_open得到,随内核持续特性

shm_open、 shm_unlink
#include <sys/mman.h>
       #include <sys/stat.h>        /* For mode constants */
       #include <fcntl.h>           /* For O_* constants */

       int shm_open(const char *name, int oflag, mode_t mode);

       int shm_unlink(const char *name);

参数:
shm_open
name:共享内存对象路径
oflag和mode参数参考open函数,oflag至少未可读。
return:成功返回文件描述符,失败-1
共享内存对象路径可由任意进程调用

shm_unlink:
删除一个共享内存区对象
删除名字不会影响已经打开共享内存的进程,知道共享内存使用完毕(引用为0);
不过删除后open会失败;
return:成功返回0,失败-1

fturncate

调整不同文件或者共享内存的大小

int truncate(const char *path, off_t length);
int ftruncate(int fd, off_t length);

对于普通文件:
大小大于length,则多余的丢弃,小于则是否增加文件大小未说明
对于共享内存:调整未length大小

fstat

通过文件描述符获取共享内存的信息

创建共享内存的步骤

1)shm_open打开或创建一个共享内存,返回文件描述符
2)ftruncate调整共享内存大小
3)mmap映射共享内存到进程

共享内存的使用

1)shm_open打开一个共享内存,返回文件描述符
2)fstat获取大小
3)根据大小mmap映射共享内存到进程
4)根据大小写入或读取(非系统调用)
对共享内存的读写也需要使用某种方式同步
创建时先使用shm_unlink 防止对象已经存在

System V 共享内存

System V 共享内存信息结构
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 */
};

shmget函数
#include <sys/ipc.h>
 #include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);

参数
打开或创建一个共享内存对象
key:ftok的返回值或者IPC_PRIVATE
size:创建时为一个大于0的值,操作已存在对象时应指定为0
shmflg:权限值
return:成功返回共享内存对象(标识符),失败-1

shmat
#include <sys/types.h>
#include <sys/shm.h>
 void *shmat(int shmid, const void *shmaddr, int shmflg);

参数
通过shmget打开或创建一个共享内存对象后使用shmat将对象附加到进程的地址空间
shmid:shmget的返回值(对象标识符)
shmaddr:为非空的指针时返回地址取决于shmflg是否指定了SHM_RND,未指定地址由该参数决定,指定的话返回地址由该参数向下舍入一个SHMLBA值。
若为空指针,返回地址由系统决定,系统推荐方法
shmflg:读写权限
返回值:成功返回共享内存对象映射的首地址,失败-1

shmdt
int shmdt(const void *shmaddr);

参数
断开进程对共享内存的使用,在进程结束时自动断开
shmaddr:shmat的返回值
return:成功返回0,失败-1

shmctl
#include <sys/ipc.h>
#include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_ds *buf);

参数
提供多种对共享内存的操作
shmid:shmget的返回值
cmd:命令值
IPC_RMID 删除对象并回收
IPC_SET 通过buf设置对象信息
IPC_STAT 通过buf返回对象信息
buf:共享内存对象信息结构体
return:成功返回0,失败-1

posix和system v共享内存的区别

1)system v共享内存大小在创建时固定,posix可以通过ftruecate修改

消息传递

管道(无名管道)

为啥叫无名管道?

没有系统标识符:文件名,所以在不考虑描述符传递的情况下,无名管道一般用于父子进程之间。

pipe

struct fd_pair {
           long fd[2];
       };
       struct fd_pair pipe();

       /* On all other architectures */
       int pipe(int pipefd[2]);

pipe函数创建一个管道,提供一个单向的数据流,通过文件文件描述符访问,f[0]用于读,f[1]用于写。
在这里插入图片描述

IS_FIFO

该宏定义通过st_mode 判断文件是否为管道,搭配fstat使用(获取文件属性)

无名管道的使用

无名管道通过pipe创建了两个文件文件描述符,没有文件路径标识。一个用于向管道写,一个用于向文件读。
所以在进程间使用无名管道时需要使用相同的文件描述符,要实现不同进程使用同一个无名管道要么将文件描述符在进程之间传递,要么使用父子进程,子进程继承父进程的管道的文件描述符。
之后在一个进程关闭管道的读或写端,另一个进程关闭另一个端口。如此管道在一个进程读,另一个进程写,实现了单向半双工的管道,要实现双向半双工的管道可以使用两个管道实现。

shell命令中的管道

who|sort

在shell中管道使用 **|**标识,以上的命令开启了两个进程,通过管道符,将who进程的标准输出复制到管道的写入端,将管道的读出端复制到sort的标准输入。

全双工管道

全双工管道意味着,不论从哪一个端口写入管道,写入内容都会附加到管道缓存的尾部,不论从那个端口写,都会将内容附加到缓存的头部。实现全双工的管道可以使用两个单向数据流的半双工管道实现,否则读和写在同意缓存,会出现读写混乱。

popen 和 pclose

#include <stdio.h>
FILE *popen(const char *command, const char *type);
int pclose(FILE *stream);

popen函数创建一个管道并启动另一个进程,该进程为command字符串指定的shell行命令,popen在主进程和调用命令开启的进程之间创建一个管道,主进程具体要从管道读出还是写入由type参数决定。如果是r,则从管道读取新启动进程的标准输出,如果是w,则通过管道向新进程的标准输入写入。

pclose关闭popen打开的文件描述符,并等待新进程终止,返回shell命令的终止状态。

FIFO(有名管道)

为啥叫有名管道

FIFO也是半双工的单向数据流,但是又文件路径标记,所以可以在不同进程之间使用文件路径共同使用。

mkfifo 创建有名管道

#include <sys/types.h>
 #include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);

参数:
pathname:fifo的文件路径名
mode:类似于open的mode参数。可以指定 O_CREAT | O_EXCL |0666,存在返回EEXIT错误,不存在则创建文件权限为0666.
返回值:成功创建返回0,否则返回-1
注意:不论创建失败或者成功都需要使用open或者fopen打开FIFO

当fifo存在时可以使用open或者fopen打开(一切皆文件,fifo也不例外)。
fifo只能读或者写,不能以可读可写的模式打开。
FIFO的写总是在头,读总是在尾。
FIFO不能使用lseek。
FIFO在所用调用的进程关闭后消失,但是系统中的文件名要使用unlink从文件系统系统中删除
打开FIFO读时,若没有任何进程打开该FIFO写,则读FIFO的进程将会阻塞。
管道和FIFO阻塞读写

系统对FIFO和管道的限制

  1. OPEN_MAX 一个进程在任意时刻可以打开的最大的文件描述符的个数,sysconf 函数 或者 ulimit 命令可查询
  2. PIPE_BUF 可原子的向FIFO或管道写入的数据的最大程度,即FIFO和管道的缓存大小。通常在<limits.h>中定义。
    可以在程序中调用 pathconf 或 fpathconf 获取。 或者getconf命令。

问题记录

1、posix IPC 和 System V IPC 有和区别和联系 ?

Posix Portable Operation System Interfice 可移植操作系统接口
POSIX接口和System V接口是用于多线程和进程间通信的两种主要编程接口。它们各自有不同的特点、功能和适用场景。以下是对这两种接口的详细介绍及其异同点。

在这里插入图片描述

2、指定权限受到当前进程的文件模式创建掩码修正??

umask函数或命令

3、errno的定义

4、进程的文件创建模式掩码??

5、管道和FIFO的计数器

内核为管道和FIFO维护一个计数器,用于统计使用管道和FIFO的进程的个数,计数为0则关闭管道和FIFO。在使用ulink删除文件系统中的FIFO名称时,删除文件系统中FIFO的名称之后,已经打开的文件描述符不受影响(计数器不为0),但是删除后不可再通过文件路径访问FIFO。

目录 封面 -12 封底 -11 扉页 -10 版权 -9 版权声明 -8 前言 -7 目录 -3 第一部分 简介 1 第1章 简介 2 1.1 概述 2 1.2 进程、线程与信息共享 3 1.3 IPC对象的持续性 4 1.4 名字空间 5 1.5 fork、exec和exit对IPC对象的影响 7 1.6 出错处理:包裹函数 8 1.7 Unix标准 9 1.8 书中IPC例子索引表 11 1.9 小结 13 习题 13 第2章 Posix IPC 14 2.1 概述 14 2.2 IPC名字 14 2.3 创建与打开IPC通道 16 2.4 IPC权限 18 2.5 小结 19 习题 19 第3章 System V IPC 20 3.1 概述 20 3.2 key_t键和ftok函数 20 3.3 ipc_perm结构 22 3.4 创建与打开IPC通道 22 3.5 IPC权限 24 3.6 标识符重用 25 3.7 ipcs和ipcrm程序 27 3.8 内核限制 27 3.9 小结 28 习题 29 第二部分 消息传递 31 第4章 管道和FIFO 32 4.1 概述 32 4.2 一个简单的客户-服务器例子 32 4.3 管道 32 4.4 全双工管道 37 4.5 popen和pclose函数 39 4.6 FIFO 40 4.7 管道和FIFO的额外属性 44 4.8 单个服务器,多个客户 46 4.9 对比迭代服务器与并发服务器 50 4.10 字节流与消息 51 4.11 管道和FIFO限制 55 4.12 小结 56 习题 57 第5章 Posix消息队列 58 5.1 概述 58 5.2 mq_open、mq_close和mq_unlink函数 59 5.3 mq_getattr和mq_setattr函数 61 5.4 mq_send和mq_receive函数 64 5.5 消息队列限制 67 5.6 mq_notify函数 68 5.7 Posix实时信号 78 5.8 使用内存映射I/O实现Posix消息队列 85 5.9 小结 101 习题 101 第6章 System V消息队列 103 6.1 概述 103 6.2 msgget函数 104 6.3 msgsnd函数 104 6.4 msgrcv函数 105 6.5 msgctl函数 106 6.6 简单的程序 107 6.7 客户-服务器例子 112 6.8 复用消息 113 6.9 消息队列上使用select和poll 121 6.10 消息队列限制 122 6.11 小结 124 习题 124 第三部分 同步 125 第7章 互斥锁和条件变量 126 7.1 概述 126 7.2 互斥锁:上锁与解锁 126 7.3 生产者-消费者问题 127 7.4 对比上锁与等待 131 7.5 条件变量:等待与信号发送 132 7.6 条件变量:定时等待和广播 136 7.7 互斥锁和条件变量的属性 136 7.8 小结 139 习题 139 第8章 读写锁 140 8.1 概述 140 8.2 获取与释放读写锁 140 8.3 读写锁属性 141 8.4 使用互斥锁和条件变量实现读写锁 142 8.5 线程取消 148 8.6 小结 153 习题 153 第9章 记录上锁 154 9.1 概述 154 9.2 对比记录上锁与文件上锁 157 9.3 Posix fcntl记录上锁 158 9.4 劝告性上锁 162 9.5 强制性上锁 164 9.6 读出者和写入者的优先级 166 9.7 启动一个守护进程的唯一副本 170 9.8 文件作锁用 171 9.9 NFS上锁 173 9.10 小结 173 习题 174 第10章 Posix信号量 175 10.1 概述 175 10.2 sem_open、sem_close和sem_unlink函数 179 10.3 sem_wait和sem_trywait函数 180 10.4 sem_post和sem_getvalue函数 180 10.5 简单的程序 181 10.6 生产者-消费者问题 186 10.7 文件上锁 190 10.8 sem_init和sem_destroy函数 191 10
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值