• 10进程控制1


    概念

    程序:一个保存在磁盘中的文件,规定运行时要执行的代码和要完成的动作。

    进程:把程序加载为内存中一段数据,程序的执行过程,具有产生,发展和消亡的过程

    线程:unix的最小调度单位,一个进程可以有多个线程,共享进程ID,共享进程资源。

    父子进程

    进程采用树形结构管理,一个进程启动另一个进程时,被启动的进程就是子进程,原进程就是父进程。

    fork():复制了父进程的数据,堆栈段,进程环境。另外,各个子进程拥有自己的进程环境

    init进程: 所有进程的父进程

    进程状态

    运行态: 分配到了CPU

    就绪态: 等待CPU

    睡眠态: 不能获取CPU,直到某事发生,进入就绪态

    进程属性函数:

    进程标识符 pid:进程的唯一ID号

    pid_t   getpid()       获取进程id

    pid_t   getppid()     获取父进程id

    pid_t   getpgrp()     获取进程组id

    进程用户标识号

    uid_t  getuid()         返回进程的用户ID  (uid: 用户ID)

    uid_t  geteuid()       返回进程的有效用户ID  (该进程有哪个权限)

    gid_t  getgid()          返回进程的组ID

    gid_t  getegid()       返回进程的有效组ID

    chmod u+s uid_demo    运行改程序的人,程序运行过程中,暂时拥有 文件属主 的用户权限

    进程调用

    创建新进程步骤

    1、fork()  创建新进程,实现当前进程的复制

    2.exec() 函数库修改创建的进程,创建进程的目的是为了运行新的程序,通过exec函数族修改子进程后,让子进程执行新的程序

    创建新进程  pid_t fork()

    <sys/types.h>

    <unistd.h>

    在父进程中,返回子进程PID, 子进程种返回0

    例子

    void testFork()

    {

          pid_t pid=getpid();

          pid_t ppid=getppid();

          printf("Proc id:%d ",pid);

          printf("parent Proc:%d ",ppid);

          //创建进程

          pid_t pt=fork();

          //创建失败

          if(pt<0)

          {

               perror("fail fork");

          }

          //子进程

          else if(pt==0)

          {

                printf("this is  a  chinld produce!ID:%d ",getpid());

          }

          //父进程

          else

          {

               printf("this is  a  parent produce!ID:%d ",getpid());

          }

          printf("test")     

    }

    解析:

    父子进程共享资源。当fork()使用后,后面的代码是父子进程都具备的。那么printf("test")将会输出两次。

    fork()调用后,如果创建失败,即pt<0;

    如果成功,首先启动父进程,即else

          {

               printf("this is  a  parent produce!ID:%d ",getpid());

          }

    然后将会往下继续执行,printf("test")。。。。,直到exit()或_exit()或return或exec函数簇才会结束父进程。

    当父进程结束后,才会执行子进程else if(pt==0)

          {

                printf("this is  a  chinld produce!ID:%d ",getpid());

          }

    此时还会往下继续执行,因此资源是共享的。所以还会输出printf("test");

    因此fork会使子进程复制父进程所有资源,有点多余,可使用vfork()。

    vfork()

    有时候,创建子进程为了运行其他程序,不需要复制父进程的资源。

    vfork  共享父子进程资源。调用后,父进程阻塞,直到子进程调用exec()  或 _exit();

    注意:不能使用 return 返回,或者 exit() 退出!

    区别:

    exit   _exit  return

    _exit() : 直接退出,不关闭文件,不清理I/O

    exit():   终止进程前,关闭文件,清理I/O

    return:释放句柄,变量,弹出函数调用桟,回到上一级函数

    exit 和 return 退出前,会调用 atexit() 注册的回调函数

    c++中, return 会调用局部对象的析构函数,exit, _exit不会

    建议使用fork(),

    写时拷贝机制 (copy-on-write  COW)

    fork()  函数需要复制父进程的资源 (变量)

    刚开始创建时候,并不复制相关资源

    如果只是读取值的话,访问相同的物理内存

    一旦进行写操作,则先复制资源,然后再写

    例子:

    string str1="hello world";

    string str2=str1;

    printf("str1 adrr :%p",str1.c_str());

    printf("str2 adrr :%p",str2.c_str());

    此时发现str1,str2的地址是一样的。即使str1,str2是两个不同的变量。

    修改:

    str1[0]='H';

    printf("str1 adrr :%p",str1.c_str());

    printf("str2 adrr :%p",str2.c_str());

    这时str1,str2的地址就会变成不一样了。

    这就是写时拷贝机制,只有当被修改的时候才进程拷贝。

    函数簇

    exec 函数簇。执行另一个程序作为原进程的子进程

    int execl(const char* path, const char *arg, ...)

    int execlp(const char* file, const char *arg, ...)

    int execle(const char* path, const char *arg, ..., char *const envp[]);

    int execv(const char* path, const char **argv)

    int execvp(const char* file, const char **argv)

    int execve(const char* path, const char **argv, char *const envp[])

    解析:

    execl:入参是可变参数列表(一级指针), 以 NULL 结束

    execv:  入参是参数数组(二级指针)

    p 后缀:搜索环境变量 PATH,寻找可执行文件

    e 后缀:显式传递环境变量

    注意:exec函数调用成功后,系统会把新程序的地址空间替代调用进程的地址空间,并装入新程序内容。

    例子:

    void testExec()

    {

          pid_t pid=fork();

          if(pid==0)

          {

               printf("this is a child:%d",getpid());

               //第一参数需要完整的路径

    execl("/bin/ls","ls",”-l”,NULL);  

    //或者以下两句替代

    //char *buf[128]={“ls”,”-ls”};

    //execl("/bin/ls",buf ,NULL);    

               //只需要名称

    execlp("ls","ls",”-l”,NULL); 

               printf("end");

          }

          else

          {

               printf("this is a parent:%d",getpid());

          }

    }

    解析:

    exec函数簇的主要作用是跳转。

    立即跳转到输出ls的列表。

    execl("/bin/ls","ls",”-l”,NULL);    

    并且往下的代码将不会执行。

    printf("end");不会输出。

    当然,也可以跳转到自拟其他程序。

    execlp("./mian2","./main2",NULL);   

    环境变量

    方式一:

    env 命令: 显示当前用户所有环境变量

    export 命令: 修改或者显示当前用户所有环境变量

    expote |grep:查询指定环境变量

    unset命令 :  删除某个环境变量  [  unset  AAA  ]

    在终端直接使用命令:

    设置环境变量:expoet  AA=”AAAAA”;

    修改环境变量:expoet  AA=”BBBBBB”;

    查询AA:expote |grep AA

    删除AA:unset AA

    方式二:

    extern char **environ

    UNIX系统中环境变量指针数组

    例子:

    //遍历出所有的环境变量

    extern char ** environ;

    char **p=environ;

          while(*p)

          {

               printf("%s ",*p);

               ++p;

          }

    方式三:

    头文件 <stdlib.h>

    char* getenv(char *name)   

    获取环境变量值,无则返回NULL

    int    putenv(const char *expr) 

    设置/修改环境变量, name=value, 

    只传name则删除环境变量

    例子:

          //设置环境变量

    putenv("ABC=hello world");

          pid_t pid=fork();

          if(pid==0)

          {

               printf("this is child,ABC=%s ",getenv("ABC"));

          }

          else

          {

               printf("this is parent,ABC=%s ",getenv("ABC"));

               usleep(500);

          }

          //删除环境变量

          putenv("ABC");

          printf("ABC=%s ",getenv("ABC"));

  • 相关阅读:
    【WinHec启示录】透过Windows 10技术布局,谈微软王者归来
    管中窥豹,物联网之我见
    微软借力.NET开源跨平台支持,布局物联网平台开发
    面向对象开发方式的开源硬件--.NET Gadgeteer
    【物联网智能网关-18】多通道远程安全升级
    vim 多文件编辑【超实用】
    debian下samba配置
    制作根文件系统的经验
    c语言: inline(gcc)
    Cramfs、JFFS2、YAFFS2的全面对比
  • 原文地址:https://www.cnblogs.com/gd-luojialin/p/9215971.html
Copyright © 2020-2023  润新知