C语言进程概念,孤儿进程,僵尸进程,exec函数族,守护进程

C语言进程概念,孤儿进程,僵尸进程,exec函数族,守护进程

【进程】

  1. 进程

    进程是一个程序的一次执行的过程

    每一个进程都分配一个虚拟的4G内存

    0-3G : 用户
    3G-4G : 内核

  2. 进程和程序的区别

    程序是静态的

    进程是动态的

  3. 进程的内存管理

  4. 正文段、用户数据段、系统数据段

  5. 进程号

    唯一的标识一个进程

  6. 进程的类型

交互进程(ctrl+z / jobs -l / bg / fg / & / kill -l)

ctrl + z : 使进程进入挂起状态(T), 被挂起的进程称为作业

jobs -l: 查看挂起的进程

bg % 作业号 :使作业恢复前台运行, 不能ctrl+c结束

fg % 作业号 : 使作业恢复前台运行, 可以ctrl+c结束

kill -l : 查看信号种类

kill -9 PID : 杀死进程

ps -ajx : 查看进程的运行状态

批处理进程(运维)

守护进程( 1 init )

  1. 进程的运行状态

ps -ajx

运行态R:此时进程或者正在进行,或者准备运行
内核调度程序到CPU上执行 running

等待态:此时进程需要满足一些条件,如果不满足就等待
可中断S:如果进程收到信号会醒来 ctrl+c
不可中断D:如果进程收到信号不会醒来

停止态T:此时进程被中止SIGSTOP

死亡态Z:已终止的进程、僵尸进程
但还在进程向量数组中占有一个task_struct结构
task_struct{
pid_t pid;
R;

};

< 高优先级
N 低优先级
L 有些页被锁进内存
s 会话组组长
+位于前台的进程组
l 多线程,克隆线程

ctrl + alt + f1 - f6 : 打开字符终端

用户名:user
密码:passworld

主要是为了多用户使用计算机

结束字符终端: alt + f7

top

  • top -p PID : 动态查看进程状态

  • renice -5 PID : 改变进程的NI值(默认0)

  1. 进程相关的函数
  • /fork/exit
    创建进程、退出进程

  • wait/waitpid
    回收进程资源

pid_t fork(void);
/*********************************************
*功能: 创建一个进程
*参数: 无
*返回值:

  •   成功  0 :子进程  > 0 :父进程
    
  •   失败  -1
    

**********************************************/

/*=============================================================================
#
# 创建者: 荆卫
#
# QQ : 1329177433
#
# Last modified: 2021-07-05 15:11
#
# Filename: fork.c
#
# Description(描述): 进程 fork
#
=============================================================================*/
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>

int main(int argc, const char *argv[])
{
    printf("开始创建进程\n");
    pid_t pid;
    pid = fork();
    // 创建进程失败
    if (pid < 0)
    {
        perror("fork");
        return -1;
    }
    else
        if (pid > 0) // 父进程
        {
            while(1){
                printf("i'am father!!!\n");
                sleep(1);
            }
        }
        else
            if (pid == 0) // 子进程
            {
                while(1){
                    printf("i'am son!!!\n");
                    sleep(1);
                }
            }

    return 0;
}

从fork函数往下分为两个进程开始运行。
父进程和子进程执行顺序是随机的。

  • fork函数特性:
    1.子进程创建时,几乎拷贝了父进程全部内容,包括代码段、数据段、堆栈段、文件描述符、虚拟地址空间
    2.同一个父进程创建的子进程都是属于同一个进程组 pkill -9 -g PGID
    3.进程是管理资源的最小单位

  • 思考:
    int main()
    {
    fork();
    fork();
    }
    会创建多少个进程,进程之间的关系是什么样的?
    会创建4个进程,关系如下:

在这里插入图片描述

exit和_exit函数

exit/_exit:exit调用c库,会刷新缓冲区,而_exit不会刷新缓冲区

函数原型
#include <stdlib.h>
void exit(int status);
void fun()
{
    printf("cccccc");
    exit(0); // c库函数,可以刷新缓冲区
}
int main(int argc, const char *argv[])
{
    printf("aaaaa\n");
    fun();
    printf("bbbbbb\n");
    return 0;
}

+ 函数原型
#include <unistd.h>
void _exit(int status);
void fun()
{
    printf("cccccc");
    _exit(0); // 系统调用,不会刷新缓冲区
}
int main(int argc, const char *argv[])
{
    printf("aaaaa\n");
    fun();
    printf("bbbbbb\n");
    return 0;
}

wait函数和waitpid函数

函数原型
pid_t wait(int *status);
/**************************************
* 功能:父进程等待子进程退出,并回收子进程资源(僵尸进程),阻塞
* 参数: @status  获取子进程退出状态
返回值:
	结束返回pid
	没结束返回0 
***************************************/

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

