Linux 7种 进程间通信方式

传统进程间通信

        通过文件实现进程间通信 必须人为保证先后顺序        A--->硬盘---> B(B不知道A什么时候把内容传到硬盘中)

1.无名管道

2.有名管道

3.信号

IPC进程间通信

4.消息队列

5.共享内存

6.信号灯集

7.socket通信

一、无名管道(亲缘关系的进程   64K)

原理:

        如果A和B进程想要通过无名管道通信,那就必须在内核空间创建一个无名管道(64K),A和B进程必须是亲缘关系的进程,A进程向管道的一端写数据,B进程可以从管道的另外一端读数据。在A进程和B进程进行数据传输的时候是不允许使用lseek函数的。无名管道是半双工的通信方式。如果A进程一直向管道中写数据写满64K的时候A进程阻塞,直到B进程读一部分数据之后A才能继续写。如果B进程在读数据的时候,无名管道是空的,B进程阻塞。(PS:使用无名管道时,不会创建管道文件,使用有名管道文件,会创建)

API

int pipe(int pipefd[2]);
功能:创建一个无名管道
参数:
    @pipefd[2]:返回的管道的两端
     pipefd[1]:写端
     pipefd[0]:读端
返回值:成功返回0,失败返回-1置位错误码

实例

#include <head.h>
// 子进程从终端读取字符串 通过无名管道 父进程读 然后打印到终端
int main(int argc, const char *argv[])
{
    int pipefd[2];
    if (-1 == pipe(pipefd))
    {
        perror("pipe error");
        exit(-1);
    };
    char buff[128] = {0};
    pid_t pid;
    pid = fork();
    if (pid == -1)
    {
        perror("fork error");
        exit(-1);
    }
    else if (pid == 0)
    {
        // 子进程
        close(pipefd[0]); // 关闭子进程的读端   因为父子进程同时各自拥有读和写 把对应的关一个
        while (1)
        {
            memset(buff, 0, sizeof(buff));
            fgets(buff, sizeof(buff), stdin); // 从终端读取数据
            if ((buff[strlen(buff) - 1]) == '\n')
            {
                buff[strlen(buff) - 1] = '\0';
            }
            write(pipefd[1], buff, sizeof(buff)); // 将读到的写入到管道
            if (strcmp(buff, "quit") == 0)
            {
                break;
            }
        }
        close(pipefd[1]);
        exit(0);
    }
    else
    {
        // 父进程
        close(pipefd[1]); // 关闭父进程的写端
        while (1)
        {
            memset(buff, 0, sizeof(buff));
            read(pipefd[0], buff, sizeof(buff)); // 父进程从管道中读
            printf("父进程data:%s\n", buff);
            if (strcmp(buff, "quit") == 0)
            {
                break;
            }
        }
        close(pipefd[2]);
        wait(NULL);
    }
    return 0;
}

结果

二、有名管道(任意进程间的通信    64K)

有名管道可以实现任意进程间的通信,有名管道的大小也是64K,有名管道也是不支持lseek,有名管道也是半双工的通信方式。有名管道创建之后会在用户空间产生一个管道文件(p),这个管

文件是在内存上存储的。如果A和B两个进程想要通过有名管道通信,就打开管道文件,向管道中写向管道中读就可。

API

int mkfifo(const char *pathname, mode_t mode);
功能:创建有名管道
参数:
    @pathname:有名管道文件的路径及名字
 @mode: 管道文件的权限(mode & ~umask)
返回值:成功返回0,失败返回-1置位错误码

实例

01mkfifo.c

#include <head.h>
#define FIFO_PATH "./myfifo"
int main(int argc,const char * argv[])
{
    //创建管道文件
    if(mkfifo(FIFO_PATH,0666)==-1){
        perror("mkfifo error");
        exit(-1);
    }
    //阻塞等待读写
    getchar();
    //删除管道文件
    char buff[128]={0};
    snprintf(buff,sizeof(buff),"rm -rf %s",FIFO_PATH);
    system(buff);
    return 0;
}

02read.c

#include <head.h>
#define FIFO_PATH "./myfifo"
int main(int argc, const char *argv[])
{
    int fd;
    char buff[128] = {0};
    if (-1 == (fd = open(FIFO_PATH, O_RDONLY)))
    {
        perror("open error");
        exit(-1);
    }
    while (1)
    {
        memset(buff, 0, sizeof(buff));
        read(fd, buff, sizeof(buff));
        printf("read:[%s]\n", buff);
        if (strcmp(buff, "quit") == 0)
        {
            break;
        }
    }
    close(fd);
    return 0;
}

