苏嵌实训——day14(下)

简介: 苏嵌实训——day14(下)

3.6 创建进程:fork


头文件:#include 
       #include 
原型:pid_t fork(void);
功能:创建一个子进程
参数:无
返回值:
    成功:返回给父进程子进程的ID号,返回给子进程 0
    失败返回-1;
    fork:失败的条件只有一个,内存不足。

0a2653c851af460fa595bd959398a8f1.png

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>       
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc, char const *argv[])
{
    int fd = open("./1.txt",O_RDWR|O_CREAT,0666);
    if(-1 == fd)
    {
        perror("open");
        return -1;
    }
    pid_t pid = fork();
    if(-1 == pid)
    {
        perror("fork");
        return -1;
    }
    if(pid == 0)
    {
        printf("我是子进程\n");
        write(fd,"hello world",11);
    }
    else if(pid > 0)
    {
        printf("我是父进程,我的子进程ID号为%d\n",pid);
        char buf[123] = {0};
        sleep(1);
        lseek(fd,0,SEEK_SET);
        //close(fd);
        //fd = open("./1.txt",O_RDONLY);
        read(fd,buf,sizeof(buf));
        printf("buf = %s\n",buf);
    }
    return 0;
}


3.7 写时拷贝


fork函数创建子进程时要复制父进程的资源,但是子进程并不一定会用到这些资源,
所以说:
采用一种方式:当创建完子进程后,子进程先共享父进程的资源,如果双方有一方去修改内容,修改之前先复制一份到子进程中,这就叫做写时拷贝技术。


3.8 文件共享


如果fork之间打开了一些文件,获取了一些文件描述符,那么fork之后父进程和子进程公用这些fork之前获取的文件描述符。那么在操作过程中,有可能造成读写指针相互影响。

12345 6789


3.9 获取进程ID接口


3.9.1 getpid


头文件:#include 
       #include 
原型:pid_t getpid(void);
功能:获取自己的进程ID
参数:无
返回值:
    成功:返回自己的进程ID
    无失败


3.9.2 getppid


头文件:#include 
       #include 
原型:pid_t getppid(void);
功能:获取父进程的进程ID
参数:无
返回值:
    成功:返回父进程ID
    无失败


#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
    pid_t pid = fork();
    if(-1 == pid)
    {
        perror("fork");
        return -1;
    }
    if(0 == pid)
    {
        printf("我是子进程\n");
        printf("我是子进程,我的进程号为:%d\n",getpid());
        printf("我是子进程,我的父进程号为:%d\n",getppid());
    }
    else if(pid > 0)
    {
        sleep(1);
        printf("我是父进程\n");
        printf("我是父进程,我的子进程号为:%d\n",pid);
        printf("我是父进程,我的进程号为:%d\n",getpid());
        printf("我是父进程,我的父进程号为:%d\n",getppid());        
    }
    return 0;
}


3.10 三个结束进程的函数


exit

头文件:#include 
原型:void exit(int status);
功能:结束一个进程,先释放缓冲区
参数:status:结束进程时的状态,同return 正常结束用0,非正常结束用-1
返回值:


_exit

头文件:#include 
原型:void _exit(int status);
功能:结束一个进程,不会释放缓冲区,直接结束
参数:status:结束进程时的状态,同return 正常结束用0,非正常结束用-1
返回值:

atexit

头文件:#include 
原型:int atexit(void (*function)(void));
功能:注册一个进程结束后的运行函数
    (当进程结束时会调用function这个函数)
参数:指向返回值为void 类型参数为void的函数指针
返回值:
     成功返回0
     失败返回非0值
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void fun(void)
{
    printf("hello world!\n");
}
int main(int argc, char const *argv[])
{
    //开始注册函数
    atexit(fun);
    sleep(3);
    return 0;
}


3.11 孤儿进程:


父进程优先于子进程结束,子进程失去父亲后,子进程会认1号进程是自己的父进程。那么1号进程负责回收和管理子进程。如果说很多子进程都人1号进程为他的父进程,1号进程的负担会很大,所以我们在编写代码的时候,尽量让父进程回收完子进程资源之后再结束。孤儿进程是没有危害的。