WIFEXITED(status)  判断子进程是否正常结束
int main(int argc, const char *argv[])
{
    pid_t pid;
    pid = fork();
    if (pid < 0)
    {
        perror("fork");
        exit(-1);
    }
    else if (pid > 0)
    {
        printf("father wait....\n");
        // wait函数
        printf("----------------\n");
        int status;
        wait(&status);
        // 获取子进程的返回值
        printf("WEXITSTATUS:%d\n",WEXITSTATUS(status));
        // 获取子进程是否正常结束 1正常 0异常
        printf("wIFEXITED:%d\n",WIFEXITED(status));
        
    }
    else{
        sleep(2);
        printf("son.......\n");
            exit(20);
    }


    return 0;
}

pid_t waitpid(pid_t pid, int *status, int options);

等待指定进程退出
waitpid(pid_t pid,NULL/int *status,0/WNOHANG);
/**************************************
*参数说明:
*	pid=-1 时,等待任何一个子进程退出,没有任何限制,此时waitpid和wait的作用一模一样。
*
*	0:阻塞 / WNOHANG:非阻塞
*
*返回值:
*	结束返回pid
*	没结束返回0
***************************************/

代码示例:

int main(int argc, const char *argv[])
{
    pid_t pid;
    pid = fork();
    if (pid < 0)
    {
        perror("fork");
        exit(-1);
    }
    else if (pid > 0)
    {
        printf("father wait....\n");
        // wait函数
        printf("----------------\n");
        int status;
         // wait(&status);  // 阻塞
         waitpid(-1, NULL, WNOHANG);   // 非阻塞
        // 获取子进程的返回值
        // printf("WEXITSTATUS:%d\n",WEXITSTATUS(status));
        // 获取子进程是否正常结束 1正常 0异常
        // printf("wIFEXITED:%d\n",WIFEXITED(status));
        
    }
    else{
        sleep(2);
        printf("son.......\n");
            exit(20);
    }


    return 0;
}

僵尸进程和孤儿进程

子进程先与父进程退出---父进程未回收资源---子进程会变成僵尸进程
	危害:占用进程号、内存空间、PCB进程控制块等
	解决:wait / waitpid / 信号
	注意:任何进程结束都会变成僵尸进程,只是时间有长有短

父进程先与子进程退出---子进程会变成孤儿进程---被init进程接管(收养)

init进程:系统启动后运行的第一个用户空间进程,pid=1,会定期扫描系统,收养孤儿进程。

注意:孤儿进程一般没什么危害

exec函数簇

  1. 概念:

    函数族提供了一种在进程中启动另一个程序执行的方法。
    它可以根据指定的文件名或目录名找到可执行文件,并用它来取代原调用进程的数据段、代码段和堆栈段,在执行完之后,原调用进程的内容除了进程号外,其他全部被新程序的内容替换了。另外,这里的可执行文件既可以是二进制文件,也可以是Linux下任何可执行脚本文件。

    比如bash用到了exec函数来执行我们的可执行文件。

  2. 在Linux中使用exec函数族主要有以下两种情况

当进程认为自己不能再为系统和用户做出任何贡献时,就可以调用任何exec 函数族让自己重生。

如果一个进程想执行另一个程序,那么它就可以调用fork函数新建一个进程,然后调用任何一个exec函数使子进程重生。

  1. 函数
    #include <unistd.h>
    int execl(const char *path, const char *arg, …);
    int execv(const char *path, char *const argv[]);
    int execlp(const char *file, const char *arg, …);
    int execvp(const char *file, char *const argv[]);
    int execle(const char *path, const char *arg, …, char *const envp[]);
    int execve(const char *path, char *const argv[], char *const envp[]);

返回值:
成功不返回
失败返回 -1 更新 errno

注意:
exec函数的参数表传递方式以函数名的第五位字母来区分:

字母为"l"(list)的表示逐个列举的方式;

字母为"v"(vertor)的表示将所有参数构造成指针数组传递;

以p结尾的函数可以只给出文件名

以"e"(enviromen)结尾的两个函数execle、execve就可以在
envp[]中设置当前进程所使用的环境变量
使用execle和execve可以自己向执行进程传递环境变量,但不会继承Shell进程的环境变量

事实上,这6个函数中真正的系统调用只有execve,其他5个都是库函数,它们最终都会调用execve这个系统调用

守护进程daemon

1.守护进程:
	在linux中与用户交互的界面叫终端,从终端运行起来的程序都依附于这个终端,
	当终端关关闭时,相应的进程都会被关闭,守护进程可以突破这个限制。

2.特点:
	在后台服务的进程
	生存期很长
	守护进程独立于控制终端
	比如:init进程 pid=1 开机运行 关机才结束

3.守护进程创建流程:
	1. 创建子进程,父进程退出
		fork(void);
	2. 在子进程中创建新会话
		setsid(void);
	3. 修改工作目录
		chdir("");
	4. 修改umask (增加安全性)
		umask(); 
	5. 关闭文件描述(回收资源)
		close();
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值