进程部分、进程通讯方式

linux进程相关

查看进程

在这里插入图片描述

aux

/*
USER 表示调用进程的用户 PID 表示进程ID %CPU表示cpu占用率 %MEM表示内存占用率 TTY表示在哪个终端启用 STAT表示进程状态 START 开始时间 TIME 持续时间 COMMAND执行该进程的命令
*/

在这里插入图片描述

ajx

/* 
PPID 父进程ID PID当前进程ID PGID进程组的ID SID会话ID 
*/

在这里插入图片描述

STAT参数意义

在这里插入图片描述

top

在这里插入图片描述

kill

创建进程

/*
    #include <sys/types.h>
    #include <unistd.h>
       pid_t fork(void); //创建子进程
            返回值:返回值会返回两次 在父进程中返回子进程的PID 在子进程中返回0
            可以通过fork返回值区分父进程和子进程
            父进程中返回-1 表示子进程创建失败
*/
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
int main(){
    int pid=fork();
    if(pid>0){
        printf("父亲 pid:%d ppid:%d\n",getpid(),getppid());
    }else if ( pid ==0)
    {
        printf("儿子 pid:%d ppid:%d\n",getpid(),getppid());

    }
    for(int i=0;i<5;i++){
        printf("pid  %d     %d\n",getpid(),i);
        sleep(1);
    }
    
    return 0;
}
写时拷贝

传统的fork()系统调用直接把所有的资源复制给新创建的进程,这种实现过于简单并且效率低下。Linux的fork()使用写时拷贝(copy-on-write)页实现。写时拷贝是一种可以推迟甚至避免拷贝数据的技术。内核此时并不复制整个进程的地址空间,而是让父子进程共享同一个地址空间。只在需要写入的时候才会复制地址空间,从而使各个进程拥有各自的地址空间。也就是说,资源的复制是在需要写入的时候才会进行,在此之前,只有以只读方式共享。只有进程空间的各段的内容要发生变化时,才会将父进程的内容复制一份给子进程。也就是说,两者的虚拟空间不同,但其对应的物理空间是同一个。当父子进程中有更改相应段的行为发生时,再为子进程相应的段分配物理空间

简单的理解为:内核只为新生成的子进程创建虚拟空间结构,它们来复制于父进程的虚拟究竟结构,但是不为这些段分配物理内存,它们共享父进程的物理空间,当父子进程中有更改相应段的行为发生时,再为子进程相应的段分配物理空间。

文件共享

fork的一个特性是进程的所有打开文件描述符都被复制到子进程中,简单的理解为父子进程共享fork之前打开文件的读写漂移量。在 fork 之后处理文件描述符有以下两种常用的操作模式:

父进程等待子进程完成:在这种情况下,父进程无需对其描述符做任何处理。当子进程终止后,它曾进行过读、写操作的任一共享描述符的文件偏移量已做了相应更新。
父进程和子进程各自执行不同的程序段:在这种情况下,在 fork 之后,父进程和子进程各自关闭它们不需使用的文件描述符,这样就不会干扰对方使用的文件描述符。这种方法是网络服务进程经常使用的。

GDB进程调试

请添加图片描述

exec函数族

在这里插入图片描述
只会替换用户区的内容,不会修改内核区的数据
在这里插入图片描述

/*
#include <unistd.h>

        extern char **environ;

        int execl(const char *pathname, const char *arg, ...(char  *) NULL );
            pathname: 需要执行的文件的路径名称
            arg: 执行该执行文件的参数(可变长度的参数)第一个参数一般没什么作用,一般写执行程序的名称,从第二个参数开始写需要的参数,参数最后需要以NULL结尾
            返回值:只有出错时返回-1,成功无返回值 
        int execlp(const char *file, const char *arg, ...(char  *) NULL );
            file 需要执行的可执行文件的文件名(到环境变量中查找如果找不到就执行失败)
            其余同上
        int execle(const char *pathname, const char *arg, ..., (char *) NULL, char *const envp[] );
        int execv(const char *pathname, char *const argv[]);
            argv 需要的参数的字符串数组 例子:  char *argv[]={"a","b",NULL}
        int execvp(const char *file, char *const argv[]);
        int execvpe(const char *file, char *const argv[],char *const envp[]);     
            envp 需要执行环境的字符串数组 例子: char *argv[]={"/home/hws","/home/lib",NULL}
                      */
#include <unistd.h>
#include<stdio.h> 
 int main(){
    int pid=fork();
    if (pid>0)
    {
        printf("我是父亲 %d\n",getpid());
    }else if (pid==0)
    {
        printf("我是孩子 %d\n",getpid());
       execl("hello","hello",NULL);

    }
    printf("yes %d\n",getpid());
    return 0;
 }

进程控制

进程退出

exit是c语言库 _exit是linux库
在这里插入图片描述

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

    #include <stdlib.h>
       void exit(int status);
            status 是进程退出时的状态信息,父进程回收子进程资源时可以获取
*/
    #include <stdlib.h>
#include<stdio.h>
int main(){
    printf("hello\n");
    printf("world");

    exit(0);//退出时会调用推出处理函数 刷新缓冲区
    _Exit(0);//直接推出
    return 0;
}

孤儿进程


#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include <stdlib.h>
int main(){
    int pid=fork();
    int nums=10;
    if(pid>0){
        printf("父亲 pid:%d ppid:%d\n",getpid(),getppid());
        exit(0);

    }else if ( pid ==0)
    {
        sleep(10);

        printf("儿子 pid:%d ppid:%d\n",getpid(),getppid());

    }
    for(int i=0;i<5;i++){
        printf("pid  %d     %d\n",getpid(),i);
        sleep(1);
    }
    
    return 0;
}

