实验三 Linux进程间通信
一、实验目的
熟悉Linux下进程间通信机制,能够使用系统提供的各种通信机制实现并发进程间的数据交换。
二、实验题目
分别使用Linux下的共享存储区、消息、管道等通信机制,编程实现并发进程之间的相互通信。
三、背景材料
(一)需要用到的系统调用
实验可能需要用到的主要系统调用和库函数在下面列出,详细的使用方法说明通过“man 2 系统调用名”或者“man 3 函数名”命令获取。
fork() 创建一个子进程,通过返回值区分是在父进程还是子进程中执行; wait() 等待子进程执行完成;
getpid() 获取当前进程id;
shmget() 建立一个共享存储区;
shmctl() 操纵一个共享存储区;
shmat() 把一个共享存储区附接到进程内存空间;
shmdt() 把一个已经附接的共享存储区从进程内存空间断开; msgget() 建立一个消息队列;
msgctl() 操纵一个消息队列;
msgsnd() 发送消息;
msgrcv() 接收消息;
signal() 设置对信号的处理方式或处理过程;
pipe() 打开管道;
lockf() 锁定一个文件。
(二)使用共享存储区的示例程序
下面程序主要用来演示共享存储区的使用方法:首先要使用shmget得到共享存储区句柄(可以新建或连接已有的共享存储区,以关键字标识),然后使用shmat挂接到进程的存储空间(这样才能够访问),当使用完后,使用shmctl释放(shmctl还可以完成一些其他功能)。这种使用逻辑也适用于消息和信号量。示例程序代码如下:
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
int main(void)
{
int x, shmid;
int *shmptr;
if((shmid=shmget(IPC_PRIVATE, sizeof(int), IPC_CREAT|0666)) < 0)
printf("shmget error"), exit(1); if((shmptr=(int *)shmat(shmid, 0, 0)) == (int *)-1)
printf("shmat error"), exit(1);
printf("Input a initial value for *shmptr: ");
scanf("%d", shmptr);
while((x=fork())==-1);
if(x==0) /* child run */
{
printf("When child runs, *shmptr=%d\n", *shmptr);
printf("Input a value in child: ");
scanf("%d", shmptr);
printf("*shmptr=%d\n", *shmptr);
}
else /* parent run */
{
wait();
printf("After child runs, in parent, *shmptr=%d\n", *shmptr);
if ( shmctl(shmid, IPC_RMID, 0) < 0 )
printf("shmctl error"), exit(1);
}
}
(三)使用消息机制的示例程序
示例程序执行的进程分为两种,分别称为服务进程和客户进程:服务进程只有一个,接收各客户进程以消息形式发出的问题,接收键盘输入作为回答,再以消息形式送给提问的进程。各客户进程接收键盘输入作为问题,以消息形式发给服务进程,等待接收服务进程发来的回答消息,再开始下一轮的循环。示例程序的实际执行效果如下:
编译后执行,第一个进程实例将作为服务进程,提示:
ACT SERVER!!! Wait question and give answer.
To end this process, try Ctrl+C or use kill.
服务进程一直循环执行,直到用户按Ctrl+C终止执行,或使用kill命令杀死服务进程。 其他进程实例作为客户进程,提示:
Act as client, ask question and wait answer!
To end this process, enter end as input question.
客户进程一直循环执行,直到用户输入end。
示例程序的代码如下:
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#define MY_KEY 10071140 // need to change
#define SERVER_ID 2
#define MAX_BUF 200
void sigend(int);
struct mymsg {
long mtype;
long pid;
char buf[MAX_BUF];
};
int msgid;
int main(void)
{
struct mymsg msgbuf;
if((msgid=msgget(MY_KEY, IPC_CREAT|IPC_EXCL|0666)) < 0 ) { /* message queue exists, act as client */
msgid=msgget(MY_KEY, 0666);
printf("Act as client, ask question and wait answer!\n");
printf("To end this process, enter end as input question.\n\n"); printf("Input question in one line:\n");
fgets(msgbuf.buf, sizeof(struct mymsg)-2*sizeof(long)-1, stdin); while(strcmp(msgbuf.buf,"end\n"))
{
msgbuf.mtype=SERVER_ID;
msgbuf.pid=getpid();
msgsnd(msgid, &msgbuf, sizeof(struct mymsg)-sizeof(long), 0); msgrcv(msgid, &msgbuf, sizeof(struct mymsg), getpid(), 0); printf("Answer from server: \n%s", msgbuf.buf);
printf("Input question in one line:\n");
fgets(msgbuf.buf, sizeof(struct mymsg)-2*sizeof(long)-1, stdin); }
}
else /* acts as server */
{
signal(SIGINT, sigend);
signal(SIGTERM, sigend);
printf("ACT SERVER!!! Wait question and give answer.\n"); printf("To end this process, try Ctrl+C or use kill.\n\n");
while(1)
{
msgrcv(msgid, &msgbuf, sizeof(struct mymsg), SERVER_ID, 0); printf("Question from %d: \n%sGive answer:\n", (int)msgbuf.pid, msgbuf.buf);
fgets(msgbuf.buf, sizeof(struct mymsg)-2*sizeof(long)-1, stdin);
msgbuf.mtype=msgbuf.pid;
msgsnd(msgid, &msgbuf, sizeof(struct mymsg)-sizeof(long), 0);
}
}
}
void sigend(int sig)
{
msgctl(msgid, IPC_RMID, 0);
exit(0);
}
(四)使用管道机制的示例程序
以下示例程序摘自pipe系统调用的man手册页,因一些Linux发行版相应man手册页丢失,故摘录如下:
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int main(int argc, char *argv[])
{
int pipefd[2];
pid_t cpid;
char buf;
if (argc != 2) {
fprintf(stderr, "Usage: %s <string>\n", argv[0]);
exit(EXIT_FAILURE);
}
if (pipe(pipefd) == -1) {
perror("pipe");
exit(EXIT_FAILURE);
}
cpid = fork();
if (cpid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
if (cpid == 0) { /* Child reads from pipe */
close(pipefd[1]); /* Close unused write end */
while (read(pipefd[0], &buf, 1) > 0)
write(STDOUT_FILENO, &buf, 1);
write(STDOUT_FILENO, "\n", 1);
close(pipefd[0]);
_exit(EXIT_SUCCESS);
} else { /* Parent writes argv[1] to pipe */
close(pipefd[0]); /* Close unused read end */
write(pipefd[1], argv[1], strlen(argv[1]));
close(pipefd[1]); /* Reader will see EOF */
wait(NULL); /* Wait for child */
exit(EXIT_SUCCESS);
}
}
四、实验内容
本实验要求内容如下:
1、使用消息机制进行进程通信
实现使用消息机制的示例程序功能,改造该程序的结构,使服务器进程和客户进程执行不同的程序;服务器程序的多次执行应报错退出(保证服务器进程唯一)。
2、使用共享存储区进行通信
使用共享存储区作为通信机制,实现如下通信功能:服务进程和客户进程通过共享存储区交换一个整数;服务进程首先等待客户进程改变共享存储区的数值,显示该数值后接收键盘输入整数,修改共享存储区内容。客户进程接收键盘输入整数(如果输入0则退出),修改共享存储区内容,然后等待服务进程改变整数值后,再开始下一轮的循环。
3、使用管道进行通信
编写程序建立一个无名管道,然后生成3个子进程,使这4个进程利用同一个管道进行通信。分别试验3写1读、2写2读情况,对记录的执行结果进行解释。
五、实验报告书写要求
应在实验报告中说明如下事项:
1、相关程序的名称及存储位置,以便指导教师抽查;
2、实验内容1中自己修改部分的描述,执行结果的描述和分析;
3、实验内容2的执行结果描述及分析;
4、实验内容3的功能设计描述,执行结果的描述和分析。
第二篇:实验五 Linux进程间通信05010501
实验五 Linux进程间通信
【实验目的】
掌握管道通信程序的基本编写方法
掌握信号通信程序的基本编写方法
掌握信号量通信程序的编写方法
掌握共享内存通信程序的编写方法
掌握消息队列通信程序的编写方法
【实验学时】
建议2学时
【实验内容】
1、了解什么是管道,熟悉LINUX支持的管道通信方式
2、了解什么是信号,熟悉LINUX系统中进程之间软中断通信的基本原理。
【实验原理】
见教材第四章
【实验要求】
调试验证程序,并提交实验报告。
【实验步骤】
(1) 打开目录4-1-0-ipc,将其中的produce-core-dump.c文件编译生成可执行文件。输入:Ulimit –c 1024命令,使程序出错后可生成core dump文件,运行produce-core-dump的可执行文件,观察结果。
(2) 打开目录4-2-1-ipc-pipe和4-2-2-ipc-standard-pipe,编译并运行后,观察运行结果。打开目录4-2-3-ipc-fifo-pipe,在PC机Fedora中编译并运行后,观察运行结果。将程序交叉编译后,在开发板的控制终端与PC机Fedora的控制终端中分别运行读写管道程序,观察运行结果。
(3) 打开目录4-3-1-ipc-kill-raise、4-3-2-ipc-alarm-pause、4-3-3-1-ipc-signal、4-3-3-2-ipc-signalaction和4-3-3-3-ipc-signaleset,编译并运行程序,观察运行结果。
(4) 打开目录4-4-ipc-sem编译并运行程序,观察运行结果。
(5) 打开目录4-5-ipc-shmem编译并运行程序,观察运行结果。
(6) 打开目录4-6-ipc-msg编译并运行程序,观察运行结果。
(7) 打开目录4-7-1-ipc编译并运行程序,观察运行结果。
(8) 打开目录4-7-2-ipc编译并运行程序,观察运行结果。
(9) 将上面程序交叉编译后,在开发板上运行,观察运行结果。
【思考题】
思考题1. 管道通信
编制一段程序,实现进程的管道通信。使用pipe()建立一条管道线。两个子进程p1和p2分别向管道各写一句话:
Child 1 is sending message!
Child 2 is sending message!
而父进程则从管道中读出来自于两个子进程的信息,显示在屏幕上。
<参考程序>
# include<unistd.h>
# include<signal.h>
# include<stdio.h>
int pid1,pid2;
main()
{
int fd[2];
char OutPipe[100],InPipe[100];
pipe(fd);
while((pid1=fork())== -1);
if(pid1 == 0)
{
lockf(fd[1],1,0);
sprintf(OutPipe, "child 1 process is sending message!");
write(fd[1],OutPipe,50);
sleep(5);
lockf(fd[1],0,0);
exit(0);
}
else{
while((pid2=fork())== -1);
if(pid2 == 0)
{
lockf(fd[1],1,0);
sprintf(OutPipe, "child 2 process is sending message!");
write(fd[1],OutPipe,50);
sleep(5);
lockf(fd[1],0,0);
exit(0);
}
else{
wait(0);
read(fd[0],InPipe,50);
printf("%s\n",InPipe);
wait(0);
read(fd[0],InPipe,50);
printf("%s\n",InPipe);
exit(0);
}
}
}
实验要求:运行程序并分析结果,将上面程序修改成,在父进程中用pipe()建立一条管道,往管道里写字符串,两个子进程分别接收父进程往管道里写的字符串。
思考题2. 使用信号在进程中通信
编写一段程序,使用系统调用fork( )创建两个子进程,再用系统调用signal( )让父进 程捕捉键盘上来的中断信号(即按ctrl+c键),当捕捉到中断信号后,父进程用系统调用kill( )向两个子进程发出信号,子进程捕捉到信号后,分别输出下列信息后终止:
Child process 1 is killed by parent!
Child process 2 is killed by parent!
父进程等待两个子进程终止后,输出以下信息后终止:
Parent process is killed!
<参考程序>
/***********************************************************
功能说明:进程通信singnal()方法的应用
author: lilan
***********************************************************/
#include<stdio.h>
#include<signal.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
int wait_mark = 0;
void waiting(),stop();
int main(){
int p1, p2;
signal(SIGINT,stop);//捕捉到中断信号ctrl-c后,调用stop函数
while((p1=fork())== -1);
//int status1 = -1;
//int status2 = -1;
if(p1>0){/*在父进程中*/
//signal(SIGINT,stop);//①
while( (p2=fork()) == -1 );
if( p2>0 ){/*在父进程中*/
//signal(SIGINT,stop);//②
wait_mark=1;
waiting();
kill(p1,10);
kill(p2,12);
wait(0);//父进程执行到此,马上阻塞自己,直到有子进程结束。当发现有子进程结束时,就会回收它的资源。
wait(0);
//wait(&status1);
//wait(&status2);
//printf("child1 exit status is %d\n", WEXITSTATUS(status1)); //printf("child2 exit status is %d\n", WEXITSTATUS(status2)); printf("parent process is killed!\n");
exit(0);
}
else{ /*在子进程2中*/
wait_mark=1;
signal(12,stop); //捕捉到中断信号12后,调用stop函数
waiting();
lockf(1,1,0);
//printf("this is child 2, pid = %d , ppid = %d\n",getpid(),getppid() );
printf("child process 2 is killed by parent!\n");
lockf(1,0,0);
exit(0);
//exit(5);
}// end of if (p2>0)
}
else{ /*在子进程1中*/
wait_mark=1;
signal(10,stop);//捕捉到中断信号10后,调用stop函数
waiting();
lockf(1,1,0);
//printf("this is child 1, pid = %d , ppid = %d\n",getpid(),getppid() ); printf("child process 1 is killed by parent!\n");
lockf(1,0,0);
exit(0);
//exit(6);
}// end of if ( p1>0 )
exit(0);
}
void waiting(){
while(wait_mark!=0);
}
void stop(){
wait_mark=0;
}
(1)运行程序并分析结果。
(2)如果把signal(SIGINT,stop)放在①号和②号位置,结果会怎样并分析原因。
(3)该程序段前面部分用了两个wait(0),为什么?
(4)该程序段中每个进程退出时都用了语句exit(0),为什么?
(5)将红色部分去掉注释,蓝色部分加上注释,观察程序运行结果,并分析原因,进一步理解wait函数的使用!