开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第36天,点击查看活动详情
1、通信技术IPC(InterProcess Communication)
1.IPC方式有: *管道(包括无名管道和命名管道) *消息队列 *信号量 *共享存储 *Socket *Streams Socket和Streams支持多机通信
2、无名管道
1.半双工,只在父子进程之间,管道可以看成特殊文件用read write读写只存在于内存不生成文件。 2.原型
int pipe(int fd[2])
返回值:成功0,失败-1 管道建立成功生成2个文件描述符,fd[0]读,fd[1]写 3.用法举例 建立管道:pipe(fd),判断是否成功 创建子进程:fork,判断是否成功 在父子进程读写:父进程写,子进程读 父:close(fd[0]),write(fd[1],内容,个数) 子:close(fd[1]),read(fd[0],,) 注:写时要关闭读管道,读时要关闭写管道 读写方向一但固定就无法改变。
3、命名管道(FIFO)
1.半双工,可以无关进程之间,以特殊文件形式存在文件系统 2.原型
int mkfifo(const char* pathname,mode_t mode)
返回值:成功0 ,失败-1,可以用perror查看失败原因 参数:和open一样,文件路径名,0600(读写) 参数1:可设置是否阻塞O_NONBLOCK,默认是阻塞。
3.例: *进程1 open打开管道 read阻塞等待write(要用while) 输出读的数据 close关闭管道
*进程2 open打开管道 write写数据 close关闭管道 注:管道文件数据读后立马清空,先进先出
4、消息队列
4.1.
全双工,链表在内核中有标识符(队列ID),随机查询按类型读取,进程终止内容不会删除
4.2.API
int msgget(key_t key,int flag)
创建消息队列,成功返回0,失败-1 参数1:key可以用ftok函数,也可以直接写0x1234 参数2:创建队列IPC_CREAT,打开方式可读可写可执行0777
int msgsnd(int msqid,const void*ptr,size_t size,int flag)
添加消息,成功0失败-1 参数1:队列号key 参数2:结构体指针
struct msgbuf
{
long mtype;
char mtext[1];
}
参数3:个数用strlen算 参数4:0阻塞
int msgrcv(int msqid,void*ptr,size_t size,long type,int flag)
读取消息,成功返回数据长度 参数1:队列号 参数2:结构体指针 参数3:个数 参数4:消息类型,结构体的mtype 参数5:0阻塞等待
int msgctl(int msqid,int cmd,struct msqid_ds*buf)
控制消息队列,释放移除队列 参数1:队列号 参数2:常用IPC_RMID,释放移除队列 参数3:通常NULL
4.3.ftok函数(通常用于消息队列)
key_t ftok(const char*fname,int id)
返回值:key,队列号 参数1:当前目录 参数2:id 例:key=ftok(".",'a')
5、共享内存
1.共用一个内存,写读用指针指向这个内存 一读一写,前面内容销毁 2.API
int shmget(key_t key,size_t size,int flag)
创建打开一个共享内存 返回值:成功返回id,失败返回-1 参数1:key,用ftok 参数2:创建内存大小,必须以m为单位 参数3:创建共享内存IPC_CREAT,可读写0666 例:shmip=shmget(key,1024*4,IPC_CREAT|0666)
void *shmat(int shm_id,const void* addr,int flag)
连接共享内存地址空间 返回值:成功返回地址,失败-1 参数1:共享内存id 参数2:0 参数3:0 注:这个指针不需要free释放
int shmdt(void* addr)
断开连接 返回值:成功0失败-1 参数:id
int shmctl(int shm_id,int cmd,struct shmid_ds* buf)
控制共享内存,释放 返回值:成功0失败-1 参数1:id 参数2:IPC_RMID 参数3:0 3.例: 创建打开共享内存 连接映射共享内存 读写数据(用printf) 断开连接 释放干掉共享内存
6、信号
一、一些命令
1.用指令kill -l,查看信号名称和编号
2.忽略,捕捉,系统默认信号
3.使用
例:杀死进程
kill -信号编号 进程pid
二、信号注册,捕捉,忽略(入门)
1.API 函数:
tpyedef void(*sighandler_t)(int)
函数指针
sighandler_t signal(int signum,sighandler_t handler)
参数1:信号名称
参数2:函数指针
高级:sigaction()
2.用signal捕捉信号,然后用函数指针更改系统默认信号,改成自己要操作的内容。 例: 捕捉信号 进入函数指针里,操作要干什么 注:9 SIGKILL,系统信号改变不了
3.用函数main传参杀死进程 *API
int kill(pid_t pid,int sig)
发信号,杀死进程
参数:1.进程pid,2.信号编号加-
atoi()//char转int
参数:二级指针,mian形参 sprintf() 做字符串 参数:比printf前面多一个参数,char是指针型,做出来的字符串放到char*里面 *例: main完整有形参 把2个字符型形参用atoi转整型(pid,signum) 2种方法 用kill杀死进程 用sprintf做字符串,在用system杀死进程
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
int main(int argc,char **argv)
{
int signum;
int pid;
char cmd[128] = {0};
signum = atoi(argv[1]);
pid = atol(argv[2]);
sprintf(cmd,"kill %d %d",signum,pid);
system(cmd);
printf("send signal ok\n");
return 0;
}
//使用:./a -9 2313
4.忽略信号 signal第二个参数写宏,SIG_ICN 注:忽略不了信号9 三、信号携带信息,收发(高级) 1.sigaction(捕捉信号)
int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);
参数1:信号编号 ★参数2:结构体指针 参数3:备份,NULL 一般用法:都配置
2.struct sigaction
struct sigaction {
void (*sa_handler)(int);
void (*sa_sigaction)(int, siginfo_t *, void *);
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer)(void);
};
成员1:和signal的函数指针一样 ★成员2:多个形参的函数指针 成员3:是否阻塞,默认阻塞 成员4:SA_SIGINFO 成员5:一般不用 一般用法:配成员2,4
3.函数指针handler() 参数1:int signum接受到的信号类型编号 参数2:结构体siginfo_t,读数据:si_int,pid,发消息:一个联合体,可以发整型和字符。 参数3:void*context指针,判断是否有数据传过来,无数据指向空NULL
例: sigaction函数 配置结构体2个成员 写handler函数 函数里判断有数据打印数据 发送 1.sigqueue
int sigqueue(pid_t pid, int sig, const union sigval value);
参数1:给谁发pid 参数2:发什么信号 参数3:数据,联合体union sigval(整型,字符)
union sigval {
int sival_int;
void *sival_ptr;
};
例: 用完整main,2个参数转int 函数sigqueue 写数据联合体(特别注意段错误,共用一个内存,写字符的时候要用指针)
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
void handler(int signum, siginfo_t *info, void *context)
{
printf("get signum %d\n",signum);
if(context != NULL){
printf("get data:%d\n",info->si_int);
printf("get data:%d\n",info->si_value.sival_int);
printf("from:%d\n",info->si_pid);
}
}
int main()
{
struct sigaction buf;
buf.sa_sigaction = handler;
buf.sa_flags = SA_SIGINFO;
sigaction(SIGUSR1,&buf,NULL);
while(1);
return 0;
}
/*输出
get signum 10
get data:10
get data:10
from:48116
*/
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc,char **argv)
{
int signum;
int pid;
union sigval value;
value.sival_int = 10;
signum = atoi(argv[1]);
pid = atol(argv[2]);
sigqueue(pid,signum,value);
printf("%ddone\n",getpid());
return 0;
}
7、信号量(锁)
1.控制谁先运行 2.API
int semget(key_t key,int num_sems,int sem_flags)
创建,获取信号组 返回值:semid 参数:key,用flok 参数:信号组个数,一般 参数:IPC_CREAT|0666
int semop(int semid,struct sembuf *sops,size_t numops)
操作信号量,改变值 参数1:id 参数2:结构体指针可以有多个也可以是一个, 成员:sem_num,信号量编号0 成员:sem_op,操作信号量,-1,1 成员:sem_flg,SEM_UNDO 参数3:几个信号量
int semctl(int semid,int sem_num,int cmd,...)
控制信号量组相关信息 参数:id 参数:操作第几个信号量,0开始 参数:很多宏,常用SETVAL设置信号量值 IPC_RMID销毁 参数:联合体,union semun,开头要定义 初始化设置信号量值为1,联合体val=1 例: 创建信号量组 初始化信号量 创建父子进程 写2个函数,一个信号量加,一个减 子进程运行完信号量加 父进程先减然后运行在加 销毁锁
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <unistd.h>
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 pGetKey(int id)
{
struct sembuf set;
set.sem_num = 0;
set.sem_op = -1;
set.sem_flg = SEM_UNDO;
semop(id,&set,1);
printf("getkey\n");
}
void vPutBackKey(int id)
{
struct sembuf set;
set.sem_num = 0;
set.sem_op = 1;
set.sem_flg = SEM_UNDO;
semop(id,&set,1);
printf("put back the key\n");
}
int main()
{
key_t key;
int sem_id;
key = ftok(".",'a');
union semun initsem;
initsem.val = 0;//锁初始没有钥匙
sem_id = semget(key,1,IPC_CREAT|0666);
semctl(sem_id,0,SETVAL,initsem);
int pid = fork();
if(pid > 0){
pGetKey(sem_id);//父进程那不到,因为没有钥匙
printf("this is father\n");
vPutBackKey(sem_id);
}else if(pid == 0){
printf("this is child\n");
vPutBackKey(sem_id);//子进程执行完,放钥匙进去
}else
{
printf("fork error\n");
exit(-1);
}
semctl(sem_id,0,IPC_RMID);
return 0;
}