03write.c

#include <head.h>
#define FIFO_PATH "./myfifo"
int main(int argc, const char *argv[])
{
    int fd;
    char buff[128] = {0};
    if (-1 == (fd = open(FIFO_PATH, O_WRONLY)))
    {
        perror("open error");
        exit(-1);
    }
    while (1)
    {
        memset(buff, 0, sizeof(buff));
        printf("input:");
        fgets(buff,sizeof(buff),stdin);
        if(buff[strlen(buff)-1]=='\n'){
            buff[strlen(buff)-1]='\0';
        }
        write(fd, buff, sizeof(buff));
        if (strcmp(buff, "quit") == 0)
        {
            break;
        }
    }
    close(fd);
    return 0;
}

结果

 三、信号

        信号软件层面对中断一种模拟,用户可以给进程发送信号,内核也可以给进程发送信号,进程对信号的响应方式有三种:捕捉,默认,忽略

17号进程 SIGCHLD :当子进程退出的时候,父进程会收到这个信号

注:在所有的信号中,只有SIGKILL/SIGSTOP两个信号不能被捕捉,也不能被忽略。只能执行默认的动作。

killall  a.out            给所有a.out命名的进程发送信号(停止态的a.out不会结束)

kill -信号号 pid      给进程发送信号

htop                     给进程发送信号

API

#include <signal.h>

typedef void (*sighandler_t)(int);

sighandler_t signal(int signum, sighandler_t handler);
功能:指定进程对某个信号的响应方式
参数:
    @signum:信号号
 @handler:处理方法
        SIG_IGN :忽略
        SIG_DFL:默认
        自己实现信号处理函数 :捕捉
        void signal_handle(int signo)
     {
      //信号处理函数
     }
返回值:成功返回handler,失败返回SIG_ERR,置位错误码
int raise(int sig);
功能:给自己发信号
参数:
    @sig:信号号
返回值:成功返回0,失败返回非0
        
int kill(pid_t pid, int sig);
功能:给指定pid的进程发送信号
参数:
    @pid:进程号
        pid > 0 :给pid号的进程发信号
  pid = 0 :给同组的进程发送信号
  pid = -1:给所有有权限的进程发送信号(PS:慎用 ssh也会被杀死)
  pid <-1:首先会对pid取绝对值,给和这个绝对值相同的组的进程发送信号
    @信号号
返回值:成功返回0,失败返回-1置位错误码

unsigned int alarm(unsigned int seconds);
功能:当seconds倒计时为0的时候发送SIGALRM信号
参数:
    @seconds:秒钟数,如果填写为0,取消挂起的信号
返回值:如果alarm是第一次调用,返回0.
        如果alarm不是第一次调用,返回上一次调用的剩余秒钟数
        alarm(5);  //返回值是0
  sleep(2);  //延时2s
  alarm(5);  //返回值是3
//7秒后发出信号 第一次等了2秒 还剩3秒 又调用alarm(5)恢复到5秒 2+5=7

实例

#include <head.h>
void signal_handle(int signo){
    printf("收到了ctrl+c的信号\n");
}
int main(int argc, const char *argv[])
{
    // 1.忽略 SIG_IGN
    //  if(SIG_ERR==signal(SIGINT,SIG_IGN)){
    //      perror("signal error");
    //      exit(-1);
    //  }
    // 2.默认 SIG_DFL
    //      if(SIG_ERR==signal(SIGINT,SIG_DFL)){
    //      perror("signal error");
    //      exit(-1);
    //  }
    // 3.捕捉 SIGINT
    if (SIG_ERR == signal(SIGINT, signal_handle))
    {
        perror("signal error");
        exit(-1);
    }
    while (1);
    return 0;
}

IPC进程间通信见下篇文章

IPC进程间通信-CSDN博客(内部进程通信Internal process communication 一般问指的是System V版本的以下三个)4.消息队列5.共享内存6.信号灯集合ipcs查看IPC进程间通信ipcs -q //查看消息队列ipcs -m //查看共享内存ipcs -m //查看信号灯集。https://blog.csdn.net/CSDN_DU666666/article/details/139930078?csdn_share_tail=%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%22139930078%22%2C%22source%22%3A%22CSDN_DU666666%22%7D

  • 9
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值