首页 > 其他分享 >操作系统笔记04

操作系统笔记04

时间:2023-08-21 21:14:13浏览次数:34  
标签:返回 IPC char 操作系统 04 int 笔记 信号量 进程

进程管理
一、进程的基本概念
1、进程与程序
程序是存储在磁盘上的可执行文件,程序被加载到内存中开始运行称为进程。一个程序可以同时加载多个进程,
进程就是处于活动状态下的程序
2、进程的分类
进程根据功能不同分为三种类型:交互进程、批处理进程、守护进程
交互进程:由一个shell终端启动的进程,在运行过程中需要与用户进行交互操作,可以运行于前台,也可以在
后台运行
批处理进程:该进程是一个进程的合集,负责按顺序执行预定义好的操作
守护进程:一般处于活跃状态,运行在后台,一般都是由系统在开机时通过启动系统脚本自动创建

3、查看进程
    简单形式:ps    显示当前用户有控制终端的进程简单信息
    列表形式:ps -auxw   以最大列宽显示所有进程的详细信息
        USER        进程的属主         
        PID         进程的编号(从1开始,每个都不相同)
        %CPU        CPU的使用率
        %MEM        内存的使用率
        VSZ         虚拟内存使用的字节数
        RSS         物理内存使用的字节数
        TTY         终端设备号  ?表示的是无终端控制
        STAT        进程状态
                    O   就绪态  等待被调度
                    R   运行态  Linux没有O,就绪也用R表示    
                    S   可被唤醒的休眠态,例如系统中断、获得资源时候、收到信号都可以唤醒进入R
                    D   不可被唤醒的睡眠态,只能被系统唤醒
                    T   暂停态  收到信号SIGSTOP转入T,收到SIGCONT转入R
                    Z   僵尸态
                    <   高优先级
                    N   低优先级
                    s   进程组中的领导者
                    l   多线程的进程
                    +   处于后台的进程组
        START TIME  进程运行的总时间
        COMMAND     进程的启动命令

4、父进程、子进程、孤儿进程、僵尸进程
    一个进程可以被另一个进程创建,创建者就称为被创建者的父进程,被创建者称为创建者的子进程,
    当子进程被父进程创建启动后动后在操作系统的调度下同时进行
    当子进程先于父进程结束时,子进程会向父进程发送SIGCHLD信号,此时父进程应该去回收子进程
    的相关资源,如果父进程没有去回收子进程,那么子进程变成僵尸进程

    父进程先于子进程结束,子进程就变成孤儿进程,所有的孤儿进程都会被系统指定的一个进程领养
    接管,该进程就变成了孤儿进程的父进程

5、进程id号(进程标识符)
    每个进程都有一个以非负整数表示的唯一标识,即进程ID\PID
    进程ID在任意时刻中都是唯一的,但是可以重用,进程一旦结束
    它的进程ID会被系统回收,过一段时间后,可以继续分配给新的
    进程(延时重用)
    pid_t getpid(void);
    功能:获取调用者的进程ID
    pid_t getppid(void);
    功能:获取调用者父进程的进程ID

