C++学习:第六章Linux高级编程 - (六)进程控制、信号、定时器、信号操作、信号屏蔽

问题解答:

     1. exit(状态码)返回状态码有什么意义?

          返回值被系统得到.系统根据状态码进行日志记录.

          返回值被调用者得到:system/wait.程序会根据返回状态码进行对应处理。

          exit(状态码)=main函数中的return 状态码;

     2. 状态码的第二个字节才是exit()的返回值或者return值。

一、 进程的基本控制

     1. 进程的常见控制函数

          1.1 为什么需要控制进程?

          1.2 pause/sleep/usleep(精确到微秒的sleep)

          1.3 atexit  on_exit           回调函数

     程序在结束前会回调fun函数

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

void fun()
{
    printf("over\n");
}

int main()
{
    atexit(fun);
    printf("Process!\n");

    return 0;
}

     2. 进程与文件锁

          在多进程下文件读写是共享的

     问题:

          怎么知道一个文件正在被另外进程读写?

     解决方案:

          文件锁。(建议锁)

     API:

          fcntl(文件锁受内核参数影响)

     编程技巧:

          对文件加锁

          判定一个文件是否存在锁

     函数说明:

int fcntl(
    int fd,    //被加锁的文件描述符号
    int cmd, //锁的操作方式:F_SETLK(已经加锁,异常返回)
    F_UNLK F_SETLKW(已经加锁,则阻塞等待)
    struct flock *lk);  //锁的描述

返回值:
    0:加锁成功
    -1::加锁失败

案例:

写两个程序:

A:加锁

#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;//锁的开始,第5个字节开始
    lk.l_len=10;//锁的长度,10个字节

    //锁的进程ID,系统定义

    //加锁
    r=fcntl(fd,F_SETLK,&lk);//加锁
    if(r==0) printf("加锁成功!\n");
    else printf("加锁失败!\n");
    while(1);      

    //程序退出后系统会自动解锁
}

B:获取锁的信息

#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("PID : %d \n", lk.l_pid);//加锁的进程id
}

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

二、 信号

     1. 信号的作用

     背景:

          进程之间通信比较麻烦,但进程之间有必须通信,比如父子进程之间。

     作用:

          通知其他进程响应。进程之间通信机制。

     信号:

          接受信号的进程马上停止,调用信号处理函数。

     信号处理函数:

          默认处理函数:打印信号信息,退出进程。

          用户处理函数。

     中断:(硬件的处理)把当前所有任务停止下来去处理当前信号任务

          信号的终端处理是假的,称软中断。

     案例:

          1. 进程之中,默认信号处理

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

          3. 中断

               kill  -s    信号      进程ID

               kill  -信号            进程ID

               信号表达方式:

                    数字形式     1-31和34-64

                    宏的方式     SIGINT=2

                    其中ctrl+c 发送的信号就是2( SIGINT)

                    kill -l察看所有信号

                    信号SIGKILL   SIGSTOP不能被处理.

              案例:

             发送信号

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

void handle(int s)
{
    printf("我是信号发生!\n");  
}


main()
{
    signal(SIGINT,handle);

    while(1)
    {
        printf("进程在执行:%d!\n",getpid());
        sleep(1);
    }
}

     控制台每次输入 Ctrl+c 就会输出 我是信号发生! ,同样可以使用kill  -SIGINT      进程ID

 

     案例:使用程序发送信号

          int kill(pid_t pid,int s)

          进程ID(pid):

          >0:发送信号到指定进程

          =0:发送信号到该进程所在进程组的所有进程

          -1:发送给所有进程,除init外

          <0:发送给指定的进程组(组ID=绝对值)

     2. 信号发送与安装

     signal

     kill

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

main()
{

    while(1)
    {
        kill(4601,SIGINT);
        sleep(2);
    }
}

     3. 信号的应用

          3.1.延时器timeout

               SIGALRM(名字)

               信号发出函数:alarm

               unsigned int alarm(unsigned int seconds);

               只向本进程发送信号,不向其他进程发送信号。

               一个函数能绑定多个信号,一个信号只能绑定一个函数

#include <stdio.h>
#include <signal.h>
#include <sys/time.h>

void deal(int s)
{
    printf("起床!\n");
}