僵尸进程

在这里插入图片描述
想结束僵尸进程可以通过杀死其父进程打到目的。


#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include <stdlib.h>
int main(){
    int pid=fork();
    int nums=10;
    if(pid>0){
        while (1)
        {
            /* code */
        printf("父亲 pid:%d ppid:%d\n",getpid(),getppid());
        sleep(1);
        }
        
    }else if ( pid ==0)
    {

        printf("儿子 pid:%d ppid:%d\n",getpid(),getppid());

    }
    // for(int i=0;i<5;i++){
    //     printf("pid  %d     %d\n",getpid(),i);
    //     sleep(1);
    // }
    
    return 0;
}

释放子进程

wait

在这里插入图片描述

/*
    #include <sys/types.h>
    #include <sys/wait.h>
        pid_t wait(int *wstatus);//等待任意一个子进程结束,如果任意一个子进程结束了,会回收子进程的内核空间 一次只能回收一个
            wstatus 进程推出时的状态信息,传出参数
            返回值 成功返回子进程的id 失败返回-1
            调用wait函数的进程会被阻塞,如果没有子进程函数会立刻返回,返回-1

        pid_t waitpid(pid_t pid, int *wstatus, int options);
*/
    #include <sys/types.h>
    #include <sys/wait.h>
    #include<stdio.h>
    #include<unistd.h>
    int main(){
        pid_t pid;
        for (int i=0;i<5;i++){
            pid=fork();
            if(pid==0){
                break;
            }
        }
        if(pid>0){
            while (1)
            {
                printf("父亲 %d\n",getpid());
                int ret=wait(NULL);
                printf("child die,pid  %d",ret);
                sleep(1);
            }
            
        }else{
            while (1)
            {
                printf("孩子 %d %d\n",getpid(),getppid());
                sleep(5);

            }
        }
        return 0;
    }

在这里插入图片描述

waitpid

/*
    #include <sys/types.h>
    #include <sys/wait.h>
        pid_t wait(int *wstatus);//等待任意一个子进程结束,如果任意一个子进程结束了,会回收子进程的内核空间 一次只能回收一个 
            wstatus 进程推出时的状态信息,传出参数
            返回值 成功返回子进程的id 失败返回-1
            调用wait函数的进程会被阻塞,如果没有子进程函数会立刻返回,返回-1
        pid_t waitpid(pid_t pid, int *wstatus, int options);//回收指定进程号的子进程,可以设置是否为阻塞 
            pid:要回收的子进程id >0要回收的子进程的i号 =0回收当前进程组的所有子进程  =-1回收所有的子进程相当于wait() <-1回收某个进程组的id的绝对值
            options 设置阻塞还是非阻塞 0表示阻塞WNOHANG非阻塞
            返回值 >0返回子进程的id =0表示还有子进程活着 -1表示没有子进程了
*/

进程通讯

在这里插入图片描述

进程通信方式

在这里插入图片描述

匿名管道

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

/*

    #define _GNU_SOURCE            
    #include <fcntl.h>            
    #include <unistd.h>
        int pipe(int pipefd[2]);创建一个管道
            pipefd:传出参数 返回管道两端的文件描述符 pipefd[0]对应管道的读端 pipefd[1]对应管道的写端
            返回值: 成功返回0 失败返回-1
            注意:匿名管道只能用于具有关系的进程之间的通信 ,管道是阻塞的,如果没有数据读就会阻塞 如果管道满了写数据就会阻塞

    #include <unistd.h>
        long fpathconf(int fd, int name);//获取管道的大小

        int pipe2(int pipefd[2], int flags);创建一个管道
*/

    #define _GNU_SOURCE            
    #include <fcntl.h>            
    #include <unistd.h>
    #include<stdio.h>
    #include<stdlib.h>
    #include<sys/types.h>
    int main(){
        
        //fork之前创建管道
        int pipefd[2];
        int ret=pipe(pipefd);
        if (ret==-1){
            perror("pipe");
        }
        long size=fpathconf(pipefd[0],_PC_PIPE_BUF);
        printf("pipe size :%ld",size);
        int pid=fork();
        if (pid>0){
            printf("father pid:%d",getpid());
            //父亲
            char buf[1024];
            while (1)
            {
                int len=read(pipefd[0],buf,sizeof(buf));
                printf("child recive %s pid :%d\n",buf,getpid());
            }
        }else{
            //孩子
            printf("child pid:%d",getpid());
            char *str="yes yes";
            while (1)
            {
                /* code */
            write(pipefd[1],str,sizeof(str));
            sleep(1);

            }
            
        }
        return 0;
    }

父子进程通信案例

    #define _GNU_SOURCE            
    #include <fcntl.h>            
    #include <unistd.h>
    #include<stdio.h>
    #include<stdlib.h>
    #include<sys/types.h>
    #include<string.h>
    int main(){
        //ps aux |grep xxx 子进程ps aux子进程结束后发送给父进程 父进程 获取到数据
        int fd[2];
        int ret=pipe(fd);
        if (ret==-1){
            perror("pipe");
            exit(0);
        }
        int pid=fork();
        if (pid>0){
                //父亲
            close(fd[1]);
            char buf[1024]={0};
            int len;
            while ((len=read(fd[0],buf,sizeof(buf)-1))>0)
            {
                            printf("%s",buf);
                            memset(buf,0,1024);
            }
            
            wait(NULL);


        }else{
            //关闭读
            close(fd[0]);
            dup2(fd[1],STDOUT_FILENO);
            execlp("ps","ps","aux",NULL);  
        }
        return 0;
    }