二、进程的创建

    pid_t fork(void);
    功能:创建一个子进程
    返回值:成功时一次调用两次返回,子进程返回0,父进程返回的是子进程的pid

    当进程的数量超过系统的限制可能会创建失败返回一次,返回的是-1

    注意:
    1、子进程创建成功之后,会与父进程同样从fork处开始继续往下执行,为了让父子进程分别执行不同的功能,可以通过返回值的不同
    让它们进入不同的分支语句
    2、通过fork创建的子进程会拷贝父进程的数据段、bss段、堆、栈、I/O流缓冲区等数据,与父进程共享代码段段数据,子进程会继承
    父进程的信号处理方式
    3、fork结束后,不确定是父进程先返回还是子进程先返回,可以通过休眠函数来让另一个进程先执行(也就是sleep)
    4、通过fork创建的子进程可以共享父进程的文件描述符

         练习:通过fork创建四个子进程,再为这四个子进程各自创建两个子进程

    pid_t vfork(void);
    功能:以加载可执行程序的方式创建并运行子进程
    返回值:成功子进程返回0 父进程返回子进程pid
    注意:
    1、子进程一定先返回,但此时子进程不算完全创建成功,需要加载一个可执行程序来替换当前子进程所有资源,替换完成之后,子进程才算
    完全创建成功,此时父进程才会返回

    使用exec系列函数让子进程加载可执行文件

    int execl(const char *pathname, const char *arg, ...
                    /* (char  *) NULL */);
    path:要加载的可执行文件的路径
    arg:命令行参数 最后一个一定要以NULL结尾

    int execlp(const char *file, const char *arg, ...
                    /* (char  *) NULL */);
    file:要加载的可执行文件的名字,会根据PATH环境变量中记录的路径
    查找并加载该文件
    注意:如果想要指定一个路径,那么需要在配置文件中增加PATH路径

    int execle(const char *pathname, const char *arg, ...
                    /*, (char *) NULL, char *const envp[] */);
    path:要加载的可执行文件的路径
    arg:命令行参数 最后一个一定要以NULL结尾
    envp:环境变量表,

    int execv(const char *pathname, char *const argv[]);
    int execvp(const char *file, char *const argv[]);
    int execvpe(const char *file, char *const argv[],
                    char *const envp[]);

    注意:
    1、exec系列函数成功不返回返回值,失败才会返回-1
    2、以vfork、exec函数创建出来的子进程不会继承父进程的信号处理方式,
    但是能继承父进程的信号屏蔽集

    考点:对比fork和vfork区别?

    int system(const char *command);
    功能:创建子进程加载command程序来执行系统命令

三、进程的正常退出
1、执行了main函数的return n语句,该返回值可以被父进程接收到
2、进程执行调用了exit函数,该函数是属于C标准库提供的
void exit(int status);
功能:能在任意时候结束进程
status:结束状态码EXIT_SUCCESS\EXIT_FAILURE,效果与main函数return 的值等同
注意:该函数不会返回

        int atexit(void (*function)(void));
        功能:向内核注册绑定一个进程结束前要执行的函数

        int on_exit(void (*function)(int , void *), void *arg);
        功能:向内核注册绑定一个进程结束前要执行的函数
        arg:会在调用function的时候传递给该函数

        进程正常退出前要完成步骤:
        注意:
        1、先调用atexit\on_exit函数进行注册,如果注册多个函数,结束时这些函数的执行顺序与
        注册顺序相反
        2、冲刷并关闭所有打开状态下的标准IO流
        3、调用_exit\_Exit函数
            
    3、调用_exit\_Exit函数
            void _exit(int status);
            功能:结束进程,由系统提供
            void _Exit(int status);
            功能:结束进程,由标准库提供
            1、它们的参数status会被父进程获取到
            2、结束前会关闭所有打开状态下的系统IO的文件描述符
            3、向父进程发出结束信号SIGCHLD
            4、该函数不会返回
    4、进程的最后一个线程执行了return返回语句
    5、进程的最后一个线程执行了pthread_exit函数

四、进程的异常终止
1、进程调用了abort函数,产生了SIGABRT信号
2、进程接收到了其他进程的某些信号,导致终止
3、进程自己的错误操作导致终止,例如:段错误、除零等
4、进程的最后一个线程接收到了“取消”请求操作,并相应了请求

注意:以上的异常终止方式,都会让父进程无法获取结束进程的结束
状态码,所以才叫异常终止
注意:无论进程是如何结束的,他们的资源都会被回收,所有的
文件描述符、文件指针都会被关闭

五、子进程的回收
对于任何结束方式,都希望父进程能够获取到,通过wait、waitpid函数可以知道子进程是如何结束的以及结束状态码

pid_t wait(int *status);
功能:等待任意子进程的结束,并获取其结束状态码
status:输出型参数
返回值:子进程的ID
    1、如果所有子进程都还运行,则阻塞等待
    2、如果有一个子进程结束,立即返回该进程的结束状态码和pid
    3、如果没有子进程运行,立即返回-1
WIFEXITED(status)
    判断子进程是否是正常退出,如果是返回真
WEXITSTATUS(status)
    如果子进程是正常退出的,可以获取正确的结束状态码,只获取低八位
