传统的进程间通信(无名管道、有名管道、信号)

传统的进程间通信(无名管道、有名管道、信号)

1.无名管道 (pipe)

查看命令: man 2 pipe

头文件:#include <unistd.h>

函数原型: int pipe(int pipefd[2]);

pipefd[2] :无名管道的两个文件描述符,int型的数组,大小为2,

pipefd[0]为读端,pipefd[1]为写端

返回值:
成功:返回0
失败:返回-1

2.无名管道的特点:

	a、没有名字,因此无法使用open()打开
	b、只能用于亲缘进程间(如父子进程、兄弟进程、祖孙进程等)通信
	c、半双工工作方式,读写端是分开的,pipefd[0]为读端,pipefd[1]为写端
	d、是一种特殊的文件,只存在内存中,由内核进行管理,随进程生,随进程死
	e、对于它的读写可以使用文件IO如read、write函数
	f、无名管道的操作属于一次性操作,如果对无名管道进行读操作,数据会被全部读走



单工:固定一种方向进行通信  广播

半双工:方向不定,但是同一时间只能由一端发送到另一端  对讲机

全双工:通信方向都可以,同时可以发送也可以接收  电话

一个进程用无名管道通信

/*=============================================================================
#
# Last modified: 2021-07-07 10:22
#
# Filename: pipe.c
#
# Description(描述): 无名管道通信
#
=============================================================================*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, const char *argv[])
{
    int fd[2];
    int ret = pipe(fd);
    if (ret == -1)
    {
        perror("pipe");
        return -1;
    }
    // fd[0] 读  fd[1] 写
    write(fd[1],"hello",5);
    char buf[100]={0};
    read(fd[0],buf,sizeof(buf));
    printf("%s\n",buf);
    close(fd[0]);
    close(fd[1]);
    return 0;
}

两个进程

/*=============================================================================
#
# Filename: pipe_fork.c
#
# Description(描述): 无名管道在两个进程间的读写
#
=============================================================================*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

int main(int argc, const char *argv[])
{
    int fd[2];
    pid_t pid;
    int ret = pipe(fd);
    if (ret < 0)
    {
        perror("pipe");
        return -1;
    }

    pid = fork();
    if (pid < 0)
    {
        perror("fork");
        return -1;
    }
    else if (pid == 0)
    {   
        close(fd[0]);
        sleep(1);
        int ret = write(fd[1],"hello",5);
        if (ret < 0)
        {
            perror("write");
            exit(-1);
        }
        close(fd[1]);
    }
    else if (pid > 0)
    {
        close(fd[1]);
        char buf[100] = {0};
        int ret = read(fd[0],buf,sizeof(buf));
        if (ret < 0)
        {
            perror("read");
            exit(-1);
        }
        printf("%s\n",buf);
        close(fd[0]);
    }
    return 0;
}

3.管道读写注意事项:

1> 当管道中无数据时,执行读操作,读操作阻塞

2>向管道中写入数据时,linux将不保证写入的原子性,管道缓冲区一有空闲区域,写进程就会试图向管道写入数据。如果读进程不读走管道缓冲区中的数据,那么写操作将一直阻塞。

3>对无名管道的操作,类似一个队列,后写入的数据不会覆盖之前的数据,会在其后面存储,读取完的数据会从管道里面移除

4>向无名管道中写数据,将读端关闭,管道破裂,进程收到信号(SIGPIPE),默认这个信号会将进程杀死

4.有名管道 (fifo)

有名管道也叫命名管道,在本地磁盘可见的管道文件,可以进行文件IO操作。

特点:
1>不仅可在亲缘关系的进程间通信,也可以在不具有亲缘关系的进程间通信。
2>数据先进先出原则
3>支持除了 lseek之外的文件IO操作。

查看命令:man 3 mkfifo

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

参数:
*pathname:有名管道的名字-路径 例如:/home/farsight/myfifo
mode:八进制的权限, 例如:0777

在shell中使用mkfifo命令: mkfifo filename

eg: mkfifo f1

或者

if(mkfifo("f1",0666) == -1)
{  
		perror("mkfifo ");
		return -1;
}

注意事项:

1>管道文件只有inode号,不占磁盘块空间,当使用open函数打开文件时才会在内核空间中创建管道。

2>创建有名管道mkfifo。跟普通文件一样使用,打开open,读read,写write,关闭close。

3>只有读端和写端同时存在管道才能打开成功。

5.有名管道和无名管道的异同点

1、相同点
open打开管道文件以后,在内存中开辟了一块空间,管道的内容在内存中存放,有两个指针—-头指针(指向写的位置)和尾指针(指向读的位置)指向它。读写数据都是在给内存的操作,并且都是半双工通讯。
2、区别
有名在任意进程之间使用,无名在父子进程之间使用

有亲缘关系的进程用有名管道通信

/*=============================================================================
# Last modified: 2021-07-07 13:57
#
# Filename: mkfifo_fork.c
#
# Description(描述): 有亲缘关系的进程用有名管道通信
#
=============================================================================*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main(int argc, const char *argv[])
{
    int ret = mkfifo("f1",0666);
    if (ret < 0)
    {
        perror("mkfifo");
        return -1;
    }
    pid_t pid;
    pid = fork();
    if (pid < 0)
    {
        perror("fork");
        return -1;
    }
    else if (pid > 0)
    {
        int fd = open("f1",O_RDONLY);
        char buf[100];
        read(fd,buf,sizeof(buf));
        printf("%s\n",buf);
        close(fd);
    }
    else if (pid == 0)
    {
        int fd = open("f1",O_WRONLY);
        write(fd,"hello",5);
        close(fd);
    }
    return 0;
}

无亲缘关系的进程用有名管道通信

  1. 写端
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main(int argc, const char *argv[])
{
  /*
    int ret = mkfifo("f2",0666);
    if (ret < 0)
    {
        perror("mkfifo");
        return -1;
    }   */
    printf("等待读端.......\n");
    int fd = open("f2",O_WRONLY);
    if (fd < 0)
    {
        perror("open");
        return -1;
    }
    while(1)
    {
        printf("input:");
        char buf[100]= {0};
        fgets(buf,sizeof(buf),stdin);
        if (strncmp(buf,"quit",4) == 0)
        {
            break;
        }
        write(fd,buf,strlen(buf));
    }
    close(fd);
    return 0;
}
  1. 读端
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main(int argc, const char *argv[])
{
    
    printf("等待写端........\n");
    int fd = open("f2",O_RDONLY);
    if (fd < 0)
    {
        perror("open");
        return -1;
    }
    while(1)
    {
        char buf[100];
        int ret = read(fd,buf,sizeof(buf));
        if (ret < 0)
        {
            perror("read");
            return -1;
        }
        if (ret == 0)
        {
            break;
        }
        if (strncmp(buf,"quit",4) == 0)
            break;
        printf("recv:%s",buf);
        memset(buf,0,sizeof(buf));
    }
    return 0;
}