在这里插入图片描述

设置管道非阻塞

/*
设置管道非阻塞
int flags = fcntl(fd[e],F_GETFL); // 获取原来的flagflags = 0 NONBLOCK;
// xiu
fcntl(fd[@],F_SETFL, flags);// 设置新的flag
*/

有名管道

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
有名管道的注意事项:
1.一个为只读而打开一个管道的进程会阻塞,直到另外一个进程为只写打开管道
2.一个为只写而打开一个管道的进程会阻塞,直到另外一个进程为只读打开管道
读管道:
管道中有数据,read返回实际读到的字节数管道中无数据:
管道写端被全部关闭,read返回9,(相当于读到文件末尾)写端没有全部被关闭,read阻塞等待
写管道:
管道读端被全部关闭,进行异常终止 (收到一个SIGPIPE信号 )管道读端没有全部关闭:
管道已经满了,write会阻塞管道没有满,write将数据写入,并返回实际写入的字节数。

/*
       #include <sys/types.h>
       #include <sys/stat.h>
        int mkfifo(const char *pathname, mode_t mode);
            pathname 创建管道的名字和路径
            mode 文件权限
            返回值 成功返回0 失败返回-1

*/

read.c

    #define _GNU_SOURCE            
    #include <fcntl.h>            
    #include <unistd.h>
    #include<stdio.h>
    #include<stdlib.h>
    #include<sys/types.h>
    #include<string.h>
    int main(){
    int fd=open("fifotest",O_RDONLY);
        if (fd==-1){
            perror("opne");
            exit(0);
        }
        while (1)
        {
            char buf[1024]={0};
            int len=read(fd,buf,sizeof(buf));
            if(len==0){
                printf("写端断开了");
                break;
            }
            printf("shoudao buf %s",buf);
            /* code */
        }
        close(fd);
        return 0;
        
    }

write.c

    #define _GNU_SOURCE            
    #include <fcntl.h>            
    #include <unistd.h>
    #include<stdio.h>
    #include<stdlib.h>
    #include<sys/types.h>
    #include<string.h>
    int main(){
    int ret=access("fifotest",F_OK);
    if(ret==-1){
        printf("管道不存在创建管道\n");
        ret=mkfifo("fifotest",0664);
        if(ret==0){
         perror("mkfifo");
        }
    }else{
        printf("管道已存在\n");

    }
    //以只写的方式打开管道
    int fd=open("fifotest",O_WRONLY);
    if (fd==-1){
        perror("opne");
        exit(0);
    }
    for (int i=0;i<100;i++){
        char buf[1024];
        sprintf(buf,"this is %d\n",i);
        printf("write %d\n",i);
        write(fd,buf,sizeof(buf));
        sleep(1);
    }
    close(fd);
    return 0;
}

chat例子

A.c

    #define _GNU_SOURCE            
    #include <fcntl.h>            
    #include <unistd.h>
    #include<stdio.h>
    #include<stdlib.h>
    #include<sys/types.h>
    #include<string.h>
    int main(){
        int ret=access("fifo1",F_OK);
        if(ret==-1){
            printf("管道不存在创建管道\n");
            ret=mkfifo("fifo1",0664);
        if(ret==0){
            perror("mkfifo");
        }
        }else{
            printf("管道已存在\n");

        }
        
        int fd1=open("fifo1",O_WRONLY);
        if (fd1==-1){
            perror("write");
            exit(0);
        }
        int fd2=open("fifo2",O_RDONLY);
        if (fd2==-1){
            perror("read");
            exit(0);
        }
        char buf[128]={0};
        char buf1[128]={0};

        while (1)
        {   //先读
            memset(buf,0,128);
            int len=read(fd2,buf,sizeof(buf));
            printf("收到 buf %s",buf);
            //后写
            memset(buf1,0,128);
            fgets(buf1,128,stdin);
            ret=write(fd1,buf1,strlen(buf1));
            printf("写入 buf %s",buf1);

            
        }
        close(fd1);
        close(fd2);
        return 0;
        
    }

B.c

    #define _GNU_SOURCE            
    #include <fcntl.h>            
    #include <unistd.h>
    #include<stdio.h>
    #include<stdlib.h>
    #include<sys/types.h>
    #include<string.h>
    int main(){
        int ret=access("fifo1",F_OK);
        if(ret==-1){
            printf("管道不存在创建管道\n");
            ret=mkfifo("fifo1",0664);
        if(ret==0){
            perror("mkfifo");
        }
        }else{
            printf("管道已存在\n");

        }
        
        int fd1=open("fifo1",O_WRONLY);
        if (fd1==-1){
            perror("write");
            exit(0);
        }
        int fd2=open("fifo2",O_RDONLY);
        if (fd2==-1){
            perror("read");
            exit(0);
        }
        char buf[128]={0};
        char buf1[128]={0};

        while (1)
        {   //先读
            memset(buf,0,128);
            int len=read(fd2,buf,sizeof(buf));
            printf("收到 buf %s",buf);
            //后写
            memset(buf1,0,128);
            fgets(buf1,128,stdin);
            ret=write(fd1,buf1,strlen(buf1));
            printf("写入 buf %s",buf1);

            
        }
        close(fd1);
        close(fd2);
        return 0;
        
    }