WIFSIGNALED(status)
   判断子进程是否是异常退出,如果是返回真 
WTERMSIG(status)
    如果子进程是异常退出的,可以获取杀该子进程的信号id
    
pid_t waitpid(pid_t pid, int *status, int options);
功能:指定回收某个或者某些子进程的状态
pid:
    <-1     等待组id为abs(pid)的进程组中的任意子进程结束
    -1      等待任意子进程结束 功能与wait等同
    0       等待调用者同组的任意子进程结束
    >0      等待该进程结束
status:处理与wait等同

options:等待模式
    0       正常的阻塞模式 等同wait
    WNOHANG 非阻塞模式 如果没有子进程结束,立即返回0
    WUNTRACED 如果等待的子进程中有处于暂停态的,立即返回该子进程的状态码
    WCONTINUED 如果等待的子进程中有从暂停态转入为运行的,立即返回该子进程的状态码

    用于判断和获取status的宏函数:
    WIFSTOPPED 判断状态码,如果子进程是转入暂停态,返回真
    WSTOPSIG   当子进程是暂停态返回时,获取导致暂停态的信号id
    WIFCONTINUED 判断状态码,如果子进程是由暂停态转入运行态,返回真

注意:
1、如果没有子进程结束 wait会阻塞等待,如果在父进程的业务逻辑代码出wait,会对父进程的业务产生阻塞影响,因为子进程结束时会给父进程发送SIGCHLD信号,可以通过在父进程中注册该信号处理函数,在该函数内执行wait,就不影响父进程的业务执行,但是信号最好选择可靠信号
2、waitpid可以选择不阻塞等待,因此不需要像wait一样在信号处理函数中执行,可以直接在父进程的业务逻辑中调用不受影响

进程间通信:
一、基本概念
什么是进程间通信:
是指两个或多个进程之间需要协同工作、交互数据的过程,因为进程之间是相互独立工作的,为了协同工作就需要进行通信来交互数据
进程间通信的分类:
简单的进程间通信:
信号(携带附加信息)、文件、环境变量、命令行参数等
传统的进程间通信:
管道文件(有名管道、匿名管道)
XSI的进程间通信:
共享内存、消息队列、信号量
网络的进程间通信:
socket套接字

二、传统的进程间通信技术-管道
管道是UNIX系统中最古老的进程间通信方式,古老就意味着所有的系统都支持,早期的管道是半双工,现在有些系统的管道是全双工(假定以半双工来编写代码)
管道是一种特殊的文件,它的数据在管道文件中是流动的,读取后就会自动消失,如果文件中没有数据要读取会阻塞等待
有名管道:
基于有文件名的管道文件的进程间通信,可以是任何进程之间的通信
编程模型:
进程A 进程B
创建管道 ...
打开管道 打开管道
写数据 读数据
关闭管道 关闭管道
删除管道 ...

    创建有名管道文件
    命令: mkfifo filename
    函数:int mkfifo(const char *pathname, mode_t mode);
    功能:创建有名管道文件
    pathname:文件的路径
    mode:文件的权限掩码

匿名管道:没有名字的管道文件
    只适合通过fork创建的有血缘关系的进程间通信(父子进程、兄弟进程之间)
    int pipe(int pipefd[2]);
    功能:创建并打开一个匿名管道文件
    pipefd:输出型参数 返回该匿名管道文件的读权限fd和写权限fd
        pipefd[0]   用于读管道文件
        pipefd[1]   用于写管道文件
    
    编程模型:
        父进程                 子进程
       获取一对fd               ...
       创建子进程             共享一对fd
      关闭读fd[0]             关闭写fd[1]
      fd[1]写数据             fd[0]读数据
      关闭fd[1]               关闭fd[0]

