Linux——进程的控制 信号

一.进程的基本控制

    1.进程的常见控制函数
            1.1.为什么需要控制进程?
            1.2.pause/sleep/usleep

            1.3.atexit  on_exit

atexic.c

#include <stdio.h>
#include <stdlib.h> 
void fun()
{
	printf("over\n");
}
main()
{
	atexit(fun);//进程执行完毕后 在退出之前调用fun函数
	printf("Process!\n");
}


   

    2.进程与文件锁
            在多进程下文件读写是共享的
            问题:
                    怎么知道一个文件正在被另外进程读写?
            解决方案:
                    文件锁。(建议锁)  其实就是告诉我们一个文件的状态
            API:
                    fcntl(文件锁受内核参数影响)        
            编程技巧:
                    对文件加锁
                    判定一个文件是否存在锁
            函数说明:
                    int fcntl(
                            int fd,//被加锁的文件描述符号
                            int cmd,//操作方式:F_SETLK(已经加锁,异常返回) F_UNLK F_SETLKW(已经加锁,则阻塞)
                            struct flock *lk);//锁的描述
                    
                    返回值:
                            0:加锁成功
                            -1:加锁失败
                                                    
案例:
            写两个程序:

                        A:加锁

setlock.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
main()
{
	int fd;
	struct flock lk;
	int r;
	//打开一个文件
	fd=open("a.txt",O_RDWR);
	if(fd==-1) printf(":%m\n"),exit(-1);
	//描述锁
	lk.l_type=F_WRLCK;
	lk.l_whence=SEEK_SET;
	lk.l_start=5;
	lk.l_len=10;
	//lk.l_pid不用设置 进程自动完成
	//加锁
	r=fcntl(fd,F_SETLK,&lk);//另一个进程如果同时对该文件加锁 位置不冲突就行
	if(r==0) printf("加锁成功!\n");
	else	printf("加锁失败!\n");
	while(1);	
}


                        B:获取锁的信息

getlock.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>

main()
{
	int fd;
	struct flock lk={0};
	int r;
	
	fd=open("a.txt",O_RDWR);
	if(fd==-1) printf("::%m\n"),exit(-1);
	
	r=fcntl(fd,F_GETLK,&lk);
	if(r==0) 
		printf("得到锁成功!\n");
	if(lk.l_type==F_WRLCK)
	{
		printf("写锁!\n");
	}
	printf("start:%d,len:%d\n",lk.l_start,lk.l_len);
	
	printf("PID:%d\n",lk.l_pid);
	
}


                        
锁也是一个进程可以共享的信息。
            

二.信号

    1.信号的作用
            背景:
                    进程之间通信比较麻烦。
                    但进程之间有必须通信,比如父子进程之间。
            作用:
                    通知其他进程响应。进程之间通信机制.
                    信号:
                        接受信号的进程马上停止.调用信号处理函数.
                    信号处理函数:
                        默认处理函数.
                                打印信号信息,退出进程.
                        用户处理函数.                            
            中断:
                    软中断.
                    
案例:

        1.进程之中,默认信号处理(向进程发送没有注册的信号)

signaldeal1.c

#include <unistd.h>
#include <stdio.h>
main()
{
	while(1)
	{
		printf("jin cheng zhi xing:%d!\n",getpid());
		sleep(1);
	}
}
编译执行 在另个终端向该进程发送信号

        2.进程之中,用户信号处理

signaldeal2.c

#include <unistd.h>
#include <stdio.h>
#include <signal.h>
void handle(int s)
{
	printf("xin hao ¡\n");	
}//只有处理函数执行完毕后 主函数才继续执行(中断)

main()
{
	//signal(SIGWINCH,handle);
	signal(35,handle);
	while(1)
	 {
		printf("jin cheng zhi xing:%d!\n",getpid());
		sleep(1);
	 }
}

如果发送的信号跟注册的信号不一致,则执行默认信号处理

        3.中断
        
        kill -s 信号  进程ID向指定进程发送信号
        kill -信号  进程ID                    
        信号:数字1-31  34-64
                 宏SIGINT=2
        ctrl+d 发送信号2 SIGINT
        kill -l察看所有信号             
        
        信号SIGKILL SIGSTOP不能被处理(即不能绑定用户的程序 由系统指定得程序执行).
        
        
案例:
        发送信号
        int kill(pid_t pid,int s)
        进程ID:
                >0:发送信号到指定进程
                =0:发送信号到该进程所在进程组的所有进程
                -1:发送给所有进程,除init外
                <0:发送给指定的进程组(组ID=绝对值)
    2.信号发送与安装
            signal

            kill

kill.c

