C语言——进程管理

本文详细介绍了Linux系统中的进程基本概念,包括进程的属性、创建、等待、结束,以及进程间的关系。讨论了调度策略、时间片分配和优先级设定。此外,还深入探讨了线程的创建、结束、挂起、取消和同步,以及互斥锁在多线程同步中的应用。
摘要由CSDN通过智能技术生成

进程基本概念

进程是具有一定功能的程序关于一个数据集合的一次执行过程。Linux系统支持多个进程同时进行,每个进程属性中的安全信息里都设定有一个优先级,系统根据优先级来决定各个进程从CPU获得的时间片的大小。

Linux内核把进程成为任务(task),进程的虚拟地址空间分为用户虚拟地址空间和内核虚拟地址空间,所有进程个共享内核虚拟地址空间,每个进程有独立的用户虚拟地址空间。

进程有两种特殊形式:没有用户虚拟地址空间的进程成为内核线程,共享用户虚拟地址空间的进程成为用户线程,通常在不会引起混淆的情况下把用户线程简称为线程。共享同一个用户虚拟地址空间的所有线程组成一个线程组。

C标准库的进程术语和Linux内核的进程术语对应关系如下:

C标准库的进程术语对应的Linux内核的进程术语
包含多个线程的进程线程组
只有一个线程的进程进程或任务
线程共享用户虚拟地址空间的进程

进程的属性

每个进程都有自己的属性,下面对属性进行简单介绍:

属性作用
进程标识符(PID)系统为其分配的标识符
进程所占的内存区域为进程所分配的内存区域
相关文件的描述符进程运行中的文件描述符,包括标准输入、标准输出、标准错误输出
安全信息包括用户识别号、组识别号,供内核使用,决定进程权限
进程环境环境变量、程序调用的命令行
信号处理可通过信号进行进程间的通信
资源安排进程是调度系统资源的基本单位,不同进程轮流使用系统资源
同步处理多程序之间同步由进程来完成,使用共享内存、文件锁定等方法
进程状态运行、等待被调度、睡眠状态。

进程控制相关函数

进程创建

1、派生进程

#include <unistd.h>
pid_t fork(void);
pid_t vfork(void);

调用fork时,系统将创建一个与当前进程相同的新的进程。通常将原有的进程成为父进程,而把新生成的进程成为子进程。

测试程序1:

#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
int main(void)
{
    pid_t pid;
    printf("hello nfk!     !\n");
    printf("hello ubuntu !\n");
    printf("hello !");
    if((pid=fork())<0){
        printf("fork error!\n");
        exit(1);
    }else if(pid==0){
        printf("Child process is printing\n");
    }else{
        printf("Parent process is printing\n");
    }   
    exit(0);
}

fork函数会返回两次,从父进程中返回子进程PID,从子进程中返回0。这种方法,父进程与子进程无关。

hello nfk! !
hello ubuntu !
hello !Parent process is printing
hello !Child process is printing

测试结果如上,其中hello!打印了两次,应该是fork进程时,同时把缓冲区的数据同时复制了,从而产生了两次hello!

同时fork进程执行的程序明显从fork继续执行,而不是从头开始执行。

另外还有一种创建进程的方式为vfork,会一直等待子进程结束之后,再执行父进程。一般这种方式会搭配exec使用,

exec函数族中的函数用于执行新的程序,以新的子进程来完全替代原有的进程。所以一般使用vfork之后,使用exec函数,直接创建出一个子进程。

还有__clone函数用于进程的创建,这个函数对父子进程的共享资源有更多的控制。

进程等待

#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define FORK \
    if((pid=fork())<0){ \
        printf("fork error!\n"); \
        exit(0); \
    } \
    else if(pid==0)

#define WAIT \
    if(wait(&status)!=pid){ \
        printf("wait error!\n"); \
    exit(0); \
    }
void h_exit(int status);

int main(void)
{
    pid_t pid;
    int status;
    FORK exit(7);WAIT//子进程结束返回7,父进程等待返回结果
    h_exit(status);

    FORK exit(1);WAIT//子进程结束返回1,父进程等待返回结果
    h_exit(status);
    
    FORK printf("hello\n");WAIT 
        /*子进程返回结果0,
        同时子进程也会执行等待,但无法获取返回结果
        父进程获取返回结果0*/
    h_exit(status);
    exit(0);
}

void h_exit(int status)
{
    if(WIFEXITED(status))
        printf("normal termination,exit status=%d .\n",WEXITSTATUS(status));
    else if (WIFSIGNALED(status))
        printf("abnormal termination,exit status=%d .\n",WEXITSTATUS(status));
}

执行结果如下所示,执行wait之后,父进程会被挂起,直到子进程结束。wait专用于等待子进程,如果无子进程,则返回错误。

normal termination,exit status=7 .
normal termination,exit status=1 .
hello
wait error!
normal termination,exit status=0 .

进程结束

进程结束有以下几种方式:

#include <stdlib.h>
void exit(int status);//终止正在运行的程序
int atexit(void(*function)(void));//注册一个不带参数、返回值的函数供退出时调用
int on_exit(void(*function)(int,void*),void *arg);//注册带参数、返回值的函数供调用
void abort(void);//打断函数、发送一个SIGABRT信号,使当前进程终止
#include <unistd.h>
void _exit(int status);//关闭一些Linux特有的退出句柄
#include <assert.h>
void assert(int expression);

多个进程间的关系

进程组

#include <sys/types.h>
#include <unistd.h>

pid_t getpgrp(void);
pid_t setpgid(pid_t pid,pid_t pgid);

通过以上函数可完成获取进程组号和创建进程组或添加至某进程组。

