Linux系统编程-进程间通信

进程间通信(IPC,InterProcess Communication)是指在不同进程之间传播或交换信息。
*IPC的方式通常有管道(包括无名管道和命名管道)、消息队列、信号量、共享存储、Socket、Streams等。其中 Socket和Streams支持不同主机上的两个进程IPC。(重点)

简介

通信方式
1、无名管道半双工(男放-女取)
2、命名管道半双工(男放-女取)
3、消息队列两个半双工,实现全双工的功能
4、共享内存全双工(男放女读,不拿走 | 女放男读,不拿走)
5、信号
6、信号量

 

1、相关知识点

man 1 kill 显示的是命令行工具 kill 的手册;

man 2 kill 显示的是系统调用 kill 的手册;

history:

 

int main(int argc, char **argv) 

主函数main怎么传参呢?

> > > ./a.out 1 2 //将1传递给argc,将2传递给**argv

2、pipe(无名管道)

1 #include <unistd.h>
2 int pipe(int fd[2]);    // 返回值:若成功返回0,失败返回-1
fd[0]fd[1]
  • 半双工,(只能二选一);
  • 亲缘关系的进程之间的通信
  • 只存在于内存

 

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

int main()
{
	int fd[2];
	int pid;
	char buf[128];
	int status;

//	int pipe(int pipefd[2]);
	if(pipe(fd) == -1)
	{
		printf("creat pipe failed\n");
	}
	pid = fork();

	if(pid<0)
	{
		printf("creak child failed\n");
	}
	else if(pid > 0)  //父进程
	{
		//sleep(3);//睡3s
		printf("this is father\n");
		close(fd[0]);
		write(fd[1],"hello form father",strlen("hello form father"));
		wait(&status);//等待子进程退出
	}
	else  //子进程
	{
		printf("this is chile\n");
		close(fd[1]);
		read(fd[0],buf,128);
		printf("read from father:%s\n",buf);
		exit(0);//退出
	}
	return 0;
}

3、mkfifo(命名管道)FIFO

  • 半双工
  • 1 #include <sys/stat.h>
    2 // 返回值:成功返回0,出错返回-1
    3 int mkfifo(const char *pathname, mode_t mode);
    

4、消息队列

  • 可实现全双工(本身是单向)

  • 实验 msgGet.c & msgSend.c

 

//msgGet.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
#include <sys/msg.h>
// 创建或打开消息队列:成功返回队列ID,失败返回-1
int msgget(key_t key, int flag);
// 添加消息:成功返回0,失败返回-1
int msgsnd(int msqid, const void *ptr, size_t size, int flag);
// 读取消息:成功返回消息数据的长度,失败返回-1
int msgrcv(int msqid, void *ptr, size_t size, long type,int flag);
// 控制消息队列:成功返回0,失败返回-1
//int msgctl(int msqid, int cmd, struct msqid_ds *buf);


struct msgbuf{
	long mtype;   //message type, must be > 0
	char mtext[128];//message data
};

