进程间通信

这篇博客深入探讨了进程间通信的各种方式,包括有名管道(FIFO)和无名管道(PIPO),强调了它们在数据传输中的特点和应用场景。此外,还介绍了信号机制,如闹钟函数和kill函数,以及消息队列、共享内存和信号量等在多进程通信中的作用,这些机制为进程间的协调和数据交换提供了有效支持。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、进程间通信方式

1、有名管道(FIFO)和管道(PIPO)。

管道:(1)单向,先进先出,把一个进程的输入和输出连接在一起。

           (2)在管道的尾部写入程序,在管道的头部读出数据。

           (3)数据被一个进程读取后,将从管道删除。

           (4)进程阻塞情况:读空管道时,或者进程已经写满。、

*(无名)管道:父进程和子进程之间的通信        int   pipe( int   fieldis[2] ) ;     数组保存两个文件描述符

           fieldis[0]  用于读管道      fieldis[1]  用于写管道

父进程写程序,子进程读程序:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
void ReadData(int fd) 
{
    int ret;
    char buf[32]={0};
    while(1) 
    {
	ret = read(fd,buf,sizeof(buf));
	if(-1 == ret) 
	{
		perror("read");
		exit(1);
	}
	if(!strcmp(buf,"bye")) 
	{
		break;
	}

	printf("read from pipe: %s\n",buf);
	memset(buf, 0, sizeof(buf));

    }

    close(fd);
}

void WriteData(int fd) 
{
    int ret;
    char buf[32]={0};
    while(1) 
    {
	scanf("%s",buf);
	ret = write(fd,buf,strlen(buf));
	if(-1 == ret) 
	{
		perror("write");
		exit(1);
	}
	if(!strcmp(buf,"bye")) 
	{
    	    break;
	}
	    memset(buf, 0, sizeof(buf));
	}
	close(fd);
}

int main() 
{
    int ret ,fd[2]={0};
    pid_t pid;
    ret = pipe(fd);
    if(-1 == ret) 
    {
	perror("pipe");
	exit(1);
    }
    pid = fork();
    if(-1 == pid) 
    {
	perror("fork");
	exit(1);
    } 
    else if(0 == pid)               //子进程 
    {
	close(fd[1]);               // 关闭写端口 
	ReadData(fd[0]);            // fd[0]读数据
    } 
    else                            //父进程 
    {
	close(fd[0]);               // 关闭读端口
	int status;
	WriteData(fd[1]);           // fd[1]写数据

	wait(&status);
    }
	return 0;
}

必须在使用fork()函数之前调用pipe()创建管道,否则子进程不会继承父进程的文件描述符 

 

*有名管道:任意两个进程间的通信

写文件

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

int main()
{
    int ret, fd;
    char buf[32] = {0};                // 管道写数据

    fd = open("fifo.tmp", O_WRONLY);   // 以只写方式打开
    if (-1 == fd)
    {
	perror("open");
	exit(1);
    }

    while (1)
    {
        scanf("%s", buf);
        ret = write(fd, buf, strlen(buf));
        if (-1 == ret)
        {
            perror("read");
    	    exit(1);
        }

        if (!strcmp(buf, "bye"))  // buf内容为 bye 时结束
        {
	    break;
        }

        memset(buf, 0, sizeof(buf));  // 清空buf

    }
    close(fd);

    return 0;
}

读文件

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

int main()
{
    int ret, fd;
    char buf[32] = {0};             
    ret = mkfifo("fifo.tmp", 666);  //创建有名管道
    if (ret == -1)
    {
	perror("mkfifo");
	exit(1);
    }

    fd = open("fifo.tmp", O_RDONLY);  // 以只读方式打开
    if (-1 == fd)
    {
	perror("open");
	exit(1);
    }

    while (1)
    {
        ret = read(fd, buf, sizeof(buf));   //  从管道读取数据
	if (-1 == ret)
	{
		perror("read");
		exit(1);
	}

	if (!strcmp(buf, "bye"))
	{
		break;
	}

	printf("%s\n", buf);

	memset(buf, 0, sizeof(buf));  // 清空buf
    }

    close(fd);

    unlink("fifo.tmp");               // 删除fifo.tmp文件
    return 0;
}

二、信号

#include <stdio.h>
#include <signal.h>

void print(int num)  //num 信号的值
{
	printf("helloworld! %d \n", num);
}

int main()
{
    signal(9, SIG_IGN);  //当前进程收到信号2的时候,执行print函数(SIG_IGN表示2

    while (1);  // 保证进程不死
    return 0;
}

闹钟函数

#include <stdio.h>
#include <unistd.h>
#include <signal.h>

void print(int num)
{
    alarm(1);
    printf("helloworld\n");
}

