pipe()创建管道,可以使用的单向数据通道 用于进程间通信。数组pipefd用于返回引用管道末端的两个文件描述符。 pipefd [0] 是指管道的读取端。 pipefd [1]是指写管道的末端。写入管道写入端的数据是由内核缓冲,直到从读取端读取管道。
pipe函数
#include
int pipe(int pipefd[2]);
#define _GNU_SOURCE /* See feature_test_macros(7) */
#include /* Obtain O_* constant definitions */
#include
int pipe2(int pipefd[2], int flags);
fork的时候,两个PCB有两个fd_array,复制一份后,指向同一个file,然后指向同一块内存,pipe应该原理类似,指向同一个内核缓存
管道指令的使用(过滤)
ls | grep y
#include
#include
#include
#include
#include
/* According to earlier standards */
#include
#include
#include
#include
int main()
{
int rp,pid;
int pipefd[2];
char recfbuf[20] = "helloworld";
struct sigaction siga,oldsiga;
char writebuf[1024];
siga.sa_handler = SIG_IGN;
siga.sa_flags = SA_NOCLDWAIT;
sigemptyset(&siga.sa_mask);
sigaction(SIGCHLD,&siga,&oldsiga);
void fun(int i)
{
puts("no read\n");
}
signal(SIGPIPE,fun);
rp = pipe(pipefd);
pid = fork();
if(pid<0)
{
perror("fork");
exit(1);
}
if(pid == 0)
{
close(pipefd[0]);
dup2(pipefd[1],STDOUT_FILENO);
execlp("ls","ls","-l",NULL);
close(pipefd[1]);
exit(0);
}
// close(pipefd[0]);
close(pipefd[1]);
dup2(pipefd[0],STDIN_FILENO);
execlp("wc","wc","-c",NULL);
// open("890.c",O_WRONLY|O_CREAT|O_TRUNC,0644);
close(pipefd[0]);
puts("end");
exit(0);
}
xili271948@er04180p:~/tcp$ ./789
2452
xili271948@er04180p:~/tcp$ ls -l | wc -c
2452
#include
#include
#include
#include
#include
#include
/* According to earlier standards */
#include
#include
#include
#include
#include
int main()
{
int rp,pid,fd,num,numr,frd,rflags;
int pipefd[2];
struct sigaction siga,oldsiga;
char writebuf[1024];
char readbuf[1024];
siga.sa_handler = SIG_IGN;
siga.sa_flags = SA_NOCLDWAIT;
sigemptyset(&siga.sa_mask);
sigaction(SIGCHLD,&siga,&oldsiga);
rp = pipe(pipefd);
rflags = fcntl(pipefd[0],F_GETFL);
fcntl(pipefd[0],F_SETFL,rflags|O_NONBLOCK);
pid = fork();
if(pid<0)
{
perror("fork");
exit(1);
}
if(pid == 0)
{
//sleep(2);
close(pipefd[0]);
fd = open("123.c",O_RDONLY);
while(1)
{
num = read(fd,writebuf,1024);
if(num <0)
{
perror("read");
exit(1);
}
else if(num > 0)
write(pipefd[1],writebuf,num);
else
break;
}
puts("end of send");
close(pipefd[1]);
close(fd);
exit(0);
}
else
{
//sleep(1);
frd = open("456.c",O_WRONLY|O_CREAT|O_TRUNC,0644);
close(pipefd[1]);
numr = 1;
while(numr)
{
numr = read(pipefd[0],readbuf,num);
if(numr<0)
{
puts(strerror(errno));
}
write(frd,readbuf,numr);
}
puts("end of recv");
close(pipefd[0]);
close(frd);
}
exit(0);
}
xili271948@er04180p:~/tcp$ ./noblockpipe
Resource temporarily unavailable
Resource temporarily unavailable
Resource temporarily unavailable
Resource temporarily unavailable
Resource temporarily unavailable
end of send
Resource temporarily unavailable
end of recv
#include
#include
#include
#include
#include
/* According to earlier standards */
#include
#include
#include
#include
int main()
{
int rp,pid;
int pipefd[2];
char recfbuf[20] = "helloworld";
struct sigaction siga,oldsiga;
char writebuf[1024];
siga.sa_handler = SIG_IGN;
siga.sa_flags = SA_NOCLDWAIT;
sigemptyset(&siga.sa_mask);
sigaction(SIGCHLD,&siga,&oldsiga);
void fun(int i)
{
puts("no read\n");
}
signal(SIGPIPE,fun);
rp = pipe(pipefd);
pid = fork();
if(pid<0)
{
perror("fork");
exit(1);
}
if(pid == 0)
{
close(pipefd[0]);
write(pipefd[1],recfbuf,sizeof(recfbuf));
puts(recfbuf);
close(pipefd[1]);
exit(0);
}
close(pipefd[0]);
close(pipefd[1]);
puts("end");
exit(0);
}
xili271948@er04180p:~/tcp$ ./pip_sigpipe
end
no read
helloworld
#include
#include
int mkfifo(const char *pathname, mode_t mode);
#include /* Definition of AT_* constants */
#include
int mkfifoat(int dirfd, const char *pathname, mode_t mode);
filname是指文件名,而mode是指定文件的读写权限。mknod是比较老的函数,而使用mkfifo函数更加简单和规范,所以建议用mkfifo。
open(const char *path, O_RDONLY);//1
open(const char *path, O_RDONLY | O_NONBLOCK);//2
open(const char *path, O_WRONLY);//3
open(const char *path, O_WRONLY | O_NONBLOCK);//4
需要搭配O_RDONLY或者O_WRONLY使用,因为管道为单向
同时,mkfifo()创建的为pipe文件,通过ls -l或者stat查看,其如下,不能使用vim cat等查看
fifo读
#include
#include
#include
#include
#include
#include
#define BUFFSIZE 50
int main()
{
char rdbuf[BUFFSIZE];
int mk,fo;
fo = open("mkdfifo",O_RDONLY);
int nw;
nw = read(fo,rdbuf,BUFFSIZE);
if(nw < 0)
{
perror("read\n");
exit(1);
}
puts("read all");
puts(rdbuf);
exit(0);
}
fifo写
#include
#include
#include
#include
#include
#include
#define BUFFSIZE 50
int main()
{
int mk,fo;
mk = mkfifo("./mkdfifo",0600);
fo = open("mkdfifo",O_WRONLY);
char *wrbuf = "helloworld";
int nw;
nw = write(fo,wrbuf,BUFFSIZE);
puts("write all\n");
if(nw < 0)
{
perror("write\n");
exit(1);
}
puts("send all");
exit(0);
}
xili271948@er04180p:~/tcp$ ./wfifo
write all
send all
xili271948@er04180p:~/tcp$ ./rfifo
read all
helloworld
读
#include
#include
#include
#include
#include
#include
#include
#define BUFFSIZE 50
int main()
{
char rdbuf[BUFFSIZE];
int mk,fo;
fo = open("mkdfifo",O_RDONLY|O_NONBLOCK);
int nw;
nw = read(fo,rdbuf,BUFFSIZE);
if(nw < 0)
{
perror("read\n");
exit(1);
}
puts("read all");
puts(rdbuf);
exit(0);
}
写
#include
#include
#include
#include
#include
#include
#include
#define BUFFSIZE 50
void fun(int argc)
{
puts("SIGPIPE");
}
int main()
{
int mk,fo;
signal(SIGPIPE,fun);
//mk = mkfifo("./mkdfifo",0600);
fo = open("mkdfifo",O_WRONLY|O_NONBLOCK);
if(fo < 0)
{
perror("open");
exit(1);
}
char *wrbuf = "helloworld\n";
sleep(10);
int nw;
nw = write(fo,wrbuf,BUFFSIZE);
puts("write all");
if(nw < 0)
{
perror("write\n");
exit(1);
}
puts("send all");
exit(0);
}
父子进程之间pipe
#include
#include
#include
#include
#include
#include
#include
#define BUFFSIZE 50
int main()
{
char rdbuf[BUFFSIZE];
char* wrbuf = "helloworld";
int mk,pid;
mk = mkfifo("./testmkfifo",0600);
int nw;
pid = fork();
if(pid == 0)
{
int fr;
fr = open("testmkfifo",O_WRONLY);
write(fr,wrbuf,strlen(wrbuf)+1);
exit(0);
}
else
{
int fw;
fw = open("testmkfifo",O_RDONLY);
nw = read(fw,rdbuf,BUFFSIZE);
if(nw < 0)
{
perror("read\n");
exit(1);
}
}
puts("read all");
puts(rdbuf);
exit(0);
}
xili271948@er04180p:~/tcp$ ./fifotest
read all
helloworld
消息列队基础知识,参考https://www.jianshu.com/p/7e3045cf1ab8
消息队列本质上是位于内核空间的链表,链表的每个节点都是一条消息。每一条消息都有自己的消息类型,消息类型用整数来表示,而且必须大于 0。每种类型的消息都被对应的链表所维护:
下表中,第一列为消息类型mtype,每个type对应一个链表
ftok
key_t ftok(const char *pathname, int proj_id);
pathname为文件名,proj_id为非零,最少8位的数,类似于salt,使用文件名对应独一无二的inode号,用于生成key。
返回值为key
msgget
int msgget(key_t key, int msgflg);
获取消息队列的id,msgflg是一个位图,IPC_CREAT用于创建id,如果msgflg是IPC_PRIVATE,则key与消息队列id没有关系。
msgop
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,
int msgflg);
返回值:msgsnd成功返回0,msgrcv成功返回接收成功的mtext大小
msgp指向msgbuf,msgsz为mtext的size,msgtyp与mtype有关
msgtyp = 0:获取消息队列中的第一条消息
msgtyp > 0:获取类型为 msgtyp 的第一条消息,除非指定了 msgflg 为MSG_EXCEPT,这表示获取除了 msgtyp 类型以外的第一条消息。
msgtyp < 0:获取类型 ≤|msgtyp|≤|msgtyp| 的第一条消息。
参数 msgflg:可选项。
如果为 0 表示没有消息就阻塞。
IPC_NOWAIT:如果指定类型的消息不存在就立即返回,同时设置 errno 为 ENOMSG
MSG_EXCEPT:仅用于 msgtyp > 0 的情况。表示获取类型不为 msgtyp 的消息
MSG_NOERROR:如果消息数据正文内容大于 msgsz,就将消息数据截断为 msgsz
struct msgbuf
{
long mtype; /* message type, must be > 0 */
char mtext[1]; /* message data */
};
msgctl
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
成功返回0
struct msqid_ds {
struct ipc_perm msg_perm; /* Ownership and permissions */
time_t msg_stime; /* Time of last msgsnd(2) */
time_t msg_rtime; /* Time of last msgrcv(2) */
time_t msg_ctime; /* Time of last change */
unsigned long __msg_cbytes; /* Current number of bytes in
queue (nonstandard) */
msgqnum_t msg_qnum; /* Current number of messages
in queue */
msglen_t msg_qbytes; /* Maximum number of bytes
allowed in queue */
pid_t msg_lspid; /* PID of last msgsnd(2) */
pid_t msg_lrpid; /* PID of last msgrcv(2) */
};
The ipc_perm structure is defined as follows (the highlighted fields are settable using IPC_SET):
struct ipc_perm {
key_t __key; /* Key supplied to msgget(2) */
uid_t uid; /* Effective UID of owner */
gid_t gid; /* Effective GID of owner */
uid_t cuid; /* Effective UID of creator */
gid_t cgid; /* Effective GID of creator */
unsigned short mode; /* Permissions */
unsigned short __seq; /* Sequence number */
};
IPC_STAT 获取消息队列数据结构msqid_ds的值,并存在buf中
IPC_SET 设置消息队列中数据结构msqid_ds里ipc_perm的值,取自buf
IPC_RMID 内核中移除消息队列
1.发送端
#include
#include
#include
#include
#include
#include "mq.h"
#include
int main(int argc, char** argv)
{
int msqid;
int ifsend;
struct msgbuf sendmsg1,sendmsg2,sendmsg3,sendmsg4;
sendmsg1.mtype = 1l;
strcpy(sendmsg1.name,"datengzi"); //注意,这里有三种不同的mtype,可以通过msgrcv,选择哪个type
sendmsg1.chinese = 80;
sendmsg1.math = 90;
sendmsg2.mtype = 2l;
strcpy(sendmsg2.name,"sunyue");
sendmsg2.chinese = 80;
sendmsg2.math = 80;
sendmsg3.mtype = 1l;
strcpy(sendmsg3.name,"meisen");
sendmsg3.chinese = 70;
sendmsg3.math = 80;
sendmsg4.mtype = 3l;
strcpy(sendmsg4.name,"wangzhu");
sendmsg4.chinese = 70;
sendmsg4.math = 80;
struct msgbuf msgtest[4]={sendmsg1,sendmsg2,sendmsg3,sendmsg4};
key_t key;
key = ftok(KEYPATH,PROJ_ID);
if(key < 0)
{
perror("ftok");
exit(1);
}
if((msqid = msgget(key,0))<0) //server先运行,已经产生了key关联的消息队列id,client端只需要获取即可
{
perror("msgget");
exit(1);
}
for(int i=0; i<4; i++)
{
ifsend = msgsnd(msqid,&msgtest[0]+i,sizeof(msgtest[i])-sizeof(long),MSG_NOERROR);
if(ifsend<0)
{
perror("msgsnd");
exit(1);
}
}
puts("end");
exit(0);
}
2.接收端
#include
#include
#include
#include
#include
#include "mq.h"
#include
#include
int msgid;
void sig_handler(int);
int main(int argc, char** argv)
{
key_t key;
int msgrecvnum;
struct msgbuf recvmsg;
struct sigaction act, oldact;
act.sa_handler = sig_handler;
sigemptyset(&act.sa_mask);
sigaddset(&act.sa_mask,SIGQUIT);
act.sa_flags = 0;
sigaction(SIGINT,&act,&oldact);
key = ftok(KEYPATH,PROJ_ID);
if(key < 0)
{
perror("ftok");
exit(1);
}
msgid = msgget(key,IPC_CREAT|0666); //注意设置文件属性
if(msgid < 0)
{
perror("msgget");
exit(1);
}
while(1)
{
msgrecvnum = msgrcv(msgid,&recvmsg,sizeof(recvmsg)-sizeof(long),atoi(argv[1]),0);
if(msgrecvnum < 0)
{
perror("msgrcv");
exit(1);
}
fprintf(stderr,"the name of student is %s\n",recvmsg.name);
fprintf(stderr,"the score of chinese is %d\n",recvmsg.chinese);
fprintf(stderr,"the score of math is %d\n",recvmsg.math);
}
exit(0);
}
void sig_handler(int args)
{
int remsgctl;
puts("sig tested");
remsgctl = msgctl(msgid,IPC_RMID,NULL);
if(remsgctl < 0)
{
perror("msgctl");
exit(1);
}
}
3.结果
cmd: ipcs查看消息队列,上述例子中,使用signal函数,ctrl + c,产生信号SIGINT,触发去除消息队列函数,否则进程结束,消息队列不会删除。
xili271948@er04180p:~/tcp$ ./mq_recv 3
the name of student is wangzhu
the score of chinese is 70
the score of math is 80
^Csig tested
msgrcv: Interrupted system call
xili271948@er04180p:~/tcp$ ./mq_recv 0
the name of student is datengzi
the score of chinese is 80
the score of math is 90
the name of student is sunyue
the score of chinese is 80
the score of math is 80
the name of student is meisen
the score of chinese is 70
the score of math is 80
the name of student is wangzhu
the score of chinese is 70
the score of math is 80
^Csig tested
msgrcv: Interrupted system call