int main()
{
	struct msgbuf readBuf;
	struct msgbuf sendBuf = {988,"thank you for reach"};
	//1.获取
	int msgId = msgget(0x1234, IPC_CREAT|0777);//0777,可读可写可执行
	
	if(msgId == -1)
	{	//没获取到消息队列
		printf("get que failuer\n");
	}
	//获取到了消息队列

	msgrcv(msgId, &readBuf,sizeof(readBuf.mtext),888,0);
	// 直到读到888,否阻塞
	printf("read from que:%s\n",readBuf.mtext);	
	msgsnd(msgId,&sendBuf,strlen(sendBuf.mtext),0);//非阻塞的方式

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

// 创建或打开消息队列:成功返回队列ID,失败返回-1
int msgget(key_t key, int flag);
// 添加消息:成功返回0,失败返回-1
int msgsnd(int msqid, const void *ptr, size_t size, int flag);
// 读取消息:成功返回消息数据的长度,失败返回-1
int msgrcv(int msqid, void *ptr, size_t size, long type,int flag);
// 控制消息队列:成功返回0,失败返回-1
//int msgctl(int msqid, int cmd, struct msqid_ds *buf);

struct msgbuf{
	long mtype;   //message type, must be > 0 //888
	char mtext[128];//message data
};

int main()
{
	struct msgbuf sendBuf = {888,"this is message from quen"};
	struct msgbuf readBuf;

	//1.获取
	int msgId = msgget(0x1234, IPC_CREAT|0777);//0777,可读可写可执行
	if(msgId == -1)
	{	//没有获取到消息队列
		printf("get que failuer\n");
	}
	//获取到了消息队列

	msgsnd(msgId,&sendBuf,strlen(sendBuf.mtext),0);//非阻塞的方式发送
	printf("send over\n");
	msgrcv(msgId,&readBuf,sizeof(readBuf.mtext),988,0);
	//读另一个 mtype,直到读到988,否则阻塞
	printf("return from get:%s\n",readBuf.mtext);


	return 0;
}

4.1 ftok
  • 函数ftok把一个已存在的路径名和一个整数标识符转换成一个key_t值,称为IPC键值(也称IPC key键值)
  • 返回值
    成功:返回key_t值(即IPC 键值)
    出错:-1,错误原因存于error中

如指定文件的索引节点号为65538,换算成16进制为0x010002,而你指定的ID值为38,换算成16进制为0x26,则最后的key_t返回值为0x26010002。

可以利用 ls -l /ls -al 来查找索引节点号

//msgGet.c
//利用了ftok
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
// 创建或打开消息队列:成功返回队列ID,失败返回-1
int msgget(key_t key, int flag);
// 添加消息:成功返回0,失败返回-1
int msgsnd(int msqid, const void *ptr, size_t size, int flag);
// 读取消息:成功返回消息数据的长度,失败返回-1
int msgrcv(int msqid, void *ptr, size_t size, long type,int flag);
// 控制消息队列:成功返回0,失败返回-1
//int msgctl(int msqid, int cmd, struct msqid_ds *buf);

struct msgbuf{
	long mtype;   //message type, must be > 0
	char mtext[128];//message data
};

int main()
{
	struct msgbuf readBuf;
	struct msgbuf sendBuf = {988,"thank you for reach"};

	key_t key;
	key = ftok(".",1);//“.”表示当前目录,第二个参数随便一个数字或者字符
	printf("key = %x\n",key); //以16进制%x来表示
	//通过key来找到相关的链表

	//1.获取
	int msgId = msgget(key, IPC_CREAT|0777);//0777,可读可写可执行
	
	if(msgId == -1)
	{	//没获取到消息队列
		printf("get que failuer\n");
	}
	//获取到了消息队列

	msgrcv(msgId, &readBuf,sizeof(readBuf.mtext),888,0);
	// 直到读到888,否阻塞
	printf("read from que:%s\n",readBuf.mtext);	
	msgsnd(msgId,&sendBuf,strlen(sendBuf.mtext),0);//非阻塞的方式

	return 0;
}

 

//msgSend.c
//利用了ftok
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>

// 创建或打开消息队列:成功返回队列ID,失败返回-1
int msgget(key_t key, int flag);
// 添加消息:成功返回0,失败返回-1
int msgsnd(int msqid, const void *ptr, size_t size, int flag);
// 读取消息:成功返回消息数据的长度,失败返回-1
int msgrcv(int msqid, void *ptr, size_t size, long type,int flag);
// 控制消息队列:成功返回0,失败返回-1
//int msgctl(int msqid, int cmd, struct msqid_ds *buf);

struct msgbuf{
	long mtype;   //message type, must be > 0 //888
	char mtext[128];//message data
};

int main()
{
	struct msgbuf sendBuf = {888,"this is message from quen"};
	struct msgbuf readBuf;

	key_t key;
	key = ftok(".",1);//“.”表示当前目录,第二个参数随便一个数字或在字符
	printf("key = %x\n",key);//以16进制%x来表示
	//通过key来找到相关的链表

	//1.获取
	int msgId = msgget(key, IPC_CREAT|0777);//0777,可读可写可执行
	if(msgId == -1)
	{	//没有获取到消息队列
		printf("get que failuer\n");
	}
	//获取到了消息队列

	msgsnd(msgId,&sendBuf,strlen(sendBuf.mtext),0);//非阻塞的方式发送
	printf("send over\n");
	msgrcv(msgId,&readBuf,sizeof(readBuf.mtext),988,0);
	//读另一个 mtype,直到读到988,否则阻塞
	printf("return from get:%s\n",readBuf.mtext);


	return 0;
}

 

4.2 msgct1
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

int msgctl(int msqid, int cmd, struct msqid_ds *buf);//
//参数1:ID,
//参数2:一般用命令 IPC_RMID: 把消息队列的链表从内核中移除
//参数3:一般是 NULL
  • 程序

 

//msgGet.c
//利用了ftok
//利用了msgct1
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>

// 创建或打开消息队列:成功返回队列ID,失败返回-1
int msgget(key_t key, int flag);
// 添加消息:成功返回0,失败返回-1
int msgsnd(int msqid, const void *ptr, size_t size, int flag);
// 读取消息:成功返回消息数据的长度,失败返回-1
int msgrcv(int msqid, void *ptr, size_t size, long type,int flag);
// 控制消息队列:成功返回0,失败返回-1
int msgctl(int msqid, int cmd, struct msqid_ds *buf);

struct msgbuf{
	long mtype;   //message type, must be > 0
	char mtext[128];//message data
};

int main()
{
	struct msgbuf readBuf;
	struct msgbuf sendBuf = {988,"thank you for reach"};

	key_t key;
	key = ftok(".",1);//“.”表示当前目录,第二个参数随便一个数字或者字符
	printf("key = %x\n",key); //以16进制%x来表示
	//通过key来找到相关的链表

	//1.获取
	int msgId = msgget(key, IPC_CREAT|0777);//0777,可读可写可执行
	
	if(msgId == -1)
	{	//没获取到消息队列
		printf("get que failuer\n");
	}
	//获取到了消息队列

	msgrcv(msgId, &readBuf,sizeof(readBuf.mtext),888,0);
	// 直到读到888,否阻塞
	printf("read from que:%s\n",readBuf.mtext);	
	msgsnd(msgId,&sendBuf,strlen(sendBuf.mtext),0);//非阻塞的方式

	msgctl(msgId,IPC_RMID,NULL);
	return 0;
}
//msgSend.c
//利用了ftok
//利用了msgct1
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>

//	int msggest(key_t key, int msgflg);
//	int msgsnd(int msqid, const void *msgp, size_t msgsz,int msgflg);
//	ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp);

struct msgbuf{
	long mtype;   //message type, must be > 0 //888
	char mtext[128];//message data
};

int main()
{
	struct msgbuf sendBuf = {888,"this is message from quen"};
	struct msgbuf readBuf;

	key_t key;
	key = ftok(".",1);//“.”表示当前目录,第二个参数随便一个数字或在字符
	printf("key = %x\n",key);//以16进制%x来表示
	//通过key来找到相关的链表

	//1.获取
	int msgId = msgget(key, IPC_CREAT|0777);//0777,可读可写可执行
	if(msgId == -1)
	{	//没有获取到消息队列
		printf("get que failuer\n");
	}
	//获取到了消息队列

	msgsnd(msgId,&sendBuf,strlen(sendBuf.mtext),0);//非阻塞的方式发送
	printf("send over\n");
	msgrcv(msgId,&readBuf,sizeof(readBuf.mtext),988,0);
	//读另一个 mtype,直到读到988,否则阻塞
	printf("return from get:%s\n",readBuf.mtext);

	msgctl(msgId,IPC_RMID,NULL);
	return 0;
}

 

5、*共享内存(Shared Memory)

进程间的五种通信方式介绍_进程间通信-CSDN博客

  • 如何查看操作系统中有那些共享内存
  • 命令**ipcs -m**
  • 命令ipcrm -m shmid
  • 删除id为shmid的共享内存段
步骤做法
1、创建共享内存/打开 Shared Memory Get
2、映射 Shared Memory Attach
3、数据交换
4、释放共享内存 Shared Memory Disconnect
5、干掉 Shared Memory Controls

5.1 共享内存的API 

 

#include <sys/shm.h>
// 创建或获取一个共享内存:成功返回共享内存ID,失败返回-1
int shmget(key_t key, size_t size, int flag);
// 连接共享内存到当前进程的地址空间:成功返回指向共享内存的指针,失败返回-1
void *shmat(int shm_id, const void *addr, int flag);
// 断开与共享内存的连接:成功返回0,失败返回-1
int shmdt(void *addr); 
// 控制共享内存的相关信息:成功返回0,失败返回-1
int shmctl(int shm_id, int cmd, struct shmid_ds *buf);

6、信号
Linux 信号(signal) - 简书 (jianshu.com)

对于 Linux来说,实际信号是软中断,许多重要的程序都需要处理信号。信号,为 Linux 提供了一种处理异步事件的方法。比如,终端用户输入了 ctrl+c 来中断程序,会通过信号机制停止一个程序。
6.1 相关命令
kill 9 PID来杀死进程

kill -l 命令用于列出系统上可用的信号列表

> kill -l
1) SIGHUP       2) SIGINT       3) SIGQUIT      4) SIGILL
5) SIGTRAP      6) SIGABRT      7) SIGBUS       8) SIGFPE
9) SIGKILL     10) SIGUSR1     11) SIGSEGV     12) SIGUSR2
13) SIGPIPE    14) SIGALRM     15) SIGTERM     16) SIGSTKFLT
...