内存映射

在这里插入图片描述
在这里插入图片描述
父子进程内存映射区通信

    /*

        #include <sys/mman.h>
            void *mmap(void *addr, size_t length, int prot, int flags,//映射一个文件到内存
                  int fd, off_t offset);
                addr:映射到内存中得起始地址 传递一个NULL 由内核指定
                length:映射的内存的长度 使用文件的长度获取文件的长度 stat 或者lseek
                prot 对申请的内存映射区的操作权限 PROT_EXEC  PROT_READ  PROT_WRITE PROT_NONE  要操作映射区必须有读取的权限
                flag MAP_SHARED 映射区的数据会和磁盘文件同步 ,进程间通信必须使用该flag MAP_PRIVATE 不同步映射区的数据数据改变了不会修改原来的文件,会创建一个新的文件  MAP_SHARED_VALIDATE 
                fd 需要操作或者映射的文件描述符 文件的大小大于0不能为0
                offset 偏移量 一般不用 必须指定4k的整数倍 一般填0
                返回值:成功返回指向内存的地址 失败返回-1的地址       
            int munmap(void *addr, size_t length);//释放内存映射
                addr 要释放的内存地址
                length 释放内存的大小
            使用内存映射实现进程之间的通信:
            1.关系进程
                先创建内存映射区,创建子进程,父子进程共享内存映射区
            2.没有关系进程
                准备一个大小非0的磁盘文件 进程一通过磁盘文件创建内存映射区 进程二通过磁盘文件创建内存映射区 使用内存映射区通信
            内存映射区通信是非阻塞的        
    */
    #define _GNU_SOURCE            
    #include <fcntl.h>            
    #include <unistd.h>
    #include<stdio.h>
    #include<stdlib.h>
    #include<sys/types.h>
    #include<string.h>
    #include <sys/mman.h>

    int main(){
        int fd=open("a.txt",O_RDWR);
        int size=lseek(fd,0,SEEK_END);

        //创建内存映射区
        void *ptr =mmap(NULL,size,PROT_WRITE|PROT_READ,MAP_SHARED,fd,0);
        if (ptr==MAP_FAILED){
            perror("mmap");
            exit(0);
        }
        //创建子进程
        int pid=fork();
        if(pid>0){
                //父进程
                wait();
                char buf[64];
                strcpy(buf,(char*)ptr);
                printf("recive %s",buf);
        }else{
                //子进程
                strcpy((char*)ptr,"yes yes yes ");

        }
        //关闭内存映射区

        munmap(ptr,size);
        close(fd);
        return 0;
    }

内存映射文件拷贝

  #define _GNU_SOURCE            
    #include <fcntl.h>            
    #include <unistd.h>
    #include<stdio.h>
    #include<stdlib.h>
    #include<sys/types.h>
    #include<string.h>
    #include <sys/mman.h>

    int main(){
        int fd=open("ff.txt",O_RDWR);
        int size=lseek(fd,0,SEEK_END);

        int newfd=open("copy.txt",O_RDWR|O_CREAT,0777);
        if(newfd==-1){
            perror("opennew");
            exit(0);
        }
        //拓展文件大小
        truncate("copy.txt",size);
        //创建内存映射区
        void *ptr =mmap(NULL,size,PROT_WRITE|PROT_READ,MAP_SHARED,fd,0);
        void *ptr1 =mmap(NULL,size,PROT_WRITE|PROT_READ,MAP_SHARED,newfd,0);

        if (ptr==MAP_FAILED){
            perror("mmapold");
            exit(0);
        }
        if (ptr==MAP_FAILED){
            perror("mmapnew");
            exit(0);
        }
        //内存拷贝
        memcpy(ptr1,ptr,size);

        munmap(ptr1,size);
        munmap(ptr,size);

        close(newfd);
        close(fd);

        return 0;
    }

注意事项

1.如果对mmap的返回值(ptr)做++操作(ptr++),munmap是否能够成功?
void * ptr = mmap(…);
ptr++; 可以对其进行++操作munmap(ptr,len);// 错误,要保存地址

2.如果open时O RDONLY,mmap时prot参数指定PROT READPROT WRITE会怎样错误,返回MAP FAILED
open()函数中的权限建议和prot参数的权限保持一致。

3.如果文件偏移量为1999会怎样?偏移量必须是4k的整数倍,返回MAP FAILED

4.mmap什么情况下会调用失败?
第二个参数: length = 0
第三个参数: prot
只指定了写权限
prot PROT READ PROT WRITE第5个参数fd 通过open函数时指定的 O RDONLY / O WRONLY

5.可以open的时候O CREAT一个新文件来创建映射区吗?可以的,但是创建的文件的大小如果为@的话,肯定不行- 可以对新的文件进行扩展- lseek()truncate()

