IO进程—进程

目录

概念

特点

进程段

进程分类

进程状态

进程状态切换图

函数接口

创建子进程

fork

回收进程资源

wait

waitpid

结束进程

exit

_exit

获取进程号

getpid

getppid

守护进程 

特点

步骤


概念

程序:编译好的可执行文件,存放在磁盘上的指令和数据的有序集合,是静态的,没有任何执行的概念。

进程:独立的可调度的任务,是执行一个程序所分配资源的总称,是程序的一次执行过程,是动态的,包括创建、调度、执行和消亡。

特点

  1. 系统会为每一个进程分配从0-4G的虚拟空间,0-3G(用户空间)是每个进程所独有的,3-4G(内核空间)是所有进程共有的。
  2. CPU调度进程时会给进程分配时间片(几毫秒~几十毫秒),当时间片用完后,CPU再进行其他进程的调度,实现进程的轮转,从而实现多任务的操作。

进程段

Linux中的进程包含三个段:

  1. “数据段”存放的是全局变量、常数以及动态数据分配的数据空间(如malloc()函数取得的空间)等
  2. “正文段”存放的是程序中的代码
  3. “堆栈区”存放的是函数的返回地址、函数的参数以及程序中的局部变量

进程分类

  1. 交互进程:该类进程是由shell控制和运行的。交互进程既可以在前台运行,也可以在后台运行。该类进程经常与用户进行交互,需要等待用户的输入,当接收到用户的输入后,该类进程会立刻响应,典型的交互式进程有:shell命令进程、文本编辑等。
  2. 批处理文件:该类进程不属于某个终端,它被提交到一个队列中以便执行。
  3. 守护进程:该类进程在后台运行。它一般在Linux启动时开始执行,系统关闭时才结束。

进程状态

运行态(TASK_RUNNING):R

        指正在被CPU运行或者就绪的状态。这样的进程被称为running进程。

睡眠态(等待态):

        可中断睡眠态(TASK_INTERRUPPTIBLE)S:

                处于等待状态中的进程,一旦被该进程等待的资源被释放,那么该进程就会进入运行状态。

        不可中断睡眠态(TASK_UNINTERRUPTIBLE)D:

                该进程状态只能用wake_up() 函数才能唤醒。

暂停态(TASK_STOPPED)T:(又叫停止态)

        当进程收到信号SIGSTOP、SIGSTP、SIGTTIN或SIGTTOU时,就会进入暂停状态。可向其发送SIGCONT信号让进程转换到可运行状态。

死亡态:X

        进程结束。

僵尸态(TASK_ZOMBIE):Z

        当进程已经终止运行,但还占用系统资源,要避免僵尸态的产生。

进程状态切换图

进程创建后,进程进入就绪状态,当CPU调度到此进程时进入运行态,当时间片用完时,此进程就会进入就绪态,如果此进程正在执行一些IO操作(阻塞操作)会进入阻塞态,完成IO操作(阻塞结束)后又可进入就绪态,等待CPU的调度,当进程运行结束即进入结束态。

函数接口

创建子进程

fork

格式:pid_t fork(void);
功能:创建子进程
返回值:
    成功:在父进程中:返回子进程的进程号 >0
          在子进程中:返回值为0
    失败:-1并设置errno
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

int main(int argc, char const *argv[])
{
    int a = 0;
    printf("hello\n");
    pid_t pid = fork();
    printf("world\n");
    if (pid < 0)
    {
        perror("fork err");
        return -1;
    }
    else if (pid == 0)
    {
        a = 10;
        printf("in clild a:%d\n", a); //10
        //while(1);
        sleep(1);
    }
    else
    {
        printf("in parent a:%d\n", a); //0
        wait(NULL);//阻塞回收子进程资源
        printf("gggggg\n");
    }

    return 0;
}
/*
1.fork之前的代码被复制,但是不会在重新再执行一遍
fork之后代码被复制,并且会分别被执行一遍 */