三、XSI进程间通信
XSI是X/open 公司制定的用于进程间通信的系统(S)接口(I)标准
XSI进程间通信标准都需要借助系统内核完成,需要创建内核对象,内核对象以整数形式返回给用户,相当于文件描述符\文件指针,代表了某个内核对象完成某次进程间通信任务,也叫做IPC标识符
类似文件的创建需要借助文件名一样,IPC标识符的创建也需要借助IPC键值(整数),也跟文件名一样,要确保IPC键值是独一无二的
一般是通过函数生成一个独一无二的IPC键值
key_t ftok(const char *pathname, int proj_id);
功能:生成一个独一无二的IPC键值
pathname:项目路径
proj_id:项目编号
返回值:根据项目路径+项目编号自动计算出IPC键值
注意:计算IPC键值的方式不是根据pathname的字符串内容,而是依靠路径的位置,如果提供了假的路径,不管编号如何,都会得到相同的IPC键值,不正确

共享内存:
    基本特点:
        两个或多个进程共享同一块由内核负责维护的物理内存,该物理内存是需要与进程的虚拟内存进行映射后使用
    优点:不需要进行磁盘读写操作,无需复制操作,是最快的一种IPC机制
    缺点:需要考虑通信的同步问题,一般采用信号解决

    int shmget(key_t key, size_t size, int shmflg);
    功能:创建\获取共享内存
    key:IPC键值
    size:共享内存的大小,当是获取时此参数无意义写0即可
    shmflg:
        IPC_CREAT   创建共享内存,已存在时会直接获取
        IPC_EXCL    配合CREAT,如果已存在则返回错误
        获取时直接给0即可
        注意:如果是创建IPC_CREAT,需要在后面额外按位或这段共享内存的读写权限IPC_CREAT|0644
    返回值:成功返回该共享内存的IPC标识符,失败返回-1

    void *shmat(int shmid, const void *shmaddr, int shmflg);
    功能:虚拟内存与物理共享内存映射
    shmid:IPC标识符
    shmaddr:想要映射的虚拟内存首地址,一般给NULL就会自动安排
    shmflg:
        SHM_RND 只有当shmaddr不是NULL时才有效,当映射的字节数不足一页的整数倍时,会向下取整数倍的页数来映射
        SHM_RDONLY  以只读方式映射共享内存
        如不需要以上操作,一般给0即可
    返回值:成功返回映射的虚拟内存的首地址,失败返回(void*)-1

    int shmdt(const void *shmaddr);
    功能:取消映射
    shmaddr:映射过的虚拟内存首地址

    int shmctl(int shmid, int cmd, struct shmid_ds *buf);
    功能:删除/控制共享内存
    shmid:IPC标识符
    cmd:
        IPC_STAT    获取共享内存属性信息  buf输出型参数 
        IPC_SET     修改共享内存的属性 buf输入型参数
        IPC_RMID    删除共享内存  buf给NULL即可
    
    编程模型:
        进程A                       进程B
     创建共享内存                获取共享内存
     映射共享内存                映射共享内存
     写数据到内存并通知         接到通知就读内存数据
     接到通知就读内存数据       写数据到内存并通知
     取消映射                      取消映射
     删除共享内存

消息队列:
    基本特点:是一个由内核维护管理的数据链表,通过消息类型来匹配正确后收发数据

    int msgget(key_t key, int msgflg);
    功能:创建\获取消息队列
    key:IPC键值
    msgflg:
        IPC_CREAT   创建消息队列,已存在时会直接获取
        IPC_EXCL    配合CREAT,如果已存在则返回错误
        获取时直接给0即可
        注意:如果是创建IPC_CREAT,需要在后面额外按位或这段消息队列的读写权限IPC_CREAT|0644
    返回值:成功返回该消息队列的IPC标识符,失败返回-1

    int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);m
    功能:向消息队列发送消息包
    msqid:IPC标识符
    msgp:要发送的消息包的首地址
        struct msgbuf {
           long mtype;  //第一个成员必须是long类型的消息类型
           char mtext[1];   //根据需要存储数据
       };// 该结构由程序员自己设计
    msgsz:只需要数据的字节数,不包括消息类型
    msgflg:
        阻塞一般写0
        IPC_NOWAIT  当消息队列满时,不等待立即返回
    返回值:成功0 失败-1

    ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
    功能:从消息队列中接收读取数据
    msqid:IPC标识符
    msgp:消息包内存首地址
    msgsz:数据内存的字节数大小,不包括消息类型,因为不确定对方发送的字节数,因此建议给大一点
    msgtyp:要接收的消息类型
        >0  读取消息类型等于msgtyp的消息
        =0  读取消息队列中的第一条消息
        <0  读取消息队列中的消息类型小于等于abs(msgtyp)的消息如果同时有多个,则读取最小的那一个
    msgflg:
        阻塞等待一般给0即可
        IPC_NOWAIT  如果当时消息类型没有匹配,则不阻塞立即返回
    返回值:成功返回读取到的数据字节数,失败-1

    int msgctl(int msqid, int cmd, struct msqid_ds *buf);
    功能:删除/控制消息队列
    msqid:IPC标识符
    cmd:
        IPC_STAT    获取消息队列属性信息  buf输出型参数 
        IPC_SET     修改消息队列的属性 buf输入型参数
        IPC_RMID    删除消息队列  buf给NULL即可
    编程模型:
        进程A                   进程B
      创建消息队列            获取消息队列
      发送消息msg-a           接收msg-a消息
      接收msg-b消息           发送消息msg-b
      删除消息队列


