3、操作系统——进程间通信(1)(无名管道(PIPE)和有名管道(FIFO))

目录

一、六种通信方式

二、无名管道(PIPE)和有名管道(FIFO)

1、无名管道

(1)特点

(2)管道PIPE的使用

(3)注意点

(4)代码

 2、有名管道(FIFO)

(1)FIFO特征

(2)FIFO的应用

扫描二维码关注公众号,回复: 14819078 查看本文章

(3) 注意事项

  (4)read.c

  (5)write.c

三、信号

1、命令

2、信号/事件 产生方式

3、信号的种类

4、注意

5、信号的相关函数

 (1)kill(产生一个信号,把某个进程杀死,signal捕获产生的信号)

 (2)signal(跟kill配套使用)

 (3)raise

 (4)pause(挂起)

 (5)sigprocmask(阻塞/解除)

 (6)信号集操作函数簇

 (7)sigqueue(发给谁,发送的信号,携带的数据)(跟sigaction是一对)

 (8)sigaction

 6、例程

 (1)子进程中捕获信号并相应

 (2)信号阻塞的应用

 (3)sigqueue和aigaction的应用

一、六种通信方式

1、无名管道(PIPE)和有名管道(FIFO)

2、信号(signal)

3、system V-IPC 之共享内存

 4、system V-IPC 之消息队列

 5、system V-IPC 之信号量

 6、套接字

二、无名管道(PIPE)和有名管道(FIFO)

1、无名管道

(1)特点

a、一边出、一边进  b、没有名字,不能用open打开

c、只用于亲缘进程间(父子、兄弟、祖孙进程)d、不能用lseek()定位

(2)管道PIPE的使用

(3)注意点

a、pipe 中的参数是一个具有两个整型数的数组,用来存放文件描述符,一个是读端,另一个是 写端

b、图说明管道在创建子进程后的状态

 c、pipefd[0] --> 读取端口 

        pipefd[1] --> 写入端口

(4)代码

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

char * buf  = NULL ; 
int pipefd[2]

void f(void)
{
    while(1)
    {
        printf("父进程*请输入:\n");
        fgets(buf , 1024 , stdin );
        int ret_val = write( pipefd[1] , buf ,  strlen(buf)+1 );
        printf("成功写入:%d字节 \n" , ret_val );

        ret_val = read( pipefd[0] , buf , 1024 );
        printf("父进程*成功读取:%d字节  内容:%s \n" , ret_val , buf );
    }
}

void s(void)
{
    while(1)
    {
        int ret_val = read( pipefd[0] , buf , 1024 );
        printf("子进程*成功读取:%d字节  内容:%s \n" , ret_val , buf );

        printf("子进程*请输入:\n");
        fgets(buf , 1024 , stdin );
        ret_val = write( pipefd[1] , buf , strlen(buf)+1 );
        printf("成功写入:%d字节 \n" , ret_val );
    }
}

int main(int argc, char const *argv[])
{    
    if(pipe( pipefd))
    {
        perror("pipe error");
        return -1 ;
    }
    buf = calloc(1, 1024);
    int pid = fork();
    if ( pid > 0 )
    {
        f();
    }
    else if (pid == 0)
    {
        s();
    }
    else 
    {
        perror("fork error ");
    }  
    return 0;
}

 2、有名管道(FIFO)

(1)FIFO特征

a、有名字,存储于普通文件系统中 b、可以用open()获取 FIFO 的文件描述符

c、使用统一的 read( )/write( )来读写 d、不能使用 lseek( )来定位

e、具有写入原子性,支持多写者同时进行写操作而数据不会互相践踏

f、First In First Out,最先被写入 FIFO 的数据,最先被读出来

(2)FIFO的应用

(3) 注意事项

a、管道文件在打开的时候如果只有一方(读者/写者)则阻塞等待对方到达然后同时打开 文件 。

 (4)read.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#define FIFO_PATH "/tmp/my_fifo"

int main(int argc, char const *argv[])
{
    // 创建管道文件
    if( access(FIFO_PATH, F_OK)) // 判断管道文件是否已存在 如果存在则返回 0 否则 -1 
    {
        if( mkfifo(FIFO_PATH , 0666 )) // 创建
        {
            perror("mkfifo error");
            return -1 ;
        }
    }

    printf("管道文件创建成功!! \n") ;


    // 打开管道文件
    int fd_fifo = open(FIFO_PATH, O_RDONLY );
    if (-1 == fd_fifo)
    {
        perror("open error");
        return -1 ;
    }
    printf("管道文件打开成功!! \n") ;
    

    // 读取信息
    char buf[128]= {0};
    int ret_val = read(fd_fifo , buf  , sizeof( buf ));
    printf("成功读取:%d 字节 内容:%s \n" , ret_val , buf );

    // 关闭文件
    close(fd_fifo);


    return 0;
}

(5)write.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#define FIFO_PATH "/tmp/my_fifo"

int main(int argc, char const *argv[])
{
    // 创建管道文件
    if( access(FIFO_PATH, F_OK)) // 判断管道文件是否已存在 如果存在则返回 0 否则 -1 
    {
        if( mkfifo(FIFO_PATH , 0666 )) // 创建
        {
            perror("mkfifo error");
            return -1 ;
        }
    }
    
    printf("管道文件创建成功!! \n") ;

    // 打开管道文件
    int fd_fifo = open(FIFO_PATH, O_WRONLY );
    if (-1 == fd_fifo)
    {
        perror("open error");
        return -1 ;
    }
    printf("管道文件打开成功!! \n") ;
    

    // 写入信息
    int ret_val = write(fd_fifo , "hello Even" , sizeof("hello Even"));
    printf("成功写入:%d 字节 \n" , ret_val);

    // 关闭文件
    close(fd_fifo);

    return 0;
}