时间片分配

调度策略和参数

内核支持的调度策略如下:

  1. 限期进程使用限期调度策略(SCHED_DEADLINE)。
  2. 实时进程支持两种调度策略:先进先出调度(SCHED_FIFO)和轮流调度(SHCHED_RR)。
  3. 普通进程支持两种调度策略:标准轮流分时(SCHED_NORMAL)和空闲(SCHED_IDLE)。
  • 限期调度策略有3个参数:运行时间runtime、截止期限deadline和周期period。每个周期运行一次,在截止期限之前执行完,一次运行的时间长度是runtime。

  • 先进先出调度没有时间片,非常霸道,如果没有更高优先级的实时进程,并且它不睡眠,那么它将一直霸占处理器。

  • 轮流调度没有时间片,进程用完时间片以后加入优先级对应运行队列的尾部,把处理器让给优先级相同的其他实时进程。

  • 标准轮流分时策略使用完全公平调度算法,把处理器时间公平地分配给每个进程。

  • 空闲调度策略用来执行优先级非常低的后台作业,优先级比使用标准轮流分时策略和相对优先级为19的普通进程还要低,进程的相对优先级对空闲调度策略没有影响。

限期进程的优先级比实时进程高,实时进程的优先级比普通进程高。

限期进程的优先级是-1。

实时进程的优先级是1~99,优先级数值。

普通进程的静态优先级是100139,优先级数越小,表示优先级越高,可通过修改nice值(即相对优先级,取值范围是-2019)改变普通进程的优先级,优先级等于120加上nice值。

调度类:

调度类调度策略调度算法调度对象
停机调度类停机进程
限期调度类SCHED_DEADLINE最早期限优先限期进程
实时调度SCHED_FIFO,SCHED_RR先进先出,轮流调度实时进程
公平调度SCHED_NORMAL,SCHED_IDLE完全公平调度算法普通进程
空闲调度每个处理器的空闲进程

内核在进行调度时,会从运行队列中进行选择,根据调度类的优先级进行调度,优先级从高到低分别是停机、限期、实时、公平、空闲。也就是从限期队列、实时队列、公平队列中进行选择进程进行调度。

优先级设定

#include <unistd.h>
int nice(int inc);//改变进程的动态优先级

#include <sys/time.h>
#include <sys/resource.h>
int setpriority(int which,int who,int prio);//设置优先级
int getpriority(int which,int who);//获取优先级

#include <sched.h>
int sched_set_priority_max(int policy)//设为指定策略的最高优先级
int sched_set_priority_min(int policy);//设为指定策略的最低优先级

进程同步

文件锁定、信号、信号量、管道、套接字,可完成进程间同步,防止多个进程抢相同的资源。

线程

在同一个进程内部又可以分为若干线程,Linux系统中的线程所具有的最大特点是:线程的调用是由系统内核调度程序来实现的,每一个线程都有自己的进程号,线程所消耗的系统资源较少,互相间的通信也较易实现。

线程的创建

#include<pthread.h>
int pthread_create(pthread_t* thread,pthread_attr *attr,void *(*start_rouline) (void*),void *arg)

其中start_rouline指向该线程所调用的程序的函数指针。

线程属性设置暂不分析。

线程结束、挂起、取消

线程结束:

#include <pthread.h>
void pthread_exit(void *retval);

线程挂起:

#include <pthread.h>
int pthread_join(pthread_t th,void **thread_return);

挂起当前线程,直至指定线程终止,th为要等待终止的线程。thread_return用于存放其他线程的返回值,具体用法考虑后续的例程。

用户可以挂起一个线程,直至某条件得到满足

#include <pthread.h>
pthread_cond_t cond=PTHREAD_COND_INITIALIZER;
int pthread_cond_init(pthread_cond_t *cond,pthread_condattr_t *cond_attr);//初始化一个对象
int pthread_cond_destroy(pthread_cond_t *cond);//检测是否有线程在等待指定线程
int pthread_cond_signal(pthread_cond_t *cond);//用于重启一个挂起线程
int pthread_cond_broadcast(pthread_cond_t *cond);//重启所有挂起进程
int pthread_cond_wait(pthread_cond_t *cond,pthread_mutex_t *mutex);//等待互斥线程
int pthread_cond_timedwait(pthread_cond_t *cond,pthread_mutex_t *mutex,const struct timespec *abstime);//可指定时间,等待互斥线程

线程取消:

#include <pthread.h>
int pthread_cancel(pthread_t thread);//取消线程
int pthread_setcancelstate(int state,int * oldstate);//设置线程自身状态
int pthread_setcanceltype(int type,int * oldstate);//设置取消的相应方式,立即取消和到取消点取消
int pthread_testcancel(void);//设置取消点

type:

PTHREAD_CANCLE_ASYNCHRONOUS 立刻取消

PTHREAD_CANCLE_DEFERRED 延迟取消至取消点

互斥

互斥锁,用户对资源进行上锁,同一时刻只能有一个线程给他上锁,当另外一个线程试图解锁,他将被挂起,直到互斥解锁为止。

互斥的函数有:

#include <pthread.h>
int pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutexattr_t *mutexattr);//构造互斥
int pthread_mutex_destroy(pthread_mutex_t *mutex);//取消一个互斥
int pthread_mutex_lock(pthread_mutex_t *mutex);//互斥上锁
int pthread_mutex_trylock(pthread_mutex_t *mutex);//只有当互斥被锁住时,才将线程挂起
int pthread_mutex_unlock(pthread_mutex_t *mutex);//解锁一个互斥

  • 1
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

塔通天

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值