信号量:
    基本特点:由内核维护的一个"全局变量",用于记录进程之间共享资源的数量,限制进程对共享资源的访问
    信号量更像是一种数据操作锁,本身是不具备数据交换功能,而是通过控制其他的通信资源(共享内存、消息队列、文件等)来实现进程间通信的协调
    1、如果信号量的值>0,说明又可以使用的资源,使用时需要将信号量-1,然后再使用
    2、如果信号量的值等于0,说明没有资源可以使用,此时进程会进入休眠,直到信号量的值大于0,进程就会被唤醒,执行步骤1
    3、当资源使用完毕后,需要将信号量+1,正在休眠的进程就可以被唤醒执行步骤1
    int semget(key_t key, int nsems, int semflg);
    功能:创建\获取信号量的IPC标识符
    key:IPC键值
    nsems:信号量的数量
    semflg:
        IPC_CREAT   创建信号量,已存在时会直接获取
        IPC_EXCL    配合CREAT,如果已存在则返回错误
        获取时直接给0即可
        注意:如果是创建IPC_CREAT,需要在后面额外按位或这段信号量的读写权限IPC_CREAT|0644
    返回值:成功返回该信号量的IPC标识符,失败返回-1

    int semop(int semid, struct sembuf *sops,size_t nsops);
    功能:对某个或某些信号量进行加减操作
    semid:IPC标识符
    sops:
        struct sembuf{
        unsigned short sem_num;  // 信号量的下标
        short          sem_op;   // 
                1   信号量+1
                -1  信号量尝试-1,如果为0则休眠阻塞
                0   等待信号量的值为0,否则阻塞休眠

        short          sem_flg; 
                0           阻塞 
                IPC_NOWAIT  不阻塞
                SEM_UNDO  如果进程终止了没有手动还原信号量+1,系统会自动还原+1  
        };
    nsops:表示sops指针指向多少个连续的结构体,意味着要加减多少个信号量,一般每次只操作一个时写1即可

    int semctl(int semid, int semnum, int cmd, ...);
    功能:删除\控制信号量
    semid:IPC标识符
    semnum:第几个信号量 下标从0开始
    cmd:
        IPC_STAT   获取信号量属性
        IPC_SET    设置信号量属性
        IPC_RMID   删除信号量
        GETALL      获取所有信号量的值
        GETNCNT     获取正在等待减信号量的进程数
        GETVAL     通过返回值获取下标为semnum信号量的值
        GETZCNT    获取正在等待信号量的值为0的进程数
        SETVAL      设置下标为semnum信号量的值
        SETALL      设置所有信号量的值
    ...:
    union semun {
           int              val;    // 用于设置信号量的值
           struct semid_ds *buf;// 用于获取\设置信号量的属性
           unsigned short  *array;  //用于批量设置\获取信号量的值
       };

    使用流程:
        1、创建信号量
        2、设置信号量管理的资源数
        3、减\加信号量
        4、删除信号量

标签:返回,IPC,char,操作系统,04,int,笔记,信号量,进程
From: https://www.cnblogs.com/c-learnmore/p/17647091.html

