LINUX编程学习笔记(十五) 进程控制 文件锁 信号处理与屏蔽

来源:互联网 发布:数控机床编程入门基础 编辑:程序博客网 时间:2024/06/05 01:19

原地址:http://blog.csdn.net/a8887396/article/details/9143347


一 进程的基本控制

1 进程的常见控制函数

1.1 为什么需要控制进程?

1.2 休眠 pause/sleep/usleep

1.3 on_exit atexit

int atexit(void (*function)(void)); 
int on_exit(void (*function)(int , void *), void *arg);
注册一个函数,在调用exit或者main函数return前调用。
atexit的函数较简单,无参数
on_exit的函数函数中int是exit或者rerurn的返回值(1字节的 不需要宏解析),void*是后面的arg


可以注册多次,但只调用一次。
多进程中子进程会复制该注册,但只要运行一次其他的都被取消。
[plain] view plaincopy
  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3.   
  4.   
  5. void func()  
  6. {  
  7.     printf("before over\n");  
  8. }  
  9. int main()  
  10. {  
  11.     printf("Process\n");  
  12.     atexit(func);  
  13.     return 0;  
  14. }  



[plain] view plaincopy
  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3.   
  4.   
  5. void func(int statue,void *args)  
  6. {  
  7.     printf("before over\n");  
  8.     printf("statue = %d\n",statue);  
  9.     printf("args = %s\n",(char *)args);  
  10. }  
  11.   
  12.   
  13. int main()  
  14. {  
  15.     printf("Process\n");  
  16.     char *str = "hello";  
  17.     on_exit(func,(void *)str);  
  18.     return 99;  
  19. }  



zhao@ubuntu:~/unix/6$ ./a.out 
Process
before over
statue = 99
args = hello


    2 进程与文件锁

在多进程下文件读写是共享的
问题:怎么知道一个文件正在被另外进程读写?
解决方案: 文件锁(默认是建议锁 强制锁需重编译内核)
API:
fcntl (文件锁受内核参数影响)
编程技巧:
对文件加锁
判定一个文件是否存在锁
函数说明:
int fcntl(
int fd, //被加锁的文件描述符
int cmd, //锁的操作方式: F_SETLK (若已加锁,异常返回) F_SETLKW(若已加锁则等待) F_UNLK(解锁)
struct flock *fk //锁的描述
);

  struct flock {
               ...
               short l_type;    /* Type of lock: F_RDLCK,
                                   F_WRLCK, F_UNLCK */
               short l_whence;  /* How to interpret l_start:
                                   SEEK_SET, SEEK_CUR, SEEK_END */
               off_t l_start;   /* Starting offset for lock */
               off_t l_len;     /* Number of bytes to lock */
               pid_t l_pid;     /* PID of process blocking our lock
                                   (F_GETLK only) */
               ...
           };




加锁
[plain] view plaincopy
  1. #include <unistd.h>  
  2. #include <fcntl.h>  
  3. #include <stdlib.h>  
  4. #include <stdio.h>  
  5.   
  6.   
  7. int main()  
  8. {  
  9.     int fd;  
  10.     struct flock lk={0};  
  11.     //打开一个文件  
  12.     fd = open("test",O_RDWR|O_CREAT,0666);  
  13.     if(fd == -1)  
  14.     {  
  15.         printf("::%m\n"),exit(-1);  
  16.     }  
  17.       
  18.       
  19.     //描述锁  
  20.     lk.l_type = F_WRLCK;  
  21.     lk.l_whence=SEEK_SET;  
  22.     lk.l_start=5;  
  23.     lk.l_len=10;  
  24.       
  25.     //加锁  
  26.     int r = fcntl(fd,F_SETLK,&lk);  
  27.     if(r == 0) printf("加锁成功\n");  
  28.     else  
  29.     {  
  30.         printf("加锁失败\n");  
  31.           
  32.     }  
  33.       
  34.     while(1);//程序结束自动解锁  
  35.       
  36. }  




