进程相关的函数

  1. fork()函数:
    /*
        #include <sys/types.h>
        #include <unistd.h>
    
        pid_t fork(void);
            函数的作用:用于创建子进程。
            返回值:
                fork()的返回值会返回两次。一次是在父进程中,一次是在子进程中。
                在父进程中返回创建的子进程的ID,
                在子进程中返回0
                如何区分父进程和子进程:通过fork的返回值。
                在父进程中返回-1,表示创建子进程失败,并且设置errno
    
            父子进程之间的关系:
            区别:
                1.fork()函数的返回值不同
                    父进程中: >0 返回的子进程的ID
                    子进程中: =0
                2.pcb中的一些数据
                    当前的进程的id pid
                    当前的进程的父进程的id ppid
                    信号集
    
            共同点:
                某些状态下:子进程刚被创建出来,还没有执行任何的写数据的操作
                    - 用户区的数据
                    - 文件描述符表
            
            父子进程对变量是不是共享的?
                - 刚开始的时候,是一样的,共享的。如果修改了数据,不共享了。
                - 读时共享(子进程被创建,两个进程没有做任何的写的操作),写时拷贝。
            
    */
    
    #include <sys/types.h>
    #include <unistd.h>
    #include <stdio.h>
    
    int main() {
    
        int num = 10;
    
        // 创建子进程
        pid_t pid = fork();
    
        // 判断是父进程还是子进程
        if(pid > 0) {
            // printf("pid : %d\n", pid);
            // 如果大于0,返回的是创建的子进程的进程号,当前是父进程
            printf("i am parent process, pid : %d, ppid : %d\n", getpid(), getppid());
    
            printf("parent num : %d\n", num);
            num += 10;
            printf("parent num += 10 : %d\n", num);
    
    
        } else if(pid == 0) {
            // 当前是子进程
            printf("i am child process, pid : %d, ppid : %d\n", getpid(),getppid());
           
            printf("child num : %d\n", num);
            num += 100;
            printf("child num += 100 : %d\n", num);
        }
    
        // for循环
        for(int i = 0; i < 3; i++) {
            printf("i : %d , pid : %d\n", i , getpid());
            sleep(1);
        }
    
        return 0;
    }
    
    /*
    实际上,更准确来说,Linux 的 fork() 使用是通过写时拷贝 (copy- on-write) 实现。
    写时拷贝是一种可以推迟甚至避免拷贝数据的技术。
    内核此时并不复制整个进程的地址空间,而是让父子进程共享同一个地址空间。
    只用在需要写入的时候才会复制地址空间,从而使各个进行拥有各自的地址空间。
    也就是说,资源的复制是在需要写入的时候才会进行,在此之前,只有以只读方式共享。
    注意:fork之后父子进程共享文件,
    fork产生的子进程与父进程相同的文件文件描述符指向相同的文件表,引用计数增加,共享文件偏移指针。
    */

     

  2. exec函数族:
    /*  
        #include <unistd.h>
        int execl(const char *path, const char *arg, ...);
            - 参数:
                - path:需要指定的执行的文件的路径或者名称
                    a.out /home/nowcoder/a.out 推荐使用绝对路径
                    ./a.out hello world
    
                - arg:是执行可执行文件所需要的参数列表
                    第一个参数一般没有什么作用,为了方便,一般写的是执行的程序的名称
                    从第二个参数开始往后,就是程序执行所需要的的参数列表。
                    参数最后需要以NULL结束(哨兵)
    
            - 返回值:
                只有当调用失败,才会有返回值,返回-1,并且设置errno
                如果调用成功,没有返回值。
    
    */
    #include <unistd.h>
    #include <stdio.h>
    
    int main() {
    
    
        // 创建一个子进程,在子进程中执行exec函数族中的函数
        pid_t pid = fork();
    
        if(pid > 0) {
            // 父进程
            printf("i am parent process, pid : %d\n",getpid());
            sleep(1);
        }else if(pid == 0) {
            // 子进程
            // execl("hello","hello",NULL);
    
            execl("/bin/ps", "ps", "aux", NULL);
            perror("execl");
            printf("i am child process, pid : %d\n", getpid());
    
        }
    
        for(int i = 0; i < 3; i++) {
            printf("i = %d, pid = %d\n", i, getpid());
        }
    
    
        return 0;
    }

     

  3. 进程退出:
    相关函数是:

    #include <stdlib.h>

    void exit(int status);这个函数是标准c库的函数,会刷新缓冲区,

    #include<stdlib.h>
    #include<stdio.h>
    int main(){
        printf("hello\n");
        printf("world");
        exit(0);
    }

    得到的结果是:

     

     

     ,但是用函数_exit();函数是不会刷新缓冲区的:
    相关函数是:

       #include <unistd.h>
    
        void _exit(int status);

    这个函数是unix系统函数,不会刷新缓冲区:

    #include<unistd.h>
    #include<stdio.h>
    int main(){
        printf("hello\n");
        printf("world");
        _exit(0);
    }

    得到的结果是:

     

  4.  

     进程回收:wait

    /*
        #include <sys/types.h>
        #include <sys/wait.h>
        pid_t wait(int *wstatus);
            功能:等待任意一个子进程结束,如果任意一个子进程结束了,次函数会回收子进程的资源。
            参数:int *wstatus
                进程退出时的状态信息,传入的是一个int类型的地址,传出参数。
            返回值:
                - 成功:返回被回收的子进程的id
                - 失败:-1 (所有的子进程都结束,调用函数失败)
    
        调用wait函数的进程会被挂起(阻塞),直到它的一个子进程退出或者收到一个不能被忽略的信号时才被唤醒(相当于继续往下执行)
        如果没有子进程了,函数立刻返回,返回-1;如果子进程都已经结束了,也会立即返回,返回-1.
    
    */
    #include <sys/types.h>
    #include <sys/wait.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    
    
    int main() {
    
        // 有一个父进程,创建5个子进程(兄弟)
        pid_t pid;
    
        // 创建5个子进程
        for(int i = 0; i < 5; i++) {
            pid = fork();
            if(pid == 0) {
                break;
            }
        }
    
        if(pid > 0) {
            // 父进程
            while(1) {
                printf("parent, pid = %d\n", getpid());
    
                // int ret = wait(NULL);
                int st;
                int ret = wait(&st);
    
                if(ret == -1) {
                    break;
                }
    
                if(WIFEXITED(st)) {
                    // 是不是正常退出
                    printf("退出的状态码:%d\n", WEXITSTATUS(st));
                }
                if(WIFSIGNALED(st)) {
                    // 是不是异常终止
                    printf("被哪个信号干掉了:%d\n", WTERMSIG(st));
                }
    
                printf("child die, pid = %d\n", ret);
    
                sleep(1);
            }
    
        } else if (pid == 0){
            // 子进程
             while(1) {
                printf("child, pid = %d\n",getpid());    
                sleep(1);       
             }
    
            exit(0);
        }
    
        return 0; // exit(0)
    }

     

    进程回收:waitpid

    /*
        #include <sys/types.h>
        #include <sys/wait.h>
        pid_t waitpid(pid_t pid, int *wstatus, int options);
            功能:回收指定进程号的子进程,可以设置是否阻塞。
            参数:
                - pid:
                    pid > 0 : 某个子进程的pid
                    pid = 0 : 回收当前进程组的所有子进程    
                    pid = -1 : 回收所有的子进程,相当于 wait()  (最常用)
                    pid < -1 : 某个进程组的组id的绝对值,回收指定进程组中的子进程
                - options:设置阻塞或者非阻塞
                    0 : 阻塞
                    WNOHANG : 非阻塞
                - 返回值:
                    > 0 : 返回子进程的id
                    = 0 : options=WNOHANG, 表示还有子进程或者
                    = -1 :错误,或者没有子进程了
    */
    #include <sys/types.h>
    #include <sys/wait.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    
    int main() {
    
        // 有一个父进程,创建5个子进程(兄弟)
        pid_t pid;
    
        // 创建5个子进程
        for(int i = 0; i < 5; i++) {
            pid = fork();
            if(pid == 0) {
                break;
            }
        }
    
        if(pid > 0) {
            // 父进程
            while(1) {
                printf("parent, pid = %d\n", getpid());
                sleep(1);
    
                int st;
                // int ret = waitpid(-1, &st, 0);
                int ret = waitpid(-1, &st, WNOHANG);
    
                if(ret == -1) {
                    break;
                } else if(ret == 0) {
                    // 说明还有子进程存在
                    continue;
                } else if(ret > 0) {
    
                    if(WIFEXITED(st)) {
                        // 是不是正常退出
                        printf("退出的状态码:%d\n", WEXITSTATUS(st));
                    }
                    if(WIFSIGNALED(st)) {
                        // 是不是异常终止
                        printf("被哪个信号干掉了:%d\n", WTERMSIG(st));
                    }
    
                    printf("child die, pid = %d\n", ret);
                }
               
            }
    
        } else if (pid == 0){
            // 子进程
             while(1) {
                printf("child, pid = %d\n",getpid());    
                sleep(1);       
             }
            exit(0);
        }
    
        return 0; 
    }

     



  5. 匿名管道通信:
    父子进程之间的通信:
    /*
        #include <unistd.h>
        int pipe(int pipefd[2]);
            功能:创建一个匿名管道,用来进程间通信。
            参数:int pipefd[2] 这个数组是一个传出参数。
                pipefd[0] 对应的是管道的读端
                pipefd[1] 对应的是管道的写端
            返回值:
                成功 0
                失败 -1
    
        管道默认是阻塞的:如果管道中没有数据,read阻塞,如果管道满了,write阻塞
    
        注意:匿名管道只能用于具有关系的进程之间的通信(父子进程,兄弟进程)
    */
    
    // 子进程发送数据给父进程,父进程读取到数据输出
    #include <unistd.h>
    #include <sys/types.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    int main() {
    
        // 在fork之前创建管道
        int pipefd[2];
        int ret = pipe(pipefd);
        if(ret == -1) {
            perror("pipe");
            exit(0);
        }
    
        // 创建子进程
        pid_t pid = fork();
        if(pid > 0) {
            // 父进程
            printf("i am parent process, pid : %d\n", getpid());
    
            // 关闭写端
            close(pipefd[1]);
            
            // 从管道的读取端读取数据
            char buf[1024] = {0};
            while(1) {
                int len = read(pipefd[0], buf, sizeof(buf));
                printf("parent recv : %s, pid : %d\n", buf, getpid());
                
                // 向管道中写入数据
                //char * str = "hello,i am parent";
                //write(pipefd[1], str, strlen(str));
                //sleep(1);
            }
    
        } else if(pid == 0){
            // 子进程
            printf("i am child process, pid : %d\n", getpid());
            // 关闭读端
            close(pipefd[0]);
            char buf[1024] = {0};
            while(1) {
                // 向管道中写入数据
                char * str = "hello,i am child";
                write(pipefd[1], str, strlen(str));
                //sleep(1);
    
                // int len = read(pipefd[0], buf, sizeof(buf));
                // printf("child recv : %s, pid : %d\n", buf, getpid());
                // bzero(buf, 1024);
            }
            
        }
        return 0;
    }

    兄弟进程之间的通信:

    #include<unistd.h>
    #include<stdio.h>
    #include<sys/types.h>
    #include<wait.h>
    #include<string.h>
    #define processmax 2
    int main(){
        //创建管道
        int piped[2];
        int ret = pipe(piped);
        if(ret==-1){
            perror("pipe");
            return -1;
        }
        //创建两个子进程,这里有点新颖
        pid_t pid[processmax];
        pid_t p=1;
        for(int i=0;i<processmax&&p>0;i++){
            p=pid[i]=fork();
        }
        if(p>0){
            //表示父进程
            //父进程关闭管道,只让子进程通信
            close(piped[0]);
            close(piped[1]);
            printf("父进程关闭管道\n");
            while(1) {
                //父进程负责回收子进程资源
                int st;
                int ret = waitpid(-1, &st, WNOHANG);
                if(ret == -1) {
                    break;
                } else if(ret == 0) {
                    // 说明还有子进程存在
                    continue;
                } else if(ret > 0) {

                    if(WIFEXITED(st)) {
                        // 是不是正常退出
                        printf("退出的状态码:%d\n", WEXITSTATUS(st));
                    }
                    if(WIFSIGNALED(st)) {
                        // 是不是异常终止
                        printf("被哪个信号干掉了:%d\n", WTERMSIG(st));
                    }

                    printf("child die, pid = %d\n", ret);
                }
               
            }
        }else if(p==0){
            if(pid[0]==0){
               //表示第一个子进程,负责写管道
             
                char buff[1024];
                while (1){
                    printf("%d开始写数据:",getpid());
                    memset(buff,0,1024);
                    fgets(buff,1024,stdin);
                    int ret=write(piped[1],buff,strlen(buff));
                    if(ret==-1){
                        perror("write");
                        return -2;
                    }
                    while(lseek(piped[1],0,SEEK_END)==0){   
                    }
                    sleep(0.5);
                }
                close(piped[0]);
                close(piped[1]);
            }
            if(pid[1]==0){
                //表示第二个子进程,不断打印收到的数据
                char buff[1024]={0};
                while(1){
                    memset(buff,0,1024);
                    int ret = read(piped[0],buff,sizeof(buff));
                    if(ret==-1){
                        perror("read");
                        return -3;
                    }
                    printf("%d收到了%d大小的数据为:%s",getpid(),ret,buff);
                }
                close(piped[0]);
                close(piped[1]);
            }
        }
        return 0;
    }

    设置管道非阻塞:

    #include <unistd.h>
    #include <sys/types.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <fcntl.h>
    /*
        设置管道非阻塞
        int flags = fcntl(fd[0], F_GETFL);  // 获取原来的flag
        flags |= O_NONBLOCK;            // 修改flag的值
        fcntl(fd[0], F_SETFL, flags);   // 设置新的flag
    */
    int main() {
    
        // 在fork之前创建管道
        int pipefd[2];
        int ret = pipe(pipefd);
        if(ret == -1) {
            perror("pipe");
            exit(0);
        }
    
        // 创建子进程
        pid_t pid = fork();
        if(pid > 0) {
            // 父进程
            printf("i am parent process, pid : %d\n", getpid());
    
            // 关闭写端
            close(pipefd[1]);
            
            // 从管道的读取端读取数据
            char buf[1024] = {0};
    
            int flags = fcntl(pipefd[0], F_GETFL);  // 获取原来的flag
            flags |= O_NONBLOCK;            // 修改flag的值
            fcntl(pipefd[0], F_SETFL, flags);   // 设置新的flag
    
            while(1) {
                int len = read(pipefd[0], buf, sizeof(buf));
                printf("len : %d\n", len);
                printf("parent recv : %s, pid : %d\n", buf, getpid());
                memset(buf, 0, 1024);
                sleep(1);
            }
    
        } else if(pid == 0){
            // 子进程
            printf("i am child process, pid : %d\n", getpid());
            // 关闭读端
            close(pipefd[0]);
            char buf[1024] = {0};
            while(1) {
                // 向管道中写入数据
                char * str = "hello,i am child";
                write(pipefd[1], str, strlen(str));
                sleep(5);
            }
            
        }
        return 0;
    }

     

  6. 有名管道通信:
    写数据:
    #include<sys/types.h>
    #include<sys/stat.h>
    #include<unistd.h>
    #include<sys/stat.h>
    #include<fcntl.h>
    #include<stdio.h>
    #include<string.h>
    int main(int argc,char* argv[]){
        //创建管道
        int ret = access("fifo",F_OK);
        if(ret==-1){
            ret=mkfifo("fifo",0664);
            if(ret==1){
                perror("fifo");
                return -1;
            }
        }
        //打开管道
        int fd = open("fifo",O_WRONLY);
        if(fd==-1){
            perror("open");
            return -2;
        }
        //写数据
        char buff[1024];
        while(1){
            printf("%d开始写数据:\n",getpid());
            memset(buff,0,sizeof(buff));
            fgets(buff,sizeof(buff),stdin);
            ret = write(fd,buff,strlen(buff));
            if(ret==-1){
                perror("write");
                return -3;
            }
        }
        close(fd);
        return 0;
    }

    读数据:

    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <fcntl.h>
    
    // 从管道中读取数据
    int main() {
    
        // 1.打开管道文件
        int fd = open("fifo", O_RDONLY);
        if(fd == -1) {
            perror("open");
            exit(0);
        }
    
        // 读数据
        while(1) {
            char buf[1024] = {0};
            int len = read(fd, buf, sizeof(buf));
            if(len == 0) {
                printf("写端断开连接了...\n");
                break;
            }
            printf("recv buf : %s\n", buf);
        }
    
        close(fd);
    
        return 0;
    }

     



  7. 内存映射:
    父子进程的通信
    #include<stdio.h>
    #include<unistd.h>
    #include<sys/mman.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include<sys/wait.h>
    #include<string.h>
    int main(){
        //创建一个文件用于内存映射
        int ret=access("mmap.txt",F_OK);
        int fd;
        if(ret==-1){
            fd=open("mmap.txt",O_RDWR,0664);
            if(fd==-1){
                perror("open");
                return -1;
            }
        }else{
            fd=open("mmap.txt",O_RDWR);
        }
        //内部必须有数据
        write(fd,"1",1);
        //创建文件映射区
        int len=lseek(fd,0,SEEK_END);
        //这里有个小技巧
        char* buff=(char*)mmap(NULL,len,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
        if(buff==MAP_FAILED){
            perror("mmap");
            return -2;
        }
        //创建进程
        pid_t pid=fork();
        if(pid>0){
            wait(NULL);
            printf("%s",buff);
        }else if(pid==0){
            memcpy(buff,"nihaoa",strlen("nihaoa"));
        }
      //关闭内存映射区
      munmap(buff,size);
    return 0; }

    匿名映射:

    /*
        匿名映射:不需要文件的实体进行内存映射
    
    */
    #include<stdio.h>
    #include<sys/mman.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include<unistd.h>
    #include<stdlib.h>
    #include<sys/wait.h>
    #include<string.h>
    int main(){
        //创建匿名映射
        int len=4096;
    //因为是匿名映射,不用文件,所以文件描述符的位置放上-1,表示没有文件描述符
    void* ptr = mmap(NULL,len,PROT_READ|PROT_WRITE,MAP_SHARED|MAP_ANONYMOUS,-1,0); if(ptr == MAP_FAILED){ perror("mmap"); exit(0); } pid_t pid=fork(); if(pid>0){ strcpy((char*)ptr,"hello,world"); wait(NULL); }else if(pid==0){ sleep(1); char buff[64]={0}; strcpy(buff,(char*)ptr); printf("%s\n",buff); } munmap(ptr,len); return 0; }

     



  8. 定时器:
    kill(给某个进程发送信号):
    /*  
        #include <sys/types.h>
        #include <signal.h>
    
        int kill(pid_t pid, int sig);
            - 功能:给任何的进程或者进程组pid, 发送任何的信号 sig
            - 参数:
                - pid :
                    > 0 : 将信号发送给指定的进程
                    = 0 : 将信号发送给当前的进程组
                    = -1 : 将信号发送给每一个有权限接收这个信号的进程
                    < -1 : 这个pid=某个进程组的ID取反 (-12345)
                - sig : 需要发送的信号的编号或者是宏值,0表示不发送任何信号
    
            kill(getppid(), 9);
            kill(getpid(), 9);
            
        int raise(int sig);
            - 功能:给当前进程发送信号
            - 参数:
                - sig : 要发送的信号
            - 返回值:
                - 成功 0
                - 失败 非0
            kill(getpid(), sig);   
    
        void abort(void);
            - 功能: 发送SIGABRT信号给当前的进程,杀死当前进程
            kill(getpid(), SIGABRT);
    */
    
    #include <stdio.h>
    #include <sys/types.h>
    #include <signal.h>
    #include <unistd.h>
    
    int main() {
    
        pid_t pid = fork();
    
        if(pid == 0) {
            // 子进程
            int i = 0;
            for(i = 0; i < 5; i++) {
                printf("child process\n");
                sleep(1);
            }
    
        } else if(pid > 0) {
            // 父进程
            printf("parent process\n");
            sleep(2);
            printf("kill child process now\n");
            kill(pid, SIGINT);
        }
    
        return 0;
    }

    alarm函数:
    对于alarm函数的说明:

    //实际上消耗的时间=内核处理时间+用户时间+io消耗的时间
    //其中io消耗的时间是比较长的
    /*
        #include<unistd.h>
        unsigned int alarm(unsigned int seconds);
            -功能:设置定时器(闹钟),函数调用,开始倒计时
                    当倒计时为零,函数会给当前进程发送信号SIGNALARM
            -参数:
                seconds:倒计时的时长,如果参数为零,定时器无效
                取消一个定时器也就是传一个0,通过alarm(0);
            -返回值:
                -之前没有定时器,返回0
                -之前有设置定时器,返回之前定时器剩余的时间
    
            -SIGNALRM:默认终止当前的进程,每一个进程都只有唯一的定时器
            alarm(10);//返回0
            sleep(1);过去一秒
            alarm(5);//覆盖之前的定时器,返回10-1=9                
    
    */
    //这里有个小注意的地方,要是你在使用alarm函数的时候打印
    //数据的时候,不使用\n是不会刷新缓冲区的,最后没有输出
    #include <stdio.h>
    #include <unistd.h>
    
    int main() {
    
        int seconds = alarm(5);
        printf("seconds = %d\n", seconds);  // 0
    
        sleep(2);
        seconds = alarm(2);    // 不阻塞
        printf("seconds = %d\n", seconds);  // 3
    
        while(1) {
        }
    
        return 0;
    }

    setitimer函数:

    /*
        #include <sys/time.h>
        int setitimer(int which, const struct itimerval *new_value,
                         struct itimerval *old_value);
            -功能:设置定时器(闹钟),可以代替alarm函数,精度更高
            -参数:
                -which:定时器以什么时间计时
                    -ITIMER_REAL:真实时间,时间到达,发送SIGALRM 常用
                    -ITIMER_VIRTUAL:用户时间,时间到达发送SIGVTALRM
                    -ITIMER_PROF:以该进程在用户态和内核态下所消耗的时间来计算,时间到了发送SIGVTALRM
                -new_value:
                struct itimerval {
                   struct timeval it_interval; //每个阶段的间隔时间
                   struct timeval it_value;    //延迟多长时间执行定时器
                };
                假如要求:过10秒后,每隔2秒定时一次,那么10给it_interval,2给it_value
                -old_value:记录上一次的定时的时间参数,一般不适用,指定为null
                struct timeval {
                    time_t      tv_sec;//秒数        
                    suseconds_t tv_usec;  //微妙  
                };
            -返回值:
                    -0:成功
                    --1:失败,设置错误号errno
               
    */
    #include<stdio.h>
    #include<sys/time.h>
    #include<stdlib.h>
    #include<string.h>
    //过3秒之后,每隔2秒定时一次
    int main(){
        struct itimerval new_value;
        //设置间隔时间3s
        new_value.it_interval.tv_sec=3;
        new_value.it_interval.tv_usec=0;//也就是3.0s
        //设置延迟的时间2s
        new_value.it_value.tv_sec=2;
        new_value.it_value.tv_usec=0;//也就是2.0s
    
        int ret = setitimer(ITIMER_REAL,&new_value,NULL);//非阻塞
        printf("定时器开始了");
        if(ret==-1){
            perror("setitimer");
            exit(0);
        }
        getchar();
        return 0;
    }

    捕捉信号:signal函数:

    /*
        #include <signal.h>
        typedef void (*sighandler_t)(int);
        sighandler_t signal(int signum, sighandler_t handler);
        -功能:设置某个信号的捕捉行为
        -参数:
            -signum:要捕捉的信号
            -handler:捕捉到信号之后要如何处理
                -SIG_IGON:忽略信号
                -SIG_DEL:使用默认的行为
                -回调函数:这个函数是内核的系统调用,程序员只负责写,捕捉到信号之后如何处理信号
                回调函数:
                    -需要程序员实现,提前准备好,函数的类型根据实际的需求、、
                    -不是程序员自己调用的,而是当信号产生,由系统内核调用
                    -函数指针是实现回调的手段,函数实现之后,将函数名放到函数指针的位置就可以
    
        -返回值:    
            -成功,返回上一次的handler
            -失败:返回SIG_ERR,设置错误号    
    
        SIGKILL SIGSTOP不能被捕捉或者忽略
    */
    #include<stdio.h>
    #include<sys/time.h>
    #include<stdlib.h>
    #include<signal.h>
    #include<string.h>
    
    void myalarm(int num){//num表示捕捉到的信号的编号
        printf("捕捉到的信号编号为:%d\n",num);
    }
    //过3秒之后,每隔2秒定时一次
    int main(){
        //捕捉信号,写在定时器之前
        //signal(SIGALRM,SIG_DFL);
        //signal(SIGALRM,SIG_IGN);
        signal(SIGALRM,myalarm);
        struct itimerval new_value;
        //设置间隔时间3s
        new_value.it_interval.tv_sec=3;
        new_value.it_interval.tv_usec=0;//也就是3.0s
        //设置延迟的时间2s
        new_value.it_value.tv_sec=2;
        new_value.it_value.tv_usec=0;//也就是2.0s
    
        int ret = setitimer(ITIMER_REAL,&new_value,NULL);//非阻塞
        printf("定时器开始了\n");
        if(ret==-1){
            perror("setitimer");
            exit(0);
        }
        getchar();
        return 0;
    }

     

  9. 信号集函数:
    对自定义的信号集进行操作:
    /*
        以下的信号集相关函数都是对自定义的信号集操作
    
        int sigemptyset(sigset_t *set)
        -功能:,初始化功能,清空信号集中的数据,将信号集中的所有标注位设置为0
        -参数:
            -set:传出参数,需要操作的信号集
        -返回值:
            -0:成功
            --1:失败
    
        int sigfillset(sigset_t* set)
        -功能:将信号集中的所有标志设置为1
        -参数:
            -set:传出参数,需要操作的信号集
        -返回值:
            -0:成功
            --1:失败  
    
        int sigaddset(sigset_t* set,int signum);
        -功能:设置信号集中的某个元素对应的标志位为1,表示阻塞这个信号
        -参数:
            -set:传出参数,需要操作的信号集
        -返回值:
            -0:成功
            --1:失败 
    
        int sigdelset(sigset_t* set)
        -功能:设置信号集中的某个信号对应的标志位为0,表示不阻塞某个信号
        -参数:
            -set:传出参数,需要操作的信号集
        -返回值:
            -0:成功
            --1:失败 
    
        int sigismember(const sigset_t* set,int signum)
        -功能:判断某个信号是否阻塞
        -参数:
            -set:需要操作的信号集
            -signum:需要判断的那个信号
        -返回值:
            -1:signum阻塞
            -0:signum不阻塞
            --1:调用失败    
    */
    #include<stdio.h>
    #include<signal.h>
    int main(){
        //创建一个信号集
        sigset_t set;
        //清空信号集
        sigemptyset(&set);
        //判断一下信号SIGINT是不是在set里面
        int ret = sigismember(&set,SIGINT);
        if(ret==0){
            printf("sigint不阻塞\n");
        }else if(ret==1){
            printf("sigint阻塞\n");
        }else{
            printf("失败调用");
        }
        //添加几个信号到信号集中
        sigaddset(&set,SIGINT);
        sigaddset(&set,SIGQUIT);
        //判断SIGINT是否在set信号集中
        ret = sigismember(&set,SIGINT);
        if(ret==0){
            printf("sigint不阻塞\n");
        }else if(ret==1){
            printf("sigint阻塞\n");
        }else{
            printf("失败调用");
        }
        //从信号集中删除一个信号
        sigdelset(&set,SIGQUIT);
        //判断一下sigquit是否在set里
        ret = sigismember(&set,SIGQUIT);
        if(ret==0){
            printf("sigint不阻塞\n");
        }else if(ret==1){
            printf("sigint阻塞\n");
        }else{
            printf("失败调用");
        }
        return 0;
    }

    sigprocmask函数:

    /*
        int sigprocmask(int how,const sigset_t *set,siset_t* oldset)
            -功能:将自定义的信号集中的数据设置到内核中的阻塞信号集
            -参数:
                -how:如何对内核阻塞信号进行处理
                    -SIG_BLOCK:将用户设置的自定义信号集添加到内核中,内核中原来的数据不变
                        假设内核中的阻塞信号集是mask,则相当于mask|set
                    -SIG_UNBLOCK:根据用户设置的数据,对内核中的数据进行解除阻塞
                        相当于mask&=~set(注意这个操作,有点神奇)
                    -SIG_SETMASK:覆盖原来内核中阻塞集的值
                -set:我们自定义的信号集
                -oldset:保存设置之前内核中的阻塞信号集内容  
            -返回值:
                成功:0
                失败:-1
                    设置错误号:EFAULT、EINVAL
        int sigpending(sigset_t* set)
            -功能:获取内核中的未决信号集
            -参数:
                -set:传出参数,传出的是内核中未决信号集的内容
    */             
    //编写一个程序返回所有的常规信号(1-31)的未决信号状态打印到屏幕
    //设置某些信号是阻塞的,通过键盘产生这些信号
    #include<stdio.h>
    #include<signal.h>
    #include<stdlib.h>
    int main(){
        //设置2、3号信号阻塞
        sigset_t set;
        sigemptyset(&set);
    
        //将2,3号信号添加到信号集
        sigaddset(&set,SIGINT);
        sigaddset(&set,SIGQUIT);
    
        //修改内核中的阻塞信号集
        sigprocmask(SIG_BLOCK,&set,NULL);
        while(1) {
       
            // 获取当前的未决信号集的数据
            sigset_t p;
            sigemptyset(&p);
            sigpending(&p);
    
            // 遍历前32位
            for(int i = 1; i <= 31; i++) {
                if(sigismember(&p,i) == 1) {
                    printf("1");
                }else if(sigismember(&p, i) == 0) {
                    printf("0");
                }else {
                    perror("sigismember");
                    exit(0);
                }
            }
    
            printf("\n");
        }
        return 0;
    }
        

     

posted @ 2022-10-05 19:53  铜锣湾陈昊男  阅读(10)  评论(0)    收藏  举报