6.mmap后关闭文件描述符,对mmap映射有没有影响?int fd = open(“xxx”);mmap(,…,fd.close(fd);映射区还存在,创建映射区的fd被关闭,没有任何影响

7.对ptr越界操作会怎样?void * ptr = mmap(NULL, 100,.33.);越界操作操作的是非法的内存 ->段错误

匿名映射

不需要文件实体做内存映射 只适用于亲属关系的进程通信

  #define _GNU_SOURCE            
    #include <fcntl.h>            
    #include <unistd.h>
    #include<stdio.h>
    #include<stdlib.h>
    #include<sys/types.h>
    #include<string.h>
    #include <sys/mman.h>

    int main(){

        //创建匿名内存映射区
        int size=4096;
        void *ptr =mmap(NULL,size,PROT_WRITE|PROT_READ,MAP_SHARED|MAP_ANONYMOUS,-1,0);
        if (ptr==MAP_FAILED){
            perror("mmap");
            exit(0);
        }
        int pid=fork();
        if(pid>0){
            printf("pid%d\n",getpid());

            strcpy((char *)ptr,"hello");
            wait(NULL);
        }else{
            sleep(1);
            printf("%s pid%d\n",(char *)ptr,getpid());
        }
        //释放内存映射区
        munmap(ptr,size);
        return 0;
    }

信号

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

信号函数

在这里插入图片描述

kill,raise,abort
/*
    #include <sys/types.h>
    #include <signal.h>
       int kill(pid_t pid, int sig);//给任何进程或者进程组pid 发送任何信号sig
            pid >0将信号发送给指定进程 =0发送给当前进程组 =-1发送给每一个有权限接受信号的进程 <-1发送给进程组的id绝对值
            sig 宏 等于0表示不发送信号

    #include <signal.h>
       int raise(int sig);   //给当前进程发送信号
            返回值 0代表成功 非0代表失败

    #include <stdlib.h>
       void abort(void);       //发送SIGABRT给当前进程 杀死当前进程 
*/
    #include <fcntl.h>            
    #include <unistd.h>
    #include<stdio.h>
    #include<stdlib.h>
    #include<sys/types.h>
    #include<string.h>
    #include <sys/mman.h>
    #include<signal.h>
    int main(){
        int pid=fork();
        if(pid>0){
            int i=0;
            for (i=0;i<5;i++){
                printf("%d\n",i);
                sleep(1);
            }
        }else{
            printf("父亲\n");
            sleep(2);
            printf("杀死\n");
            kill(pid,SIGINT);
        }
        return 0;
    }
alarm
 /*
 
    #include <unistd.h>
        unsigned int alarm(unsigned int seconds);设置定时器当倒计时为0时,函数会给当前进程发送一个信号SIGALARM
            seconds 倒计时市场 ,单位为秒
            返回值 之前没有定时器返回0
                    之前有定时器,返回之前定时器的剩余时间
        SIGALARM 进程收到这个信号,默认终止当前的进程,每个进程都有且只有一个唯一的定时器
        该函数时不阻塞的    定时器与进程状态无关

 */
    #include <fcntl.h>            
    #include <unistd.h>
    #include<stdio.h>
    #include<stdlib.h>
    #include<sys/types.h>
    #include<string.h>
    #include <sys/mman.h>
    #include<signal.h>
    int main(){
        int seconds=alarm(5);
        printf("seconds = %d\n",seconds);
        sleep(2);
        seconds=alarm(10);
        printf("seconds = %d\n",seconds);
        while (1)
        {
    /* code */
        }

        return 0;

    }
setitimer
/*
    #include <sys/types.h>
    #include <signal.h>
       int kill(pid_t pid, int sig);//给任何进程或者进程组pid 发送任何信号sig
            pid >0将信号发送给指定进程 =0发送给当前进程组 =-1发送给每一个有权限接受信号的进程 <-1发送给进程组的id绝对值
            sig 宏 等于0表示不发送信号
/*
    #include <sys/time.h>
        int getitimer(int which, struct itimerval *curr_value);
        int setitimer(int which, const struct itimerval *new_value,struct itimerval *old_value); //设置定时器 可以替代alarm 精度比alarm高 精度为微秒 可以实现周期性的定时器
            which   ITIMER_REAL真实时间包括内核时间用户时间和切换时间 时间到达后发送SIGALRM    
                    ITIMER_VIRTUAL用户消耗的时间 时间到达后发送SIGVTALRM    
                    ITIMER_PROF包括用户时间和内核时间 时间到达后发送SIGPROF
            new_value 设置定时器的属性,  
                struct itimerval {定时器结构体
                    struct timeval it_interval; 每个阶段的时间 间隔时间
                    struct timeval it_value;   延迟多长时间执行
                };
                struct timeval {时间结构体
                     time_t      tv_sec;        秒数
                      suseconds_t tv_usec;       微秒
                };
                过十秒后每隔两秒发送一次 
           old_value 记录上一次设置定时器的参数 不使用可以设置为NULL
           返回值 成功返回0 失败返回-1      
*/              
#include <fcntl.h>            
#include <unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<string.h>
#include <sys/mman.h>
#include<signal.h>
#include <sys/time.h>
int mian(){
    //3妙后每两秒定时一次
    struct itimerval new;
    new.it_interval.tv_sec=3;
    new.it_interval.tv_usec=0;
    new.it_value.tv_sec=2;
    new.it_value.tv_usec=0;

    int res=setitimer(ITIMER_REAL,&new,NULL);
    
    if (res==-1){
        perror("settimer");
        exit(0);
    }

    sleep(10);

    return 0;
}
signal信号捕捉

在这里插入图片描述

/*
    #include <signal.h>
        typedef void (*sighandler_t)(int);

        sighandler_t signal(int signum, sighandler_t handler);设置某个信号的捕捉行为
            signum 要捕捉的信号
            handler 捕捉到信号要如何处理
                SIG_IGN 忽略信号
                SIG_DFL 使用信号默认的行为
                回调函数 函数由内核去调用,程序员只负责写,捕捉到信号后如何去处理
                返回值 成功上一次注册的信号处理函数的地址,第一次调用返回NULL
                       失败返回SIG_ERR
        SIGKILL SIGSTOP不能被捕捉 不能被忽略
*/

#include <fcntl.h>            
#include <unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<string.h>
#include <sys/mman.h>
#include<signal.h>
#include <sys/time.h>
void myalarm(int nums){//int 类型的参数表示捕捉到的信号的值
    printf("catch %d\n",nums);
    printf("yes yes yes \n");

}
int main() {
    //设置信号捕捉哈函数
    signal(SIGALRM,myalarm);

    // 过3秒以后,每隔2秒钟定时一次
    struct itimerval new;
    new.it_interval.tv_sec=3;
    new.it_interval.tv_usec=0;
    new.it_value.tv_sec=2;
    new.it_value.tv_usec=0;
    printf("开始了\n");
    int res=setitimer(ITIMER_REAL,&new,NULL);
    
    if (res==-1){
        perror("settimer");
        exit(0);
    }

    getchar();
    return 0;
}
信号集

在这里插入图片描述
1.用户通过键盘 Ctrl + C, 产生2号信号SIGINT (信号被创建)

2.信号产生但是没有被处理 (未决)
- 在内核中将所有的没有被处理的信号存储在一个集合中 (未决信号集)
- SIGINT信号状态被存储在第二个标志位上
- 这个标志位的值为0, 说明信号不是未决状态
- 这个标志位的值为1, 说明信号处于未决状态

3.这个未决状态的信号,需要被处理,处理之前需要和另一个信号集(阻塞信号集),进行比较
- 阻塞信号集默认不阻塞任何的信号
- 如果想要阻塞某些信号需要用户调用系统的API

4.在处理的时候和阻塞信号集中的标志位进行查询,看是不是对该信号设置阻塞了
- 如果没有阻塞,这个信号就被处理
- 如果阻塞了,这个信号就继续处于未决状态,直到阻塞解除,这个信号就被处理
在这里插入图片描述
操作自定义信号集

/*  
    以下信号集都是对自定义的信号集进行操作
    #include <signal.h>

       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 传出参数 需要操作的信号集
            signum 需要阻塞的信号
            返回值 成功返回0 失败返回-1
       int sigdelset(sigset_t *set, int signum);设置信号集中的某一个信号为0 表示不阻塞这个信号
            set 传出参数 需要操作的信号集
            signum 需要不阻塞的信号
            返回值 成功返回0 失败返回-1
       int sigismember(const sigset_t *set, int signum);判断某个信号是否阻塞
            set 传出参数 需要操作的信号集
            signum 需要判断的信号
            返回值 返回1表示阻塞 返回0表示不阻塞 返回-1表示出错
*/
#include <fcntl.h>            
#include <unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<string.h>
#include <sys/mman.h>
#include<signal.h>
#include <sys/time.h>
int main(){
    sigset_t set;
    sigemptyset(&set);
    int res=sigismember(&set,SIGINT);
    if (res==0){
        printf("不阻塞\n");
    }else if(res==1){
        printf("阻塞\n");

    }else{
        perror("sig");
    }

    res=sigaddset(&set,SIGINT);
    res=sigismember(&set,SIGINT);
    if (res==0){
        printf("不阻塞\n");
    }else if(res==1){
        printf("阻塞\n");

    }else{
        perror("sig");
    }
    res=sigaddset(&set,SIGQUIT);
    res=sigismember(&set,SIGQUIT);
    if (res==0){
        printf("不阻塞\n");
    }else if(res==1){
        printf("阻塞\n");

    }else{
        perror("sig");
    }


    res=sigdelset(&set,SIGINT);
    res=sigismember(&set,SIGINT);
    if (res==0){
        printf("不阻塞\n");
    }else if(res==1){
        printf("阻塞\n");

    }else{
        perror("sig");
    }
    res=sigdelset(&set,SIGQUIT);`在这里插入代码片`
    res=sigismember(&set,SIGQUIT);
    if (res==0){
        printf("不阻塞\n");
    }else if(res==1){
        printf("阻塞\n");

    }else{
        perror("sig");
    }

    return 0;
}

对内核阻塞集做处理

/*
        #include <signal.h>
        int sigprocmask(int how, const sigset_t *set, sigset_t *oldset)//将自定义信号集中的数据设置到内核中 1.设置阻塞2.接触阻塞3.替换
            how 
                SIG_BLOCK将用户设置的阻塞信号集添加到内核中非阻塞信号不做处理 mask|set  
                SIG_UNBLOCK将用户设置的非阻塞信号集添加到内核中阻塞信号不做处理   mask= mask&(~set)  
                SIG_SETMASK替换信号集
            set 用户自定义的信号集
            oldset 保存之前内核中的信号集     可以为NULL
            返回值 成功返回0 失败返回-1
        int sigpending (sigset_t *set)获取内核中的未决信号集
            set 传出参数保存获取到的味觉信号集
            int 成功返回0 失败返回-1    
*/
#define _POSIX_SOURCE
#include <fcntl.h>            
#include <unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<string.h>
#include <sys/mman.h>
#include<signal.h>
#include <sys/time.h>
#include <stdlib.h>
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
int main(){
    sigset_t set;
    sigemptyset(&set);
    sigaddset(&set,SIGINT);
    sigaddset(&set,SIGQUIT);
    
    sigprocmask(SIG_BLOCK,&set,NULL);
    while (1)
    {   
        sigset_t get;
        sigemptyset(&get);
        sigpending(&get);
        for (int i=1;i<=31;i++){
            if( (sigismember(&get,i))==0){
                printf("0");
            }else if( (sigismember(&get,i))==1){
                printf("1");

            }else{
                perror("yes");
            }
        }
        sleep(1);
        printf("\n");
     }
     return 0;
    
}
sigaction信号捕捉

在这里插入图片描述

/*
    #include <signal.h>
       int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);检查或改变信号的处理 即信号的捕捉
            signum 需要捕获的信号编号
            act 捕捉到信号的处理动作
            oldact   之前对信号捕捉的设置
            返回值 成功返回0 失败返回-1
    struct sigaction {
        void     (*sa_handler)(int);函数指针 捕捉到信号的处理
        void     (*sa_sigaction)(int, siginfo_t *, void *);不常用 
        sigset_t   sa_mask;临时阻塞信号集,在信号捕捉执行函数执行中阻塞 结束后恢复
        int        sa_flags;    使用哪一个信号处理函数 如果0表示执行sa_handler 如果是SA_SIGINFO表示使用sa_sigaction
            void     (*sa_restorer)(void);废弃了 不需要使用
        }                
*/
#define _POSIX_SOURCE
#include <fcntl.h>            
#include <unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<string.h>
#include <sys/mman.h>
#include<signal.h>
#include <sys/time.h>
void myalarm(int nums){//int 类型的参数表示捕捉到的信号的值
    printf("catch %d\n",nums);
    printf("yes yes yes \n");

}
int main() {
    //设置信号捕捉哈函数
    struct sigaction aa;
    aa.sa_flags=0;
    aa.sa_handler=myalarm;
    sigemptyset(&aa.sa_mask);//清空临时阻塞信号集
    sigaction(SIGALRM,&aa,NULL);

    // 过3秒以后,每隔2秒钟定时一次
    struct itimerval new;
    new.it_interval.tv_sec=3;
    new.it_interval.tv_usec=0;
    new.it_value.tv_sec=2;
    new.it_value.tv_usec=0;
    printf("开始了\n");
    int res=setitimer(ITIMER_REAL,&new,NULL);
    
    if (res==-1){
        perror("settimer");
        exit(0);
    }

   while (1)
   {
    /* code */
   }
   

    return 0;
}
SIGCHLD信号

在这里插入图片描述

#define _POSIX_SOURCE
#include <fcntl.h>            
#include <unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<string.h>
#include <sys/mman.h>
#include<signal.h>
#include <sys/time.h>
    #include <sys/wait.h>

void myalarm(int nums){
    while (1)
    {
        int ret=waitpid(-1,NULL,WNOHANG);
        if (ret>0){
            printf("杀死一个子进程%d\n",ret);
        }else if (ret==0){
            break;
        }else{
            break;
        }
    }
    
}
int main(){
    int pid;
    for(int i=0;i<20;i++){
        pid=fork();
        if (pid==0){
            break;
        }
    }
    if (pid>0){
        struct sigaction aa;
        aa.sa_flags=0;
        aa.sa_handler=myalarm;
        sigemptyset(&aa.sa_mask);//清空临时阻塞信号集
        sigaction(SIGCHLD,&aa,NULL);
        while (1)
        {
            printf("fatehr %d\n",getpid());
        }
        
    }else{
        {
            printf("son %d\n",getpid());
        }
    }
}

共享内存

在这里插入图片描述
在这里插入图片描述

/*
    #include <sys/ipc.h>
    #include <sys/shm.h>
        int shmget(key_t key, size_t size, int shmflg);创建一个新的共享内存段,或者获取一个既有得共享内存段表示,新创建得内存段种得数据都为0
            key整形 通过这个找到或创建一个共享内存 16进制非0
            size 共享内存大小
            shmflg 属性
                访问权限 附加属性创建/判断共享内存是不是存在
                    创建 IPC_CREAT
                    判断是否存在IPC_EXCL需要和IPC_CREAT一起使用 IPC_CREAT|IPC_EXCL|0664
            返回值:失败返回-1 成功大于0得值表示共享内存区的id        
           #include <sys/types.h>

       void *shmat(int shmid, const void *shmaddr, int shmflg);将内存区和当前进程关联
            shmid 共享内存区的id由shmget返回值获取
            shmaddr 申请共享内存的起始地址 可以输入NULL由内核指定
            shmflg 对共享内存的操作    SHM_RDONLY必须有读权限才能操作内存     0读写权限
            返回值:成功返回共享内存的首地址 失败(void *)-1

        int shmdt(const void *shmaddr);    解除进程和共享内存的关联
            shmaddr 共享内存的首地址
            返回值 成功返回0 失败返回-1

        int shmctl(int shmid, int cmd, struct shmid_ds *buf);    操作如删除共享内存,共享内存要删除才会消失,创建共享内存的进程被销毁对共享内存无影响
            shmid 共享内存id
            cmd 对共享内存的操作·IPC_STAT获取共享内存的当前状态   IPC_SET设置共享内存的状态     IPC_RMID标记共享内存被销毁
            buf 需要设置或者获取的共享内存的属性信息
                IPC_STAT buf存储数据   
                IPC_SET buf中存需要初始化的数据 设置到内核中    
                IPC_RMID 传NULL就可以
             struct shmid_ds {
               struct ipc_perm shm_perm;     Ownership and permissions 权限
               size_t          shm_segsz;    Size of segment (bytes) 大小
               time_t          shm_atime;    Last attach time 上一次关联时间
               time_t          shm_dtime;    Last detach time 上一次接触时间
               time_t          shm_ctime;    Creation time/time of last 创建时间
                                               modification via shmctl() 
               pid_t           shm_cpid;     PID of creator 创建进程的pid
               pid_t           shm_lpid;     PID of last shmat(2)/shmdt(2) 
               shmatt_t        shm_nattch;   No. of current attaches 与共享内存关联的进程数量
                 };
        
        key_t ftok(const char *pathname, int proj_id);根据指定的路径名和 int值生成共享内存的key
            pathname 指定一个存在的路径
            proj_id int类型 但是系统调用只会使用其中的一个字节范围0-255 一般指定一个字符'a'
            返回值 成功返回生成的key 失败返回-1
*/
#include <fcntl.h>            
#include <unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<string.h>
#include <sys/mman.h>
#include<signal.h>
#include <sys/time.h>
    #include <sys/ipc.h>
    #include <sys/shm.h>
int main(){
    //创建一个共享内存区
    int shmid=shmget(100,4096,IPC_CREAT|0664);
    //和当前进程进行关联
    void *ptr=shmat(shmid,NULL,0);
    //写数据
    char *str="hello world";
    memcpy(ptr,str,strlen(str)+1);

    printf("按任意键位继续");
    getchar();
    //接触关联
    shmdt(ptr);
    //删除共享内存
    shmctl(shmid,IPC_RMID,NULL);

    return 0;
    
}
#include <fcntl.h>            
#include <unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<string.h>
#include <sys/mman.h>
#include<signal.h>
#include <sys/time.h>
    #include <sys/ipc.h>
    #include <sys/shm.h>
int main(){
    //创建一个共享内存区
    int shmid=shmget(100,0,IPC_CREAT);
    if (shmid==-1){
        perror("shmget");
        exit(0);
    }
    //和当前进程进行关联
    void *ptr=shmat(shmid,NULL,0);
    //读数据
    printf("%s\n",(char*)ptr);
    printf("按任意键位继续");
    getchar();

    //接触关联
    shmdt(ptr);
    //删除共享内存
    shmctl(shmid,IPC_RMID,NULL);

    return 0;
    
}

问题1 操作系统如何知道一块贡献内存关联了多少进程?
共享内存维护了一个结构体struct shmid_ds 这个结构体中有一个成员shm_nattach记录了关联的进程个数
在这里插入图片描述
问题2 可以不可以对共享内存多次删除 shmctl
因为shmctl只是标记共享内存删除,只有当与之想关联的进程数为0时才真正删除。当共享内存的key为0时表示这个共享内存被标记删除。如果一个进程和共享内存取消关联,那么这个进程就不能继续操作这个共享内存。

问题3 共享内存和内存映射的区别
1.共享内存可以直接创建 内存映射需要磁盘文件
2.共享内存效率更高
3.共享内存 所有进程操作一快共享内存 内存映射每个进程在自己的虚拟空间中的内存时独立的
4.数据安全 进程突然推出:共享内存还存在 内存映射区消失
电脑死机 共享内存就没有了 内存映射区中的数据还存在
5.生命周期
进程退出 内存映射区销毁
进程退出 共享内存还在 如果一个进程退出会自动和一个共享内存区取消关联

守护进程

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

#define _POSIX_SOURCE
#include <fcntl.h>            
#include <unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<string.h>
#include <sys/mman.h>
#include<signal.h>
#include <sys/time.h>
#include <fcntl.h>            
#include <unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<string.h>
#include <sys/mman.h>
#include<signal.h>
#include <sys/time.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include<time.h>    
void myalarm(int nums){//int 类型的参数表示捕捉到的信号的值


    int  fd=open("/home/hws/tt/c2/l12/time.txt",O_RDWR|O_APPEND);
    if(fd==-1){
        perror("open");
    }
    time_t tm=time(NULL);
    struct tm*loc=localtime(&tm);
    char buf[1024];
    sprintf(buf,"%d-%d-%d %d:%d:%d\n",loc->tm_year,loc->tm_mon,loc->tm_mday,loc->tm_hour,loc->tm_min,loc->tm_sec);
    printf("%s\n",buf);
    write(fd,buf,strlen(buf));
    close(fd);
    return ;
     
}
int main(){
    /*
        守护进程每隔两秒获取系统时间,将时间写入磁盘文件
    */

    int pid=fork();
    if(pid>0){
        exit(0);
    }else{
        int sid=setsid();
         int res=umask(022);
         if (res==-1){
            perror("umask");
         }
         res=chdir("/");
        if (res==-1){
            perror("chdir");
         }
            //     关闭文件描述符
         int fd=open("/dev/null",O_RDWR);
         dup2(fd,0);
         dup2(fd,1);
         dup2(fd,2);
         // 6.业务逻辑

        // 捕捉定时信号
        struct sigaction act;
        act.sa_flags = 0;
        act.sa_handler = myalarm;
        sigemptyset(&act.sa_mask);
        sigaction(SIGALRM, &act, NULL);

        struct itimerval val;
        val.it_value.tv_sec = 2;
        val.it_value.tv_usec = 0;
        val.it_interval.tv_sec = 2;
        val.it_interval.tv_usec = 0;
        // 创建定时器
        setitimer(ITIMER_REAL, &val, NULL);
        printf("到这里");
        while (1)
        {
            sleep(10);
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值