注意这里只是对5 到 10加了锁 当加锁区域不一样时 还可以成功加上另外的锁
如果区域重叠 就会导致加锁失败


读锁
[plain] view plaincopy
  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <unistd.h>  
  4. #include <fcntl.h>  
  5.   
  6.   
  7. int main()  
  8. {  
  9.     int fd;  
  10.     struct flock lk={0}; //要是不初始化 就不能成功得到锁  
  11.     fd = open("test",O_RDWR);  
  12.     if(fd <0)  
  13.     {  
  14.         printf("::%m\n"),exit(-1);  
  15.     }  
  16.       
  17.     int r = fcntl(fd,F_GETLK,&lk);  //F_GETLK  
  18.     if(r == 0)  
  19.     {  
  20.         printf("得到锁成功\n");  
  21.     }  
  22.     else  
  23.     {  
  24.         printf("得到锁失败\n");  
  25.           
  26.     }  
  27.     if(lk.l_type == F_WRLCK)  
  28.     {  
  29.         printf("写锁\n");  
  30.     }  
  31.     printf("start:%d,len %d\n",lk.l_start,lk.l_len);  
  32.       
  33.       
  34. }  



解锁
[plain] view plaincopy
  1. #include <unistd.h>  
  2. #include <fcntl.h>  
  3. #include <stdlib.h>  
  4. #include <stdio.h>  
  5.   
  6.   
  7. int main()  
  8. {  
  9.     int fd;  
  10.     struct flock lk={0};  
  11.     //打开一个文件  
  12.     fd = open("test",O_RDWR|O_CREAT,0666);  
  13.     if(fd == -1)  
  14.     {  
  15.         printf("::%m\n"),exit(-1);  
  16.     }  
  17.       
  18.       
  19.     //描述锁  
  20.     lk.l_type = F_WRLCK;  
  21.     lk.l_whence=SEEK_SET;  
  22.     lk.l_start=20;  
  23.     lk.l_len=10;  
  24.       
  25.     //加锁  
  26.     int r = fcntl(fd,F_SETLK,&lk);  
  27.     if(r == 0) printf("加锁成功\n");  
  28.     else  
  29.     {  
  30.         printf("加锁失败\n");  
  31.           
  32.     }  
  33.       
  34.     sleep(3);  
  35.     /* 。。。对文件进行操作。。。*/  
  36.     lk.l_type = F_UNLCK;  
  37.     r = fcntl(fd,F_SETLK,&lk);  
  38.     if(r == 0) printf("解锁成功\n");  
  39.     else  
  40.     {  
  41.         printf("解锁失败\n");  
  42.     }  
  43.               
  44. }  


注意 这些都只是建议锁,即使你读取到有锁,或者加锁失败,你都还可以对文件进行操作



二 信号

1 信号的作用

通知其他进程相应。进程间的一种通信机制。

接收信号的进程会马上停止,执行信号处理函数。(软中断)


1.1进程之中,默认的信号处理

[plain] view plaincopy
  1. #include <unistd.h>  
  2. #include <stdio.h>  
  3. #include <signal.h>  
  4.   
  5.   
  6. int main()  
  7. {  
  8.     while(1)  
  9.     {  
  10.         printf("进程在执行\n");  
  11.         sleep(1);  
  12.     }  
  13.       
  14. }  



怎么发信号: kill -s 信号 进程id  
 kill -s 1 7038 //向进程7038中 发送信号1
 kill  -s SIGUP 7038  //使用信号的宏
 kill -l 查看所有信号


进程在执行
进程在执行
进程在执行
进程在执行
进程在执行
挂起


ctrl+c 相当与发送信号2 SIGINT 中断信号




1.2进程之中,用户的信号处理

一个进程只能绑定一个函数
一个函数可以绑定在多个进程上
[plain] view plaincopy
  1. #include <unistd.h>  
  2. #include <stdio.h>  
  3. #include <signal.h>  
  4.   
  5.   
  6. void handle(int s)  
  7. {  
  8.     printf("我是信号%d\n",s);  
  9.     sleep(2);  
  10. }//函数返回后 主进程才继续  
  11.   
  12.   
  13. int main()  
  14. {  
  15.     signal(SIGINT,handle); //注册信号处理函数  
  16.     while(1)  
  17.     {  
  18.         printf("进程在执行\n");  
  19.         sleep(1);  
  20.     }  
  21. }  




