Linux学习-(进程间,线程间通信)

发布于:2025-08-19 ⋅ 阅读:(20) ⋅ 点赞:(0)

异步:多线程、多进程本质是异步执行
 
线程间同步机制:让多个线程在执行某个任务时,具有先后顺序的执行

信号量


实现线程间同步。

步骤:1. 定义信号量对象:sem_t
           2. 初始化信号量:sem_init();
           3. 申请信号量:P操作:int sem_wait(sem_t *sem);
           释放信号量:V操作:int sem_post(sem_t *sem);
            PV操作
           4. 销毁信号量:int sem_destroy(sem_t *sem);

主要函数

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

功能

初始化信号量

参数

sem:要初始化的信号量对象地址
pshared:
0:线程间共享
非0:进程间共享
value:信号量的初始值(初始资源数)

返回值

成功:0
失败:-1

练习

创建三个线程,分别按顺序打印A-> B -> C

#include<stdio.h>
#include <pthread.h>
#include <semaphore.h>
#include<unistd.h>
sem_t sem_a;
sem_t sem_b;
sem_t sem_c;

void*task1(void*arg)
{
    while(1)
    {
        sem_wait(&sem_a);
        printf("A\n");
        sem_post(&sem_b);
        sleep(1);
    }

}


void*task2(void*arg)
{
    while(1)
    {
        sem_wait(&sem_b);
        printf("B\n");
        sem_post(&sem_c);
        sleep(1);
    }
}

void*task3(void*arg)
{
    while(1)
    {
        sem_wait(&sem_c);
        printf("C\n");
        sem_post(&sem_a);
        sleep(1);
    }
}

int main(int argc,const char*argv[])
{
    pthread_t tid[3];

    sem_init(&sem_a,0,1);
    sem_init(&sem_b,0,0);
    sem_init(&sem_c,0,0);

    pthread_create(&tid[0],NULL,task1,NULL);
    pthread_create(&tid[1],NULL,task2,NULL);
    pthread_create(&tid[2],NULL,task3,NULL);

    pthread_join(tid[0],NULL);
    pthread_join(tid[1],NULL);
    pthread_join(tid[2],NULL);

    sem_destroy(&sem_a);
    sem_destroy(&sem_b);
    sem_destroy(&sem_c);

    return 0;


}

死锁

死锁指的是在多线程环境中,每个执行流(线程)都有未释放的资源,
且互相请求对方未释放资源,从而导致陷入永久等待状态的情况。

现象

现象1:忘记释放锁
现象2:重复加锁
现象3:多线程多锁,抢占锁资源不当
如:线程A获取了1锁,线程B获取了2锁,同时线程A还想获取2锁,线程B还想获取1锁

产生死锁的四个必要条件

(1)互斥条件:一个资源每次只能被一个进程使用(一个执行流获取锁后,其它执行流不能再获取该锁)。
(2)请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放(执行流本身使用着一把锁并不释放,还在请求别的锁)。
(3)不剥夺条件:进程已获得的资源,在未使用完之前,不能强行剥夺(A执行流拿着锁,其它执行流不能释放)。
(4)循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系(多个执行流拿着对方想要的锁,并且各执行流还去请求对方的锁)。

解决方法

1.锁一定要成对出现
2.使线程的加锁顺序一致
3.破坏环路等待条件
使用非阻塞锁,一旦线程发现请求的锁被使用,就去释放自己拥有的锁
pthread_mutex_trylock();
int sem_trywait(sem_t *sem);

进程间通信

IPC机制(interprocess communicate)

进程间空间独立,无法直接通信,需要IPC机制实现通信。

同一主机进程间通信

1、古老的通信方式
无名管道
有名管道
信号:进程间通知机制
2、IPC对象通信system v
共享内存*:效率最高
消息队列
信号量集(信号灯)

主要用在不同主机进程间通信

3、socket通信
网络通信

IPC机制

有名管道:可以用于同一主机,任意进程间通信
无名管道:只能用于同一主机,具有亲缘关系的进程间通信(父子进程间)

无名管道

操作流程