SIGINT (Ctrl + c)

6.2 信号的处理
信号的处理方法    
忽略    大多数信号可以使用这个方式来处理,但是有两种信号不能被忽略(分别是 SIGKILL和SIGSTOP)
捕捉    类似于单片机里的中断,中断来时,执行中断处理函数
默认动作    当发生了该信号,系统会自动执行
6.3 API
6.3.1atoi

int atoi(const char *str);
  • C标准库函数,用于将字符串转换为整数(int)
kill [选项] 进程号
  • -kill -9 id 表示杀死该进程
6.4 携带消息-收发信号
  • 发消息

    • 1、用什么发?

    • 2、怎么放入消息?

  • 收消息

    • 1、用什么绑定函数
    • 2、如何读出消息
6.4.1 sigaction
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);

struct sigaction {
   void (*sa_handler)(int); //信号处理程序,不接受额外数据,SIG_IGN 为忽略,SIG_DFL 为默认动作
   void (*sa_sigaction)(int, siginfo_t *, void *); //信号处理程序,能够接受额外数据和sigqueue配合使用
   sigset_t  sa_mask;
   //阻塞关键字的信号集,可以再调用捕捉函数之前,把信号添加到信号阻塞字,信号捕捉函数返回之前恢复为原先的值。
   int sa_flags;//影响信号的行为SA_SIGINFO表示能够接受数据
 };