3.12 僵尸进程


子进程优先于父进程结束,子进程会认为该父进程会回收自己的资源,但是父进程没有回收子进程资源的功能,或者说父进程一直在忙于自己的事情,未曾去回收子进程资源,子进程资源就得不到回收,但是子进程任务已经结束了,所以说子进程就变成了僵尸进程,僵尸是有危害。注意:以后在写

fork时都需要回收子进程资源。


#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>       
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
int main(int argc, char const *argv[])
{
    pid_t pid = fork();
    if(-1 == pid)
    {
        perror("fork");
        return -1;
    }
    if(pid == 0)
    {
        exit(0);    //子进程结束,父进程未结束,子进程就变成了僵尸进程
    }
    else if(pid > 0)
    {
        while(1)
        {
            sleep(1);
        }
    }
    return 0;
}


3.12.1 处理僵尸进程


wait

头文件:#include 
原型:pid_t wait(int *stat_loc);
功能:阻塞等待回收任意一个子进程资源
参数:stat_loc:进程结束的状态(一般不考虑进程结束的状态直接写NULL)
返回值:
     成功会返回接受到的进程的ID号
     失败返回-1
注意:如果父进程调用wait来回收资源,那么会阻塞等待,会降低父进程的工作效率


#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>       
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/wait.h>
int main(int argc, char const *argv[])
{
    pid_t pid = fork();
    if(-1 == pid)
    {
        perror("fork");
        return -1;
    }
    if(pid == 0)
    {
        exit(0);    //子进程结束,父进程未结束,子进程就变成了僵尸进程
    }
    else if(pid > 0)
    {
        printf("父进程运行中...\n");
        sleep(5);
        printf("接受成功,接受到子进程的ID号为%d\n",wait(NULL));
        sleep(5);
    }
    return 0;
}


waitpid

头文件:#include 
       #include 
原型:pid_t waitpid(pid_t pid, int *wstatus, int options);
功能:回收子进程资源,可以不阻塞回收也可以指定回收哪一个
参数:
    pid:
    pid == -1:回收任意一个子进程,如果采用阻塞方式与wait一样
    pid == 0: 回收同进程组中的任意一个子进程
    pid < -1;回收同进程组中,等于pid绝对值的子进程
        注意: -:代表同组,对pid进行取绝对值,|pid| == 正数
    wstatus:回收到的子进程结束时返回的状态
    options:操作方式
        0:阻塞回收
        WNOHANG:非阻塞方式回收(如果去回收,没有紫禁城结束,立马返回,如果说已经有子进程结束,
        立马回收)              
返回值:
     成功会返回接受到的进程的ID号
     失败返回-1
注意:需要频繁的去调用函数去查看子进程结束与否


#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>       
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/wait.h>
int main(int argc, char const *argv[])
{
    pid_t pid = fork();
    if(-1 == pid)
    {
        perror("fork");
        return -1;
    }
    if(pid == 0)
    {
        sleep(5);
        exit(0);    //子进程结束,父进程未结束,子进程就变成了僵尸进程
    }
    else if(pid > 0)
    {
        pid_t pid1;
        while(1)
        {
            printf("父进程运行中...\n");
            if(0 < (pid1 = waitpid(-1,NULL,WNOHANG)))
            {
                printf("会受到子进程资源,子进程的ID号为: %d\n",pid1);
            }
            sleep(1);
        }
    }
    return 0;
}


3.13 守护进程


是一种特殊的进程机制,默默地对我的工作进行服务的进程。是一个后台进程。init进程就是一个守护进程。后台进程:只允许向终端写入数据,不允许从终端读取数据。如果一旦对终端输入进行获取,那么就会立即终止后台进程。要脱离终端的管理,脱离回话的管理。守护进程一般用于:服务器,http,tftp,ftp。

附加:ctrl + c给前台所有的进程发一个终止信号

创建守护进程是有固定步骤的:


变成孤儿进程 kill:给进程发一个信号