#include <stdio.h>
#include <signal.h>
main()
{
	int i;
	//while(1)
	for(i=0;i<5;i++)
	{
		kill(4601,35);
		
	}
}



    3.信号的应用
            3.1.延时器timeout 到指定延时时间后发送一个信号SIGALRM;
                    SIGALRM

                    信号发出函数:alarm() 指定多长时间后向本进程发送一个SIGALRM信号

alarm.c

#include <stdio.h>
#include <signal.h>
void deal(int s)
{
	printf("起床!\n");
	//alerm(5);每隔多长时间后做什么事情 类似定时器
}
main()
{		
	signal(SIGALRM,deal);
	alarm(5);
	while(1)
	{
		//.....
	}
}

            3.2.定时器
                int setitimer(int which,//计时方式 一般使用ITIMER_REAL
                                        //ITIMER_REAL  / ITIMER_VIRTUAL /ITIMER_PROF
                            const struct itimerval *val,//定时器的时间参数
                            struct itimer *oldval);//返回原来设置的定时器
                                                            //如果=NULL,则不返回
            struct itimerval
            {
                    struct timeval it_interval;//间隔时间
                    struct timeval it_value;//延时时间
            }
            struct timeval
            {
                 long tv_sec;
                 long tv_usec;

            }

alarm2.c

#include <signal.h>
#include <sys/time.h>
void deal(int s)
{
	printf("起床!\n");
	
}
main()
{
	struct itimerval v={0};
	
	signal(SIGALRM,deal);
	
	v.it_value.tv_sec=3;
	v.it_value.tv_usec=1;//如果全设置为0 无穷时间后才触发 一般设为1
	v.it_interval.tv_sec=1;
	setitimer(ITIMER_REAL,&v,0);
	while(1)
	{
		//.....
	}
}


            信号应用:
                    系统与应用程序之间
                    应用于应用程序之间
                    父子进程之间
        案例1:
                    使用定时器信号,实现多任务.
                    实现:
                            实现7位随机数

                            使用中断实现时间的显示

demo1.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <curses.h>
#include <math.h>
#include <time.h>
#include <sys/time.h>
#include <string.h>
WINDOW *wtime,*wnumb;
int num,i;
void showtime(int s)
{
	time_t tt;
	struct tm *t;  
	if(s==SIGALRM)
	{
		tt=time(0);
		t=localtime(&tt);
		mvwprintw(wtime,1,1,"%02d:%02d:%02d",t->tm_hour,t->tm_min,t->tm_sec);
		refresh();
		wrefresh(wtime);
		wrefresh(wnumb);//可选
	}
}

main()
{
	struct itimerval val;
	initscr();	
	curs_set(0);
	signal(SIGALRM,showtime);
	
	bzero(&val,sizeof(struct  itimerval));
	val.it_value.tv_sec=0;
	val.it_value.tv_usec=1;
	val.it_interval.tv_sec=1;
	
	wnumb=derwin(stdscr,3,11,(LINES-3)/2,(COLS-11)/2);	
	wtime=derwin(stdscr,3,10,0,COLS-10);
	
	box(wnumb,0,0);
	box(wtime,0,0);
	
	setitimer(ITIMER_REAL,&val,0);
		
	refresh();
	wrefresh(wtime);
	wrefresh(wnumb);
	
	while(1)
	{
		num=0;
		for(i=0;i<7;i++)
		{
			num=num*10+rand()%10;
		}
		mvwprintw(wnumb,1,2,"%07d",num);
		refresh();
		wrefresh(wnumb);
		wrefresh(wtime);
		usleep(100000);
	}
	
	
	endwin();
}


        案例2: 屏幕显示随机数 按下任意键就会停止 再按下继续显示
                    实现父子进程之间通信
                    
                    控制进程.
                                        

            sleep与pause函数被信号影响后,sleep不再继续sleep;pause不再pause.

