Linux-LPC通信
dup2 () 句柄重定向
会把第一个绑定的句柄关闭了 在重定向
popen() 获取进程句柄
运行一个命令 返回命令结果
FILE* finput = popen("echo -e texe!","r");
read(fileno(finput),buf,100);
fileno() 获取此句柄
无名管道 pipe
简单传输速度快
- 无名管道 (pipe) :只有创建该管道的查询,才能找到该管道(亲缘进程管道)
- 如果 fork 一个新进程就必须 每一个进程都必须关闭一个 pipe的 fd
- 只能单向传输
- 如果对方 把 两个pipe fd 都关闭 当 要读写时 就会立马退出 并且收到 SIGPIPE信号
int pipe_fd[2];
pipe(pipe_fd); //创建pipe 管道
read(pipe_fd[0],buf,SIZ,0);//从管道中读取信息
close(pipe_fd[1]); //关闭 一个pipe
有名管道 fifo
没有办法实现双向通信 传输速度慢
- FIFO 是依赖于文件系统
和普通文件一样 拥有文件权限和其他属性,所有程序都可以通过fifo 的fd来找到有名管道
- 命令行创建一个 有名管道
mknod FIFOTEST p
- p 代表管道文件
- fifo 必须 两端都同时打开 fifo 是 才能写进管道 否则就会阻塞 等待 其他进程打开
int ret = mkfifo(./file.tmp,0666|S_IFIFO);//创建 fifo 管道文件
int fd = open(./file.tmp,O_WRONLY|O_CREAT|0644);//打开 fifo管道
- fifo 是一个文件,存储信息在内存中
- 只能单向通信
- 只能进行少量数据传输
- 只能一对一传输
- 两个进程,中途退出一个进程,未退出的一段如果是写操作的话,返回sigpipe信息
- 未退出的一端如果是阻塞读操作的话,该操作不会继续阻塞,而直接返回0
system v ipc XSI消息队列
system v 和 ipc 共同的结构体
struct msqid_ds
struct msqid_ds { // 消息结构体链表
struct ipc_perm msg_perm; // 描述当前 msg 的权限
time_t msg_stime; // 最后发送时间
time_t msg_rtime; // 最后接收时间
time_t msg_ctime; // 最后改变时间
unsigned long __msg_cbytes; // 消息的字节数
msgqnum_t msg_qnum; // 消息的个数
msglen_t msg_qbytes; // 最大的字节数
pid_t msg_lspid; // 最后一个发送进程的PID
pid_t msg_lrpid; // 最后一个接收的进程的PID
struct msg * msq_first ; // 头指针
struct msg * msq_last ; // 尾指针
};
- 拥有者及权限对象
struct ipc_perm
struct ipc_perm
{
__kernel_key_t key;// 当前的 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;
};
优点和缺点
优点:
1. 消息队列是两个不相关进程之间传递数据的有效渠道.
2. 与命名管道相比,具有的优势是:
独立于发送以及接收进程而存在;
消除命名管道的打开以及关闭而存在的困难;
可以提前查看紧急信息;
避免命名管道的同步以及阻塞问题;
缺点:
与命名管道一样,每个数据块都有一个最大
度限制;
系统中所有队列包含的全部数据块长度也有一个上限;
- 创建key
- key :通信双方约定好的值,32位的整形数据
- id :系统会为IPC分配一个唯一的ID,通信双方都需要知道ID才能使用当前IPC方法,创建者会返回当前ID值,
- 使用命令 lpcs 可以查看所有 共享内存的字段
#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id);
pathname: 路径
proj_id
key 31-24 proj_id 低8位
key 23-16 st_dev属性的低8位
key 15-0 st_ino属性的低16位;
- 创建对象:
int msgget(key_t key, int msgflg);
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t key, int msgflg);
参数1: key_t key
参数2:int msgflg 确定消息队列的访问权限,
IPC_CREAT 找到当前的key对应的IPC,如果没有IPC则进行创建
IPC_EXCL 如果存在则返回失败
IPC_NOWAIT 返回值为当前ipc对应的id值;
msgget(key,0600|IPC_CREAT);
- 设置对象:
int msgctl(int msqid,int cmd,struct msqid_ds *buf);
cmd:
IPC_RMID 删除
IPC_SET 设置ipc_perm参数
IPC_STAT 获取ipc_perm参数
IPC_INFO 获取ipc信息 和ipc
- 使用对象:
- 发送
int msgsnd(int msqid,const void *msg_ptr,size_t sz,int msgflg);
参数 sz:只是发送数据的长度 而不是全结构体长度
msqid : 是 msgget的返回值 用于发送struct msgbuf
{
long mtype; // 发送消息的类型
char mtext[1]; // 发送的数据
- 发送
- 接收
- ssizet msgrcv(int msqid, void _msgp, size_t msgsz, long msgtyp,int msgflg);
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
msqid: 从哪一个 消息队列中收;
msgp: 收取存放的结构体;
msgsz:收取的大小;
msgtyp:收取的类型
msgtyp 0 默认接收第一条消息
msgtyp > 0 接收第一条msgtyp消息
msgtyp < 0 接收小于msgtyp绝对值的消息
XSL信号量
概念:
- 实现进程间同步
- 表示系统可用资源的个数
对象:
struct semid_ds {
struct ipc_perm sem_perm; //和IPC权限一样
time_t sem_otime;//最后一次调用信号操作的时间
time_t sem_ctime; // 最后一次信号改变的时间
unsigned long sem_nsems; //信号的个数
struct sem * sem_base;// 单向指针 指向信号头节点
};
struct sem{
int semval // 信号量的值
int sempid // 最近一个操作的进程号
}
对象操作
- 创建对象: 创建信号对象
int semget(key_t k,int n,int semflag);
#include <sys/ipc.h>
#include <sys/sem.h>
int semget(key_t key, int nsems, int semflg);
nsems:创建信号的个数
semflg:权限 IPC_CREAT
- 设置对象:
int semctl(int sid,int semnum,int cmd,union semun a);
int semctl(int sid,int semnum,int cmd,union semun a);
union semun {
int val; /* 信号的值 */
struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
unsigned short* array; /* Array for GETALL, SETALL */
struct seminfo *__buf; /* Buffer for IPC_INFO*/
};
cmd 如果对于整个信号量集合,则有如下设置方法
IPC_STAT ipc_perm
IPC_SET ipc_perm
IPC_RMID
IPC_INFO 信号量的系统信息
cmd 如果对于单个信号量集合,则有如下设置方法
GETPID 获取信号拥有者的PID
semnum:参数无效
GETVAL 获取信号的值 并且返回信号的值
semnum:获取第几个信号的值
GETALL 获取所有信号的值
semnum:0
semun: 存储信号的地址
SETVAL 设置某一个信号的值
semnum:设置第几个信号的值
semun: 设置的值
SETALL 设置所有信号的值
semnum:0
semun: 设置的值
- 使用对象
int semop(int s,struct sembuf *sb,size _t n);
s:操作信号的id;
n:第几个信号
struct sembuf{
unsigned short int sem_num; //信号的值
short int sem_op; //选项
short int sem_flg; //权限
// sem_flg 的值为 SEM_UNDO 时 当当前进程退出时 撤回当前进程使用的所有资源
};
sem_op:的值
+ 表示进行加
- 表示进行减
XSI共享内存
- 数据量最大
- 传输最快
- mmap
共享内存对象
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 */
}
创建共享内存
int shmget(key_t key, size_t size, int shmflg);
共享内存的权限
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);
shmflg:权限
SHM_R
SHM_W
设置共享内存
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
SHM_INFO
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
struct shm_info {
int used_ids; /* # of currently existing segments */
unsigned long shm_tot; // Total number of shared memory pages
unsigned long shm_rss; // # of resident shared memory pages
unsigned long shm_swp; /* # of swapped shared memory pages */
unsigned long swap_attempts;/* Unused since Linux 2.4 */
unsigned long swap_successes;/* Unused since Linux 2.4 */
};
SHM_STAT // 设置
SHM_LOCK // 上锁
SHM_UNLOCK // 解锁
使用对象
链接 void _shmat(int shmid, const void _shmaddr, int shmflg);
**删除对象int shmdt(const void shmaddr);
#include <sys/types.h>
#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr, int shmflg);
SHM_RDONLY //
SHM_REMAP //
SHM_EXEC // 可执行方式
注意
1.父子进程的共享内存约定
fork函数 共享内存会自动取消
exec执行一个新的程序 自动卸载
exit 共享内存会自动取消
创建 读取 例:
// 创建共享内存
int shid=shmget((key_t)654321,(size_t)2048,0600|IPC_CREAT);
// 链接到共享内存中
void *sharem=shmat(shid,NULL,0);
// 判断 共享内存中的数据是否为 end
strcmp(sharem,"end");
// 取消链接
shmdt(sharem);
进行文件句柄的传输
对象
可以传递 有效数据 也可以传递控制数据
#include<sys/socket.h>
struct msghdr {
void *msg_name; // 消息名
socklen_t msg_namelen; /* size of address */
struct iovec *msg_iov; // 用于传递 有效数据
// 把数据合成一个数据来传递
size_t msg_iovlen; // 有几个有效数据
void *msg_control; // 用于传输 控制数据
size_t msg_controllen; /* ancillary data buffer len */
int msg_flags; /* flags on received message */
};
// 传输有效数据的结构体
struct iovec { /* Scatter/gather array items */
void *iov_base; //数据的头
size_t iov_len; //数据的长度
};
// 传输控制数据的结构体
struct cmsghdr {
socklen_t cmsg_len; // 当前控制数据 有多少bit 包含头长度
int cmsg_level; // 控制数据 级别
int cmsg_type; // 数据类型 SCM_RIGHTS
unsigned char cmsg_data[]; // 具体数据
};
例:创建 连接
#include<sys/un.h>
int local_fd = socket(AF_LOCAL,SOCK_STREAM,0); // 创建本地套接字
struct sockaddr_un myaddr; //创建御间套接字
myaddr.sun_family = AF_LOCAL;// 协议类型
// 这里省略创建文件 和 检测文件是否存在
strncpy(myaddr.sun_path,"./file",strlen("./file")); //文件路径
bind(local_fd,(struct sockaddr*)&myaddr,sizeof(myaddr)); // 绑定当前文件
listen(local_fd,5); // 监听
struct sockaddr_un peeraddr; //创建御间套接字
int len = sizeof(myaddr);
int nwe_fd = accept(local_fd,(struct sockaddr*)&peeraddr,&len); // 等待连接
// 此连接也是使用 connect 函数连接
// 但是注意 struct sockaddr_un 中的 sun_path 必须绑定同一个文件
例: 发送 链接数据
int file_fd = open(file,|O_RDWR|O_APPEND); // 打开或者新建一个文件
struct cmsghdr *cmsg; // 发送控制消息
cmsg->cmsg_len = sizeof(struct cmsghdr) + sizeof(file_fd);//当前控制数据大小 当前头大小 + 控制数据大小
cmsg->cmsg_level = SOL_SOCKET; // 控制数据 级别
cmsg->cmsg_type = SCM_RIGHTS; // 发送数据 类型
memcpy(CMSG_DATA(cmsg),&file_fd,sizeof(file_fd));
// CMSG_DATA() 获取数据端地址
// 选择 发送 有效数据
struct msghdr msg;
msg.msg_control = cmsg; // 控制数据地址
msg.msg_controllen = cmsg->cmsg_len;// 控制数据长度
struct iovec iov[1]; //存放 有效数据
iov[0].iovbase = "hello";
iov[0].iov_len = strlen("hello");
msg.msg_iov = iov; // 有效数据首地址
msg.msg_iovlen = 1; // 有效数据个数