首页 理论教育 Linux系统编程:进程间通信实践

Linux系统编程:进程间通信实践

时间:2023-11-18 理论教育 版权反馈
【摘要】:功能:管道是一种把两个进程之间的标准输入和标准输出连接起来的机制,从而提供一种让多个进程间通信的方法。

Linux系统编程:进程间通信实践

●实验目的:

学会进程间通信方式:无名管道、有名管道、信号、消息队列。

●实验要求:

在父进程中创建一无名管道,并创建子进程来读该管道,父进程来写该管道;

在进程中为SIGBUS注册处理函数,并向该进程发送SIGBUS信号;

创建一消息队列,实现向队列中存放数据和读取数据。

●实验器材:

软件:安装了Linux的vmware虚拟机

硬件:PC机一台。

●实验步骤:

(1)无名管道的使用。

①编写实验代码pipe_rw.c。

②编译应用程序pipe_rw.c。

③运行应用程序。

子进程先睡2秒,让父进程先运行,父进程分两次写入“hello”和“pipe”,然后阻塞等待子进程退出,子进程醒来后读出管道里的内容并打印到屏幕上再退出,父进程捕获到子进程退出后也退出。

④由于fork函数让子进程完整地拷贝了父进程的整个地址空间,所以父子进程都有管道的读端和写端。我们往往希望父子进程中的一个进程写一个进程读,那么写的进程最后关掉读端,读的进程最好关闭掉写端。

(2)信号处理

①编写实验代码sig_bus.c。

用signal系统调用为SIGBUS信号注册信号处理函数my_func,然后将进程挂起等待SIGBUS信号。所以需要向该进程发送SIGBUS信号才会执行自定义的信号处理函数。

②编译应用程序sig_bus.c。

③运行应用程序。

先在一个终端中运行sig_bus,会看到进程挂起,等待信号。

然后在另一个终端中,查找到运行sig_bus这个产生的进程号,用kill命令发送SIGBUS信号给这个进程。

可以看到前面挂起的进程在接收到这个信号后的处理。

用自定义信号处理函数my_func来处理,所以打印了“I have get SIGBUS”这样一句话。

●上机报告要求:

1.总结pipe()、signal()的函数定义原型、返回值和参数的意义。

(1)pipe()。

表头文件:#include<unistd.h>

定义函数:int pipe(int filedes[2]);

函数说明(参数):pipe()会建立管道,并将文件描述词由参数filedes数组返回。filedes[0]为管道里的读取端,filedes[1]则为管道的写入端。

返回值:若成功则返回0,否则返回-1,错误原因存于errno中。

阻塞问题:当管道中的数据被读取后,管道为空。一个随后的read()调用将默认被阻塞,等待某些数据写入。(www.xing528.com)

功能:管道是一种把两个进程之间的标准输入和标准输出连接起来的机制,从而提供一种让多个进程间通信的方法。当进程创建管道时,每次都需要提供两个文件描述符来操作管道。其中一个对管道进行写操作,另一个对管道进行读操作。对管道的读写与一般的IO系统函数一致,使用write()函数写入数据,使用read()读出数据。

(2)signal()。

表头文件:#include<signal.h>

功能:设置某一信号的对应动作。

函数原型:void (*signal(int signum, void(* handler)(int)))(int);

或者:typedef void(*sig_t) (int);sig_t signal(int signum, sig_t handler);

可看成是signal()函数(它自己是带有两个参数,一个为整型,一个为函数指针的函数),而这个signal()函数的返回值也为一个函数指针,这个函数指针指向一个带整型参数,并且返回值为void的一个函数。

参数说明:第一个参数signum指明了所要处理的信号类型,它可以取除了SIGKILL和SIGSTOP外的任何一种信号。

第二个参数handler描述了与信号关联的动作,它可以取以下三种值:

①一个返回值为正数的函数地址。

此函数必须在signal()被调用前声明,handler中为这个函数的名字。当接收到一个类型为sig的信号时,就执行handler 所指定的函数。这个函数应有如下形式的定义:intfunc(int sig);

sig是传递给它的唯一参数。执行了signal()调用后,进程只要接收到类型为sig的信号,不管其正在执行程序的哪一部分,就立即执行func()函数。当func()函数执行结束后,控制权返回进程被中断的那一点继续执行。

②SIGIGN。

这个符号表示忽略该信号。执行了相应的signal()调用后,进程会忽略类型为sig的信号。

③SIGDFL。

这个符号表示恢复系统对信号的默认处理。

函数说明:signal()会依参数signum 指定的信号编号来设置该信号的处理函数。当指定的信号到达时就会跳转到参数handler指定的函数执行。当一个信号的信号处理函数执行时,如果进程又接收到了该信号,该信号会自动被储存而不会中断信号处理函数的执行,直到信号处理函数执行完毕再重新调用相应的处理函数。但是如果在信号处理函数执行时进程收到了其他类型的信号,该函数的执行就会被中断。

返回值:返回先前的信号处理函数指针,若有错误则返回SIG_ERR(-1)。

附加说明:在信号发生跳转到自定的handler处理函数执行后,系统会自动将此处理函数换回原来系统预设的处理方式,如果要改变此操作请改用sigaction()。

下面的情况可以产生Signal:

a.按下CTRL+C产生SIGINT。

b.硬件中断,如除0,非法内存访问(SIGSEV),等等。

c.Kill函数可以对进程发送Signal。

d.Kill命令。实际上是对Kill函数的一个包装。

e.软件中断。如当Alarm Clock超时(SIGURG),当Reader中止之后又向管道写数据(SIGPIPE),等等。

2.命名管道FIFO

功能:管道最大的劣势就是没有名字,只能用于有一个共同祖先进程的各个进程之间。FIFO代表先进先出,但它是一个单向数据流,也就是半双工,与管道不同的是:每个FIFO都有一个路径与之关联,从而允许无亲缘关系的进程访问。

头文件:#include <sys/types.h> #include <sys/stat.h>

函数原型:int mkfifo(const char *pathname, mode_t mode);

参数:这里pathname是路径名,mode是sys/stat.h里面定义的创建文件的权限。

利用命名管道FIFO实现类似本节第一个实验的功能。一个程序fifo_read.c写数据“Hi Linux”,另一个程序fifo_write.c读数据并打印出来。

免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。

我要反馈