管道函数:
#include “unistd.h”
int pipe(int p[2]) 返回:成功时为0,出错时为-1。
该文件返回两个文件描述字:p[0]—打开读,p[1]—打开写.管道的读写和文件的读写一样。例:
共享内存系统调用函数:
以下函数的原形在:sys/types.h 、sys/ipc.h和sys/shm.h包含文件中。
(1)创建/获取内存区:
int shmget(key_t key,int size,int shmflag);
key—关键字,必须是本地唯一的。
size—共享内存大小(字节),内核会把这个数值向上舍入取最近的虚拟内存帧的大小。
shnflg—标志参数,指定选项及其权限位。
IPC_CREAT—创建新的信号量集
IPC_EXCEL—如果信号量集已经存在,则返回错误。
<XXX XXX XXX>-- 和文件、目录一样权限。
返回一个共享内存ID--shmid
语法格式:shmid=shmget(key,size,flag)
(2)连接共享内存:
把内存变量和共享内存连接起来,便于使用共享内存。
char *shmat(int shmid,char *shmaddr,int shmflag);
shmid—由shmget函数产生的共享内存ID;
shmaddr—字符型指针变量;
shmflg—共享内存标志参数,含义同上。
语法格式:virtaddr=shmat(id,addr,flag)
其中:id是共享存储区的标识符,addr是用户要使用共享存储区附接的虚地址,若addr是0,系统选择一个适当的地址来附接该共享区。flag规定对此区的读写权限,以及系统是否应对用户规定的地址做舍入操作。如果flag中设置了shm_rnd即表示操作系统在必要时舍去这个地址。如果设置了shm_rdonly,既表示只允许读操作。viraddr是附接的虚地址。
(3)分离共享内存
把内存指针变量和共享内存分离。
int shmdt(char *shmaddr);
shmaddr—内存指针变量;
其中,当调用成功时,返回0值,调用不成功,返回-1,addr是系统调用shmat所返回的地址。
(4)释放共享内存:
int shmctl(int shmid,int cmd,struct shmid_ds *buf);
其中:调用成功时返回0,否则返回-1。shmid为被共享存储区的标识符。cmd规定操作的类型。规定如下:
IPC_STAT: 返回包含在制定的shmid相关数据结构中的状态信息,并且把它放置在用户存储区中的*buf指针所指的数据结构中。执行此命令的进程必须有读取允许权。
IPC_SET: 对于指定的shmid,为它设置有效用户和小组标识和操作存取权。
IPC_RMID 删除制定的shmid以及与它相关的共享存储区的数据结构。
SHM_LOCK: 在内存中锁定指定的共享存储区,必须是超级用户才可以进行此操作。
BUF 是一个用户级数据结构地址。
范例:
消息队列系统调用函数:
以下函数的原形在:sys/types.h 、sys/ipc.h和sys/msg..h包含文件中。
(1) msgget函数用于创建一个新的消息队列或访问一个已存在的消息队列
int msgget(key_t key,int msgflag);
key:指定为常值IPC_PRIVATE或系统中唯一的关键字值。
msgflag: IPC_CREAT—创建消息队列
IPC_EXCL—检查消息队列是否存在
<权限>。
返回:成功时为非负标识符,出错时为一1。返回值是一个整数标识符,其他三个msg函数就用它来指代该队列。它是基于指定的key产生的。
(2)使用msgget打开一个消息队列后,我们使用msgsnd往其上放置一个消息。
Int msgsnd(int msgid,struct msgbuf *msgf,size_t nbytes,int flag);
其中:msgid—由msgget()创建函数返回。
*msgf—是一个结构指针,该结构具有如下的模板,
struct msgbuf{
long mtype;/*消息的类型*/
char mtext[1];/*消息的数据*/
};
在实际应用中,1个字节的消息是没有任何用途的,因此,用户可以根据自己的需定义消息结构的内容。但:
第一成员必须是long mtype; 必须进行强制类型转换: (struct msgbuf *)
nbytes—实际消息块的大小。这是位于长整数消息类型之后的用户自定义数据的长度sizeof(message)-sizeof(long)。
flag—选项标志位:可以是0,也可以是IPC-NOWAIT0— 不使用标志,PC-NOWAIT—表示当消息队列满时不等待,返回错误信息。
(3)使用msgrcv函数从某个消息队列中读出一个消息。
int msgrcv(int msgid,struct msgbuf *msgp,size_t nbytes,long type,int flag);
msgid—消息队列编号;
msgp—存放消息的内容;
nbyte—为信息数据的长度,即mtext参数的长度。
type—接受消息的类型;type =0返回队列内第一项信息;type>0返回队列内第一项与mtype相同的信息type<0返回队列内第一项mtype小于或等于mtype绝对值的信息。
flag—接受选项标志;0或IPC-NOWAIT
IPC-NOWAIT—表示没有消息不等待,返回错误
0—等待消息到来,进程挂起。
(4)删除消息队列:
int msgctl(int msgid,int cmd,struct msgid_ds *buf);
msgctl()提供了几种方式来控制信息队列的运作。参数msgid为欲处理的信息队列识别代码,参数cmd为欲控制的操作,有下列几种数值:
IPC__STAT 把信息队列的msqid—ds结构数据复制到参数buf.
IPC__SET 将参数buf所指的msqid—ds结构中的msqid.uid、msg_perm.gtd、msg_perm.mode和msg_qbytes参数复制到信息队列的msqid—ds结构内。
IPC__RMID 删除信息队列和其数据结构。
范例:
具体代码实现
- 编写一段程序,实现进程的管道通信。
使用系统调用pipe()建立一条管道线,两个子进程P1和P2分别向管道各写一句话:
child 1 is sending a message!
child 2 is sending a message!
而父进程则从管道中读出来自两个子进程的信息,并显示子进程号,显示在屏幕上。
#include<sys/types.h>
#include<sys/wait.h>
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include<stdlib.h>
int pid1, pid2;
int main() {
int fd[2];
int pid1, pid2;
char OutPipe[100], InPipe[100];
pipe(fd);
while ((pid1 = fork()) == -1);
if (pid1 == 0) {
printf("child process1 %d\n", getpid());
lockf(fd[1], 1, 0);
sprintf(OutPipe, "child1 is sending a message!");
write(fd[1], OutPipe, 50);
sleep(5);
lockf(fd[1], 0, 0);
exit(0);
}
else {
while ((pid2 = fork()) == -1);
if (pid2 == 0) {
printf("child process2 % d\n", getpid());
lockf(fd[1], 1, 0);
sprintf(OutPipe, "child2 is sending a message!");
write(fd[1], OutPipe, 50);
sleep(5);
lockf(fd[1], 0, 0);
exit(0);
}
else {
printf("parent process %d\n", getpid());
wait(0);
read(fd[0], InPipe, 50);
printf("%s\n", InPipe);
wait(0);
read(fd[0], InPipe, 50);
printf("%s\n", InPipe);
exit(0);
}
}
编写两个程序,分别通过共享内存和消息队列实现两个进程的信息交换:
//实现与rose的相互通信
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <errno.h>
#include <unistd.h>
#include <signal.h>
#include <wait.h>
#include <sys/msg.h>
#include <sys/types.h>
#include <sys/ipc.h>
//定义消息结构体
struct msgbuf
{
long mtype;
char mtext[64];
};
int main(int argc, char *argv[])
{
//定义key值
key_t key;
key = ftok(".", 10);
//根据key值申请消息队列ID号
int msgid;
msgid = msgget(key , IPC_CREAT|0666);
struct msgbuf buf;
pid_t pid = fork();
//父进程持续发信息
if(pid > 0)
{
printf("I'm Jack.\n");
while(1)
{
memset(buf.mtext,0,sizeof(buf.mtext));
//写标识是1的信息进队列
buf.mtype = 1;
fgets(buf.mtext,64,stdin);
msgsnd(msgid,&buf,strlen(buf.mtext),0);
//发送quit后,杀掉子进程,之后退出
if(strncmp(buf.mtext,"quit",4) == 0)
{
kill(pid,SIGKILL);
break;
}
}
wait(NULL);
}
//子进程持续读信息
if(pid == 0)
{
while(1)
{
memset(buf.mtext,0,sizeof(buf.mtext));
//从队列中读标识是2的信息
msgrcv(msgid,&buf,64,2,0);
printf("receive from rose:%s",buf.mtext);
//收到quit,杀掉父进程,之后退出
if(strncmp(buf.mtext,"quit",4) == 0)
{
kill(getppid(),SIGKILL);
break;
}
}
}
//删除队列信息
msgctl(msgid, IPC_RMID, NULL);
return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <errno.h>
#include <unistd.h>
#include <signal.h>
#include <wait.h>
#include <sys/msg.h>
#include <sys/types.h>
#include <sys/ipc.h>
//自定义消息结构体
struct msgbuf
{
long mtype;
char mtext[64];
};
int main(int argc, char *argv[])
{
//定义key值
key_t key;
key = ftok(".", 10);
//申请消息队列ID号
int msgid;
msgid = msgget(key , IPC_CREAT|0666);
struct msgbuf buf;
pid_t pid = fork();
//父进程持续读信息
if(pid > 0)
{
printf("I'm Rose.\n");
while(1)
{
memset(buf.mtext,0,sizeof(buf.mtext));
//从队列中读标识是1的信息
msgrcv(msgid,&buf,64,1,0);
printf("receive from jack:%s",buf.mtext);
//收到quit后,杀掉子进程,之后退出
if(strncmp(buf.mtext,"quit",4) == 0)
{
kill(pid,SIGKILL);
break;
}
}
wait(NULL);
}
//子进程持续发信息
if(pid == 0)
{
while(1)
{
memset(buf.mtext,0,sizeof(buf.mtext));
//写标识是2的信息进队列
buf.mtype = 2;
fgets(buf.mtext,64,stdin);
msgsnd(msgid,&buf,strlen(buf.mtext),0);
//发送quit后,杀掉父进程,之后退出
if(strncmp(buf.mtext,"quit",4) == 0)
{
kill(getppid(),SIGKILL);
break;
}
}
}
//删除队列消息
msgctl(msgid, IPC_RMID, NULL);
return 0;
}
总结
进程高级通讯有以下方式:
-
管道通讯:管道是一种半双工的通讯方式,分为匿名管道和命名管道。匿名管道只能在父子进程之间使用,而命名管道可在不同进程之间使用。
-
消息队列通讯:消息队列是一种异步通信的方式,通讯双方可以独立地进行读和写操作,无需等待对方的响应。消息队列支持多个读者和写者,适用于进程间较为复杂的通讯场景。
-
共享内存通讯:共享内存是一种高效的通讯方式,多个进程可以同时访问同一块内存区域。通讯双方可以直接在内存中进行数据传输,无需经过内核的拷贝操作,因此效率很高。
-
信号量通讯:信号量是一种用于进程间同步和互斥的机制。通过对共享资源的访问进行计数,可以实现多个进程之间对共享资源的互斥访问。信号量还可以用于进程间消息通知的场景。
-
socket通讯:socket是一种用于网络通讯的标准接口,也可以用于进程间通讯。在不同机器之间使用socket进行通讯,需要指定IP地址和端口号。
总之,选择合适的进程通讯方式需要根据具体的场景来决定,不同的通讯方式都有自己的特点和适用范围。