管道
//记住命令 cd /proc/pid/fd
读写测试
关于父进程先创建管道还是先创建子进程
父子进程通信
代码疑问:为啥每次都能读到数据?//因为父子进程抢占式执行很有可能子进程先执行,然后什么也没有读到就退出了
管道的特性
管道的特性:
半双工 : 数据只能从写端流向读端,//单向通信
匿名管道没有标识符,只能具有亲缘性关系的进程进行进程间通信
管道的生命周期跟随进程,进程退出了,管道在内核当中就销毁了
管道的大小为64k
怎么验证?
如果没有人读,疯狂往管道当中写,会是什么情况呢?
验证大小
当管道写满之后,在调用wirte往管道当中写的时候,wirte就会阻塞
验证先后写入管道的数据是无间隔的
验证管道中的数据是读走的而不是拷贝走的
管道属性
阻塞属性:读写两端文件描述符初始属性为阻塞属性
//当调用write函数一直给管道写数据,而我们不把管道数据读走,写满之后write会阻塞
//当调用read函数一直从管道读数据,等到读完了它就会阻塞起来,等到管道中有数据才能继续读
写代码求证
//没有胃口时可以吃点橘子甜(带点酸)刺激胃,帮助饮食
在操作系统中找一下 O_NONBLOCK 宏定义
这里系统显示的是 八进制
O_RDONLY | O_NONBLOCK == (0 | 2048) == (2048)
O_WRONLY | O_NONBLOCK == (1 | 2048) == (2049)
为啥文件描述符的属性信息需要按位或的方式进行设置
结论:文件描述符的属性信息,在操作系统内核中用比特位表示
为啥是按位或?
你本来是非阻塞和只读,但是你同只写进行或运算,最后面的比特位 (0 | 1)变成了1
这样一来它就是阻塞属性了, 当你想让它拥有啥属性,进行对应的或运算
-------------------------------------------------------------------------------------------
操作系统内核中大量位操作
1.位操作快
2.节省内存
父子进程通信/关闭父的写/关闭子的读
读属性设置为非阻塞属性___不关闭写操作
读空管道
返回值为-1
资源暂时不可用
读属性非阻塞——关闭写操作
关闭管道所有写的接口
非阻塞读空管到
success
返回值为0
写属性非阻塞___不关闭读操作
读不关闭一直写,管道写满,调用write返回-1
写属性非阻塞___关闭所有读操作
写操作非阻塞___关闭了所有读操作
程序调用子进程时走到了write函数会发生崩溃提前退出父进程尚未退出//变僵尸进程
原因:
调用写进程时收到SIGPIPE信号,发生崩溃
创建命名管道
命令 mkfifo [文件名]
结论:
1.1数据还是存储在内核的缓冲区当中
1.2管道文件的作用是让不同的进程找到这块缓冲区
mkfifo函数
int mkfifo(const char *pathname, mode t mode)
参数:
pathname :待要创建的命名管道文件 (带路径)
mode t :指定管道文件的权限 (0664)
返回值:
成功:0
失败:-1
特性:
支持不同的进程进行通信,不依赖亲缘性
因为不同的进程可以通过命名管道文件,找到命名管道(操作系统内核缓冲区)
不同进程使用命名管道进行通信
共享内存的接口
2.1 创建或者获取共享内存接口
2.2 附加
2.3 分离
2.4 操作
#include<sys/shm.h>//需要的库函数
//ipcs
当销毁共享内存空间时,上面附着进程时
共享内存空间已经销毁但是操作系统用以描述该共享内存的结构体没有释放
消息队列
发送消息
发送消息
int msgsnd(int msqid, const void *msgp,size_t msgsz,int msgflg)
参数:
msgid:消息队列 ID
msgp: 指向 msgbuf 的指针,用来指定发送的消息
msgsz:要发送消息的长度
msgflg: 创建标记,如果指定 IPC NOWAIT,失败会立刻返回
返回值:
成功返回 0
失败返回 -1,并设置 erron
msgsz(message size) :这个参数的取值并不是整个struct msgbuf结构体的大小,而是结构体当中mtext变量的大小(里面存的是我们的message data)
接受消息
接收消息
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz,long msgtyp,int msgflg);
参数:
//可以按照类型从消息队列当中接收消息 回忆: 在进行msgsnd的时候, msgtype的大小一定是大于0的
msgid: 消息队列 ID
msgsz: 指向 msgpbuf 的指针,用来接收消息
//struct msgbuf{...} : 出参
msgsz:要接收消息的长度
注意:参数 msgsz 指定由 msgp 参数指向的结构的成员 mtext 的最大大小(以字节为单位)
msgtyp 也有3种方式
msgtyp: 接收消息的方式
1. msgtyp = 0:读取队列中的第一条消息//就相当于没有区分类型, 按照插入的顺序, 先进先出
2. msgtyp > 0: 读取队列中类型为 msgtyp 的第一条消息,除非在 msgflg 中指定了 MSG_EXCEPT,否则将读取类型不等于msgtyp 的队列中的第一条消息。
3. msgtyp< 0:读取队列中最小类型小于或等于 msgtyp 绝对值的第一条消息
msgflg:创建标记,如果指定 IPC_NOWAIT,获取失败 会立即返回
返回值:
成功返回实际读取消息的字节数
失败返回-1 ,并设置erron
操作消息队列接口
操作消息队列的接口
int msgctl(int msqid , int cmd ,struct msqid_ds *buf);
参数:
msqid: 消息队列 ID
cmd:控制命令
例如 IPC_RMID,删除命令
IPC_STAT,获取状态
buf:存储消息队列的相关信息的 buf
返回值:
成功根据不同的 cmd 有不同的返回值
失败返回 -1,并设置 erron
要使用到snprintf函数//
//函数原型为int snprintf(char *str, size_t size, const char *format, ...)。
//两点注意:
//(1) 如果格式化后的字符串长度 < size,则将此字符串全部复制到str中,并给其后添加一个字符串结束符
//('\0');
//(2) 如果格式化后的字符串长度 >= size,则只将其中的(size-1)个字符复制到str中,并给其后添加一个字
//符串结束符('\0'),返回值为欲写入的字符串长度
send.c
#include<stdio.h>
#include<sys/msg.h>
#include<unistd.h>
/* 1.目的:往消息队列中发送消息
* 2.操作方法
* 2.1 创建消息队列
* 2.2 组织要发送消息,并且要发送出去
* 1 send msg1
* send msg2
* send msg3
* ...
* ...
*/
struct msgbuf{
long mtype;
char mtext[255];
};
void main()
{
int G_id=msgget(0x78787878,IPC_CREAT | 0664);
if(G_id<0)
{
perror("msgget");
return ;
}
int i;
for( i=0;i<10;++i)
{
struct msgbuf mb;
mb.mtype=i;
snprintf(mb.mtext,sizeof(mb.mtext),"send msg%d",i);
msgsnd(G_id,&mb,sizeof(mb.mtext),0);
}
}
receive.c
#include<stdio.h>
#include<sys/msg.h>
#include<unistd.h>
/* 1.目的:从消息队列中获取消息
* 2.操作方法
* 2.1 获取或者创建消息队列
* 2.2 调用msgrecv函数进行获取消息
* 1 receive msg1
* receive msg2
* ...
* 6:receive msg6//输入消息类型我们就会接收到
* 消息类型为6的msg
*/
struct msgbuf{
long mtype;
char mtext[255];
};
void main()
{
int G_id=msgget(0x78787878,IPC_CREAT | 0664);
if(G_id<0){
perror("msgget");
return ;
}
struct msgbuf mb;
msgrcv(G_id,&mb,sizeof(mb.mtext),0,0);
//msgrcv(文件的操作句柄 , 指向msgbuf的指针以接受消息
//, 接受消息数组长度 , 接受消息类型 , 阻塞接受)
printf("%s\n",mb.mtext);
}
查看消息队列
//消息队列生命周期跟随操作系统内核
//ipcrm -q [qid] //删除消息队列