• Linux 进程


    Linux 进程

    计算机体系结构与操作系统

    • [x] 施工完毕

    冯诺依曼结构

    John von Neumann

    组件:输入设备输出设备存储器控制器运算器

    工作:从输入设备输入指令到存储器中(即内存),控制器分析指令(取指令)并交由运算器执行运算任务,之后将相关结果写入存储器中,并由输出设备输出结果。

    冯诺依曼体系结构是一种逻辑结构,它确定了计算机各个模块的职能。

    操作系统的功能

    现代计算机装载了大量硬件设备,需要一套基本的程序集合来执行任务和管理资源。这一套程序集合就是操作系统,通常也称为内核。操作系统需要将必要的任务开放为接口以暴露给应用开发者调用,这些接口就被称为系统调用,如创建进程、调整进程优先级、执行 I/O 操作等。这些系统调用在一些高级语言中也被封装为各自的方法或函数,这些方法和函数就是我们常说的 API。现代操作系统的一个很重要的功能就是管理和调度进程。

    进程和线程的概念

    • [x] 施工完毕

    资源

    进程是操作系统资源分配和调度的基本单位。这里的资源指的是包括内存空间、CPU 占用在内的所有运算资源。进程控制块PCB)为操作系统用来描述进程的数据结构,记录进程的状态和控制需要的所有信息。进程状态包括新建就绪运行中等待终止五种,由调度程序管理。程序计数器记录进程接下来要执行的指令地址。进程控制块还记录了进程的调度信息、优先级页表等。

    调度

    在引入线程的操作系统中,进程调度的基本单位为线程。调度方法主要有先来先服务最短作业优先轮转等策略。引起调度的事件一般包括执行完毕或异常退出、提出 I/O 请求、主动请求挂起、时间片结束等。调度程序的任务包括建立进程信息和调度队列、采用调度策略对应的算法来选中进程、完成上下文切换(恢复现场)。

    进程的创建(进程克隆)

    • [x] 施工完毕

    • fork 函数

    #include <unistd.h>
    #include <sys/types.h>
    
    // 进程克隆
    pid_t fork(void);
    // 对于原进程,返回值为新进程的 pid
    // 对于新进程,返回值为 0
    // 若执行失败,返回值为 -1
    
    // 等待子进程结束(任意一个子进程结束即返回)
    pid_t wait(int * status);
    // 返回值为子进程的 pid
    // 若未曾 fork 则返回 -1
    // status 传入一个变量的地址用来接受子进程的退出状态(置为 NULL 以忽略退出状态)
    
    // 等待指定子进程结束
    pid_t waitpid(pid_t pid, int * status, int option);
    // 对于参数 pid:
    // -1 等待所有 PGID 等于父进程 pid 的绝对值的⼦进程
    // 1 等待所有⼦进程
    // 0 等待所有 PGID 等于调⽤进程的⼦进程
    // >0 等待 PID 等于 pid 的⼦进程
    // option 指定选项,如 WNOHANG/WUNTRACED 等
    
    // 暂停当前进程,直到接收到一个信号
    int pause(void);
    // 挂起进程,单位为秒
    unsigned int sleep(unsigned int);
    // 类似的,usleep 函数也用于挂起进程,单位为微秒
    

    unistd.h 库中定义了 open / closeread / writefork / exec / sleep 等函数,参考 unistd.h;有关更多系统调用函数,另请参阅 System calls

    • 信号
    #include <signal.h>
    
    // 注册信号,即针对特定信号注册回调函数
    // 原型
    void (* signal(int sig, void (* func) (int))) (int);
    // 等价于
    typedef void (* sighandler_t) (int);
    sighandler_t signal(int signum, sighandler_t handler);
    // SIGABRT 异常终止; SIGFPE 算术异常; SIGILL 非法函数镜像; SIGINT 中断; SIGSEGV 非法访问; SIGTERM 终止;
    
    // 向指定进程发送信号
    int kill(pid_t pid, int sig);
    

    执行一个程序(进程替换)

    • [x] 施工完毕

    • exec 函数族

    int execl(const char *path, const char *arg, ...);
    int execlp(const char *file, const char *arg, ...);
    int execle(const char *path, const char *arg, ..., char *const envp[]);
    int execv(const char *path, char *const argv[]);
    int execvp(const char *file, char *const argv[]);
    // 以上函数在实现中都调用了 execve
    int execve(const char *path, char *const argv[], char *const envp[]);
    // path 要执行的文件路径(可以是相对路径)
    // file 同 path,但当其不包含 '/' 时会从 $PATH 环境变量中寻找
    // argv 参数列表(以 NULL 结束)
    // envp 环境变量列表(以 NULL 结束)
    

    异常情形(僵尸与孤儿)

    • [x] 施工完毕

    僵尸进程

    子进程退出,但父进程忽略了这个事件,因此操作系统为保护其状态,不会释放子进程的资源。在父进程中调用 wait 函数可避免。

    孤儿进程

    与僵尸进程相反,当子进程仍在运行时,父进程先于其退出,这时子进程成为一个后台进程,其 ppid 将变为 1,即被 init 进程“收养”。孤儿进程对系统完全没有危害,常见于守护进程。

    进程协作

    • [ ] 待施工

    • 信号灯/信号量

      • 二值信号灯:类似互斥锁,只能取 0 或 1。与互斥锁不同的是其允许其他的进程修改其值,而互斥锁必须由进程自身解锁。
      • 计算信号灯:可取任意非负值(有时也可取负值,表示饥饿进程的数量)。

    信号灯的 P/V 操作是指对资源的请求与释放操作,表现为信号量的增减。P 操作表示请求资源,当条件允许时,申请成功,信号量自减;V 操作则为解除请求,信号量自增。

    #include <sys/ipc.h>
    #include <sys/types.h>
    
    // 信号灯的结构如下
    struct sem {
        // 当前值
        int semval;
        // 最近一次操作的 pid
        int sempid;
    }
    // 文件名到键值
    key_t ftok (char *pathname, char proj);
    int semget(key_t key, int nsems, int semflg);
    int semop(int semid, struct sembuf *sops, unsigned nsops);
    int semctl(int semid,int semnum,int cmd,union semun arg);
    
    • 共享内存
    • 消息队列
    // 消息结构
    struct my_message {
        // 消息类型
        long int message_type;
        // 消息数据
        // ...
    };
    // 模式与访问权限结构
    struct msgid_ds {
        uid_t shm_perm.uid;
        uid_t shm_perm.gid;
        mode_t shm_perm.mode;
    };
    
    // 创建和访问队列
    int msgget(key_t, key, int msgflg);
    // 添加消息(msg_sz 为消息数据的大小而不是结构体大小)
    int msgsend(int msgid, const void *msg_ptr, size_t msg_sz, int msgflg);
    // 接收消息
    int msgrcv(int msgid, void *msg_ptr, size_t msg_st, long int msgtype, int msgflg);
    // 控制
    int msgctl(int msgid, int command, struct msgid_ds *buf);
    
    • 管道
      • 无名管道:仅适用于 fork 自同一父进程的进程之间或父子进程间。又称匿名管道。
      • 有名管道:形式上类似于文件,操作上类似管道。又称命名管道。
    // 无名管道
    #include <unistd.h>
    // 创建管道
    int pipe(int file_descriptor[2]);
    // 读写数据(使用 read/write 系统调用)
    ssize_t read(int fd, void *buf, size_t count);
    ssize_t write(int fd, const void *buf, size_t count); 
    // 修改描述符
    int dup(int file_descriptor);
    int dup2(int file_descriptor_one, int file_descriptor_two);
    
    // 有名管道
    #include <sys/types.h>
    #include <sys/stat.h>
    // 创建
    int mkfifo(const char *filename, mode_t mode);
    int mknod(const char *filename, mode_t mode | S_IFIFO, (dev_t)0);
    // 打开/关闭操作同普通文件,使用系统调用 read/write
    int open(const char *pathname, int flags);
    int open(const char *pathname, int flags, mode_t mode);
    int close(int fd);
    

    线程管理

    • [x] 施工完毕

    • pthread 线程库(POSIX 标准)

    更多内容可参考 POSIX Threads Programming

    #include <pthread.h>
    
    // 相关类型
    // 线程句柄,即 tid,本质上为 int 类型
    // 考虑兼容性,使用 pthread_equal() 比较大小
    pthread_t tid;
    int pthread_equal(pthread_t t1, pthread_t t2);
    // 线程属性,包括挂起属性、调度策略、有效范围和优先级等
    pthread_attr_t attr;
    // 互斥锁,确保操作的原子性
    pthread_mutex_t mutex;
    // 条件变量,类似于信号,可用于线程等待直到条件成立
    pthread_cond_t cond;
    
    // 相关函数
    // 创建线程
    int pthread_create(pthread_t *tidp,const pthread_attr_t *attr,
        (void*)(*start_rtn)(void*),void *arg);
    // 获取自身 tid
    pthread_t pthread_self(void);
    // 线程退出,value_ptr 用于接收返回值
    void pthread_exit(void *value_ptr);
    // 线程取消(终止)
    int pthread_cancel(pthread_t thread);
    // 等待线程结束(类似进程 wait)
    int pthread_join(pthread_t thread, void **value_ptr);
    // 挂起线程
    int pthread_detach(pthread_t thread);
    
    // 初始化/注销线程属性
    int pthread_attr_init(pthread_attr_t *attr);
    int pthread_attr_destroy(pthread_attr_t *attr);
    // 获取/设置线程属性
    int pthread_attr_getdetachstate(const pthread_attr_t *attr,
        int *detachstate);
    int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
    
    // 创建和注销互斥锁、互斥锁属性
    int pthread_mutexattr_init(pthread_mutexattr_t *mattr);
    int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr);
    // 必须在锁处于开放状态时注销
    int pthread_mutex_destroy(pthread_mutex_t *mutex);
    int pthread_mutexattr_destroy(pthread_mutexattr_t *attr);
    // 互斥锁相关操作
    int pthread_mutex_lock(pthread_mutex_t *mutex);
    int pthread_mutex_trylock(pthread_mutex_t *mutex);
    int pthread_mutex_unlock(pthread_mutex_t *mutex);
    
  • 相关阅读:
    JavaScript知识回顾
    HTML和CSS相关知识回顾
    springmvc文件上传和下载
    jsp页面调试中的问题记录
    mybatis传参的几种方式
    ssm中调试遇到的坑
    idea新手日记
    Oracle安装
    mysql5安装
    Servlet 的原理----无脑笔记
  • 原文地址:https://www.cnblogs.com/lost-melody/p/12002710.html
Copyright © 2020-2023  润新知