数据传输特点:

	1、读端不存在时,写端写入数据将会阻塞
	2、读端意外结束,写端再写数据将会管道破裂,该进程结束

6.信号

简单概念:信号是在软件层次上对中断机制的一种模拟
#include <signal.h>

int kill(pid_t pid, int  signo); //kill把信号发送给进程或进程组;
int raise(int  signo); //raise把信号发送给(进程)自身.

返回值:成功则返回0,
		出错则返回-1

特点:
raise(signo);   等价于  kill(getpid(), signo);
alarm();//设置闹钟
pause();//程序暂停

可以为当前进程定义闹钟,时间到了会发出SIGALRM信号。
每个进程只能有一个alarm,当重新定义时,会重新计时。
如果之前定义了一个闹钟,则这次定义返回的是上次闹钟剩余的时间,否则返回0.

pause函数的作用,是让当前进程暂停运行,交出CPU给其他进程去执行;
当前进程进入pause状态后,当前进程会表现为“卡住、阻塞住”;
要退出pause状态,当前进程需要被信号唤醒。

信号的三种处理方式:

1.忽略 2.默认 3.自定义信号处理函数

sighandler_t signal(int signum, sighandler_t handler);
参数:
/********************************
*signum:   	捕获信号,设置信号的处理方式
*handler: 	SIG_IGN:忽略
*         	SIG_DFL:默认
*         	自定义的信号处理函数
*********************************/

SIGINT   :  CTRL + C

SIGQUIT  :  CTRL + \

SIGTSTP  : CTRL + Z

SIGKILL  :  立即结束进程

SIGALRM  :  当一个定时器结束时发出

SIGSTOP  :  暂停一个进程

一、进程间通信方式

传统进程间通信方式:
	无名管道、有名管道、信号
	
system V的IPC对象:
	共享内存(share memory)、消息队列(message queue)、信号灯集
	
BSD: 套接字

二、共享内存share memor

	是一种通信效率最高的进程间通信方式,
		进程间通信时直接访问内存,不需要进行数据的拷贝。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值