main()
{
    signal(SIGALRM,deal);
    alarm(5);
    while(1)
    {
        //.....
    }
}

          3.2.定时器

               int setitimer(int which,      //计时方式:

                    //ITIMER_REAL      自然界真实时间作为计时方式,推荐

                    //ITIMER_VIRTUA  由程序实际运行时间作为计时方式

                    //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;       //微妙

                    }

#include <stdio.h>
#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_sec=0;
    v.it_value.tv_usec=1;
    v.it_interval.tv_sec=1;

    //alarm(5);
    setitimer(ITIMER_REAL,&v,0);

    while(1)
    {
        //.....
    }
}

          3.3 信号应用:

               系统与应用程序之间

               应用于应用程序之间

               父子进程之间

          案例1:

               使用定时器信号,实现多任务。

          实现:

               实现7位随机数

          使用中断实现时间的显示

#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(10000);
    }

    endwin();
}

     案例2:

          实现父子进程之间通信

          控制进程。

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

#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)//显示7位数的随机数
        {
            if(isstop==1)
            {
                pause();//pause会被信号中断停止 ***
            }

            num=0;
            for(i=0;i<7;i++)//产生7位随机数
            {
                num=num*10+rand()%10;
            }

            mvwprintw(w,1,2,"%07d",num);//显示随机数
            refresh();//刷新屏幕。
            wrefresh(w);
            usleep(10000);//暂停10毫秒
        }
    }
    else//child
    {
        //输入
        int ch;
        while(1)//处理按键
        {
            ch=getch();
            //if(ch==KEY_ENTER)
            {
                kill(getppid(),SIGUSR1);
            }
        }
    }
    endwin();
}

     练习:

     在curses显示7位随机数

          其他信号函数

          raise

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

          信号有丢失。(信号压缩)

          由于历史的缘故:信号有压缩的需求.

          可靠信号(实时信号)与不可靠信号(非实时信号).

          早期信号 1-31 31个信号,不可靠(与系统有关).

          后期信号34-64 31个信号,可靠信号(用户信号)       

     5. 信号的操作

          信号导致的问题

          5.1 信号屏蔽    

               int sigprocmask(int how,         //操作方式

                    SIG_BLOCK         //

                    SIG_UNBLOCK   //

                    SIG_SETMASK    //

                    const sigset_t *sigs,   //操作的信号集合

                    sigset_t *oldsigs);             //返回原来操作的信号集合

               5.1.1.声明信号集合

                    sigset_t  sigs;

               5.1.2 加入屏蔽信号

                    一组信号集合维护函数:

                    1. 清空集合                            sigemptyset

                    2. 添加信号到集合         sigaddset

                    3. 从集合删除信号         sigdelset

                    4. 添加所有信号到集合 sigfillset

                    5. 判定信号是否在集合 sigismember

             5.1.3 屏蔽信号

             5.1.4 解除屏蔽

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

void h(int s)
{
    printf("抽空处理int信号\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");
}

          5.2 信号屏蔽的切换

               int sigsuspend(sigset_t *sigs);

                    屏蔽新的信号,原来的信号失效.

               sigsuspend

                    是阻塞函数,对参数信号屏蔽。

                    对参数没有指定的信号不屏蔽,但当没有屏蔽信号处理函数调用完毕

               sigsuspend返回条件:

                    信号发生,并且信号是非屏蔽信号

                    信号必须要处理,而且处理函数返回后 sigsuspend才返回。

                    sigsuspend放在sigprocmask函数后使用时,sigsuspend设置新的屏蔽信号,保存旧的屏蔽信号。而且当sigsuspend返回的时候,会自动恢复sigprocmask旧的屏蔽信号。

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

void h(int s)
{
    printf("非屏蔽信号发生!\n");
}

main()
{
    sigset_t sigs;

    signal(SIGWINCH,h);
    sigemptyset(&sigs);
    sigaddset(&sigs,2);

    printf("屏蔽开始!\n");
    sigsuspend(&sigs);
    printf("屏蔽结束!\n");
}

          5.3 查询被屏蔽的信号

               int sigpending(sigset_t *sets);

     回顾:

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

思考:

              信号处理函数调用过程中,是否被其他信号影响.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值