int main()
{
    alarm(1);                // 每秒钟发送一个SIGALRM(闹钟)信号,alarm函数生命周期只有一次
    signal(SIGALRM, print);  // 每接收到一个SIGALRM(闹钟)信号,就调用一次print函数

    while (1);

    return 0;
}

kil函数  ---- 发送给进程

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

int main()
{
	int id;

	scanf("%d", &id);
	kill(id, SIGKILL);  // 杀死进程号为id的进程

	return 0;	
}

raise函数  ---- 发送信号给本进程

#include <stdio.h>
#include <signal.h>

int main()
{
	raise(2);    // 等价于  kill(getpid(),2);  终止本程序
	while (1);
	return 0;
}

三、消息队列

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>

#define MSGKEY   1234  // 键值

struct msgbuf {
	long mtype;     /* message type, must be > 0 */ // 消息类型
	char mtext[64];  /* message data */ // 消息数据
};

int main()
{
	struct msgbuf mbuf;
	int ret;

	int msgid = msgget(MSGKEY, IPC_CREAT | IPC_EXCL);  // 创建消息队列
	if (-1 == msgid)
	{
		perror("msgget");
		exit(1);
	}

	pid_t pid = fork();          // 创建进程
	if (-1 == pid)
	{
		perror("fork");
		exit(1);
	}
	else if (0 == pid)    //子进程发送
	{
		while (1)
		{
			memset(&mbuf, 0, sizeof(mbuf));  // 清空buf
	
			mbuf.mtype = 1;   //消息类型
			scanf("%s", mbuf.mtext);

			ret = msgsnd(msgid, &mbuf, sizeof(mbuf.mtext), 0);   // 发送消息
			if (-1 == ret)
			{
				perror("msgsnd");
				exit(1);
			}

			if (!strcmp(mbuf.mtext, "bye"))  // 输入bye时,结束进程
			{
				mbuf.mtype = 2;              
				msgsnd(msgid, &mbuf, sizeof(mbuf.mtext), 0);  // 把消息发送给父进程
				break;
			}
		}
	}
	else                 //父进程接收
	{
		while (1)
		{
			memset(&mbuf, 0, sizeof(mbuf));
		
			ret = msgrcv(msgid, &mbuf, sizeof(mbuf.mtext), 2, 0);
			if (-1 == ret)
			{
				perror("msgrcv");
				exit(1);
			}

			if (!strcmp(mbuf.mtext, "bye"))   // // 收到bye时,结束进程
			{	
				kill(pid, 2);   // 杀死子进程
				break;
			}
			
			printf("\t\t\t%s\n", mbuf.mtext);

			// memset(&mbuf, 0, sizeof(mbuf));
		}
	}

	sleep(1);
	msgctl(msgid, IPC_RMID, NULL); // 删除创建消息队列的信号量msgid

	return 0;
}
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>

#define MSGKEY   1234   // key值 (键值)

struct msgbuf {
	long mtype;     /* message type, must be > 0 */ // 消息类型
	char mtext[64];  /* message data */   // 消息数据
};

int main()
{
	struct msgbuf mbuf;
	int ret;

	int msgid = msgget(MSGKEY, 0);  // 创建消息队列
	if (-1 == msgid)
	{
		perror("msgget");
		exit(1);
	}

	pid_t pid = fork();  // 创建进程
	if (-1 == pid)
	{
		perror("fork");
		exit(1);
	}
	else if (0 == pid)    //子进程发送
	{
		while (1)
		{
			memset(&mbuf, 0, sizeof(mbuf));
	
			mbuf.mtype = 2;                       //消息类型
			scanf("%s", mbuf.mtext);

			ret = msgsnd(msgid, &mbuf, sizeof(mbuf.mtext), 0);  // 发送消息
			if (-1 == ret)
			{
				perror("msgsnd");
				exit(1);
			}

			if (!strcmp(mbuf.mtext, "bye"))
			{
				mbuf.mtype = 1;
				msgsnd(msgid, &mbuf, sizeof(mbuf.mtext), 0);  //发给父进程
				break;
			}
		}
	}
	else                 //父进程接收
	{
		while (1)
		{
			memset(&mbuf, 0, sizeof(mbuf));
		
			ret = msgrcv(msgid, &mbuf, sizeof(mbuf.mtext), 1, 0);  // 接收消息
			if (-1 == ret)
			{
				perror("msgrcv");
				exit(1);
			}

			if (!strcmp(mbuf.mtext, "bye"))
			{
				kill(pid, 2);  // 杀死收到消息类型为2的进程(即子进程)
				break;
			}
			
			printf("\t\t\t%s\n", mbuf.mtext);   

		// memset(&mbuf, 0, sizeof(mbuf));
		}
	}

	return 0;
}

四、共享内存

#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

#define SHMKEY   1234
#define SHMSIZE  4096       //以页为单位分配共享内存
#define SEMKEY   1234

