linux高级编程:线程(二)、进程间的通信方式

线程:

回顾线程(一):

1.线程间通信问题

   线程间共享同一个资源(临界资源)

   互斥:

        排他性访问

        linux系统 -- 提供了Posix标准的函数库 -- 互斥量(互斥锁)

   原子操作:---

   机制:

        加锁 -- 解锁

        锁 --- 操作系统 --- (实现的机制,需要操作系统来

        | - 加锁 -- 用户态 -- 切换到(耗时) -- 内核态 -- 获得了 -- 内核态 -- 用户态 -- 解锁 |

   函数:

        pthread_mutex_t mutex;

        pthread_mutex_init();

        pthread_mutex_lock();

        pthread_mutex_trylock();    //  尝试获得锁,若没获得则返回非0值。

                

        pthread_mutex_unlock();

        pthread_mutex_destroy();

线程的同步:

      

        同步 ==》有 一定先后顺序的 对资源的排他性访问。

        要同步的原因:互斥锁可以控制排他访问但没有次序。
    
        信号量 --- 实现线程间的同步.
    
        来源  生活 --- 交通信号灯

信号量的分类:


        1、无名信号量 ==》线程间通信
        2、有名信号量 ==》进程间通信

同步机制:

        信号量(个数) --- 反映的是资源的数量

        考虑的时候,站在使用这的角度考虑

        站在a的角度考虑。。。。。。

框架:

        1. 信号量的定义       sem_t  sem  //造了一类资源
        2. 信号量的初始化   sem_init 
        3. 信号量的PV操作 (核心) sem_wait()/ sem_post()
        4. 信号量的销毁。   sem_destroy

  

信号量函数:

1、定义

sem_t 名字;

2、初始化

int sem_init(sem_t *sem, int pshared, unsigned int value);

        功能:

                将已经定义好的信号量赋值。

        参数:

                @sem 要初始化的信号量

                @pshared

                        pshared = 0 ;表示线程间使用信号量(一般填这个)

                        !=0 ;表示进程间使用信号量

                @value:

                        信号量的初始值,一般无名信号量(一开始的资源的个数)

                        都是二值信号量,0 1

                        0 表示红灯,进程暂停阻塞

                        1 表示绿灯,进程可以通过执行

                        也可以是多个,变成计数信号量

                返回值:

                        成功 0,失败 -1;

3、PV操作

int sem_wait(sem_t *sem); //p操作

        功能:

                判断当前sem信号量是否有资源可用。

                如果sem有资源(==1),则申请该资源,程序继续运行

                如果sem没有资源(==0),则线程阻塞等待,一旦有资源

                则自动申请资源并继续运行程序。

                消耗了这个sem,就没有了

             注意:sem 申请资源后会自动执行 sem = sem - 1;

        参数:

                @sem 要判断的信号量资源

                返回值:

                        成功 0 ,失败 -1

int sem_post(sem_t *sem); //V操作

        功能:

                函数可以将指定的sem信号量资源释放

                并默认执行,sem = sem+1;

                线程在该函数上不会阻塞。

                产生了这个sem就有了,可以由wait(sem)接受去消耗

                参数:

                @sem 要释放资源的信号量

                返回值:

                        成功 0,失败 -1;

4、销毁

int sem_destroy(sem_t *sem);

练习:   hello world
#include<stdio.h>
#include<semaphore.h>
#include<errno.h>
#include<pthread.h>
#include<stdlib.h>sem_t sem_h;
sem_t sem_w;void *do_hello(void *arg)
{while(1)	{sem_wait(&sem_h);printf("hello ");sem_post(&sem_w);}return NULL;
}void *do_world(void *arg)
{while(1)	{sem_wait(&sem_w);printf("world\n");sem_post(&sem_h);}return NULL;}
typedef void *(*threadF_t)(void *);
int main(int argc, const char *argv[])
{int i;pthread_t tid[i];threadF_t pFunc[2] = {do_hello,do_world};sem_init(&sem_h,0,1);sem_init(&sem_w,0,0);for(i = 0;i < 2;++i){int ret = pthread_create(&tid[i],NULL,pFunc[i],NULL);if(ret != 0){errno = ret;perror("pthread_create fail");exit(EXIT_FAILURE);}}sem_destroy(&sem_h);sem_destroy(&sem_w);pthread_detach(tid[0]);pthread_detach(tid[1]);printf("---main----exit\n");pthread_exit(NULL);return 0;
}

进程间的通信方式:

三大类:

1.同主机   ---- 基于内存的 
         

 古老的通信方式 
                    //管道  ---- 
                             无名管道  
                             有名管道
                    //信号  
    
          IPC对象通信(改进)
                     消息队列(用的相对少,这里不讨论)
                     共享内存(*) //最高效 
                     信号量集() //信号量  

 
2.     

        //不同主机 、多台主机
          socket //网络部分 
  
        //同一主机
        2.1、古老的通信方式
                管道:
                           无名管道  
                           有名管道  
                           信号

        2.2、IPC对象通信 system v    BSD     suse fedora   kernel.org
                消息队列(用的相对少,这里不讨论)
                共享内存(*) //最高效 
                信号量集() //信号量  


        //不同主机 


3、socket通信

        网络通信

 

1、pipe  无名管道

使用框架:

                创建管道 ==》读写管道 ==》关闭管道

1、无名管道 ===》管道的特例 ===>pipe函数
    特性:
            1.1  亲缘关系进程使用
            1.2  有固定的读写端

   

流程:
    创建并打开管道: pipe函数
    

函数:

    #include <unistd.h>
    int pipe(int pipefd[2]);
    int pipe(int *pipefd);
    int fd[2];
            功能:创建并打开一个无名管道
            参数:  @pipefd[0] ==>无名管道的固定读端//0 -- 标准输入
                         @pipefd[1] ==>无名管道的固定写端//1 -- 标准输出 
            返回值: 成功 0
                            失败 -1;

注意事项:

         1、无名管道的架设应该在fork之前进行。  

关闭管道: close();

练习:父进程输入、子进程打印

#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>
#include <sys/wait.h>int main(int argc, const char *argv[])
{int fd[2];if(pipe(fd) < 0){perror("pipe fail");return -1;}char buf[100] = {0};int ret = 0;pid_t pid = fork();while(1){if(pid < 0){perror("fork fail");return -1;}else if(pid > 0){close(fd[0]);printf(">");fflush(stdout);ret = read(0,buf,sizeof(buf));buf[ret] = '\0';write(fd[1],buf,strlen(buf) + 1);if(strncmp(buf,"quit",4) == 0){wait(NULL);close(fd[1]);return 0;}}else if(pid == 0){close(fd[1]);ret = read(fd[0],buf,sizeof(buf));printf("date = %s\n",buf);if(strncmp(buf,"quit",4) == 0){close(fd[0]);exit(0);}}}return 0;
}

管道的读写规则:
   

    1.读端存在,写管道
         管道空:可以写数据
         管道满:会造成-->写阻塞 
      
    2.读端不存在,写管道
         系统会给进程发一个信号SIGPIPE(管道破裂)

    3.写端存在,读管道
         管道空,读不到数据,
         这时会造成读操作阻塞

    4.写端不存在,读管道 
         如果管道中有数据,则读取这些数据!
         如果没有数据,读操作不阻塞,立即返回!

2、fifo有名管道

有名管道===》fifo ==》有文件名称的管道。
                                                                      文件系统中可见

框架:

    (1).创建有名管道 -- 类似 文件 (管道文件) 
    (2).打开有名管道 -- open 
    (3).读写管道     -- read/write 
    (4).关闭管道  ==》卸载有名管道 //close  

1、创建:mkfifo     //创建了一个有名管道

#include <sys/types.h>
#include <sys/stat.h>
 remove();

int mkfifo(const char *pathname, mode_t mode);

        功能:
                      在指定的pathname路径+名称下创建一个权限为
                      mode的有名管道文件。
        参数:@pathname要创建的有名管道路径+名称
                      mode  8进制文件权限。
        返回值:  成功 0
                        失败  -1;

2、打开有名管道 open

注意:该函数使用的时候要注意打开方式,
    因为管道是半双工模式,所有打开方式直接决定


    当前进程的读写方式。
    一般只有如下方式:
    int fd-read = open("./fifo",O_RDONLY); ==>fd 是固定读端    //阻塞,只有双方以对应的方式打开的时候才会
    int fd-write = open("./fifo",O_WRONLY); ==>fd 是固定写端   
    不能是 O_RDWR 方式打开文件。
    不能有 O_CREAT 选项,因为创建管道有指定的mkfifo函数
    
    有名管道打开:
    注意,
    如果一端是以只读,或者只写方式打开的。
    程序会阻塞,
    阻塞在打开操作。
    直到另一端,以只写或只读方式打开。
    A.c --- 只读 
    B.c --- 只写 

练习:实现双向通信:

        打开两个有名通道文件

// a
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>int main(int argc, const char *argv[])
{if (mkfifo("a_2_b",0666) < 0 && errno != EEXIST){perror("mkfifo fail");return -1;}if (mkfifo("b_2_a",0666) < 0 && errno != EEXIST){perror("mkfifo fail");return -1;}int fd_w = open("a_2_b",O_WRONLY);if (fd_w< 0){perror("open fail");return -1;}int fd_r = open("b_2_a",O_RDONLY);if (fd_r< 0){perror("open fail");return -1;}pid_t pid = fork();if (pid < 0){perror("fork fail");return -1;}char buf[1024] = {0};if (pid > 0){while (1){printf(">");fflush(stdout);fgets(buf,sizeof(buf),stdin);write(fd_w,buf,strlen(buf)+1);if (strncmp(buf,"quit",4) == 0){close(fd_w);close(fd_r);}}}else if (pid == 0){while (1){printf("<");int ret = read(fd_r,buf,sizeof(buf));printf("ret = %d: %s\n",ret,buf);}}return 0;
}// b
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>int main(int argc, const char *argv[])
{if (mkfifo("a_2_b",0666) < 0 && errno != EEXIST){perror("mkfifo fail");return -1;}if (mkfifo("b_2_a",0666) < 0 && errno != EEXIST){perror("mkfifo fail");return -1;}int fd_r = open("a_2_b",O_RDONLY);if (fd_r< 0){perror("open fail");return -1;}int fd_w = open("b_2_a",O_WRONLY);if (fd_w< 0){perror("open fail");return -1;}pid_t pid = fork();if (pid < 0){perror("fork fail");return -1;}char buf[1024] = {0};if (pid > 0){while (1){printf(">");fflush(stdout);fgets(buf,sizeof(buf),stdin);write(fd_w,buf,strlen(buf)+1);}}else if (pid == 0){while (1){printf("<");int ret = read(fd_r,buf,sizeof(buf));printf("ret = %d: %s\n",ret,buf);if (strncmp(buf,"quit",4) == 0){close(fd_w);close(fd_r);}}}return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/717443.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

精通Matplotlib:从入门到精通的绘图指南

在本篇文章中&#xff0c;我们将深入探索Matplotlib库&#xff0c;这是一个强大的Python绘图库&#xff0c;广泛用于数据可视化。Matplotlib让我们能够以简单而直观的方式创建各种静态、动态和交互式的图表。无论你是数据分析师、科研人员&#xff0c;还是任何需要数据可视化的…

用Redis如何实现延迟队列?

在Redis中实现延迟队列可以利用有序集合&#xff08;Sorted Set&#xff09;和定时任务的方式。下面是一个基本的实现思路&#xff1a; 添加延迟任务&#xff1a; 将任务信息作为一个字符串存储在Redis中&#xff0c;同时将其对应的执行时间作为分数(score)存储在有序集合中。使…

Bililive-go 实现直播自动监控录制

前言 最近有直播录制的需求&#xff0c;但是自己手动录制太麻烦繁琐&#xff0c;于是用了开源项目Bililive-go进行全自动监控录制&#xff0c;目前这个项目已经有3K stars了 部署 为了方便我使用了docker compose 部署 version: 3.8 services:bililive:image: chigusa/bilil…

win环境nginx实战配置详解

项目中经常使用nginx做负载均衡&#xff0c;接口路由、文件、文档的上传及下载、视频的代理播放等等&#xff0c;都离不开nginx的支持&#xff0c;今天我们分享一下其个使用场景。 1、配置文件 nd-nginx.conf 全局配置 #全局配置端&#xff0c;对全局生效&#xff0c;主要设置…

leetcode-字符串相加

415. 字符串相加 题目中已经说明不能使用库函数直接将输入的字符串转换为整数。这就需要我们自己实现大数加法的逻辑&#xff0c;我们可以从两个字符串的最后一位开始&#xff0c;逐位相加&#xff0c;同时记录进位。如果某一位相加的结果超过10&#xff0c;那么需要向前进位。…

javascript实现的星座查询

今天在这个网站http://xzxys.wiicha.com/看到查询星座幸运色的效果&#xff0c;想研究一下代码&#xff0c;结果右键禁用。后来参考了一下别人的代码&#xff0c;琢磨着先实现了一下星座查询的功能&#xff0c;输入月份和日期四位数后&#xff0c;可以查询属于哪个星座&#xf…

群体风暴之锤(War3地图编辑器)

文章目录 0、大致原理1、创建隐形单位2、新事件开端3、环境→新条件4、动作4.1、单位组4.1.1、圆范围内单位4.1.2、指定条件 4.2、对单位组内的所有单位释放风暴之锤 0、大致原理 真MK向目标点释放风暴之锤时选定&#xff08;以技能释放点为圆心&#xff0c;设定半径&#xff0…

Python编程语言常用的包管理工具介绍

conda是一个开源的包管理器和环境管理器&#xff0c;用于安装、运行和更新包和它们的依赖项。conda可以用于Python编程语言&#xff0c;但它也支持其他编程语言。conda的主要特点是它能够在不同的环境中管理不同的包集合&#xff0c;这使得它非常适合于数据科学和机器学习项目&…

洛谷 P1439 最长公共子序列

题目描述 给出 1,2,…,n 的两个排列 P1​ 和 P2​ &#xff0c;求它们的最长公共子序列。 输入格式 第一行是一个数 n。 接下来两行&#xff0c;每行为 n 个数&#xff0c;为自然数 1,2,…,n 的一个排列。 输出格式 一个数&#xff0c;即最长公共子序列的长度。 输入输出…

详解算法的时间复杂度和空间复杂度!

目录 ​编辑 1. 算法效率 2. 时间复杂度 2.1 时间复杂度的概念 2.2 大O的表示渐进法 2.3 一个栗子 3. 空间复杂度 4. 常见复杂度对比 5. 完结散花 ​​​​​​​ 悟已往之不谏&#xff0c;知来者犹可追 创作不易&#xff0c;宝子们&#xff01;如果这篇文章对你们有…

Flex布局

Flex布局是一种用于创建灵活且自适应的布局模型&#xff0c;它使得元素能够更好地响应不同的屏幕尺寸和设备。Flex布局基于容器和项目的概念&#xff0c;通过设置容器的属性来控制项目的布局和对齐方式。 Flex布局的关键概念包括&#xff1a; 父容器&#xff08;Flex容器&…

Git实战(3)之merge与rebase区别

1,采用merge和rebase后,git log的区别,merge命令不会保留merge的分支的commit 2,处理冲突的方式: (一股脑)使用merge命令合并分支,解决完冲突,执行git add .和 git commit -mfix conflict。这个时候会产生一个commit。(交互式)使用rebase命令合并分支,解决完冲突,…

一种求最大最小值的方法(C语言)

作者在做项目时需要分析大量数据&#xff0c;其中需要用到最大值最小值的求解。这里分享一种简单好用的方法&#xff0c;并避免在代码中出现过多的for循环。 这个方法用到了qsort函数。 首先我们需要定义一个比较函数用来比较2个值的大小并通过返回值来表示比较的结果。 int…

STM32标准库开发——FLASH闪存

FLASH介绍 一般来说&#xff0c;宣传的FLASH的大小只是说程序存储器的大小&#xff0c;不包括系统存储器以及选项字节这俩个部分 IAP是内置在boot loader中的一道程序&#xff0c;可以用于辅助下载&#xff0c;用户可以通过有线通信协议或者无线协议实现对程序的更新升级。 FLA…

如何使用grafana 下JSON API访问展示接口数据

一.新增connection 点击左侧菜单栏&#xff0c;选择Add new connection 下载安装即可。 二. 增加对应url和参数 1. 添加新的数据源 2. 配置对应url 3.新建仪表盘和添加接口url和参数等

LeetCode每日一题之 移动0

前言&#xff1a; 我的每日一题专栏正式开始更新&#xff0c;我会分享关于我在LeetCode上刷题时的经验&#xff0c;将经典题型拿出来详细讲解&#xff0c;来提升自己及大家的算法能力&#xff0c;希望这篇博客对大家有帮助。 题目介绍&#xff1a; 题目链接&#xff1a;. - …

SpringBoot+aop实现主从数据库的读写分离

读写分离的作用是为了缓解写库&#xff0c;也就是主库的压力&#xff0c;但一定要基于数据一致性的原则&#xff0c;就是保证主从库之间的数据一定要一致。如果一个方法涉及到写的逻辑&#xff0c;那么该方法里所有的数据库操作都要走主库。 一、环境部署 数据库&#xff1a;…

深入了解Java虚拟机(JVM)

Java虚拟机&#xff08;JVM&#xff09;是Java程序运行的核心组件&#xff0c;它负责解释执行Java字节码&#xff0c;并在各种平台上执行。JVM的设计使得Java具有跨平台性&#xff0c;开发人员只需编写一次代码&#xff0c;就可以在任何支持Java的系统上运行。我们刚开始学习Ja…

【leetcode】用队列实现栈

大家好&#xff0c;我是苏貝&#xff0c;本篇博客带大家刷题&#xff0c;如果你觉得我写的还不错的话&#xff0c;可以给我一个赞&#x1f44d;吗&#xff0c;感谢❤️ 点击查看题目 思路: 在做此题之前&#xff0c;我们先要实现队列&#xff0c;这在上个博客中已经写过&#…

学习人工智能的方法及方向!

目录 一、第一部分&#xff1a;了解人工智能 二、人工智能学习路线图 三、职业规划 四、未来展望 五、总结 在这个信息爆炸的时代&#xff0c;想要系统性地学习人工智能&#xff08;AI&#xff09;并找到对应方向的工作&#xff0c;你需要一个明确的学习路径和职业规划。本…