demo2.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <curses.h>
#include <math.h>
WINDOW *w;
int num,i;
int isstop=0;
void handle(int s)
{
	if(s==SIGUSR1)
	{
		if(isstop==0){ 
			isstop=1;
		}
		else{
			isstop=0;
		}		
	}
}
main()
  {
	      initscr();
	      curs_set(0);
	      w=derwin(stdscr,3,11,(LINES-3)/2,(COLS-11)/2);
	      keypad(stdscr,TRUE);//支持功能键
	      keypad(w,TRUE);
	      noecho();
	      box(w,0,0);
	      refresh();
	      wrefresh(w);	
	      if(fork())
		{
			signal(SIGUSR1,handle);
			while(1)
			{
				if(isstop==1)
				{
					pause();//阻塞 直到信号发送
				}
				num=0;
				for(i=0;i<7;i++)
				{
					num=num*10+rand()%10;
				}
				mvwprintw(w,1,2,"%07d",num);
				refresh();
				wrefresh(w);
				usleep(10000);
			
			}
		}
		else//child
		{
			//输入
			int ch;
			while(1)
			{
				ch=getch();
				kill(getppid(),SIGUSR1);				
			}
			
		}	
		endwin();
}
                             

            其他信号函数
             raise(int sig) 向自己发送信号
            
    4.信号的可靠与不可靠以及信号的含义
            信号有丢失.(信号压缩) 向指定进程多次发送信号 进程收到的次数有丢失的现象
            由于历史的缘故:信号有压缩的需求.
            可靠信号(实时信号)与不可靠信号(非实时信号).
            
            早期信号 1-31 31个信号, 不可靠(与系统有关).
            后期信号34-64 31个信号,可靠信号(用户信号)  进程收到的次数不会丢失  
    
    
    5.信号的操作

            信号导致的问题 想在执行某段代码的时候不被信号中断


            1.信号屏蔽    
                    int sigprocmask(int how,//操作方式SIG_BLOCK /SIG_UNBLOCK / SIG_SETMASK                                
                        const sigset_t *sigs,//操作的信号集合
                        sigset_t *oldsigs);//返回原来操作的信号集合
                    1.声明信号集合
                            sigset_t  sigs;
                     2.加入屏蔽信号
                             一组信号集合维护函数:
                             2.1. 清空集合sigemptyset
                             2.2. 添加信号到集合sigaddset
                             2.3. 从集合删除信号sigdelset
                             2.4. 添加所有信号到集合sigfillset
                             2.5. 判定信号是否在集合sigismember
                    3.屏蔽信号

                    4.解除屏蔽

aa.c

#include <stdio.h>
#include <signal.h>
main()
{
	int sum=0;
	int i;
	//1.
	sigset_t sigs;
	//2.加入屏蔽
	sigemptyset(&sigs);	
	sigaddset(&sigs,SIGINT);
	//3.设置屏蔽
	sigprocmask(SIG_BLOCK,&sigs,0);
	for(i=1;i<=5;i++)
	{
		sum+=i;
		sleep(1);
	}
	printf("sum=%d\n",sum);
	//4.解除屏蔽
	sigprocmask(SIG_UNBLOCK,&sigs,0);
	printf("Over!\n");//如果在信号屏蔽后的进程中有信号产生 
                          //就会排队等待 解除屏蔽后会先执行信号的操作
}

  

                        
            2.信号屏蔽的切换
                    int sigsuspend(sigset_t *sigs);
                    屏蔽新的信号,原来的信号失效.
                    sigsuspend是阻塞函数.对参数信号屏蔽.
                    对参数没有指定的信号不屏蔽,但当没有屏蔽信号处理函数调用完毕
                    sigsuspend返回条件:
                                1.信号发生,并且信号是非屏蔽信号
                                2.信号必须要处理,而且处理函数返回后,sigsuspend才返回.
                    
                    sigsuspend设置新的屏蔽信号,保存旧的屏蔽信号
                    而且当sigsuspend返回的时候,恢复旧的屏蔽信号.
                        
                            
                        
            3.查询被屏蔽的信号

                    int sigpending(sigset_t *sets);

mask.c

#include <stdio.h>
#include <signal.h>
void h(int s)
{
	printf("抽空处理SIGINT信号\n");
}
main()
{
	int sum=0;
	int i;
	//1.声明
	signal(SIGINT,h);
	sigset_t sigs,sigp,sigq;
	//2.加入屏蔽
	sigemptyset(&sigs);
	sigemptyset(&sigp);
 	sigemptyset(&sigq);
	
	sigaddset(&sigs,SIGINT);
	//3.设置屏蔽
	sigprocmask(SIG_BLOCK,&sigs,0);
	for(i=1;i<=10;i++)
	{
		sum+=i;
		sigpending(&sigp);
		if(sigismember(&sigp,SIGINT))
		{
			printf("SIGINT在排队!\n");
			sigsuspend(&sigq);
			//使原来屏蔽信号无效,开放原来信号
			//使新的信号屏蔽,
			//当某个信号处理函数处理完毕
			//sigsuspend恢复原来屏蔽信号,返回 
		}
		sleep(1);
	}
	printf("sum=%d\n",sum);
	sigprocmask(SIG_UNBLOCK,&sigs,0);
	printf("Over!\n");
}


