Linux进程间通信(6.5)

进程通信方式 


一.无名管道  


(1)创建无名管道 


#include <unistd.h>


int pipe(int pipefd[2]);
功能:创建无名管道 


参数:
pipefd 获得文件描述符,pipefd[0]:用于读管道,pipefd[1]:用于写管道
传参方法 int pfd[2];
if(pipe(pfd) < 0)


返回值:
成功返回0,失败返回-1,并置errno


注意:无名管道只能用于亲缘间进程通信(原因:亲缘间进程存在数据的拷贝)


练习:用无名管道实现,父子进程通信 
父亲进程:循环从键盘输入数据,然后写入管道 
子进程  :循环从管道中读取数据,然后打印
如果是"quit",父子进程退出 


3.管道的读写规则 


1.读端存在,写管道


管道空闲,则写入数据;管道满,写管道则阻塞


2.读端不存在,写管道 


此时写管道没有意义,内核会发送SIGPIPE信号,杀死写管道进程 


-----------------------------------------------------------------------  


1.写端存在,读管道 


管道中有数据 >= 要求读取数据  读取要求大小的数据 
管道中有数据 <  要求读取数据  读取管道中实际大小数据 
管道中没有数据,则读管道阻塞 


2.写端不存在,读管道 
管道中有数据,读取数据,管道中没有数据,读管道不阻塞,立即返回 0


虽然具有亲缘关系的进程同时拥用无名管道的读端和写端,
但是如果要实现两进程相互双向通信,需要使用两个无名管道。
注意一般利用管道进行读写时读进程先关写端,写进程先关闭读端。


二.有名管道
特点:
    1.在文件系统中存在文件名,不同的进程可通过管道文件名找到同一管道进行通信
    2.因为上条特点,可用于非亲缘关系进程间通信


A.创建有名管道
       #include <sys/types.h>
       #include <sys/stat.h>


       int mkfifo(const char *pathname, mode_t mode);
       功能:创建一个有名管道
       参数:
            pathname    管道文件创建路径
            mode        创建的管道文件权限 3位8进制数
        返回值:成功返回0,失败返回-1,并置errno


注意:如果有名管道己经存在,再次mkfifo(3)会失败,如下处理
if(mkfifo(path,0666) < 0 && errno != EEXIST)
{
    //出错处理
}




B.打开有名管道文件,利用open函数
注意:
1.如果只有一个进程以只读方式打开一个有名管道,则进程阻塞,直到另一进程以写方式打开
2.如果只有一个进程以只写方式打开一个有名管道,则进程阻塞,直到另一进程以读方式打开
3.如果以读写方式打开,不会阻塞


C.管道的读写:管道(有名,无名)有相同的读写规则.


练习:
*1.两个进程利用管道进行读写,A进程读,B进程写。B输入quit后,两进程退出。
(当B进程读stdin到文件结尾,两进程结束)
2.两终端通过两个有名管道进行聊天。需两对父子进程



有名管道与无名管道异同点:
相同:
1.都是基于文件描述符的的进程间通信方式。
2.都可以利用read(),write()函数等文件IO函数进行读写。与普通文件读写区别是管道中数据读完后就没有,数据只能读到一次
3.都不可使用lseek()进行定位操作。
4.数据都是先入先出FIFO。
5.读写都遵循管道读写规则。


不同:
1.无名管道只能用于具有亲缘关系的进程间通信;有名管道可用于并不相关的两个进程之间相互通信。


2.无名管道通过pipe函数创建并获得操作的一对文件描述符,具有亲缘关系的进程才能使用;
而有名管道是通过mkfifo函数创建,任何进程都可以根据有名管道文件名及其所在路径利用open()函数打开,
close()函数关闭,read()/write()读写。


3.无名管道只在内核中有一段缓冲区,只能通过pipe函数创建并获得读写的文件描述符;
而有名管道不光在内核中有一段缓冲区,而且在文件系统中有一个文件名,这个文件名对应了内核中的一段缓冲区。
与普通文本文件区别在于有名管道仅仅在文件系统中有一个名字,数据是存放在内存中(内核空间)。