1. 创建无名管道:pipe();  pipefd[0] ---->读端
2. 写管道-->write();     pipefd[1] ---->写端
3. 读管道-->read();
4. 关闭管道-->close();

管道本质

内核空间中的一段缓冲区,遵循先进先出特点。
无名管道的:读端:pipefd[0]
写端:pipefd[1]
读写端不能交换。
 无名管道默认大小:65536bytes = 64K

管道特性

1. 写阻塞:读端和写端都存在,向管道中写数据,当管道满时,发生写阻塞。
2. 读阻塞:读端和写端都存在,从管道中读数据,若管道为空,则发生读阻塞。
3. 读返回0:当写端关闭,从管道中读数据,若管道中有数据,则读到数据;
若管道中没有数据,read则返回0,不再阻塞。
4. 管道破裂:读端关闭,向管道中写入数据,发生管道破裂(异常)

练习

父进程从终端获取数据,子进程负责打印

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
int main(int argc,const char*argv[])
{
    int pipefd[2];
	int ret = pipe(pipefd);
	if (ret < 0)
	{
		perror("pipe error");
		return -1;
	}
    char buff[1024] = {0};
    fgets(buff,sizeof(buff),stdin);
    pid_t pid = fork();
    if(pid > 0)
    {
        close(pipefd[0]);
        write(pipefd[1],buff,strlen(buff));
        close(pipefd[1]);
        wait(NULL);

    }
    else if(0 == pid)
    {
        close(pipefd[1]);
		read(pipefd[0], buff, sizeof(buff));
		printf("buff = %s\n", buff);

		close(pipefd[0]);
    }
    else
	{
		perror("fork error");
	}

	return 0;
}

有名管道

操作流程

1. 创建管道文件 mkfifo、mkfifo();
2. 打开管道文件 open();
3. 写管道文件 write();
4. 读管道文件 read();
5. 关闭管道文件 close();
6. 删除管道文件 int remove(const char *pathname);

管道本质

内核空间的一段缓冲区,但这块缓冲区和一个管道文件相关联。

主要函数

int mkfifo(const char *pathname, mode_t mode);
功能:创建一个管道文件
参数:
pathname:管道文件的名称
mode:管道文件的读写执行权限
返回值:
成功:0;
失败:-1

练习

一个进程负责从终端获取数据,通过有名管道,使另一个进程打印数据

文件1:
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<errno.h>
int main(int argc,const char*argv[])
{
    int ret = mkfifo("./mkfifo",0664);
    if(ret != 0 && errno != EEXIST)
    {
        perror("mkfifo error");
        return -1;
    }
    int fd1 = open("./1.txt",O_RDONLY);
    int fd2 = open("./mkfifo",O_WRONLY);
    if(fd1 < 0 || fd2 < 0)
    {
        perror("open error");
        return -1;
    }
    char buff[1024] = {0};
    ssize_t cnt = read(fd1,buff,sizeof(buff));
    while(cnt != 0)
    {
        write(fd2,buff,cnt);
        cnt = read(fd1,buff,sizeof(buff));
    }
   

    close(fd1);
    close(fd2);
    remove("./mkfifo");
    return 0;

}
文件2:
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<errno.h>
int main(int argc,const char*argv[])
{
    int ret = mkfifo("./mkfifo",0664);
    if(ret != 0 && errno != EEXIST)
    {
        perror("mkfifo error");
        return -1;
    }

    int fd1 = open("./mkfifo",O_RDONLY);
    int fd2 = open("./2.txt",O_WRONLY | O_CREAT | O_TRUNC,0664);
    if(fd1 < 0 || fd2 < 0)
    {
        perror("open error");
        return -1;
    }
    char buff[1024] = {0};
    ssize_t cnt = read(fd1,buff,sizeof(buff));
    while(cnt != 0)
    {
        write(fd2,buff,cnt);
        cnt = read(fd1,buff,sizeof(buff));

    }
    close(fd1);
    close(fd2);
    return 0;

}
  


 






 


网站公告

今日签到

点亮在社区的每一天
去签到