回顾
共7种方式:
古老的进程间通信方式:
管道:
无名管道
有名管道
信号
系统V IPC进程对象
共享内存
消息队列
信号量集
socket通信 //网络
-------------------------
无名管道
pipe()
特点:
用于亲缘关系进程间
(继承相关的文件描述符 -- 对应实际同一个管道)
管道的特点:
1.形成的数据流
2.FIFO(first in first out ) // -- 数据结构的特点 -- 队列(排队)
FIFO (first in last out)// 栈的特点
3.管道中的的数据,读取之后,就没有了
管道读写规则:
a.读端存在,写管道
b.独断不存在,写管道 --- 股电脑破裂 SIGPIPE 信号 --- 进程收到这信号后会被结束掉
c.写段存在,读管道 --- 没数据,读操作阻塞
d.写端变速存在,读管道 -- 没数据,读操作不阻塞, 立即返回 返回0 表示没数据
有名管道:
创建
mkfifo // 在系统中创建了一个 管道文件的名字
// 不同的进程(毫无亲缘关系的进程)可以通过该名字 最终操作到同一个管道进而实现通信
------------------------------------------
信号通信:
异步、同步:
用户自定义操作:
signal
#include <signal.h>
typedef void (*sighandler_t)(int);
// ===》void (*xx)(int); == void fun(int);
===》xx是 void fun(int) 类型函数的函数指针
===》typedef void(*xx)(int) sighandler_t;
sighandler_t signal(int signum, sighandler_t handler);
功能:
用于向操作系统注册信号处理函数,以便在特定信号发生时执行相应的操作。
参数:
@signum 是信号编号,它是一个整数,用于指定要处理的信号。
@handler是一个函数指针,它指向的信号处理函数将在接收到 signum
指定的信号时被 调用。
- 当handler为
SIG_IGN
时,表示忽略此信号(需要注意的是,SIGKILL
和SIGSTOP
信号不能被忽略)。 - 当handler为
SIG_DFL
时,表示接到此信号后的动作是系统默认动作。 - 当handler是一个函数地址时,表示当接收到对应编号的
signo
信号时,执行该函数。
返回值:
signal
函数的返回值也是一个 sighandler_t
类型的函数指针,它指向之前注册 在 signum
上的信号处理函数。如果注册失败,signal
函数将返回 SIG_ERR
。
练习:
1、自定义处理信号10和12
#include<stdio.h>
#include<unistd.h>
#include<signal.h>void handler_10_12(int sno)
{printf("recieve %d\n",sno);
}int main(int argc, const char *argv[])
{if(signal(SIGUSR1,handler_10_12) == SIG_ERR){perror("signal fail");return -1;}if(signal(SIGUSR2,handler_10_12) == SIG_ERR){perror("signal fail");return -1;}while(1){printf("hello\n");sleep(1);}return 0;
}
2、通过信号处理的方式,回收僵尸态子进程 // 异步处理收尸,不阻碍父进程的操作
#include<stdio.h>
#include<unistd.h>
#include<signal.h>
#include<stdlib.h>
#include <sys/wait.h>void do_wait(int sno)
{wait(0);printf("child wait\n");
}int main(int argc, const char *argv[])
{if(signal(SIGCHLD,do_wait) == SIG_ERR){perror("signal fail");return -1;}pid_t pid = fork();if(pid < 0){perror("fork fail");return -1;}if(pid > 0){int i = 0;while(1){printf("father = %d\n",i++);sleep(1);}}if(pid == 0){int i = 0;while(1){printf("child = %d\n",i++);sleep(1);}}return 0;
}
3、
函数: alarm(); 计数 括号内写秒数,到时发送SIGALRM信号 则说明超时1次。
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>int count;void handler(int signo)
{printf("---signal---%d\n",++count);;if(count == 3){printf("---timeout---\n");exit(0);}alarm(3);
}int main(int argc, const char *argv[])
{alarm(3);char buf[1024] = {0};signal(SIGALRM,handler);while(1){fgets(buf,sizeof(buf),stdin);count = 0;alarm(3);}return 0;
}
kill
#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);
功能:通过该函数可以给pid进程发送信号为sig的系统信号。
参数: @pid 要接收信号的进程pid
@sig 当前程序要发送的信号编号 《=== kill -l
返回值: 成功 0
失败 -1;
raise
int raise(int sig)<==> kill(getpid(),int sig);
功能:给进程自己发送sig信号
pause
int pause(void);
功能:
进程暂停,不再继续执行,除非收到其他信号。
IPC对象:
(*)共享内存 //进程间效率最高的通信方式
shm,sem,msg
system v : 共享内存 信号量集
IPC对象操作通用框架:
0x ftok
key值 ==> 申请 ==》读写 ==》关闭 ==》卸载
key值 //
申请 -- xxxget //key <--> ipc对象
读写
关闭
卸载
key值:===》唯一键值
创建方式有三种:
1、IPC_PRIVATE 固定的私有键值,其值等于 0x0
一般用于有亲缘关系的进程间使用。
2、ftok()创建临时键值。
#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id);
功能:通过该函数可以将pathname指定的路径用来以
proj_id生成唯一的临时键值。
参数:@pathname 路径+名称===》任意文件,只要不会
被删除重建即可。
@proj_id 整形的数字,一般用ASCII码的单字符
表示与参数1的运算。
返回值:成功 返回唯一键值
失败 -1;
申请:
1、申请对象:shmget()
#include <sys/ipc.h>
#include <sys/shm.h>
ps aux|grep a.out
shared memory get IPC_CREAT|0666
int shmget(key_t key, size_t size, int shmflg);
功能:
使用唯一键值key向内核提出共享内存使用申请
参数:
@key 唯一键值
@size 要申请的共享内存大小
@shmflg 申请的共享内存访问权限,八进制表示
如果是第一个申请,则用IPC_CREAT
如果要检测是否存在,用IPC_EXCL
返回值:
成功 返回共享内存id,一般用shmid表示
失败 -1;
2、映射对象:shmat()
void *shmat(int shmid, const void *shmaddr, int shmflg);
功能:将指定shmid对应的共享内存映射到本地内存。
参数:
@shmid 要映射的本地内存
@shmaddr 本地可用的地址,如果不确定则用NULL,表示
由系统自动分配。
@shmflg
0 , 表示读写
SHM_RDONLY, 只读
返回值:
成功 返回映射的地址,一般等于shmaddr
失败 (void*)-1
3、删除对象:shmctl
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
功能:
修改共享内存属性,也可以删除指定的共享内存对象。
参数:@shmid 要删除的共享内存对象
@cmd
@IPC_RMID 删除对象的宏
@buff NULL 表示只删除对象。
返回值:成功 0
失败 -1
练习:
// a
#include<stdio.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<string.h>
#include<unistd.h>
#include <signal.h>int main(int argc, const char *argv[])
{key_t key = ftok("/",'A');if (key < 0){perror("ftok fail");return -1;}int shmid = shmget(key,1024,IPC_CREAT|0666);if(shmid < 0){perror("shmget fail");return -1;}void *p = shmat(shmid,NULL,0);if(p == (void *)-1){perror("shmat fail");return -1;}pid_t *pid = p;pid_t pid_b = *pid;printf("pid_b = %d\n",pid_b);while(1){fgets(p,1024,stdin);kill(pid_b,10);printf("p = %s\n",(char *)p);if(strncmp((char *)p,"quit",4) == 0){break;}}return 0;
}//b
#include<stdio.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<string.h>
#include<unistd.h>
#include<signal.h>void handler(int signo)
{}int main(int argc, const char *argv[])
{key_t key = ftok("/",'A');if (key < 0){perror("ftok fail");return -1;}int shmid = shmget(key,1024,IPC_CREAT|0666);if(shmid < 0){perror("shmget fail");return -1;}void *p = shmat(shmid,NULL,0);pid_t *pid = p;*pid = getpid();printf("pid = %d\n",*pid);if(p == (void *)-1){perror("shmat fail");return -1;}signal(SIGUSR1,handler);while(1){ pause();printf("p = %s\n",(char *)p);if(strncmp((char *)p,"quit",4) == 0){break;}}return 0;
}
消息队列
信号量集