相关文章

  • 【学习笔记】优化建图相关(线段树优化,倍增优化)
    优化建图发现并没有人写得很详细的样子,那我也摆烂好惹点击查看目录目录前言线段树优化建图单点连区间区间连区间例题解题:倍增优化建图例题解题:前言众所周知,连边的时间复杂度一般是\(O(1)\),但,当连边的对象是一个连续的树上区间的时候,我们或许有更优的连边方式:优化建图。......
  • day04
    信号管理一、基本概念  1、中断    当程序进程接收到消息后,中止当前正在进行的进程,转而去执行其他任务等其他任务执行结束后再返回刚刚中止的位置,可以继续往下运行    中断分为硬件中断、软件中断,硬件中断是由硬件设备引发的、软件中断是执行了中断指令......
  • iwebsec-xss 04 xss修复示例
    01、题目分析这一题就不是解题了,是教如何实现防范xss漏洞的,因此我们重点分析源码,是如何实现防范xss的02、xss按照第一关的xss方式去访问,可以明显发现没有出弹窗,而是把js代码作为文字输出到界面上03、源码分析<?phprequire_once'../header.php';?><html> <head> <titl......
  • Nodejs Function遇见WorkerProcessExitException : node exited with code -107374079
    问题描述NodejsFunction,使用BlobTrigger用于处理上传到StorageBlob的文件,但是最近发现偶发报错:Exceptionwhileexecutingfunction:Functions.AzureBlobTrigger--->Microsoft.Azure.WebJobs.Script.Workers.WorkerProcessExitException:nodeexitedwithcode-1073740791......
  • openGauss学习笔记-46 openGauss 高级数据管理-子查询
    openGauss学习笔记-46openGauss高级数据管理-子查询子查询或称为内部查询,嵌套查询,指的是在数据库查询的WHERE子句中嵌入查询语句,相当于临时表。一个SELECT语句的查询结果能够作为另一个语句的输入值。子查询可以与SELECT,INSERT,UPDATE和DELETE语句一起使用。以下是子查询必须遵......
  • docker ubuntu20.04 安装教程
    ubuntu20.04安装docker教程本博客测试安装时间2023.8月一、docker安装内容:dockerEngine社区版和dockerCompose二、安装环境:ubuntu20.04三、安装步骤:#如果已经安装过docker,请先卸载,没安装则跳过forpkgindocker.iodocker-docdocker-composepodman-dockercontai......
  • 数据治理专业认证CDMP学习笔记(思维导数据治理专业认证CDMP学习笔记(思维导图与知识点)-
    大家好,我是独孤风,一位曾经的港口煤炭工人,目前在某国企任大数据负责人,公众号大数据流动主理人。在最近的两年的时间里,因为公司的需求,还有大数据的发展趋势所在,我开始学习数据治理的相关知识。数据治理需要进行系统的学习才能真正掌握,也需要进行专业的考试认证才能证明自己在数据治理......
  • linux操作系统八股
    C++分配内存的方式:newdeletemallocfree、placementnew数组new跟delete   程序空间布局管理:这些是站在最理想情况的程序员设计角度来谈的栈mmap堆BSS数据段TEXT段 讲讲malloc是C库函数,底层会调用brk跟mmap两种系统调用,缺省值是128kb,也就是说小于128kb的时候......
  • 【学习笔记】网络流各种形式及模型
    各种形式普通网络流P3376【模板】网络最大流Dinic#include<bits/stdc++.h>usingnamespacestd;intn,tot=1,first[210],nnext[10010],to[10010],w[10010],que[210],src,des,lev[210],cur[210];voidadd(intx,inty,intz){ nnext[++tot]=first[x]; first[x]=tot; t......
  • C语言笔记 - “%”符号的用法
    1、%-运算符%表示取模运算,也就是取余数。例如6%4=22、%-引导符/占位符引导符用于控制输入输出的格式。常见于printf("%d",a);scanf("%d",&a);语句。%s - 字符串 (String)%c - 字符 (Char)%d - 十进制有符号型输出 (Decimal)①%6d整数输出,宽度是6位,不足左边补......