io进线程-4进程

进程:程序执行一次的过程


进程和程序的区别:

程序是静态的二进制有序集合,进程是动态的执行过程
程序只有文本和数据,进程除了文本和数据还有系统级别的数据
进程是资源分配的最小单位

进程结构、类型和运行状态

PID唯一地标识了一个进程
Linux中的进程包含三个段:
    数据段:存放的是全局变量、常数以及动态数据分配的数据空间(如malloc函数取得的空间等)
    正文段:存放的是程序中的代码
    堆栈段:存放的是函数的返回地址、函数的参数以及程序中的局部变量
    
Linux中的进程类型:
    交互进程:该类进程是由shell控制和运行的。交互进程既可以在前台运行,也可以在后台运行
    批处理进程:该类进程不属于某个终端,他被提交到一个队列中以便顺序执行。
    守护进程:该类进程在后台运行,他一般在Linux启动时开始执行,系统关闭才结束。
 一般学习的是交互进程,守护进程就是后台平常不显示的进程,批处理不管
 
进程的运行状态:
    运行态:此时进程正在运行或者准备运行
    等待态:此时进程在等待一个事件的发生或某种系统资源
    停止态:此时进程已被终止
    死亡态:这是一个已终止的进程,但还在进程向量数组中占用一个task_struct结构  

1.创建进程(fork、vfork)

	注意fork和vfork的区别
pid_t fork(void);
功能:创建子进程
返回值:成功返回等于或者大于0的数,失败返回-1
    子进程返回0,父进程返回子进程ID号
    
pid_t vfork(void);
功能:创建子进程
返回值:成功返回等于或者大于0的数,失败返回-1
    子进程返回0,父进程返回子进程ID号
注:只能先运行子进程,并且子进程结束了(exit或者exec)才能运行父进程  


  fork  逻辑:
    在运行父进程时,会将所有的父进程的内容拷贝到子进程中,
    到生孩子那一步,pc指向下一个指令,子进程中pc也是如此,故不会嵌套的创建子节点
  vfork 逻辑:(写时拷贝)
    子进程暂时先占用父进程的内存来使用,父进程不能使用该内存,
    只有子进程用exit或者exec退出时,父进程才能使用改内存,子进程调用exec
    即子进程去执行一个新的进程,指向了一个新的内存空间,父进程可以使用原空间了

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

fork-eg:  
int main()
{
    pid_t pid = fork();
    if(pid < 0)
    {
    perror("fork");
    return -1;
    }
    else if(pid == 0)
        printf("I am child\n");    //child
    else
        printf("I am father\n");    //parent
}
vfork-eg:
int main()
{
    pid_t pid = vfork();
    if(pid < 0)
    {
        perror("vf");
        return -1;
    }
    else if(pid == 0)
    {
        printf("I am child\n");    //child
        //return 0;    //return 0;都没用,要用exit函数退出
    }    
    else
        printf("I am father\n");    //parent
}

2.进程退出(exit、_exit)

void exit(int status);
功能:让当前进程退出
status:进程退出时想要传递的值(传递给回收者)

void _exit(int status);
功能:让当前进程退出
status:进程退出时想要传递的值(传递给回收者)

_exit进程退出时不清空IO缓冲区,exit退出时要清空
eg:
int main()
{
    printf("ikun");
    _exit(0);
}------------>不会打印ikun,没有将缓冲区的内容吐出来

3.exec函数族(execl)

exec函数族提供了一种在进程中启动另一个程序的执行的方法。
    它可以根据指定的文件名和目录名找到该文件,并用它取代原调用进程的数据段、代码段和堆栈段。
    在执行完之后,原调用进程的内容除了进程号外,其他全部被替换了

在这里插入图片描述

eg:
int main()
{
    int ret = execl("/bin/ls", "sh", NULL);    
    if(ret < 0)
    {
        perror("execl");
        return -1;
    }
}
gcc后运行./a.out    即在a.out进程里调用ls进程