函数特点:

  1. 子进程几乎拷贝了父进程的全部内容。包括代码、数据、系统数据段中的pc值、栈中的数据、父进程中打开的文件等;但是他们的PID、PPID是不一样的。
  2. 父子进程有独立的地址空间,互不影响;当在相应的进程中改变全局变量、静态变量,都互相不影响。
  3. 若父进程先结束,,子进程成为孤儿进程,被init进程收养,子进程变成后台进程。
  4. 若子进程先结束,父进程如果没有及时回收,子进程变成僵尸进程(要避免僵尸进程)

总结:

  1. fork之前的代码会被复制,但是不会再执行一次,fork之后的代码会被复制,并且分别执行一遍
  2. fork两个进程相互独立,子进程几乎拷贝了父进程的所有代码,但内存空间独立
  3. fork之前打开的文件,fork之后拿到相同的文件描述符,操作同一个文件指针

回收进程资源

wait

格式:pid_t wait(int *status);
功能:回收子进程资源(阻塞)
参数:status:子进程退出状态,不接受子进程状态设为NULL
返回值:
    成功:回收的子进程的进程号
    失败:-1

waitpid

pid_t waitpid(pid_t pid, int *status, int options);
功能:回收子进程资源
参数:
    pid:>0    指定子进程进程号
         =-1   任意子进程
         =0    等待其组ID等于调用进程的组ID的任一子进程
         <-1   等待其组ID等于pid的绝对值的任一子进程
    status:子进程退出状态
    options:0:阻塞
        	WNOHANG:非阻塞
返回值:
    正常:结束的子进程的进程号
          当使用选项WNOHANG且没有子进程结束时:0
    出错:-1
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

int main(int argc, char const *argv[])
{
    //创建父子进程
    pid_t pid = fork();
    if (pid < 0)
    {
        perror("fork err");
        return -1;
    }
    else if (pid == 0)
    {
        printf("hello\n");
        sleep(1);
    }
    else
    {
        printf("world\n");
        //wait(NULL);//阻塞回收任意子进程的资源
        //waitpid(-1,NULL,0);//等同于wait(NULL)
        //waitpid(-1,NULL,WNOHANG);//不阻塞
        waitpid(pid, NULL, 0);//阻塞回收指定子进程
        printf("ni hao\n");
    }

    return 0;
}

结束进程

exit

格式:void exit(int status);
功能:结束进程,刷新缓存
参数:退出的状态
不返回
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>

void fun(int a, int b)
{
    printf("china\n");
    printf("a+b=%d", a + b);
    //_exit(0);//不刷新缓冲不会打印a+b,不打印hhhhh
    exit(0);//刷新缓冲区,不打印hhhhh
    return;
}

int main(int argc, char const *argv[])
{
    //创建父子进程
    pid_t pid = fork();
    if (pid < 0)
    {
        perror("fork err");
        return -1;
    }
    else if (pid == 0)
    {
        printf("hello\n");
        fun(3, 4);
        sleep(1);
        printf("hahahahaha\n");
    }
    else
    {
        printf("world\n");
        waitpid(pid, NULL, 0); //阻塞回收指定子进程
        printf("ni hao\n");
    }

    return 0;
}

_exit

void _exit(int status);
功能:结束进程,不刷新缓存
参数:status是一个整型的参数,可以利用这个参数传递进程结束时的状态。
返回值:
    通常0表示正常结束;
    其他的数值表示出现了错误,进程非正常结束

return 和 exit 的区别:

exit:是一个函数,不管在子函数还是主函数,都可以结束进程(进程的退出)

return:是关键字,当子函数中有return时返回到函数调用位置,并不结束进程(函数的退出)

获取进程号

getpid

格式:pid_t getpid(void);
功能:获取当前进程的进程号

getppid

pid_t getppid(void);
功能:获取当前进程的父进程号
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int main(int argc, char const *argv[])
{
    pid_t pid = fork();
    if (pid < 0)
    {
        perror("fork err");
        return -1;
    }
    else if (pid == 0) //子进程
    {
        printf("inchild  pid:%d,ppid:%d\n", \
        getpid(),getppid());
    }
    else //父进程
    {
        printf("inparent pid:%d cpid:%d\n",\
        getpid(),pid);
    }

    return 0;
}

 

