进程之间的通信方式(数据的传递):
1.普通的方式
文件 内存映射(mmap) 映射到同一个物理空间
父进程传递给子进程:
main函数参数列表 环境列表
2.信号 kill -sig pid
给指定进程pid发生一个sig信号
3.传统古老的方式 pipe 管道
4.IPC进程间通信
共享内存 sharemomery
消息队列 message
信号量集 semoper
5.网络通信
socket
信号提供了一种异步处理的机制
查看各类信号 kill -l
一张信号图
信号分为可靠信号(实时信号)和不可靠信号
可靠信号:
位于[SIGRTMIN(34),SIGRTMAX(64)]区间的信号都是可靠信号。
支持排队,不会丢失。
无论可靠信号还是不可靠信号,都可以通过sigqueue/sigaction函数发送/安装,以获得比其早期版本kill/signal函数更可靠的使用效果。
不可靠信号:
那些建立在早期机制上的信号被称为“不可靠信号”,小于SIGRTMIN(34)的信号都是不可靠信号。
不支持排队,可能会丢失。同一个信号产生多次,进程可能只收到一次该信号。
进程每次处理完这些信号后,对相应信号的响应被自动恢复为默认动作,除非显示地通过signal函数重新设置一次信号处理程序。
信号的处理
忽略。
终止进程。
终止进程同时产生core文件。 core文件就是段错误
捕获并处理。当信号发生时,内核会调用一个事先注册好的用户函数(信号处理函数)。
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
void sigtstp_proc(int sig){
printf("%d进程接收到一个信号:%d\n",getpid(),sig);
signal(sig,sigtstp_proc);
}
void sig_proc(int sig){
printf("%d进程接收到一个信号:%d\n",getpid(),sig);
}
int main(){
printf("进程:%d\n",getpid());
if(signal(SIGINT,SIG_IGN)==SIG_ERR){
perror("singal");
return -1;
}
if(signal(SIGQUIT,SIG_DFL)==SIG_ERR){
perror("signal");
return -1;
}
if(signal(SIGTSTP,sigtstp_proc)==SIG_ERR){
perror("signal");
return -1;
}
if(signal(6,sig_proc)==SIG_ERR){
perror("signal");
return -1;
}
if(signal(9,SIG_IGN)==SIG_ERR){
perror("signal");
}
for(;;);
return 0;
}
signal
sighandler_t signal (int signum,sighandler_t handler); 注册信号的函数
成功返回原来的信号处理函数指针或SIG_IGN/SIG_DFL常量,
SIG_IGN: 忽略该信号;
SIG_DFL: 默认处理。
失败返回SIG_ERR
父子signal
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
void sigint_proc(int sig){
printf("%d进程接收到%d信号!\n",getpid(),sig);
}
int main(){
if(signal(SIGINT,sigint_proc)==SIG_ERR){
perror("signal");
return -1;
}
pid_t id = fork();
if(id == -1){
perror("fork");
return -1;
}
if(id == 0){
printf("子进程:%d\n",getpid());
}else{
printf("父进程:%d\n",getpid());
}
for(;;);
return 0;
}
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
void sigint_proc(int sig){
printf("%d进程接收到%d信号!\n",getpid(),sig);
}
int main(){
if(signal(SIGINT,sigint_proc)==SIG_ERR){
perror("signal");
return -1;
}
pid_t id = fork();
if(id == -1){
perror("fork");
return -1;
}
if(id == 0){
printf("子进程:%d\n",getpid());
execve("rundead",NULL,NULL);
}else{
printf("父进程:%d\n",getpid());
}
for(;;);
return 0;
}
子进程会继承父进程的信号处理方式
//子程调用exec函数后, exec函数将被父进程设置为捕获的信号恢复至默认处理, 其余保持不变。
子进程结束会向父进程发送SIGCHLD
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
void sigchild_proc(int sig){
printf("%d 进程收到%d信号!\n",getpid(),sig);
pid_t id = wait(NULL);
printf("%d 进程的子进程%d结束了!\n",getpid(),id);
}
int main(){
if(signal(SIGCHLD,sigchild_proc)==SIG_ERR){
perror("signal");
return -1;
}
pid_t id[3];
int i;
for(i=0;i<3;i++){
id[i] = fork();
if(id[i]==-1)return-1;
if(id[i]==0)
break;
}
if(i<3){
printf(" 我是快乐的子进程:%d\n",getpid());
sleep(i*i+3);
return i;
}
for(;;);
return 0;
}
命令
mykill 函数实现命令行功能
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
int main(int argc,char *argv[]){
if(argc < 3){
printf("%s -sig pid\n",argv[0]);
return -1;
}
int sig = -atoi(argv[1]);
pid_t pid = atoi(argv[2]);
int ret = kill(pid,sig);
if(ret == -1){
if(sig == 0 && errno == ESRCH){
printf("%d进程不存在!\n",pid);
}
perror("kill");
return -1;
}
return 0;
}
gcc mykill.c -o kl
kl -2 2668
raise.c
向调用进程自身发送sig信号。成功返回0,失败返回-1。
类似kill(getpid(),pid)
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <errno.h>
void sigint_proc(int sig){
printf("%d进程收到%d信号!\n",getpid(),sig);
}
int main(){
if(signal(SIGINT,sigint_proc)==SIG_ERR){
perror("signal");
return -1;
}
raise(SIGINT);
kill(getpid(),SIGINT);
for(;;);
return 0;
}
pause.c
进入睡眠状态
只有调用了信号处理函数并从中返回以后,该函数才会返回
ps aux | grep a.out
可查看程序状态 T是暂停状态
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
void sigint_proc(int sig){
}
int main(){
if(signal(SIGINT,sigint_proc)==SIG_ERR){
perror("signal");
return -1;
}
printf("begin...\n");
pause();
printf("end!\n");
return 0;
}
sleep
该函数要么返回0(睡够),要么返回剩余秒数(被信号中断)。
只有睡够seconds秒,或调用了信号处理函数并从中返回以后,该函数才会返回。
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
void sigint_proc(int sig){
}
int main(){
if(signal(SIGINT,sigint_proc)==SIG_ERR){
perror("signal");
return -1;
}
unsigned int ret = sleep(10);
printf("ret = %u\n",ret);
return 0;
}
alarm
一个计时器,结果又有点像时钟
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <time.h>
void sigalarm_proc(int sig){
system("clear");
time_t t = time(NULL);
struct tm *lt = localtime(&t);
printf(" %2d:%2d:%2d \n",lt->tm_hour,lt->tm_min,lt->tm_sec);
alarm(1);
}
int main(){
if(signal(SIGALRM,sigalarm_proc)==SIG_ERR){
perror("signal");
return -1;
}
alarm(3);
int ret = alarm(1);
printf("%d \n",ret);
for(;;);
return 0;
}
信号集与信号阻塞(信号屏蔽)
#include <stdio.h>
#include <signal.h>
void test(sigset_t *t,int sig){
if(sigismember(t,sig)==1){
printf("信号集中有%d信号!\n",sig);
}else{
printf("信号集中没有%d信号!\n",sig);
}
}
int main(){
sigset_t set;
sigemptyset(&set);
test(&set,2);
test(&set,3);
test(&set,4);
sigaddset(&set,SIGINT);
test(&set,2);
test(&set,3);
sigfillset(&set);
test(&set,2);
test(&set,3);
test(&set,4);
sigdelset(&set,SIGINT);
test(&set,2);
test(&set,3);
test(&set,4);
return 0;
}
结果:
信号集中没有2信号!
信号集中没有3信号!
信号集中没有4信号!
信号集中有2信号!
信号集中没有3信号!
信号集中有2信号!
信号集中有3信号!
信号集中有4信号!
信号集中没有2信号!
信号集中有3信号!
信号集中有4信号!
sigemptyset(&set);//信号集中所有的信号清除
sigaddset(&set,SIGINT);//将信号SIGINT添加到信号集中
sigfillset(&set);//将所有信号都添加到信号集中
sigdelset(&set,SIGINT);//从信号集中删除SIGINT信号
sigpromask 信号屏蔽
一个终端运行
另一个终端
kill -2 pid
kill -3 pid
kill -40 pid
kill -40 pid
kill -40 pid
kill -5 pid
只有当捕获到-5的信号,前面才会执行,并且不可靠信号相同的只能执行一次,可靠信号可全部重复执行
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
sigset_t set;
sigset_t oldset;
void sig_proc(int sig){
printf("收到了%d信号\n",sig);
}
void sig5_proc(int sigs){
sigset_t sigpset;
sigpending(&sigpset);
int sig = 1;
for(;sig<65;sig++){
if(sigismember(&sigpset,sig)==1){
printf("%d 信号处于未决!\n",sig);
}
}
sigprocmask(SIG_SETMASK,&oldset,NULL);
}
int main(){
printf("进程:%d\n",getpid());
sigemptyset(&set);
sigaddset(&set,2);
sigaddset(&set,3);
sigaddset(&set,4);
sigaddset(&set,40);
sigaddset(&set,41);
sigaddset(&set,42);
signal(2,sig_proc);
signal(3,sig_proc);
signal(4,sig_proc);
signal(40,sig_proc);
signal(41,sig_proc);
signal(42,sig_proc);
signal(5,sig5_proc);
int ret = sigprocmask(SIG_BLOCK,&set,NULL);
if(ret == -1){
perror("sigprocmask");
}
for(;;);
sigprocmask(SIG_UNBLOCK,&set,NULL);
return 0;
}
sigaction
?1) 缺省情况下,在信号处理函数的执行过程中,会自动屏蔽这个正在被处理的信号。 而对于其它信号则不会屏蔽,通过sigaction::sa_mask成员可以人为指定。 在信号处理函数的执行过程中,需要加入进程信号掩码中的信号。 并在信号处理函数执行完之后,自动解除对这些信号的屏蔽。 ?2)sigaction::sa_flags可为以下值的位或: SA_ONESHOT/SA_RESETHAND执行完一次信号处理函数后,即将对此信号的处理恢复为 默认方式(这也是老版本signal函数的缺省行为。 SA_NODEFER/SA_NOMASK 在信号处理函数的执行过程中,不屏蔽这个正在被处理的信号。 ?SA_NOCLDSTOP 若signum参数取SIGCHLD,则当子进程暂停时,不通知父进程。 SA_RESTART 系统调用一旦被signum参数所表示的信号中断,会自行重启。 SA_SIGINFO 使用信号处理函数指针2,通过该函数的第二个参数,提供更多信息。
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
void sigint_proc(int sig){
printf("收到信号%d\n",sig);
sleep(10);
printf("proc end!\n");
}
int main(){
sigset_t set;
sigemptyset(&set);
struct sigaction act={};
act.sa_handler = sigint_proc;
act.sa_mask = set;
act.sa_flags = SA_NOMASK;
int ret = sigaction(2,&act,NULL);
if(ret == -1){
perror("sigaction");
return -1;
}
for(;;);
return 0;
}
和上面一个相比较而言flags处多 | 了SA_ONESHOT
//在处理sig这个信号时,自动屏蔽该信号,2(SIGINT)一次,后面的2就会被屏蔽
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
void sigint_proc(int sig){
printf("收到信号%d\n",sig);
sleep(10);
printf("proc end!\n");
}
int main(){
sigset_t set;
sigemptyset(&set);
struct sigaction act={};
act.sa_handler = sigint_proc;
act.sa_mask = set;
act.sa_flags = SA_NOMASK|SA_ONESHOT;
int ret = sigaction(2,&act,NULL);
if(ret == -1){
perror("sigaction");
return -1;
}
for(;;);
return 0;
}
SA_SIGINFO 多一个函数可以传递其他参数
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
void sigint_proc(int sig){
printf("收到信号%d\n",sig);
sleep(10);
printf("proc end!\n");
}
void sigaction_proc(int sig,siginfo_t *info,void *ptr){
printf("%d进程收到一个信号:%d\n",getpid(),sig);
printf("该信号来自进程:%d,并且携带数据:%d\n",info->si_pid,info->si_value.sival_int);
}
int main(){
printf("进程:%d\n",getpid());
sigset_t set;
sigemptyset(&set);
struct sigaction act={};
act.sa_sigaction = sigaction_proc;
act.sa_mask = set;
act.sa_flags = SA_NOMASK|SA_SIGINFO;
int ret = sigaction(2,&act,NULL);
if(ret == -1){
perror("sigaction");
return -1;
}
for(;;);
return 0;
}
sigqueue
因为kill不能带数据
所以有sigqueue 可以传递数据
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
int main(int argc,char *argv[]){
if(argc < 4){
printf("%s -sig pid data\n",argv[0]);
return -1;
}
printf("%d\n",getpid());
int sig = -atoi(argv[1]);
int pid = atoi(argv[2]);
int data = atoi(argv[3]);
union sigval val;
val.sival_int = data;
int ret = sigqueue(pid,sig,val);
if(ret == -1){
perror("sigqueue");
return -1;
}
return 0;
}
和sigaction联合使用
两个终端
sigqueue取别名sigq
运行sigaction 等待信号
另一个终端 sigq -2 3814 100
sigaction输出数据
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
void sigint_proc(int sig){
printf("收到信号%d\n",sig);
sleep(10);
printf("proc end!\n");
}
void sigaction_proc(int sig,siginfo_t *info,void *ptr){
printf("%d进程收到一个信号:%d\n",getpid(),sig);
printf("该信号来自进程:%d,并且携带数据:%d\n",info->si_pid,info->si_value.sival_int);
}
int main(){
printf("进程:%d\n",getpid());
sigset_t set;
sigemptyset(&set);
struct sigaction act={};
act.sa_sigaction = sigaction_proc;
act.sa_mask = set;
act.sa_flags = SA_NOMASK|SA_SIGINFO;
int ret = sigaction(2,&act,NULL);
if(ret == -1){
perror("sigaction");
return -1;
}
for(;;);
return 0;
}
time
计时器
SIGALRM - 真实计时器
SIGVTALRM - 虚拟计时器
SIGPROF - 实用计时器
ITIMER_REAL: 真实计时器;
ITIMER_VIRTUAL: 虚拟计时器;
ITIMER_PROF: 实用计时器。
定时任务
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <time.h>
#include <sys/time.h>
void sigalarm_proc(int sig){
system("clear");
time_t t = time(NULL);
struct tm *lt = localtime(&t);
printf("%2d:%2d:%2d\n",lt->tm_hour,lt->tm_min,lt->tm_sec);
}
int main(){
if(signal(SIGALRM,sigalarm_proc)==SIG_ERR){
perror("signal");
return -1;
}
struct itimerval t = {{1,0},{3,0}};
int ret = setitimer(ITIMER_REAL,&t,NULL);
if(ret == -1){
perror("setitmer");
return -1;
}
for(;;);
return 0;
}
|