demo1.c

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
void handle(int s)
{
	printf("外部用户中断处理...!\n");
	sleep(3);
	printf("外部用户中断处理完毕!\n");
}
main()
{
	int sum=0;
	int i;
	sigset_t sigs,sigt,sigu;
	sigemptyset(&sigs);
	sigemptyset(&sigt);
	sigemptyset(&sigu);
	
	sigaddset(&sigs,SIGINT);
	//sigfillset(&sigs);
	
	signal(SIGINT,handle);
	
	sigprocmask(SIG_BLOCK,&sigs,0);
	for(i=0;i<10;i++)
	{			
		printf("正在拷贝电影<%d>!\n",i);
		sleep(5);//模拟业务处理时间比较长
		printf("正在拷贝电影<%d>完毕!\n",i);
		sigpending(&sigu);
		if(sigismember(&sigu,SIGINT))
		{
			sigsuspend(&sigt);
		}		
	}
	printf("所有电影拷贝完毕\n",sum);
	sigprocmask(SIG_UNBLOCK,&sigs,0);
	printf("over!\n");
}


6.最新版本的信号发送与处理
        sigqueue/sigaction                                        
1.思考:信号中断函数调用中是否被其他信号中断.                                     
        信号函数调用中只屏蔽本身信号,不屏蔽其他信号.

sighandle.c


#include <stdio.h>
#include <signal.h>
void handle(int s)
{
	printf("Hello!start\n");
	sleep(10);
	printf("Hello!end\n");
}
main()
{
	signal(SIGINT,handle);
	while(1);
}

以上代码在执行过程中多次按下Ctrl+C 信号排队(有丢失 每次执行一个)

2.怎么保证函数调用中屏蔽指定的信号呢?
        sigaction可以指定处理函数调用的时候屏蔽信号;在处理信号的时候,接受数据.功能类似signal
        
        sigqueue发送信号的时候,可以发送数据.
        
        sigaction/sigqueue是signal/kill的增强版本
        
3.函数说明                    
        使用sigaction(相当与signal)/sigqueue(相当与kill)有两个理由.
        3.1.稳定
        3.2.增强功能
    int sigaction(
        int sig,//被处理信号
        const struct sigaction*action,//处理函数及其参数
        struct sigaction*oldact//返回原来的处理函数结构体
        )
    返回:
            0:成功
            -1:失败
    struct sigaction
    {
        void (*sa_handler)(int);
        void (*sa_sigaction)(int,siginfo_t*,void*);
        sigset_t *mask;//屏蔽信号
        int flags;//SA_SIGINFO 信号处理两个函数靠着个参数指定 如果指定先执行下面的那个
        void**//保留成员.
    }
案例:
            1.使用sigaction处理信号,使用kill发送信号

sigaction.c

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
void handle(int s)
{
	printf("OOOK!\n");
	sleep(5);
	printf("K000!\n");
}


main()
{
	struct sigaction act={0};
		
	act.sa_handler=handle;
	sigemptyset(&act.sa_mask);
	sigaddset(&act.sa_mask,SIGINT);//函数不被SIGINT干扰(先排队 结束后影响)
	                               //(函数本身就不会被SIGUSR1干扰)
	act.sa_flags=0;	
	sigaction(SIGUSR1,&act,0);
	
	while(1);	
}


            2.使用sigaction处理信号,使用sigqueue发送信号

sigaction2.c

#include <stdio.h>
#include <signal.h>
#include <unistd.h>

void handle(int s,siginfo_t* info,void *d)
{
	printf("OOOK:%d\n",info->si_int);
	sleep(5);
	printf("K000!\n");
}

main()
{
	struct sigaction act={0};

	act.sa_sigaction=handle;
	sigemptyset(&act.sa_mask);
	sigaddset(&act.sa_mask,SIGINT);
	
	act.sa_flags=SA_SIGINFO;
	
	sigaction(SIGUSR1,&act,0);
	
	while(1);	
}

sigqueu.c

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
main()
{
	union sigval val;
	val.sival_int=8888;
	
	sigqueue(3972,SIGUSR1,val);//用他来向指定进程发送信号
}


            3.发送信号的同时处理数据       

回顾:

        1.进程控制sleep pause
        2.理解信号的中断流程
        3.发射信号(Shell/code),处理信号
        4.alarm与setitimer
        5.信号应用:实现简单多任务与进程之间通信
        6.使用信号+sleep/pause控制进程
        7.信号的屏蔽
        8.了解sigpending与 sigsuspend的使用.

任务:
        
        1.写一个程序,创建两个子进程,分别计算1-5000与5001-1000素数,
                 通过信号让,父进程专门完成数据保存.
        
        2.完成如下作业:
                 在屏幕水平同时显示移动两个字符.
        
思考:
        信号处理函数调用过程中,是否被其他信号影响.                                
            
明天:
    1.信号与进程间数据传递
            sigqueue=kill与sigaction=signal
    2.IPC:
            基于文件
                    无序文件:映射
                    有序文件:管道文件:有名/匿名
                                     socket文件
            基于内存
                    无序内存
                                内存共享
                    有序内存        
                                共享队列
            

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值