守护进程 

特点

守护进程是后台进程;生命周期比较长,从系统启动时开启,系统关闭时结束;它是脱离控制终端且周期执行的进程。

步骤

1. 创建子进程,父进程退出

        让子进程变成孤儿进程,成为后台进程;fork()

2. 在子进程中创建新会话

        让子进程成为会话组组长,为了让子进程完全脱离终端;setsid()

3. 改变进程运行路径为根目录

        原因是进程运行的路径不能被删除或卸载;chdir("/")

4. 重设文件权限掩码

        目的:增大进程创建文件时权限,提高灵活性;umask(0)

5. 关闭文件描述符

        将不需要的文件关闭;close()

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>

int main(int argc, char const *argv[])
{
    //创建子进程,父进程退出
    //让子进程变成孤儿进程,成为后台进程;
    pid_t pid = fork();
    if (pid < 0)
    {
        perror("fork err");
        return -1;
    }
    else if (pid == 0)
    {
        //2. 在子进程中创建新会话
        //让子进程成为会话组组长,为了让子进程完全脱离终端;
        setsid();
        //3.改变进程运行路径为根目录
        //原因进程运行的路径不能被删除或卸载;
        chdir("/");
        //4.重设文件权限掩码
        umask(0);
        //5.关闭文件描述符
        for (int i = 0; i < 3; i++)
            close(i);
        while (1)
            ; //让子进程不结束
    }
    else
        exit(0);

    return 0;
}

练习1:

创建一个守护进程,循环间隔1s向文件写入一串“hello”字符

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main(int argc, char const *argv[])
{
    //1.创建父子进程,父进程退出
    pid_t pid = fork();
    if (pid < 0)
    {
        perror("fork err");
        return -1;
    }
    else if (pid == 0)
    {
        //2.在子进程中创建新会话,让子进程成为会话组组长
        setsid();
        //3.改变运行路径为根目录
        chdir("/");
        //4.重设文件掩码
        umask(0);
        //5.关闭文件描述符
        for (int i = 0; i < 2; i++)
            close(i);
        //打开文件
        int fd = open("/tmp/info.log", O_RDWR | O_CREAT | O_TRUNC, 0666);
        if (fd < 0)
        {
            perror("open err");
            return -1;
        }
        while (1)
        {
            write(fd, "hello\n", 6);
            sleep(1);
        }
    }
    else
    {
        exit(0);
    }

    return 0;
}

练习2:

通过父子进程完成对文件的拷贝(cp),父进程要求从文件开始到文件的一半开始拷贝,子进程拷贝从文件的一半到文件的末尾。要求用文件IO命令行传参 

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/wait.h>

int main(int argc, char const *argv[])
{
    ssize_t s = 0;
    char buf[32] = {0};
    //1.打开文件
    int src = open(argv[1], O_RDONLY);
    if (src < 0)
    {
        perror("src err");
        return -1;
    }
    int dest = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0666);
    if (dest < 0)
    {
        perror("dest err");
        return -1;
    }
    off_t off = lseek(src, 0, SEEK_END) / 2;
    //2.创建父子进程
    pid_t pid = fork();
    if (pid < 0)
    {
        perror("fork err");
        return -1;
    }
    else if (pid == 0)
    {
        //子进程复制后半部分
        //移动文件指针
        lseek(src, off, SEEK_SET);
        lseek(dest, off, SEEK_SET);
        //循环读写
        while ((s = read(src, buf, 32)) != 0)
            write(dest, buf, s);
    }
    else
    {
        //父进程复制前半部分
        wait(NULL);
        //移动指针
        lseek(src, 0, SEEK_SET);
        lseek(dest, 0, SEEK_SET);
        //循环读写
        while (off > 0)
        {
            if (off > 32)
                s = read(src, buf, 32);
            else
                s = read(src, buf, off);
            write(dest, buf, s);
            off -= s; //剩下要读的字符个数
        }
    }
    close(src);
    close(dest);

    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值