C语言基础-进程相关

  

进程的概念

程序

  • 存放在磁盘上的 指令和数据的有序集合(文件)
  • 静态的

 

进程

  • 执行一个程序所分配的资源总称
  • 进程是程序一次执行过程
  • 动态的,包括创建、调度、执行和消亡

 

进程控制块(pcd)

  • 进程标识PID
  • 进程用户
  • 进程状态、优先级
  • 文件描述符表

 

进程类型

  • 交互进程:在shell下启动。以在前台运行,也可以在后台运行
  • 批处理进程:和终端无关,被提交到一个作业队列中,以便顺序执行
  • 守护进程:和终端无关一直在后台运行

 

进程状态

  • 运行态:
    • 正在运行
    • 准备运行
  • 等待态:进程在等待一个事件的发生或某种资源
    • 可终端
    • 不可中断
  • 停止态:进程被中止,收到信号后可继续运行
  • 死亡态:以终止的进程,但pcb没有被释放

 

查看进程信息

  • ps 查看系统进程快照
  • top 查看进程动态信息   
  • /proc 查看进程详细信息

 

修改进程优先级

  • nice 按用户指定的优先级运行进程
    • nice -n num ./test
    • num范围是 -20~19 越低优先级越高
    • 普通用户是没办法指定负数的优先级
  • renice 改变正在运行进程的优先级
    • renice -n num pid
    • 普通用户是没办法提高该优先级,只能进行降低

 

进程相关

  • jobs 查看后台进程
  • bg 将挂起的进程在后台运行
    • bg id
    • id是jobs的后台进程编号
  • fg 吧后台运行的程序放到前台来运行
    • fg id
    • id 是jobs的后台进程编号
  • ctrl + z 将前台进程放到后台挂起

 

 

C与进程相关函数

 

父子进程

  • 子进程继承了父进程的内容
  • 父子进程有独立的地址空间,互不影响
  • 若父进程先结束
    • 子进程成为孤儿进程,被init进程收养
    • 子进程变成后台进程
  • 若子进程先结束
    • 父进程如果没有及时回收,子进程变成僵尸进程

 

进程创建 - fork

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

// 创建新的进程,失败返回-1
// 成功时父进程返回子进程的进程号,子进程返回0

创建完子进程后,从fork后下一条语句开始执行;

父子进程的执行顺序不定

父子进程可以多次调用fork

获取当前进程 - getpid

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

 

demo:创建子进程

#include <stdio.h>
#include <unistd.h>

int main(int argc, char *argv[]) {
    pid_t pid;
    if ((pid = fork()) < 0) {
        perror("fork");
        return -1;
    } else if (pid == 0) {
        printf("child process: my pid is :%d\n",getpid());
    }else{
        printf("parent process :%d\n",getpid());
    }
}

/* 输出:
parent process :7124
child process: my pid is :7125
*/

 

 

结束进程

#include <stdlib.h>
void exit(int  status);

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

// 引入的头文件不同
//*** exit结束进程时会刷新(流)的缓冲区
// _exit结束时不会刷新缓冲区 可能会造成流文件丢失

 

exit(0) // 退出当前程序

 

进程资源回收

  • 子进程结束时由父进程回收
  • 孤儿进程由init进程回收
  • 若没有及时回收会出现僵尸进程
#include <unistd.h>
pid_t wait(int *status);

// status指定保存子进程返回值和结束方式的地址
// status为NULL表示直接释放子进程PCB,不接受返回值

// 成功时返回回收的子进程号
// 失败是返回EOF
// 若子进程没有结束,父进程会一直阻塞
// 若有多个子进程,那个先结束就先回收

 

Demo:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int main(int argc, char *argv[]) {
    pid_t pid;
    int status = 0;
    if ((pid = fork()) < 0) {
        perror("fork");
        return -1;
    } else if (pid == 0) {
        printf("child process: my pid is :%d\n", getpid());
        sleep(1);
        exit(2);

    } else {
        wait(&status);
        printf("%x\n", status);
        printf("parent process :%d\n", getpid());
    }
}

 

 

进程回收的另一种方式

#include <unistd.h>
pid_t waitpid(pid_t pid,int * status,int option);