创建一个新的会话,变成会首进程,setsid

头文件:#include 
       #include 
原型: pid_t setsid(void);
功能:创建一个新的会话,并且创建者称为会话的首进程
参数:无
返回值:
     成功会返回一个新的会话ID号
     失败返回-1
修改默认工作目录文件 chdir
头文件:#include 
原型: int chdir(const char *path);
功能:修改工作路径
参数:路径
返回值:
成功会返回0
失败返回-1
给与最高文件权限 umask
关闭文件描述符 close(0);注意:如果说也不会对终端进行操作,请关闭所有的文件描述符
开始做守护事件,如日志文件的写入。
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>       
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/wait.h>
int main(int argc, char const *argv[])
{
    pid_t pid = fork();
    if(-1 == pid)
    {
        perror("fork");
        return -1;
    }
    if(pid == 0)
    {
        //子进程
        //创建一个会话,自己成为自己的主人
        if(-1 == setsid())
        {
            perror("setsid");
            return -1;
        }
        //修改工作目录
        if(-1 == chdir("/"))
        {
            perror("chdir");
            return -1;
        }
        umask(0);
        close(0);
        close(1);
        close(2);
        FILE * fp = fopen("./1.txt","w+");
        while(1)
        {
            printf("11112\n");
            //写日志文件
            fprintf(fp,"helloworld\n");
            fflush(fp);
            sleep(1);
        }
    }
    //不去管父进程,父进程会自动结束
    return 0;
}


练习:使用两个进程共同去完成复制一个文件,将一个文件拷贝到另一个文件中


#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
    //使用文件IO打开文件,获取文件大小
    int fd = open(argv[1],O_RDONLY);
    if(-1 == fd)
    {
        perror("open");
        return -1;
    }
    int fd1 = open(argv[2],O_WRONLY|O_CREAT|O_TRUNC,0666);
    if(-1 == fd1)
    {
        perror("open1");
        return -1;
    }
    //获取文件大小,利用偏移量
    int len = lseek(fd,0,SEEK_END);
    printf("len = %d\n",len);
    //需要一个从开头,一个从中间开始获取
    len = len / 2;
    pid_t pid = fork();
    if(-1 == pid)
    {
        perror("fork");
        return -1;
    }
    if(pid == 0)
    {
        lseek(fd,len,SEEK_SET);
        lseek(fd1,len,SEEK_SET);
        char buf[123] = {0};  //接收读到的输入,写入数据时使用
        ssize_t ret  =0;
        while((ret = read(fd,buf,123)))
        {
            if(-1 == write(fd1,buf,ret))
            {
                perror("write");
                return -1;
            }
        }
        close(fd);
        close(fd1);
    }
    else if(pid > 0)
    {
        //父进程 ----》从头开始
        lseek(fd,0,SEEK_SET);
        lseek(fd1,0,SEEK_SET);
        char buf[123] = {0};
        ssize_t ret = 0;
        while(len)
        {
            if(len >= 123)
            {
                ret = read(fd,buf,123);
            }
            else
            {
                ret = read(fd,buf,len);
            }
            write(fd1,buf,ret);
            len = len - ret;
        }
    }
    return 0;
}
相关文章
|
4天前
|
Java 关系型数据库 MySQL
|
4天前
|
SQL 前端开发 数据库
|
消息中间件 存储 Linux
苏嵌实训——day16(上)
苏嵌实训——day16(上)
苏嵌实训——day16(上)
|
消息中间件 Linux
苏嵌实训——day16(下)
苏嵌实训——day16(下)
苏嵌实训——day16(下)
|
网络协议 安全 网络安全
苏嵌实训——day18
苏嵌实训——day18
苏嵌实训——day18
|
安全 调度
苏嵌实训——day15(上)
苏嵌实训——day15(上)
|
存储 人工智能 算法
苏嵌实训——day8(上)
苏嵌实训——day8(上)
|
存储 Linux 程序员
苏嵌实训——day13(上)
苏嵌实训——day13(上)
苏嵌实训——day13(上)