共享内存
当进程A和进程B有一块共享的内存空间时,这两个进程之间的数据交互就会变的很简单,只需要像读取自己内存空间中的元素一样去读取数据即可。实现共享内存进行数据交互的一般步骤:
- 创建/打开共享内存
- 内存映射
- 数据交换
- 断开与共享内存的连接
- 清除共享内存
相关api
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
//创建或获取一个共享内存:成功返回共享内存ID,失败返回-1
int shmget(key_t key, size_t size, int shmflg);
//连接共享内存到当前进程的地址空间:成功返回指向共享内存的指针,失败返回-1
void *shmat(int shmid, const void *shmaddr, int shmflg);
//断开与共享内存的连接:成功返回0,失败返回-1
int shmdt(const void *shmaddr);
//控制共享内存的相关信息:成功返回0,失败返回-1
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
共享内存实现数据通信:
shmw.c:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(){
int shmid;
char *shmaddr;
key_t key;
key = ftok(".",1);
shmid = shmget(key,1024*4,IPC_CREAT|0666);
if(shmid == -1){
printf("create share memory fail\n");
}
shmaddr = shmat(shmid,0,0);//第二个0是让系统自动分配空间,第三个0是默认可读可写
printf("shmat ok!\n");
strcpy(shmaddr,"hello world!");
sleep(5);
shmdt(shmaddr);
shmctl(shmid,IPC_RMID,0);
printf("quit!\n");
return 0;
}
shmr.c:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(){
int shmid;
char *shmaddr;
key_t key;
key = ftok(".",1);
shmid = shmget(key,1024*4,0); //0代表正常获取共享内存
if(shmid == -1){
printf("create share memory fail\n");
}
shmaddr = shmat(shmid,0,0);//第二个0是让系统自动分配空间,第三个0是默认可读可写
printf("read data from w:%s\n",shmaddr);
shmdt(shmaddr);
printf("quit!\n");
return 0;
}
gcc shmw.c -o w;
gcc shmr.c -o r;
现在一个终端执行./w在另一个终端执行./r执行结果为为:
./w
shmat ok!
quit!
./r
read data from w:hello world!
quit!
另外我们可以利用ipcs -m指令查看当前系统中的共享内存。
共享内存也存在缺点,当进程A和进程B使用共享内存进行通信时,可能会出现这样一种情况:进程A和B同时向共享内存中写数据,可能会导致数据出现错乱。即共享内存不能实现原子操作,这种情况需要借助信号量来进行解决。
linux信号概述
对于linux来说,实际信号是软中断,许多重要的程序需要处理信号。信号,为linux提供了一种处理异步事件的方法。比如,终端用户输入了ctrl+c来终端程序,会通过信号机制停止一个程序。可以通过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 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR
31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3
38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7
58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
63) SIGRTMAX-1 64) SIGRTMAX
信号处理
对于信号我们可以采取三种方式来进行处理:忽略、捕捉和默认动作。
忽略信号
大多数信号可以使用这个方式来处理,但是有两种信号不能被忽略(分别是SIGKILL和SIGSTOP)。
捕捉信号
可以写一个信号处理函数,然后把这个函数告诉内核,当该信号产生时,由内核来调用用户自定义的函数,一次来实现某种信号的处理。
系统默认动作
对于每个信号来说,系统都对应有默认的处理动作,当发生了该信号,系统会自动执行。
最简单的一个信号,当我们执行一个死循环时,我们可以直接利用kill -SIGKILL pid号来杀死该进程:
#include <stdio.h>
int main(){
while(1);
return 0;
}
运行这代断码会一直处理死循环,此时我们先利用 ps -aux|grep a.out来查看该进程的pid号,之后利用kill -9 pid或kill -SIGKILL pid来杀死该进程:
./a.out
Killed
信号变成
*#include <signal.h>
typedef void (sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
signum:就是选择上面的64种信号类型。handler是个函数指针,指明了遇到signum这种类型的信号将作何处理。
正常来说像我们上面的代码遇到while(1)卡死循环的情况我们可以用ctrl+C来终止进程,这是系统的默认动作,当然我们也可以捕捉信号,自己写一个handler函数来改变默认的行为:
#include <stdio.h>
#include <signal.h>
void handler(int signum){
printf("signum=%d\n",signum);
printf("never quit!\n");
}
int main(){
signal(SIGINT,handler);
while(1);
return 0;
}
运行结果:
./a.out
^Csignum=2
never quit!
^Csignum=2
never quit!
^Csignum=2
never quit!
^Csignum=2
never quit!
^Csignum=2
never quit!
我们无法用ctrl+c来终止这个死循环进程。可以用kill -9 +pid来杀死进程。
同时我们也可以通过参数的方式利用代码来杀死进程:
#include <sys/types.h>
#include <signal.h>
#include <stdio.h>
#include <signal.h>
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);
printf("send signal ok!\n");
return 0;
}
将这段代码编译为可执行程序,gcc signal1Ctl.c -o myKill;
首先运行./a.out就是上面那个死循环函数,直接在另一个终端查询该进程的pid号,然后运行./myKill 9 pid来杀死进程
除了利用kill来完成上述操作,我们也可以利用system来调用脚本:
#include <sys/types.h>
#include <signal.h>
#include <stdio.h>
#include <signal.h>
int main(int argc,char *argv[]){
int pid;
int signum;
char cmd[128];
signum = atoi(argv[1]);
pid = atoi(argv[2]);
printf("signum=%d,pid=%d\n",signum,pid);
//kill(pid,signum);
sprintf(cmd,"kill -%d %d",signum,pid);//cmd="kill signum pid"
system(cmd);
printf("send signal ok!\n");
return 0;
}
一样可以实现上述效果。