union semun {
	int              val;    /* Value for SETVAL */
	struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
	unsigned short  *array;  /* Array for GETALL, SETALL */
	struct seminfo  *__buf;  /* Buffer for IPC_INFO(Linux specific) */
};

void sem_p(int semid)
{
	int ret;
	struct sembuf sbuf;

	sbuf.sem_num = 0;      //第一个 从0开始
	sbuf.sem_op = -1;      //p操作
	sbuf.sem_flg = SEM_UNDO;  

	ret = semop(semid, &sbuf, 1);
	if (-1 == ret)
	{
		perror("semop");
		return;
	}
}

void sem_v(int semid)
{
	int ret;
	struct sembuf sbuf;

	sbuf.sem_num = 0;      //第一个 从0开始
	sbuf.sem_op = 1;      //v操作
	sbuf.sem_flg = SEM_UNDO;  

	ret = semop(semid, &sbuf, 1);
	if (-1 == ret)
	{
		perror("semop");
		return;
	}
}

int main()
{
	int shmid, semid, ret;
	void *shmaddr;
	int count = 0;

	shmid = shmget(SHMKEY, SHMSIZE, IPC_CREAT | IPC_EXCL);   //创建共享内存
	if (-1 == shmid)
	{
		perror("shmget");
		exit(1);
	}

	semid = semget(SEMKEY, 1, IPC_CREAT | IPC_EXCL);    //创建信号量
	if (semid == -1)
	{
		perror("semget");
		exit(1);
	}

	union semun unsem;
	unsem.val = 1;    //初始化成二值信号量
	ret = semctl(semid, 0, SETVAL, unsem);    //初始化信号量
	if (-1 == ret)
	{
		perror("semctl");
		exit(1);
	}

	shmaddr = shmat(shmid, NULL, 0);    //映射到虚拟地址空间
	if (NULL == shmaddr)
	{
		perror("shmat");
		exit(1);
	}

	*(int *)shmaddr = count;       //数据写到共享内存

	while (1)
	{
		sem_p(semid);             //p操作  拔钥匙
		count = *(int *)shmaddr;     //读取数据
		usleep(100000);
		if (count >= 100)
		{
			break;
		}

		printf("Process A : count = %d\n", count);

		count++;

		*(int *)shmaddr = count;    //写回共享内存
		sem_v(semid);           //v操作  加一操作  插钥匙
	}

	shmdt(shmaddr);               //解除映射
	shmctl(shmid, IPC_RMID, NULL);

	semctl(semid, 0, IPC_RMID);  //删除创建共享内存的信号量semid
	return 0;
}
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

#define SHMKEY   1234
#define SHMSIZE  4096       //以页为单位分配共享内存
#define SEMKEY   1234

union semun {
	int              val;    /* Value for SETVAL */
	struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
	unsigned short  *array;  /* Array for GETALL, SETALL */
	struct seminfo  *__buf;  /* Buffer for IPC_INFO(Linux specific) */
};

void sem_p(int semid)
{
	int ret;
	struct sembuf sbuf; 

	sbuf.sem_num = 0;      //第一个 从0开始
	sbuf.sem_op = -1;      //p操作
	sbuf.sem_flg = SEM_UNDO;  

	ret = semop(semid, &sbuf, 1);
	if (-1 == ret)
	{
		perror("semop");
		return;
	}
}

void sem_v(int semid)
{
	int ret;
	struct sembuf sbuf;

	sbuf.sem_num = 0;      //第一个 从0开始
	sbuf.sem_op = 1;      //v操作
	sbuf.sem_flg = SEM_UNDO;  

	ret = semop(semid, &sbuf, 1);
	if (-1 == ret)
	{
		perror("semop");
		return;
	}
}

int main()
{
	int shmid, semid, ret;
	void *shmaddr;
	int count = 0;

	shmid = shmget(SHMKEY, SHMSIZE, 0);   //创建共享内存
	if (-1 == shmid)
	{
		perror("shmget");
		exit(1);
	}

	semid = semget(SEMKEY, 1, 0);    //创建信号量
	if (semid == -1)
	{
		perror("semget");
		exit(1);
	}

	shmaddr = shmat(shmid, NULL, 0);    //映射到虚拟地址空间
	if (NULL == shmaddr)
	{
		perror("shmat");
		exit(1);
	}

	while (1)
	{
		sem_p(semid);                  //  p操作  拔钥匙
		count = *(int *)shmaddr;       //  读取数据
		usleep(100000);
		if (count >= 100)
		{
			break;
		}

		printf("Process B : count = %d\n", count);

		count++;

		*(int *)shmaddr = count;    // 写回共享内存
		sem_v(semid);               // v操作  加一操作  插钥匙
	}

	shmdt(shmaddr);                 // 解除映射
	return 0;
}

五、信号量

用于保护临界资源

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值