4.无名管道创建时能获得一对用于读写的文件描述符,所以pipe()函数执行并不会阻塞;
而有名管道创建后,再打开时单独以一种方式打开(读或写),都会阻塞等待另一种方式打开,但以读写方式打开时不会阻塞。


三、信号
信号(软件中断信号)提供了一种进程间异步通信方式,可用于实现进程间的同步!
信号用于内核通知进程发生了异步事件,也可以用于不同进程相互的发送消息实现进程间通信。


1.一个完整的信号生命周期
可以分为三个阶段:1.信号产生;2信号发送或传递;3.信号注销和处理


2.Linux 下信号处理:进程对信号的响应方式
忽略信号:      对信号不做任何处理,但是有两个信号不能忽略:即SIGKILL及SIGSTOP。
捕捉信号:      定义并注册信号处理函数,当信号发生时,执行相应的处理函数。
执行缺省操作:  Linux对每种信号都规定了默认操作 


3.信号的使用
发送给进程的消息通常是一个数,即信号的编号,并用此标识区分不同信号。
操作系统中用一组前缀为SIG的宏来表示可以由内核或进程发送的信号


使用信号进行通信
    1.定义信号处理函数 
    2.向内核注册信号处理函数


    3.向指定进程发送对应信号


4.设置进程对信号的处理方式--注册信号处理函数

、、================================================================================/*{{{*/
void fun(int );//函数声明
void *fun1(int);//指针函数的声明
void (*pfun)(int) = fun;//函数指针变量定义

typedef int a;
typedef void (*pfun)(int);//给函数指针类型void (*)(int) 另起了类型别名pfun

pfun q;//void (*q)(int)
、、================================================================================/*}}}*/

    #include <signal.h>

    typedef void (*sighandler_t)(int);
    //信号处理函数的类型

    sighandler_t signal(int signum, sighandler_t handler);

    
    void (* signal(int signum, void (* handler )(int) )) (int);
    功能:注册信号处理函数
    参数:
            signum  注册处理方式的信号
            handler 信号处理方式
    
        handler可以传参的值有哪些
        SIG_IGN,忽略信号
        SIG_DFL,按照默认方式处理信号
        传自己实现信号处理函数的函数名,类型要求为void sig_handler(int signum);
    
    返回值:成功返回信号原处理方式对应函数指针值;失败返回函数指针SIG_ERR


注意:1.sighandler_t类型说明,确定信号处理函数的原型为,void (*sighandler_t)(int);
        sighandler函数的参数int为当内核给进程发送信号时,信号编号
      2.信号处理函数的有一个int类型参数,作用当其被调用时,保存接收到的信号值
    
练习:父进程不阻塞,不轮询方式回收僵尸态子进程资源 
提示:捕捉SIGCHLD信号,信号处理函数中应调用waitpid,以非阻塞方式调用


linux 系统下调用signal显式忽略子进程的SIGCHLD也可实现,由init进程回收子进程资源,


5.信号的发送

#include <sys/types.h>
#include <signal.h>

int kill(pid_t pid, int sig);
kill - send signal to a process


#include <signal.h>

int raise(int sig);
raise - send a signal to the caller

    
#include <unistd.h>

unsigned int alarm(unsigned int seconds);
alarm - set an alarm clock for delivery of a signal

#include <unistd.h>

int pause(void);
pause - wait for signal

//pause(2)和kill(2)/raise(2)/alarm(2)一起可实现进程间同步
练习:检测用户是否输入,如果用户3s没有输入,则超时一次,如果超时3次自动退出


*选做作业:利用有名管道实现文件传输
A进程,将文件内容读出,写入管道,直至文件结尾为止
B进程,从管道中将内容读出,写入另一文件,直到管道为空

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值