阅读量:0
一、概念
信号通信,其实就是内核向用户空间进程发送信号,只有内核才能发信号,用户空间进程不能发送信号。信号已经是存在内核中的了,不需要用户自己创建。
信号通信的框架 * 信号的发送(发送信号进程):kill、raise、alarm * 信号的接收(接收信号进程) : pause()、 sleep、 while(1) * 信号的处理(接收信号进程) :signal二、相关函数
1.信号的发送(发送信号进程)
所需头文件: #include<signal.h> #include<sys/types.h> 函数原型:int kill(pid_t pid, int sig); 参数: 函数传入值:pid 正数:要接收信号的进程的进程号 0:信号被发送到所有和pid进程在同一个进程组的进程 ‐1:信号发给所有的进程表中的进程(除了进程号最大的进程外) sig:信号 函数返回值:成功 0 出错 ‐1示例:#include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <signal.h> int main(int argc,char *argv[]) { int pid,sig; if(argc != 3) { printf("输入错误\n"); return -1; } sig = atoi(argv[1]); pid = atoi(argv[2]); printf("pid = %d,sig = %d\n",pid,sig); int rnt = kill(pid,sig); if(rnt == 0) { printf("杀死进程%d成功\n",pid); } return 0; } ~
运行结果:2.raise: 发信号给自己 == kill(getpid(), sig)
所需头文件: #include<signal.h> #include<sys/types.h> 函数原型: int raise(int sig); 参数: 函数传入值:sig:信号 函数返回值: 成功 0 出错 ‐1示例:#include <stdio.h> #include <signal.h> #include <sys/types.h> #include <unistd.h> int main() { printf("hello;\n"); //int rnt = raise(9); int rnt = kill(getpid(),9); if(rnt == 0) { printf("杀死自己成功\n"); } printf("world\n"); return 0; }
运行结果:3.alarm : 发送闹钟信号的函数
alarm 与 raise 函数的比较: 相同点:让内核发送信号给当前进程 不同点: alarm 只会发送SIGALARM信号 alarm 会让内核定时一段时间之后发送信号, raise会让内核立刻发信号 所需头文件#include <unistd.h> 函数原型 unsigned int alarm(unsigned int seconds) 参数: seconds:指定秒数 返回值: 成功:如果调用此alarm()前,进程中已经设置了闹钟时间,则 返回上一个闹钟时间的剩余时间,否则返回0。 出错:‐1示例:#include <stdio.h> #include <signal.h> #include <unistd.h> int main() { int i; alarm(5); printf("alarm start\n"); for(i =0 ;i<20;i++) { sleep(1); printf("i = %d\n",i); } return 0; }
运行结果:4.信号的接收
接收信号的进程,要有什么条件:要想使接收的进程能收到信号,这个进程不能结束 : sleep pause:进程状态为S (休眠)函数原型 int pause(void); 函数返回值:成功:0,出错:‐1示例:#include <stdio.h> #include <signal.h> #include <unistd.h> int main() { int i,rnt; printf("pause start\n"); pause(); for(i =0 ;i<20;i++) { sleep(1); printf("i = %d\n",i); } return 0; }
运行结果: 5.信号的处理
收到信号的进程,应该怎样处理? 处理的方式: 1.进程的默认处理方式(内核为用户进程设置的默认处理方式) A:忽略B:终止进程C: 暂停 2.自己的处理方式: 自己处理信号的方法告诉内核,这样你的进程收到了这个信号就会采用你自己的的处理方式、 函数原型 void (*signal(int signum, void (*handler)(int)))(int); 参数:signum:指定信号 handler: 1.SIG_IGN:忽略该信号。 2.SIG_DFL:采用系统默认方式处理信号 3.自定义的信号处理函数指针 函数返回值 成功:设置之前的信号处理方式 出错:‐1 signal 函数有二个参数,第一个参数是一个整形变量(信号值),第二个参数是一个函数指针,是我们自己写的处理函 数;这个函数的返回值是一个函数指针。1.示例:自定义的信号处理函数指针
#include <stdio.h> #include <signal.h> #include <unistd.h> void myfun(int signum) { int i; while(i<5) { printf("signum = %d,i = %d\n",signum,i); i++; sleep(1); } } int main() { int i,rnt; signal(14,myfun);//14信号是定时器到时发出 printf("alarm start\n"); alarm(7); for(i =0 ;i<10;i++) { sleep(1); printf("i = %d\n",i); } return 0; }
运行结果:PS:回调函数执行完之后会重新回到主函数继续执行2.示例:忽略该信号
#include <stdio.h> #include <signal.h> #include <unistd.h> void myfun(int signum) { int i; while(i<5) { printf("signum = %d,i = %d\n",signum,i); i++; sleep(1); } } int main() { int i,rnt; signal(14,myfun);//14信号是定时器到时发出 printf("alarm start\n"); alarm(7); signal(14,SIG_IGN); for(i =0 ;i<10;i++) { sleep(1); printf("i = %d\n",i); } return 0; }
运行结果:3.示例:采用系统默认方式处理信号
#include <stdio.h> #include <signal.h> #include <unistd.h> void myfun(int signum) { int i; while(i<5) { printf("signum = %d,i = %d\n",signum,i); i++; sleep(1); } } int main() { int i,rnt; signal(14,SIG_DFL); printf("alarm start\n"); alarm(7); for(i =0 ;i<10;i++) { sleep(1); printf("i = %d\n",i); } return 0; }
运行结果:三、信号灯
信号灯集合(可以包含多个信号灯)IPC对象是一个信号的集合(多个信号量) , 一个信号灯有多个信号量。1.相关函数
1.函数原型: int semget(key_t key, int nsems, int semflg);
功能:创建一个新的信号量或获取一个已经存在的信号量的键值。 函数参数: key:和信号灯集关联的key值 nsems: 信号灯集中包含的信号灯数目 semflg:信号灯集的访问权限 函数返回值: 成功:信号灯集ID 出错:‐1示例:#include<stdio.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> #include <stdlib.h> int main() { int semID; semID = semget(IPC_PRIVATE,3,0755); if(semID == -1) { printf("信号灯创建失败\n"); perror("semget:"); return -1; } else { printf("信号灯创建成功,semID = %d\n",semID); } system("ipcs -s");//查看信号灯信息 return 0; }
运行结果:2.函数原型:int semctl ( int semid, int semnum, int cmd,…union semun arg(不是地址));
功能:控制信号量,删除信号量或初始化信号量 函数参数: semid:信号灯集ID semnum: 要修改的信号灯编号 cmd : GETVAL:获取信号灯的值 SETVAL:设置信号灯的值 IPC_RMID:从系统中删除信号灯集合 函数返回值: 成功:0 出错:‐1 示例:#include<stdio.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> #include <stdlib.h> int main() { int semID; semID = semget(IPC_PRIVATE,3,0755); if(semID == -1) { printf("信号灯创建失败\n"); perror("semget:"); return -1; } else { printf("信号灯创建成功,semID = %d\n",semID); } system("ipcs -s");//查看信号灯信息 semctl(semID,1,IPC_RMID,NULL); printf("删除过后的信号灯\n"); system("ipcs -s"); return 0; } ~
运行结果:
三、PV操作
int semop(int semid ,struct sembuf *sops ,size_t nsops); 功能:用户改变信号量的值。也就是使用资源还是释放资源使用权 参数: semid : 信号量的标识码。也就是semget()的返回值 sops是一个指向结构体数组的指针。 struct sembuf{ unsigned short sem_num; //信号灯编号; short sem_op; //对该信号量的操作。‐1 ,P操作,1 ,V操作 short sem_flg; //0阻塞,1非阻塞 }; sem_op : 操作信号灯的个数 //如果其值为正数,该值会加到现有的信号内含值中。通常用于释放所控资源的使用权;如果sem_op的值为负 数,而其绝对值又大于信号的现值,操作将会阻塞,直到信号值大于或等于sem_op的绝对值。通常用于获取资源的使用 权;如果sem_op的值为0,则操作将暂时阻塞,直到信号的值变为0。 示例:#include <stdio.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> #include <sys/shm.h> #include <unistd.h> #define SEM_READ 0 #define SEM_WRITE 0 union semun { int val; }; void pfuntion(int id,int index) { struct sembuf sem; sem.sem_num = index; sem.sem_op = -1; sem.sem_flg = 0; semop(id,&sem,1); } void vfuntion(int id,int index) { struct sembuf sem; sem.sem_num = index; sem.sem_op = 1; sem.sem_flg = 0; semop(id,&sem,1); } int main() { key_t key; pid_t pid; int shmid,semid; char *shmaddr; key = ftok("a.c",1); semid = semget(key,2,IPC_CREAT|0755);//创建信号灯 if(semid == -1) { perror("semget"); return -1; } shmid = shmget(key,128,IPC_CREAT|0755);//创建共享内存 if(shmid == -1) { perror("shmget"); return -2; } //初始化信号灯 union semun mynu; //初始化读 mynu.val = 0;//读的信号灯值 semctl(semid,SEM_READ,SETVAL,mynu); //初始化写 mynu.val = 1;//写的信号灯值 semctl(semid,SEM_WRITE,SETVAL,mynu); pid = fork(); if(pid == -1) { perror("fork"); return -3; } if(pid > 0) { printf("这是一个父进程\n"); while(1) { shmaddr = (char *)shmat(shmid,NULL,0); pfuntion(semid,SEM_READ); printf("父进程请输入数据:"); fgets(shmaddr,32,stdin); vfuntion(semid,SEM_WRITE); } } if(pid == 0) { printf("这是一个子进程\n"); while(1) { shmaddr = (char *)shmat(shmid,NULL,0); pfuntion(semid,SEM_READ); printf("子进程读取到的数据是:%s\n",shmaddr); vfuntion(semid,SEM_WRITE); } } return 0; }
运行结果: