目录
一、共享内存
实现步骤:
a、创建/打开共享内存 shmget
b、建立共享内存映射区 shmat
c、往共享内存写入数据
d、释放共享内存映射区 shmdt
e、干掉共享内存 shmctl
#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);
shmget()创建或打开共享内存
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);
key_t key: keyid由ftok获得
size_t size: 必须以1024为单位分配
int shmflg: 共享内存的权限,如:IPC_CREAT|0666
IPC_CREAT:如果不存在就创建
IPC_EXCL:如果已经存在则返回失败
返回值:成功:返回共享内存标识符,失败:返回-1
shmat()建立共享内存的映射区
SYNOPSIS
#include <sys/types.h>
#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr, int shmflg);
int shmid: 共享内存标识符
const void *shmaddr: 共享内存映射地址,默认写0
int shmflg: 共享内存具有可读可写权限,默认写0
返回值:成功:返回共享内存段映射地址,失败:-1
shmdt释放共享内存映射区
#include <sys/types.h>
#include <sys/shm.h>
int shmdt(const void *shmaddr);
const void *shmaddr:共享内存映射地址
返回值:成功0,失败-1
shmctl干掉共享内存
#include <sys/ipc.h>
#include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
int shmid: 共享内存标识符
int cmd: 一个指令,一般写删除指令:IPC_RMID
struct shmid_ds *buf: 一个buf,一般写0
实现共享内存的双方通信
写入端代码如下:
#include <sys/ipc.h>
#include <sys/shm.h>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
//shmw.c
int main()
{
int shmId;
char *shmaddr;
key_t key;
key=ftok(".",1);
shmId=shmget(key,1024*4,IPC_CREAT|0666);
// shmId= shmget(key,1024*4,IPC_CREAT|0666);
if(shmId== -1){
printf("shm creat fail\n");
exit(-1);
}
shmaddr=shmat(shmId,0,0);
printf("shmat ok!\n");
strcpy(shmaddr,"jinlibaobao hello");
sleep(5);
shmdt(shmaddr);
shmctl(shmId,IPC_RMID,0);
return 0;
}
读取端代码如下:
#include <sys/ipc.h>
#include <sys/shm.h>
#include<stdio.h>
#include<string.h>
#include <stdlib.h>
//shmr.c
int main()
{
int shmId;
char *shmaddr;
key_t key;
key=ftok(".",1);
shmId= shmget(key,1024*4,0);
if(shmId == -1){
printf("shm creat fail\n");
exit(-1);
}
shmaddr=shmat(shmId,0,0);
printf("shm ok!\n");
printf("context is %s\n",shmaddr);
shmdt(shmaddr);
printf("quit");
return 0;
}
查看共享内存
ipcs -m
上面的写入端代码要注释shmctl函数或提前退出不执行shmctl才可以查看具体共享内存号
删除共享内存
ipcs -m xxx
xxx为shmid号码
二、信号
对于 Linux来说,实际信号是软中断,许多重要的程序都需要处理信号。信号,为 Linux 提供了一种处理异步事件的方法。比如,终端用户输入了 ctrl+c 来中断程序,会通过信号机制停止一个程序。
信号概述
信号的名字和编号:每个信号都有一个名字和编号,这些名字都以“SIG”开头,例如SIGIO、SIGCHLD等等
查看信号:kill -l
信号的处理方法:忽略、捕捉、默认动作、杀死进程
忽略信号(SIG_IGN):大多数信号可以使用这个方式来处理,但是有两种信号不能被忽略(分别是SIGKILL和SIGSTOP)
捕捉信号:需要告诉内核,用户希望如何处理某一种信号,说白了就是写一个信号处理函数,然后把这个函数告诉内核
系统默认动作:对于每个信号来说,系统都对应由默认的处理动作,当发生了该信号,系统会自动执行。
杀死进程:
a.out指正在运行的程序
(1)查看进程号:ps -aux|grep a.out
(2)kill -9 进程号
比如我们要杀杀死进程a.out,则先查询a.out的进程号,再配合kill -9杀死进程
入门:收消息signa,发消息kill
高级:收消息sigaction,发消息sigqueue
signal()函数收消息
功能设置某个信号的捕捉行为
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
int signum: 表示要捕捉的信号
sighandler_t handler: 一个自定义的处理信号的函数,信号的编号为这个自定义函数的参数
参数handler表示信号的处理方式,有三种情况:
(1)SIG_IGN:忽略参数signum所指的信号。signal(SIGINT,SIG_IGN );把ctrl+c信号忽略
(2)一个自定义的处理信号的函数,信号的编号为这个自定义函数的参数。
(3)SIG_DFL:恢复参数signum所指信号的处理方法为默认值。 一般不关心signal的返回值。
返回值:成功返回信号处理程序的先前值,失败返回 SIG_ERR 错误并设置 errno。
举例,实现按下键盘ctrl+c无法终止程序
#include <signal.h>
#include<stdio.h>
//typedef void (*sighandler_t)(int);
// sighandler_t signal(int signum, sighandler_t handler);
void handler(int signum){
printf("signum is %d\n",signum);
printf("never quit\n");
}
int main(){
signal(SIGINT,handler);//注册捕捉Ctrl+c的信号
while(1);//一直循环不退出程序
return 0;
}
kill()函数发消息
功能把信号sig发送给目标进程pid
#include <sys/types.h>
#include <signal.h>
kill()把信号sig发送给目标进程pid
int kill(pid_t pid, int sig);
pid_t pid: 代表当前进程的进程号
int sig: 代表信号的编号,如SIGINT的编号是2
返回值:成功返回0,失败返回-1并设置errno:
实现发送信号杀死a.out进程
#include <sys/types.h>
#include <signal.h>
#include<stdio.h>
// int kill(pid_t pid, int sig);
int main(int argc,char **argv){
int pid;
int signum;
signum=atoi(argv[1]);//把指令的第二项转为数字,要不然是字符串
pid=atoi(argv[2]);//把指令的第三项转为数字
printf("signum=%d pid=%d\n",signum,pid);//打印信号码、进程号
kill(pid,signum);//把信号发给这个pid的进程
printf("kill send ok!\n");
return 0;
}
sigaction()函数收消息
#include <signal.h>
int sigaction(int signum, const struct sigaction *act,
struct sigaction *oldact);
int signum: 要捕获的信号
const struct sigaction *act: 捕捉信号后想要干嘛,是一个结构体
struct sigaction *oldact: 输出先前信号的处理方式,默认为NULL
struct sigaction {
void (*sa_handler)(int);
void (*sa_sigaction)(int, siginfo_t *, void *);
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer)(void);
}
void (*sa_sigaction)(int, siginfo_t *, void *): 是一个函数
int sa_flags: 接收数据
#include <signal.h>
#include<stdio.h>
//int sigaction(int signum, const struct sigaction *act,
// struct sigaction *oldact);
void handler(int signum, siginfo_t * info, void * context){
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 act;
printf("pid is %d\n",getpid());
act.sa_sigaction=handler;
act.sa_flags=SA_SIGINFO;
sigaction(SIGUSR1,&act,NULL);
while(1);
return 0;
}
sigqueue()函数发消息
#include <signal.h>
int sigqueue(pid_t pid, int sig, const union sigval value);
pid_t pid: 接收指定信号的进程pid,如9、10
int sig: 确定即将发送的信号
const union sigval value: 是一个联合结构体union sigval,指定了信号传递的参数
#include <signal.h>
#include<stdio.h>
// int sigqueue(pid_t pid, int sig, const union sigval value);
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;
sigqueue(pid,signum,value);
printf("%ddone\n",getpid());
return 0;
}
三、信号量
信号量跟之前的管道、消息队列、共享内存、信号四种不同,他不涉及数据,负责管理临界资源。
1、semget()函数
创建或获取一个信号量组:若成功返回信号量集ID,失败返回-1
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semget(key_t key, int nsems, int semflg);
key_t key: 一个key值
int nsems: 信号量的个数
int semflg: 获取信号量的权限,如果没有就创建一个信号量
2、semop()函数
完成对信号量的P操作或V操作
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
取钥匙
int semop(int semid, struct sembuf *sops, unsigned nsops);
int semid: 信号量的id值
struct sembuf *sops: PV操作的具体内容结构体
unsigned nsops: 操作结构的数量,大于或等于1
返回值——成功返回0。错误返回-1,errno表示错误
3、semctl()函数
控制信号量的信息:删除信号量或初始化信号量
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semctl(int semid, int semnum, int cmd, ...);
int semid: 要操作信号量的id
int semnum: 要操作第几个信号量
int cmd: 默认写SETVAL,设置信号量的值
信号量拿钥匙、取钥匙代码实战
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include<stdio.h>
#include <unistd.h>
// int semget(key_t key, int nsems, int semflg);
//int semctl(int semid, int semnum, int cmd, ...);
//int semop(int semid, struct sembuf *sops, unsigned nsops);
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) */
};
//封装P操作拿锁
void pGetKey(int semid){
struct sembuf sops;
sops.sem_num = 0; /* Operate on semaphore 0 */
sops.sem_op = -1; /* Wait for value to equal 0 */
sops.sem_flg = SEM_UNDO;
semop(semid,&sops,1);
printf("get the key\n");
}
//封装V操作换锁
void vPutKey(int semid){
struct sembuf sops;
sops.sem_num = 0; /* Operate on semaphore 0 */
sops.sem_op = 1; /* Wait for value to equal 0 */
sops.sem_flg = SEM_UNDO;
semop(semid,&sops,1);
printf("put the key\n");
}
int main(){
int semid;
key_t key;
key=ftok(".",1);
semid=semget(key,1,IPC_CREAT|0666);//获取/创建信号量。1:信号量集合有一个信号量
union semun initsem;
initsem.val=1;
semctl(semid,0,SETVAL,initsem);//初始化信号量,
int pid;
pid=fork();
if(pid>0){
//去拿锁
pGetKey(semid);
printf("this is a father\n");
//去换锁
vPutKey(semid);
semctl(semid,0,IPC_RMID);
}else if(pid==0){
printf("this is a child\n");
vPutKey(semid);
}else {
printf("creat fail\n");
}
return 0;
}