进程在执行
^C我是信号2 
进程在执行
^C我是信号2 
进程在执行
^C我是信号2 
进程在执行
进程在执行
进程在执行


发现ctrl+c 发送的是信号2
发现关不掉 kill -s 9




注意:信号SIGKILL SIGSTOP 不能被处理



2 信号的发送与安装、

       int kill(pid_t pid, int sig);//向进程pid发送sig信号
进程ID:
>0 发送信号到指定进程
=0 发送信号到该进程所在进程组的所有进程
-1  发送所有进程  不包含init
<-1 发送给指定的进程组 (组ID=绝对值)

[plain] view plaincopy
  1. #include <unistd.h>  
  2. #include <stdio.h>  
  3. #include <signal.h>  
  4. #include <sys/types.h>  
  5.   
  6.   
  7. int main()  
  8. {  
  9.     while(1)  
  10.     {     
  11.         kill(7323,SIGINT);  
  12.         sleep(5);  
  13.     }  
  14. }  




3 信号的应用

3.1 延时器

SIGALRM
信号发出函数 alarm 


3.2 定时器

int setitimer(int which, //计时方式 ITIMER_REAL  ITMER_VIRTUAL ITIMER_PRO 
const struct itimerval *val, // 定时器的时间参数
struct itimer *oldval); // 返回原来设置的定时器


ITIMER_REAL: 以系统真实的时间来计算,它送出SIGALRM信号。(常用)
ITIMER_VIRTUAL: -以该进程在用户态下花费的时间来计算,它送出SIGVTALRM信号。
ITIMER_PROF: 以该进程在用户态下和内核态下所费的时间来计算,它送出SIGPROF信号。


    struct itimerval {
               struct timeval it_interval;  /* next value */  间隔时间
               struct timeval it_value;    /* current value */ 延时时间
           };


           struct timeval {
               long tv_sec;                /* seconds */
               long tv_usec;               /* microseconds */
           };



oldval 可以设置为null 不接收


[plain] view plaincopy
  1. #include <signal.h>  
  2. #include <stdio.h>  
  3. #include <unistd.h>  
  4. #include <sys/time.h>  
  5.   
  6.   
  7. void deal(int s)  
  8. {  
  9.     printf("起床!\n");  
  10. }  
  11.   
  12.   
  13. int main()  
  14. {  
  15.     signal(SIGALRM,deal);  
  16.       
  17.     struct itimerval v={0}; //创立一个结构体 结构体又有两个结构体  
  18.     v.it_interval.tv_sec = 3;  
  19.     v.it_interval.tv_usec = 1;  
  20.     v.it_value.tv_sec =5;  
  21.     v.it_value.tv_usec = 0;  
  22.     //5秒后 每隔3秒1微秒 发出SIGALRM信号  
  23.     setitimer(ITIMER_REAL,&v,NULL);  
  24.       
  25.     while(1)  
  26.     {  
  27.   
  28.   
  29.     }     
  30. }  




4 信号的可靠与不可靠以及信号的含义

信号有丢失(信号压缩)
由于历史的缘故:信号有压缩的需求
可靠信号(实时信号)与不可靠信号(非实时信号)


早期信号 1-31 31个信号都是不可靠(与系统有关,系统会产生)。
最早31个中留了SIGUSR1 SIGUSR2给用户使用
后期信号 34-64 31个信号 ,可靠信号(用户信号)
跟系统打交道,使用早期信号
处理用户产生的信号,使用可靠信号


5 信号的操作

