• linux进程


    atexit函数多次注册的时候先注册的后执行,就像压栈。
    用_exit终止进程是并不执行atexit注册的进程终止处理函数。
    进程中的虚拟地址空间:
    进程隔离;多进程同时运行
    什么进程:
    1、进程是动态过程,不是静态实物
    进程就是程序一次运行过程,所有进程都有生命周期,
    2、进程控制块PCB(process control block),就是内核中用来管理进程的数据结构。
    PID(process ID):唯一的表示进程
    API函数:getpid、getppid、getuid、geteuid、getgid、getegid
    #include <sys/types.h>
    #include <unistd.h>
    pid_t getpid(void);
    pid_t getppid(void);
    多进程调度:宏观并行,微观串行
    创建子进程:fork
    #include <unistd.h>
    pid_t fork(void);
    fork函数调用一次会返回两次,返回值等于0的就是我们就是的子进程,而返回大于0的就是父进程。
    测试代码:
    #include <stdio.h>
    #include <sys/types.h>
    #include <unistd.h>
    int main(void)
    {
    pid_t p1 = -1;
    p1 = fork();//会返回两次
    if(p1 == 0)
    {
    printf(" 子进程pid = %d ",getpid());//一定子进程
    printf("在子进程里面,父进程的ID = %d ",getppid());
    }
    if(p1 > 0)
    {
    printf(" 父进程pid = %d ",getpid());//一定是父进程
    printf("在父进程里面 p1 = %d ",p1);
    }
    if(p1 < 0)
    {
    //fork出错
    }
    //printf("Fenton 子进程和父进程都运行了这段程序pid = %d ",getpid());
    return 0;
    }
    这里会出现在子进程中用getppid函数会出问题,因为在子进程运行的时候父进程已经死掉了,所有获取的有问题。
    父子进程对文件的操作:在子进程和父进程里面对同一文件操作,对应fd的文件指针是关联的向O_APPEND之后的的样子。有时候看到的只有一个,有点想分别写,原因是程序台简短了;父进程和子进程独自打开一个文件,O_APPEND可以把父子进程独自打开的fd文件指针关联起来实现分别写。测试代码如下:
    #include <stdio.h>
    #include <sys/types.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    int main(void)
    {
    int fd1 = -1;
    int fd2 = -1;
    pid_t pid = -1;
    pid = fork();
    if(pid == 0)
    {
    fd1 = open("1.txt",O_RDWR | O_APPEND);
    if(fd1 < 0)
    {
    perror("open");
    return 0;
    }
    printf("在子进程里面,父进程的ID = %d ",getppid());
    write(fd1,"Fenton",5);
    sleep(1);
    }
    if(pid > 0)
    {
    fd1 = open("1.txt",O_RDWR|O_APPEND);
    if(fd1 < 0)
    {
    perror("open");
    return 0;
    }
    printf("在父进程里面 pid = %d ",pid);
    write(fd1,"aaaaa",5);
    sleep(1);
    }
    if(pid < 0)
    {
    //fork出错
    }
    close(fd1);
    //close(fd2);
    return 0;
    }
    父进程在没有fork之前自己多事情对子进程没有影响,但是父进程fork之后在自己的if做的事情对子进程就没有影响了,本质原因就是在fork内部复制了父进程的PCB生成了一个新的子进程,并且fork返回是子进程已经完全和父进程脱离并且独立被OS调度执行。
    进程由fork开始,终究进程也会消亡。linux中malloc和open之后没有free和close,在进程结束之后会自动回收。但是回收的时候没有回收干净,只是回收工作的时候的内存和IO,但是没有会后进程本身的内存(8kb,也就是task_struct和栈内存),所以每一个进程都需要一个帮他回收的东西,这个东西就是父进程。
    僵尸进程:
    1、子进程先于父进程结束,子进程结束,父进程并不一定理解帮子进程“收尸”,在这段之间,就成为僵尸进程。
    2、子进程除task_struct和栈外其余内存空间已清理
    3、父进程可以使用wait或waitpid以显式回收子进程的剩余待回收内存资源并且获取子进程退出状态。
    4、父进程也可以不适用wait或者waitpid回收子进程,此时父进程结束时一样回收进程(为了防止父进程忘记显式调用wait来收回内存而造成的内存泄漏)
    孤儿进程:
    1、父进程先于子进程结束,就变成孤儿进程。
    2、linux系统规定,所有孤儿进程都自动成为一个特殊进程(进程1,也就是init进程)的子进程。
    wait函数:
    #include <sys/types.h>
    #include <sys/wait.h>
    pid_t wait(int *status);
    WIFEXITED用来判断子程序是否正常退出(return exit _exit)
    WIFSIGNALED用来判断子进程是否非正常终止(被信号所终止)
    WEXITSTATUS用来得到子程序正常终止的返回值(返回无符号数)
    waitpid函数和wait一样,重点就是差别。
    waitpid就是等待回收指定pid的进程,可以工作在阻塞和非阻塞两种模式。
    ret = waitpid(-1, &status, 0);
    -1表示不等待某个特定的PID,0表示默认的方式(阻塞)。ret表示返回值
    ret = waitpid(pid, &status, 0);阻塞等待回收后PID为pid的这个子进程。
    ret = waitpid(pid, &status, 1);非阻塞等待回收后PID为pid的这个子进程。只要一调用,就马上返回。只在返回值里面标志。
    exec族函数:可以直接把一个编译好的可执行程序直接加载运行
    所以父子程序都是单独编写编译。
    #include <unistd.h>
    extern char **environ;
    int execl(const char *path, const char *arg, ...
    /* (char *) NULL */);
    int execlp(const char *file, const char *arg, ...
    /* (char *) NULL */);
    int execle(const char *path, const char *arg, ...
    /*, (char *) NULL, char * const envp[] */);
    int execv(const char *path, char *const argv[]);
    int execvp(const char *file, char *const argv[]);
    int execvpe(const char *file, char *const argv[],
    char *const envp[]);
    execl和execv函数值最基本的exec族函数,区别是传参的格式不同,execl是把参数列表(必须以NULL结束)一次排列而成,l就是list的缩写。execv是事先把参数列表放在一个字符串数组里面,然后再传参。
    execlp和execvp这两个在上面基础上加了p,较上面上个说,上面两个执行需要指定可执行的全路径(execl没有找到path这个文件则会报错),而加了p可以是file(也可以也是path,只是兼容了file,加了p的首先去找file,找到则执行,如果没找到则回去环境变量PATH所指的目录下去找,找到执行,找不到就报错)。
    execle和execvpe,这两个函数比较上面,函数原型中的参数列表也多了一个字符串数组envp形参,e就是envirment,区别就是执行可执行程序,会多传一个环境变量给执行程序。
    测试代码如下:#include <stdio.h>
    #include <sys/types.h>
    #include <unistd.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <sys/wait.h>
    int main(void)
    {
    pid_t pid = -1;
    pid_t ret = -1;
    int stat = -1;
    pid = fork();
    if(pid == 0)
    {
    //char *const arg[] = {"ls","-a","-l",NULL};
    //execv("/bin/ls",arg);//执行ls命令
    execl("./hello.o","aaa",NULL);
    return 0;
    }
    else if(pid > 0)
    {
    printf("在父进程里面,子进程的pid = %d ",pid);
    }
    else if(pid < 0)
    {
    //fork出错
    return -1;
    }
    return 0;
    }
    execlp和execvp,不加了p的需要全路径+文件名字,找不到就报错,加了p就到PATH指定的路径下面去找文件。优先环境变量PATH中去找,找不到再来当前文件下面找。
    execle和execvpe:main函数的原型不止是int main(int argc, char **argv),还可以是int main(int argc, char **argv, char **env),env就是就是给main函数传递的环境变量,第三个是字符串数组,内容是字符串数组,若是没有传递第三个参数,则会从父进程继承了一份环境变量(OS默认)。
    进程的状态:就绪态、运行态、僵尸态、等待态、停止态。
    system函数 = fork+exec
    原子操作:整个操作一旦来时就会不被打断的执行完,原子操作的好处就是不会被打断(不会引来竞争状态);坏处是自己单独连续占中CPU时间太长,影响实时性,应该尽量避免不必要的原子操作,就算不得不使用原子操作,也应该尽量减少原子操作的时间。
    进程之间的关系:
    1、无关系
    2、父子进程
    3、进程组
    4、会话
    慢慢看守护进程
    kill命令 用法 kill -信号编码 进程ID
    kill -9 xxx
    1、daemon就是守护进程的意思,简称d(进程名字后面带d的基本就是守护进程)
    2、长期运行
    3、与控制台脱离,不依赖于任何终端
    4、服务器,服务器程序一般都实现为守护进程。
    syslogd,系统日志守护进程,提供syslog功能
    cron,用来实现时间管理的,在linux定时执行程序的功能就要用到。
    测试代码如下:
    #include <stdio.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <stdlib.h>
    void cread_daemon(void);
    int main(void)
    {
    cread_daemon();
    while(1)
    {
    printf("Fenton ");
    sleep(1);
    }
    return 0;
    }
    //这个函数的作用就是把改进守护进程
    void cread_daemon(void)
    {
    pid_t pid = -1;
    pid = fork();
    if(pid <0)
    {
    perror("fork");
    exit(-1);
    }
    else if(pid > 0)
    {
    exit(0);//父进程直接退出
    }
    //这下面就是子进程的了
    pid = setsid();
    if(pid <0)
    {
    perror("setsid");
    exit(-1);
    }
    chdir("/");//设置当前进程工作目录为根目录
    umask(0);//设置为0,确保进程对文件有最大的操作权限
    //关闭所有文件描述符,这里需要动态获取,获取当前系统允许对打的文件描述虎
    for(int i = 0; i < sysconf(_SC_OPEN_MAX); i++)
    {
    close(i);
    }
    //把012定位到/dev/null
    open("/dev/null",O_RDWR);
    open("/dev/null",O_RDWR);
    open("/dev/null",O_RDWR);
    }
    使用syslog来记录文件信息
    测试代码:
    #include <stdio.h>
    #include <stdlib.h>
    #include <syslog.h>
    int main(void)
    {
    openlog("a.out", LOG_PID | LOG_CONS, LOG_USER);
    syslog(LOG_INFO, "this is my loginfo ");
    closelog();
    exit(0);
    }
    在守护进程中有一个syslogd,这个进程就是负责日志文件的写入和维护。
    怎么能让程序不能被多次运行,在守护进程中,是长时间运行的,不退出。因此./a.out
    多次运行就是有多个进程,这并不是我们想要的。有时候我们希望程序是单利运行,意思就是当我们运行的程序没有运行过,就运行,若是之前已经有一个程序的进程在运行,本次运行直接退出(提示程序在运行)。最常用的做法就是在执行的开始之初判断特定的文件是否存在,若存在则标明进程已经在运行了。
    这个文件特定的文件要古怪一点,确保不会凑巧真的在电脑中存在的。
    接下来浅谈一下进程间的通信
    同一个进程在一个地址空间之中,同一个进程的不同模块之间很多时候都是通过全局变量,也可以通过函数形参传递;
    不同进程处于不同的地址空间,因此要互相通信很难。99%不需要考虑进程间通行,因为大部分程序都是单进程的(可以通过多线程的方式解决并发问题),复杂大型的程序,因此设计的需要就被设计成多进程通信,常见的GUI程序,服务器等
    结论:IPC技术在一般的中小型程序要办用不到,,在大型程序中才会用到。
    linux提供的多种进程通信机制:
    1、无名管道和有名管道
    2、SystemV
    3、Socke域套字
    4、信号
    管道:管道是单向通信的,是半双工的。只能在父子间通行
    朱有鹏老师视频学习笔记
  • 相关阅读:
    nyoj118 修路工程 次小生成树
    nyoj99 单词连接 欧拉回路
    NYOJ289 苹果 典型背包
    nyoj 139 牌数 康拓展开
    poj1423 NYOJ_69 数字长度 斯特林公式 对数应用
    NYOJ311 完全背包 对照苹果
    sort 函数的应用
    NYOJ120 校园网络 强连接
    nyoj219 计算日期 吉姆拉森公式
    把SmartQ5系统装在SD卡上
  • 原文地址:https://www.cnblogs.com/lifeng40433/p/9510820.html
Copyright © 2020-2023  润新知