练习:父进程是打印当前时间,子进程是打印一个菱形

父进程是打印当前时间,子进程是打印一个菱形,可执行程序为app
                                //app是打印一个菱形的程序
#include <time.h>
#include <unistd.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
    time_t tm;        //打印当前时间的代码             
    struct tm *tp;
    time(&tm);
    tp=localtime(&tm);

  
//创建一个子进程
    pid_t pid=vfork();
    if(pid<0){   
        perror("vfork");
        return -1; 
}   
    else if(pid==0){   
        execl("app","./",NULL);  //子进程为运行app,运行完退出  
       //exit(0);
        //直接退出子进程
    }
                    //如果没有exec或exit退出子进程,则无法使用父进程
    else                 //子进程没退出,父进程使用了,则会出现段错误
        printf("%d-%d-%d %d:%d:%d\n",
        tp->tm_year+1900,tp->tm_mon+1,tp->tm_mday,
        tp->tm_hour,tp->tm_min,tp->tm_sec);            //父进程
    return 0;
}

4.回收进程

某个进程退出后会变成僵尸态,如果不回收这个进程,那么会一直占用系统资源
#include <sys/wait.h>

pid_t wait(int *wstatus);
功能:阻塞等待回收子进程
返回值:成功返回回收到的进程ID,失败返回-1
wstatus:保存exit的值以及子进程退出状态是否正常

pid_t waitpid(pid_t pid, int *wstatus, int options);
功能:回收子进程(可以非阻塞,也可以指定要回收谁)
返回值:成功返回进程ID,失败返回-1
pid:用于指定要回收的进程号
    -1:表示任意子进程
    >0:表示子进程号
wstatus:保存进程退出值以及状态
options:阻塞或者非阻塞的方式
    WNOHANG:非阻塞
    0:阻塞

回收子进程由父进程回收,写进父进程里

wstatus组成:2字节空+1字节的退出信息+1字节的退出状态
在这里插入图片描述

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

int main()
{
    pid_t pid = fork();
    if(pid < 0)
    {
        perror("fork");
        return -1;
    }
    else if(pid == 0)
    {
        while(1);
        char *p = NULL;
        *p = 10;        //段错误
        exit(23);
    }else
    {
        int status;
        //wait(&status);
        waitpid(-1, &status, WNOHANG);
        printf("wait success\n");
        printf("child process exit type = %d\n", status & 0x7f);    
                        //退出状态
        printf("child process exit val = %d\n", (status >> 8) & 0xff);
                        //退出信息
    }
}
----->结果  type=11 (段错误号) val=0 
如果正常退出,注释161718行,type = 0,val = 23

5.守护进程

1.创建子进程,并且退出父进程
    pid_t pid=fork();
    if(pid>0)
        exit(0);
2.创建新会话
    pid_t setsid(void);
3.更改工作目录
    int chdir(const char*path);
4.重设文件掩码
    mode_t umask(mode_t mask);
5. 关闭掉从父进程继承过来的所有文件描述符
        循环close
    for(int i=0;i<getdtablesize();i++)
        close(i);

6.练习:将在一个文件里循环打印当前时间的功能,放进守护进程

#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
    pid_t pid=fork();
    if(pid<0)
    {
        perror("fork");
        return -1;
    }
    else if(pid>0)
    {   
        exit(0);                // 1.关闭父进程
    }
    else
    {
        setsid();               // 2.创建新会话 
        chdir("/.tmp");         // 3.更改工作目录
        umask(0);               // 4.重设文件掩码
        for(int i=0;i<getdtablesize();i++)      //关闭父进程继承过来的所有的文件描述符
            close(i);
    }

    int id=open("1.txt",O_WRONLY | O_CREAT,0777);
    time_t tm;
    while(1)
    {
        time(&tm);
        dprintf(id,"%s\n",ctime(&tm));
        sleep(1);

    }

    return 0;
}

参考:https://blog.csdn.net/jianchi88/article/details/6985326

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值