//回调函数句柄sa_handler、sa_sigaction只能任选其一

 

  • 第一个参数 num:
  • 第二个参数sigaction:
    • *p1
    • *p2
      • num
      • 结构体
      • 指针
        • 空:无数据
        • 非空:有数据
    • mask:阻塞
    • intflag:
  • 第三个参数sigaction:备份原有的信号操作
6.4.2 sigqueqe
#include <signal.h>
int sigqueue(pid_t pid, int sig, const union sigval value);
union sigval {
   int   sival_int;
   void *sival_ptr;
 };
  • sig :发的是什么信号
6.5 实验
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>


//	int sigaction(int signum,const struct sigaction *act,struct sigaction *oldact);
void handler(int signum, siginfo_t *info, void *contest)
{
	printf("get sgnum %d\n",signum);
	if(contest != NULL)
	{
		printf("get data %d\n",info->si_int);
		printf("get data = %d\n",info->si_value.sival_int);
		printf("form:%d\n",info->si_pid);
	}
}

int main()
{
	struct sigaction act;
	printf("pid = %d\n",getpid());
	act.sa_sigaction = handler;
	act.sa_flags = SA_SIGINFO;//能得到信息

	sigaction(SIGUSR1,&act,NULL);

	while(1);
	return 0;
}

 

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

int main(int argc, char **argv)
{
	int signum;
	int pid;

	signum = atoi(argv[1]);
	pid = atoi(argv[2]);

	union sigval value;
	value.sival_int = 100;

//	int sigqueue(pid_t pid, int sig,const nuion sigval value);
	sigqueue(pid, signum, value);
	printf("%d,done\n",getpid());

	return 0;
}

7、信号量

  • 钥匙:信号量

  • 房间:临界资源

    • 临界资源:一次仅允许一个进程使用的资源称为临界资源(输入机、打印机等)
  • 信号量集:好几个钥匙

  • P 操作:拿锁

  • V操作:放回锁

1 #include <sys/sem.h>
2 // 创建或获取一个信号量组:若成功返回信号量集ID,失败返回-1
3 int semget(key_t key, int num_sems, int sem_flags);
4 // 对信号量组进行操作,改变信号量的值:成功返回0,失败返回-1
5 int semop(int semid, struct sembuf semoparray[], size_t numops);  
6 // 控制信号量的相关信息
7 int semctl(int semid, int sem_num, int cmd, ...);
## 7、信号量

* 钥匙:信号量
* 房间:临界资源 
  * 临界资源:一次仅允许一个进程使用的资源称为临界资源(输入机、打印机等)

* 信号量集:好几个钥匙

* P 操作:拿锁
* V操作:放回锁

```c
1 #include <sys/sem.h>
2 // 创建或获取一个信号量组:若成功返回信号量集ID,失败返回-1
3 int semget(key_t key, int num_sems, int sem_flags);
4 // 对信号量组进行操作,改变信号量的值:成功返回0,失败返回-1
5 int semop(int semid, struct sembuf semoparray[], size_t numops);  
6 // 控制信号量的相关信息
7 int semctl(int semid, int sem_num, int cmd, ...);

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值