三、信号

1、命令

kill -l   //罗列出系统中的信号

2、信号/事件 产生方式

(1)用户按键:用户使用某种特殊字符递给终端,产生一个信号(事件)递送给进程

(2)硬件故障:进程执行错误,比如访问一个无效内存地址,这时候先由硬件报告给内核,再由内核把事件递送给进程

(3)kill函数或者命令:通过函数把需要的事件直接递送给进程

3、信号的种类

(1)可靠信号:

(2)不可靠信号:信号发生了,但是它可能会丢失

 其中 1~31项为不可靠信号, 34~64为可靠信号

4、注意

(1)信号 SIGKILL 和 SIGSTOP 是两个特殊的信号,他们不能被忽略、阻塞或捕捉

(2)任何进程都可以使用函数 kill( )来产生任何信号。

(3)接收信号的目标进程,按照如下顺序来做出反应:

A) 如果该信号被阻塞,那么将该信号挂起,不对其做任何处理,等到解除对其阻塞为 止。否则进入 B。

B) 如果该信号被捕捉,那么进一步判断捕捉的类型:

        B1) 如果设置了响应函数,那么执行该响应函数。

        B2) 如果设置为忽略,那么直接丢弃该信号。 否则进入 C。

C) 执行该信号的缺省动作

(4)信号函数可以继承到子进程中

5、信号的相关函数

(1)kill(产生一个信号,把某个进程杀死,signal捕获产生的信号)

 (2)signal(跟kill配套使用)

(3)raise

(4)pause(挂起)

(5)sigprocmask(阻塞/解除)

 (6)信号集操作函数簇

(7)sigqueue(发给谁,发送的信号,携带的数据)(跟sigaction是一对)

 额外携带的数必须是一下联合体:

union sigval
{
    int sigval_int;
    void * sigval_prt;
};

(8)sigaction

struct aigaction函数

struct sigaction
{
    void (*sa_handler)(int);
    void (*sa_sigaction)(int, siginfo_t *, void *);
    sigset_t sa_mask;
    int sa_flags;
    void (*sa_restorer)(void);
};
void (*sa_sigaction)(int, siginfo_t *, void *);

6、例程

(1)子进程中捕获信号并相应

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

void func(int arg)
{
    printf("这里是信号响应函数 %d \n" , arg );//信号响应
}

int main(int argc, char const *argv[])
{
    int pid = fork();
    int i = 0 ;
    
    if (pid > 0 )
    {
        printf("这里是父进程 \n " );
        sleep(3);
        printf("猎杀时间到了..。 \n " );
        sleep(1);
        kill(pid , 4 );        //产生信号
    }
    else if (pid == 0 )
    {
        signal( 4 , func);//捕获信号
        while(1)
        {
            printf("这里是子进程 : %d \n ", i++ );
            sleep(1);
        }
    }
    return 0;
}

 (2)信号阻塞的应用

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

void func(int arg)
{
    printf("这里是信号响应函数 %d \n" , arg );
}

int main(int argc, char const *argv[])
{
    // 设置信号响应的函数
    signal( 3 , func );
    signal( 4 , func );

    // 初始化信号集
    sigset_t set ;
    sigemptyset( &set ); // 将信号集清空
    sigaddset( &set , 3 ); // 将指定的一个信号添加到信号集中
    sigaddset( &set , 4 ); // 将指定的一个信号添加到信号集中

    // 设置阻塞信号集中的信号
    sigprocmask(SIG_SETMASK , &set , NULL ); // 把信号集中的信号设置为阻塞状态

    // 给自己发送 3.4 号信号
    raise(3);
    raise(4);
    sleep(2);

    // 解除阻塞
    sigprocmask(SIG_UNBLOCK , &set , NULL ); // 把信号集中的信号设置为阻塞状态

    return 0;
}

运行后,等待2秒钟,才开始输出这里是.....

(3)sigqueue和aigaction的应用

#include <stdio.h>
#include <signal.h>
#include <strings.h>
#include <sys/types.h>
#include <unistd.h>


void func(int sig, siginfo_t * info , void * arg )
{
    printf("sig:%d , info: %s  arg:%s \n" , sig , (char * )info->si_ptr , (char*)arg );
}

int main(int argc, char const *argv[])
{

    // 定义ACT结构体并设置其信息
    struct sigaction act ;
    bzero(&act , sizeof(act)); // 清空结构体
    act.sa_sigaction = func ;
    act.sa_flags |= SA_SIGINFO ;//使用拓展信号函数而不是标准响应函数

    // 设置捕获的信号响应函数
    if( sigaction( 3 , &act, NULL ))//将信号捕获函数设置号
    {
        perror("设置捕获失败!!");
        return -1 ;
    }

    // 设置好携带的参数
    union sigval value;
    value.sival_int = 1024 ; // 设置整型数组
    value.sival_ptr = "Hello Even"; // 设置一个地址(指针)可以是任意类型的指针


    // 发送信号
    pid_t pid = getpid( );
    if(sigqueue(pid  ,  3 , value))//发送信号,成功是0,失败是-1
    {
        perror("发送信号失败!!");
        return -1 ;
    }
    printf("发送信号成功!!\n"); 
    sleep(1);

    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_45981798/article/details/129758387