1 信号屏蔽

 int sigprocmask(int how, //操作方式:SIG_BLOCK屏蔽   SIG_UNBLOCK解除屏蔽  SIG_SETMASK修改屏蔽
const sigset_t *set,//  操作的信号集合
sigset_t *oldset);   // 返回原来的被屏蔽的集合
编程步骤
1 声明信号集合
sigset_t sigs;
2 加入屏蔽信号
信号结合屏蔽函数
清空集合 sigemptyset
添加信号到集合 sigaddset
从集合删除信号 sigdelset
将所有信号加进去 sigfillset
判断信号是否在集合中 sigismember


       int sigemptyset(sigset_t *set);
       int sigfillset(sigset_t *set);
       int sigaddset(sigset_t *set, int signum);
       int sigdelset(sigset_t *set, int signum);

       int sigismember(const sigset_t *set, int signum);


查询正在排队的信号(已收到 但被sigprocmask屏蔽的信号)

  int sigpending(sigset_t *set);

sigpending()  returns  the set of signals that are pending for delivery
       to the calling thread (i.e., the signals which have been  raised  while
       blocked).  The mask of pending signals is returned in set.


[plain] view plaincopy
  1. #include <stdio.h>  
  2. #include <signal.h>  
  3. int main()  
  4. {  
  5.     sigset_t sigs; //屏蔽集合  
  6.     sigset_t sig;  
  7.     sigemptyset(&sigs);//清空集合  
  8.     sigemptyset(&sig);//清空集合  
  9.     sigaddset(&sigs,SIGINT);//  
  10.     sigprocmask(SIG_BLOCK,&sigs,0); //开始屏蔽  
  11.       
  12.     int sum=0;  
  13.     int i;  
  14.     for(i=1;i<=10;i++)  
  15.     {  
  16.         sum+=i;  
  17.         sleep(1);  
  18.         sigpending(&sig);//查询正在排队(被屏蔽)的信号  
  19.         if(sigismember(&sig,SIGINT)){ printf("SIGINT正在排队!\n");}  
  20.     }  
  21.     printf("sum=%d\n",sum);  
  22.   
  23.   
  24.     sigprocmask(SIG_UNBLOCK,&sigs,0); //解除屏蔽  
  25.     printf("over!\n");  
  26.       
  27. }  




2 信号屏蔽的切换

sigsuspend 信号屏蔽切换
int sigsuspend(const sigset_t *mask); 
sigsuspend 暂时取代目前的屏蔽信号,然后挂起进程,直到一个信号,调用一个信号处理程序或终止该进程。


1如果该信号终止该过程sigsuspend不会返回。
2 如果信号被捕获,处理完信号处理函数首,sigsuspend()会返回,且恢复以前的屏蔽信号。


       SIGKILL或SIGSTOP无法屏蔽

这个函数的主要作用是将中断限制在一定的范围内。比如signal产生的中断可能发生在任何地方
[plain] view plaincopy
  1. #include <stdio.h>  
  2. #include <signal.h>  
  3. void handle(int s)  
  4. {  
  5.     printf("SIGINT处理中\n");  
  6. }  
  7.   
  8.   
  9. int main()  
  10. {  
  11.     signal(SIGINT,handle);  
  12.     sigset_t sigp,sigq,sigs;  
  13.     sigemptyset(&sigp);  
  14.     sigemptyset(&sigq);  
  15.     sigemptyset(&sigs);  
  16.       
  17.     sigaddset(&sigp,SIGINT);  
  18.     sigprocmask(SIG_BLOCK,&sigp,0);  
  19.     printf("屏蔽SIGINT开始\n");  
  20.     int i = 0,sum=0;  
  21.     for(;i<10;i++)  
  22.     {  
  23.         sum += i;  
  24.         sleep(1);  
  25.         sigpending(&sigq);//得到排队中的信号  
  26.         if(sigismember(&sigq,SIGINT))  
  27.         {  
  28.             printf("SIGINT正在排队\n");  
  29.             sigsuspend(&sigs);//只能在这里产生SIGINT的中断  
  30.             printf("SIGINT处理结束\n");  
  31.         }  
  32.     }  
  33. }  
0 0
原创粉丝点击