// pid可用于指定回收那个子进程或任意子进程
// pid= -1 代表当前进程的任意子进程

// status指定用于保存子进程返回值和结束方式的地址
// option指定回收方式,0或WNOHANG 
// 0表示阻塞一直等到进程结束
// WNOHANG表示非阻塞

// 成功时返回子进程的pid或0 0表示当前子进程还未结束
// 失败时返回EOF

 

进程返回值和结束方式

  • 子进程通过exit、_exit/return返回某个值(0~255)
  • 父进程调用wait(&status)回收

    WIFEXITED(status) // 判断子进程是否正常结束 0为false 1为true

    WEXITSTATUS(status)  // 获取子进程返回值

    WIFSIGNALED(status) // 判断子进程是否被信号结束

    WTERMSIG(status) // 获取结束子进程的信号类型

   

 

守护进程

  • 守护进程(Deamon)是linux三种进程类型之一
  • 通常是在系统启动时运行,系统关闭时结束
  • linux系统中大量使用,很多服务程序以守护进程方式运行

 

会话、控制终端

  • linux会话(session)、进程组的方式管理进程
  • 每个进程属于一个进程组
  • 会话是一个或多个进程组的集合。通常用户打开一个终端,系统会创建一个会话。所有通过该终端运行的进程都属于这个会话
  • 终端关闭时,所有相关进程会被结束

 

守护进程特点

  • 始终在后台运行
  • 独立于任何终端
  • 周期性的执行某种任务或等待处理特定事件

 

守护进程创建

  • 创建子进程,父进程退出--子进程变为孤儿进程,被init进程收养;子进程在后台运行(还是会依附于当前终端)
if (fork() > 0) {
    exit(0);
}
  • 子进程创建新会话--子进程称为新的会话组长;脱离原先的终端
  • 更改当前工作目录--守护进程一直在后台运行,其工作目录不能被卸载一般指向根目录(/)或(tmp)
chdir("/"); // 普通用户指向这个目录可读
chdir("/tmp"); // 所有用户都是最高权限
  • 重设文件权限掩码--文件权限掩码设置为0;只影响当前进程
#include <sys/stat.h>

if (umask(0) < 0) {
    exit(-1);
}

 

  • 关闭父进程打开的文件描述符--已脱离终端 stdin/stdout/stderr 无法再使用
#include <zconf.h>

for (int i = 0; i < getdtablesize(); ++i) {
    close(i);
}

 

 

Demo:每隔1秒写入时间到time.log

 

 

 

 

 

 

 

exec函数族

  • 进程调用exec函数族执行某个程序
  • 进程当前内容被指定的程序替换
  • 实现让父子进程执行不同程序
    • 父进程创建 子进程
    • 子进程调用exec函数族
    • 父进程不受影响

 

通过shell命令去执行其他程序

#include <unistd.h>
int execl(const char *path,const  char*arg,...); /
int execlp(const char *file,const char *arg,...);

// 成功执行指定的程序
// 失败返回EOF
// path找到要执行程序的名称(要包含路径)
// arg...传递给执行程序的参数表

// file 执行程序的名称,在$PATH中查找

 

Demo:

if (execl("/bin/ls", "ls", "-a","/etc") < 0) {
        perror("excel");
    }

 

 if (execlp("ls", "ls", "-a","/etc") < 0) {
        perror("excel");
    }

 

 

通过shell命令去执行其他程序 参数为数组

#include <unistd.h>
int execv(const char * path,char * const argv[]);
int execvp(const char * file,char * const argv[]);

// 成功时执行指定的成
// 失败返回EOF
// arg... 封装成指针数组 比上面exec更加灵活

 

char *arg[] = {"ls", "-a", "/etc",NULL};
if (execv("/bin/ls", arg) < 0) {
    perror("excel");
}

 

char *arg[] = {"ls", "-a", "/etc",NULL};
if (execvp("ls", arg) < 0) {
    perror("excel");
}

 

通过shell命令去执行其他程序 字符串

#include <stdlib.h>
int system(const char * command);

// 会自动创建一个子进程去执行command
// 成功返回command指令的返回值
// 失败返回EOF

 

 

 

 

 

 

 

 

 

 

<style></style>
相关文章