php做网站流程,整合网络营销,伯维网站建设,公司电商网站建设引言#xff1a; 之前学习的进程之间交换信息的方法只能由fork或exec传送打开文件#xff0c;或者文件系统。但是这种通讯方式有局限性#xff0c;接下来将说明进程之间相互通信的其他技术——IPC(InterProcessCommunication)#xff0c;过去UNIX系统IPC是各种进程间通信方…引言 之前学习的进程之间交换信息的方法只能由fork或exec传送打开文件或者文件系统。但是这种通讯方式有局限性接下来将说明进程之间相互通信的其他技术——IPC(InterProcessCommunication)过去UNIX系统IPC是各种进程间通信方式的统称但是其中极少能在所有UNIX系统中实现移植。随着POSIX和Open Group以前是X/Open标准化的推进和影响扩大情况随已得到改善但是差别仍然存在。以下将介绍几种实现所支持的不同形式的IPC。
单机版进程间通信方式 半双工管道包括无名管道和命名管道 消息队列 信号量 共享内存
多机版进程间通讯方式
套接字socketstreams
管道父进程创建
概念 管道通常指无名管道是一种最基本的IPC机制作用于有血缘关系的进程之间完成数据传递。我们把从一个进程连接到另一个进程的一个数据流称为一个“管道”本质内核缓冲区。
特点
它是半双工的即数据只能在一个方向上流动具有固定的读端和写端当数据从管道中读取后管道中就没有了。它只能用于具有亲缘关系的进程之间的通信也是父子进程或者兄弟进程之间。它可以看成是一种特殊的文件对于它的读写也可以使用普通的read、write 等函数。但是它不是普通的文件并不属于其他任何文件系统并且只存在于内存中。本质内核缓冲区. 如何建立半双工管道
管道是由调用pipe函数来创建
#include unistd.h
int pipe(int pipefd[2]);函数的参数是含有两个元素的整型数组
返回值返回:成功返回0出错返回-1
fd参数返回两个文件描述符,fd[0]指向管道的读端read(fd[0],-,-)
其中read()函数如果读不到东西会阻塞。
fd[1]指向管道的写端,write(fd[1],-,-)。向管道文件读写数据其实
是在读写内核缓冲区。要关闭管道只需要将读端和写端close掉即可。fd[1]的写出是fd[0]的读入。0对应标准输入1对应标准输出一样。
默认标准输入对应的设备是键盘标准输出和标准错误对应的是显示器
linux下一起皆文件设备一定是文件文件不一定是设备标准输入
和标准输出就不是文件他们是链接文件什么是链接文件文件内容
是另一个文件的地址的文件称为链接文件。标准输入、标准输出解释
父进程给子进程信息
#include unistd.h
#includestdio.h
#include sys/types.h
#include sys/wait.h
#includestring.h
#includestdlib.h
int main()
{int fd[2];int status;pid_t fpid;char* writebufNULL;writebuf(char*)malloc(1024);printf(请输入父进程要传递给子进程的消息:\n);scanf(%[^\n],writebuf);if(pipe(fd)-1){printf(creat file fail\n);}fpidfork();if(fpid0){printf(创建子进程失败\n);perror(fork);}else if(fpid0){close(fd[0]);write(fd[1],writebuf,strlen(writebuf));waitpid(fpid,status,0);if(WIFEXITED(status)){printf(子进程正常结束,状态值是:%d\n,WEXITSTATUS(status));}close(fd[1]);}else{close(fd[1]);char* readbufNULL;int n_read;readbuf(char*)malloc(strlen(writebuf));n_readread(fd[0],readbuf,strlen(writebuf));printf(子进程得到数据:%s,字节数是:%d\n,readbuf,n_read);close(fd[0]);}return 0;
}这个程序起初当输入字符串中间有空格时子进程只会收到空格之前的字符串
问题诊断scanf()遇到空格、回车、Tab则认为输入结束后面的就不会当做输入了
解决方法%s换为%[^\n](^有非的意思即不遇到\n不停止)或者用gets()scanf输入字符串的时候不会接收Space空格回车EnterTab键则认为输入结束。
scanf函数直接从输入缓冲区中取数据而并非从键盘(也就是终端)缓冲区读取。1、空格空格键产生的字符ascii码十进制32
2、空字符字符串结束标志‘\0’为被动添加ascii码十进制0
3、字符0ascii码十进制48子进程发送消息给父进程
#include unistd.h
#includestdio.h
#include sys/types.h
#include sys/wait.h
#includestring.h
#includestdlib.h
int main()
{int fd[2];int status;pid_t fpid;char* writebufNULL;writebuf(char*)malloc(1024);printf(请输入父进程要传递给子进程的消息:\n);scanf(%[^\n],writebuf);if(pipe(fd)-1){printf(creat file fail\n);}fpidfork();if(fpid0){printf(创建子进程失败\n);perror(fork);}else if(fpid0){sleep(1);close(fd[1]);char* readbufNULL;int n_read;readbuf(char*)malloc(strlen(writebuf));n_readread(fd[0],readbuf,strlen(writebuf));waitpid(fpid,status,0);printf(父进程得到数据:%s,字节数是:%d\n,readbuf,n_read);if(WIFEXITED(status)){printf(子进程正常结束,状态值是:%d\n,WEXITSTATUS(status));}}else{close(fd[0]);write(fd[1],writebuf,strlen(writebuf));}return 0;
}memcpy()函数介绍、scanf输入字符串遇到空格
FIEO有名管道半双工
无名管道由于没有名字只能用于亲缘关系的进程间通信。为了克服这个缺点提出了命名管道(FIFO)也叫有名管道、FIFO 文件。命名管道FIFO不同于无名管道之处在于它提供了一个路径名与之关联以 FIFO 的文件形式存在于文件系统中这样即使与 FIFO 的创建进程不存在亲缘关系的进程只要可以访问该路径就能够彼此通过 FIFO 相互通信因此通过 FIFO 不相关的进程也能交换数据。
命名管道FIFO)和无名管道pipe有一些特点是相同的不一样的地方在于:
FIFO 在文件系统中作为一个特殊的文件而存在但 FIFO 中的内容却存放在内存中FIFO文件在磁盘上没有数据块仅仅用来标识内核中一条通道。各进程可以打开这个文件进行read/write实际上是在读写内核通道这样就实现了进程间通信。当使用 FIFO 的进程退出后FIFO 文件将继续保存在文件系统中以便以后使用。FIFO 有名字不相关的进程可以通过打开命名管道进行通信。另外使用统一fifo文件可以有多个读端和多个写端。
创建有名管道
#include sys/types.h
#include sys/stat.h
int mkfifo(const char *pathname, mode_t mode);pathname: 普通的路径名就是创建的管道的位置也就是创建后 FIFO 的名字。
mode: 文件的权限与打开普通文件的 open() 函数中的 mode 参数相同返回值成功0失败如果文件已经存在则会出错且返回 -1。如果因为文件存在引起的错误则会返回-1并将errno的值赋EEXIST可以用下面的代码找出其他的出错原因
if(mkfifo(./fifodir,0666)-1errno!EEXIST){printf(管道创建失败\n);perror(because);
}注意 当进程对命名管道的使用结束后命名管道依然存在于文件系统中除非对其进行删除操作。命名管道的数据读取后也会消失不能反复读取即且严格遵循先进先出的规则。因此每次命名管道文件使用完后其大小为0字节不会产生中间临时文件。
命名管道的默认操作
后期的操作把这个命名管道当做普通文件一样进行操作open()、write()、read()、close()。但是和无名管道一样操作命名管道肯定要考虑默认情况下其阻塞特性。下面验证的是默认情况下的特点即 open() 的时候没有指定非阻塞标志( O_NONBLOCK )。open() 以只读方式打开FIFO 时要阻塞到某个进程为写而打开此 FIFO。open() 以只写方式打开 FIFO 时要阻塞到某个进程为读而打开此 FIFO。简单一句话只读等着只写只写等着只读只有两个都执行到才会往下执行。在这里我们需要注意一点就是不能以 O_RDWR 方式打开管道文件这种行为是未定义的。倘若有一个进程以读写方式打开了某个管道那么该进程写入的数据又会被该进程本身读取而管道一般只用于进程间的单向数据通信。
管道打开方式
1只读且阻塞方式 open(const char *pathname, O_RDONLY);2只读且非阻塞方式 open(const char *pathname, O_RDONLY | O_NONBLOCK);3只写且阻塞方式 open(const char *pathname, O_WRONLY);4只写且非阻塞方式 open(const char *pathname, O_WRONLY | O_NONBLOCK);
读端代码
#includestdio.h
#include unistd.h
#include sys/types.h
#include sys/stat.h
#include fcntl.h
#includeerrno.h
#includestring.h
int main()
{char readbuf[30];int n_read;int fd;memset(readbuf,\0,30);if(mkfifo(./fifodir,0600)-1errno!EEXIST){printf(mkfifo fail\n);perror(mkfifo);}fdopen(./fifodir,O_RDONLY);while(1){n_readread(fd,readbuf,30);printf(读取到%d个字节,内容是%s\n,(int)strlen(readbuf),readbuf);}close(fd);return 0;
}写端代码
includestdio.h
#include unistd.h
#include sys/types.h
#include sys/stat.h
#include fcntl.h
#includeerrno.hint main()
{int fd;fdopen(./fifodir,O_WRONLY);while(1){write(fd,hello reader,20);sleep(1);}close(fd);return 0;
}消息队列、消息队列参数详解
什么是消息队列
消息队列是消息的链表存放在内核中并由消息队列标识符表示。消息队列提供了一个从一个进程向另一个进程发送数据块的方法每个数据块都可以被认为是有一个类型接受者接受的数据块可以有不同的类型。但是同管道类似它有一个不足就是每个消息的最大长度是有上限的(MSGMAX)每个消息队列的总的字节数(MSGMNB)系统上消息队列的总数上限(MSGMNI)。可以用cat /proc/sys/kernel/msgmax查看具体的数据。
特点
消息队列是面向记录的其中的消息具有特定的格式以及优先级消息队列独立于发送与接收进程进程终止时消息队列及其内容并不会被删除生命周期随内核消息队列会一直存在需要我们显示的调用接口删除或使用命令删除。消息队列可以实现消息的随机查询消息不一定要以先进先出的次序读取也可以按消息的类型读取。克服了管道只能承载无格式字节流的缺点消息队列可以双向通信
对于消息队列要知道如何创建一个消息队列如何将消息添加到消息队列如何从消息队列读取信息
ftok函数
#include sys/types.h
#include sys/ipc.h
key_t ftok(const char *pathname, int proj_id);fname就是你指定的文件名已经存在的文件名一般使用当前目录如
key_t key;
key ftok(., 1); 这样就是将fname设为当前目录。
id是子序号。虽然是int类型但是只使用8bits(1-255。
在一般的UNIX实现中是将文件的索引节点号取出前面加上子序号得到key_t的返回值。
如指定文件的索引节点号为65538换算成16进制为0x010002而你指定的ID值为38换算成16进制为0x26则最后的key_t返回值为0x26010002。
当删除重建文件后索引节点号由操作系统根据当时文件系统的使用情况分配因此与原来不同所以得到的索引节点号也不同。调用成功返回一个key值用于创建消息队列如果失败返回-1键值和消息队列标识符的关系
在创建一个消息队列其他ipc相同时需要先通过文件路径名和项目ID获取一个键值
然后通过此
键值由内核生成标识符在以后可通过此标识符来使用此消息队列。查询文件索引节点号的方法是
ls -i
ls -al//显示所有文件的所以值包括隐藏文件
执行结果
fhnubuntu:~/jincheng/communication$ ls -i
932272 fifo 932283 msgread 932262 msgsend.c 932274 read 932250 write.c
932256 fifo.c 932263 msgread.c 932265 nomamepipe 932279 read.c 932267 znomamepipe
932229 fifodir 932280 msgsend 932236 nomamepipe.c 932275 write 932248 znomamepipe.cmsgget函数
#include sys/types.h
#include sys/ipc.h
#include sys/msg.h
int msgget(key_t key, int msgflag);功能创建和访问一个消息队列
key某个消息队列的名字用ftok()产生
msgflag有两个选项IPC_CREAT和IPC_EXCL单独使用IPC_CREAT如果消息队列不存在则创建之
如果存在则打开返回单独使用IPC_EXCL是没有意义的两个同时使用如果消息队列不存在则创建之
如果存在则出错返回。如果将key值设为IPC_PRIVATE则创建私有的消息队列只能有一个进程访问。
msgflag由九个权限标志构成,如0644,它们的用法和创建文件时使用的mode模式标志是一样的(但是消息队列没有x(执行)权限)返回值成功返回一个非负整数即消息队列的标识码失败返回-1为什么要有键值和标识符两个值呢 标识符是对于用户操作而言的让用户感觉操作和对文件的操作相同键是对于系统内部说的。 我们使用ftok来创建键值具体你可以man一下fotk函数大概是这样的按给定的路径名取得其stat结构从该结构中取出部分st_dev和st_ino字段然后再与项目id组合起来如果两个路径名引用两个不同的文件那么对这两个路径名调用ftok通常返回不同的键但是因为i节点号和键通常都存放在长整型中于是创建键时可能会丢失信息这意味着如果使用同一项目id那么对于不同文件的两个路径名可能产生相同的键。而标识符是唯一确定的可以用来区别于其他ipc的。
msgsnd函数
#include sys/types.h
#include sys/ipc.h
#include sys/msg.h
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);功能把一条消息添加到消息队列中
参数
msgid由msgget函数返回的消息队列标识码
msgp指针指向准备发送的消息
msgzemsgp指向的消息的长度不包括消息类型的long int长整型
msgflg默认为0
返回值成功返回0失败返回-1消息结构一方面必须小于系统规定的上限另一方面必须以一个long int长整型开始接受者以此来确定消息的类型struct msgbuf {long mtype; /* message type, must be 0 */char mtext[1]; /* message data */};消息队列内一个节点类型如下
struct msq_Node
{ Type msq_type; //类型 Length msg_len; //长度 Data msg_data; //数据 struct msg_Node *next;
}; msgrcv函数
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
msgid由msgget函数返回的消息队列标识码
msgp指针指向准备接收的消息
msgzemsgp指向的要存消息内存的长度不包括消息类型的long int长整型
msgtyp:msgtyp0返回队列中第一个消息msgtyp0返回队列中消息类型为msgtyp的第一个消息msgtyp0返回队列中消息类型小于或等于msgtyp绝对值的消息如果有多个则取类型值最小的消息。
可以看出msgtyp值非0时用以非先进先出次序读消息也可以把msgtyp看做优先级的权值。
msgflg默认为0阻塞式接收消息没有该类型的消息msgrcv函数一直阻塞等待
功能是从一个消息队列接受消息
返回值成功返回实际放到接收缓冲区里去的字符个数失败返回-1msgctl函数
#include sys/types.h
#include sys/ipc.h
#include sys/msg.h
int msgctl(int msqid, int cmd, struct msqid_ds *buf);功能消息队列的控制函数 第一个参数msgqid 是消息队列对象的标识符。
第二个参数是函数要对消息队列进行的操作它可以是
IPC_STAT取出系统保存的消息队列的msqid_ds 数据并将其存入参数buf 指向的msqid_ds 结构中。
IPC_SET设定消息队列的msqid_ds 数据中的msg_perm 成员。设定的值由buf 指向的msqid_ds结构给出。
IPC_RMID将队列从系统内核中删除,此时第三个参数设为NULL。
这三个命令的功能都是明显的所以就不多解释了。唯一需要强调的是在IPC_STAT命令中队列的msqid_ds 数据中唯一能被设定的只msg_perm 成员其是ipc_perm 类型的数据。而ipc_perm 中能被修改的只有mode,pid 和uid 成员。其他的都是只能由系统来设定的。成功返回0失败返回-1消息队列需要手动删除IPC资源
linux下消息队列的查看与删除ipcsipcrm的使用
消息队列接收端
#includestdio.h
#include sys/types.h
#include sys/ipc.h
#include sys/msg.h
#includestring.h
typedef struct msgbuf
{long mtye;char mtext[128];
}MSG,*PMSG;
int main()
{key_t key;int msgid;MSG readbuf;MSG sendbuf;sendbuf.mtye666;strcpy(sendbuf.mtext,hello sender,i have receive your msg);keyftok(.,30);msgidmsgget(key,IPC_CREAT|0600);//flage使用IPC_CREAT表示若消息队列不存在则创建存在则打开后返回//0600是在没有消息队列时创建消息队列的权限和文件那里那个权限一样if(msgid-1){printf(make fail\n);perror(why);}memset(readbuf.mtext,\0,128);msgrcv(msgid,readbuf,sizeof(readbuf.mtext),777,0);printf(msgrcv得到消息:%s\n,readbuf.mtext);msgsnd(msgid,sendbuf,strlen(sendbuf.mtext),0);return 0;
}
消息队列发送端
#includestdio.h
#include sys/types.h
#include sys/ipc.h
#include sys/msg.h
#includestring.h
typedef struct msgbuf
{long mtye;char mtext[128];
}MSG,*PMSG;
int main()
{key_t key;int msgid;MSG sendbuf;MSG readbuf;sendbuf.mtye777;strcpy(sendbuf.mtext,hello i am sender);keyftok(.,30);msgidmsgget(key,IPC_CREAT|0600);//flage使用IPC_CREAT表示若消息队列不存在则创建存在则打开后返回//0600是在没有消息队列时创建消息队列的权限和文件那里那个权限一样if(msgid-1){printf(make fail\n);perror(why);}msgsnd(msgid,sendbuf,strlen(sendbuf.mtext),0);printf(msgsnd发送消息完成:%s\n,sendbuf.mtext);msgrcv(msgid,readbuf,sizeof(readbuf.mtext),666,0);printf(msgsnd读到消息:%s\n,readbuf.mtext);msgctl(msgid,IPC_RMID,NULL);//等同于在命令行删除return 0;
}