zoukankan      html  css  js  c++  java
  • 【linux高级程序设计】(第八章)进程管理与程序开发 1

    概念:

    进程号(PID):系统标识一个进程的正整数

    父进程号(PPID):父进程的进程号

    进程组号(PGID):进程组是一个或多个进程的集合。

    会话:一个或多个进程组的集合。

    控制终端的含义我不是很理解?

    函数/命令:

    ps aux:查看当前系统所有进程的基本属性

    getpid():获取当前进程PID

    getppid():获取父进程的PID

    __pid_t getpgid (__pid_t __pid):获取指定进程的进程组号,输入为0表示获取当前进程组号

    pid_t getpgrp(void):获取当前进程进程组号

    int setpgid(pid_t pid, pid_t pgid):将某个进程加入到某个进程组

              参数1:待修改进程组号的进程PID,0表示当前进程

              参数2:新的进程组号,0表示进程组号与参数1相同

    __pid_t getsid (__pid_t __pid):获取指定进程的会话号,0表示调用进程

    __pid_t setsid (void):创建新会话。如果调用进程是一个进程组组长,函数返回错误。

    pid_t tcgetpgrp(int filedes):filedes是打开的终端,获取与该终端相关联的前台进程组的进程组号

    pid_t tcsetpgrp(int filedes, pid_t pgrpid):将控制终端filedes的前台进程组ID设置为pgrpid。pgrpid应该是同一会话中的进程组ID

    pid_t tcgetsid(int filedes):获取控制终端会话首进程的会话ID

    #include<sdtio.h>
    #include<unistd.h>
    #include<fcntl.h>
    #include<stdlib.h>
    
    int main()
    {
        int fd;
        pid_t pid;
        pid = fork();
        if(pid == -1)
            perror("fork");
        else if(pid > 0)
        {
            wait(NULL);
            exit(EXIT_FAILURE);
        }
        else
        {
            if((fd = open("/dev/tty1",O_RDWR)) == -1)        //注意 tty1是在ps 查看进程中能够找到的 换成ps中找不到的会打不开
                perror("open");
            
            printf("pid=%d,ppid=%d
    ", getpid(), getppid());                 //获取进程号 父进程号
            printf("sid=%d,tcgetsid=%d
    ",getsid(getpid()), tcgetsid(fd));   //获取会话号 终端会话号
            printf("tcgetpgrp=%d
    ", tcgetpgrp(fd));                         //获取前台进程进程组号
            printf("pigd=%d
    ",getpgid(getpid()));                           //获取当前进程进程组id
        }
    }

    可以看到,进程组号,终端的前台进程组号都是当前进程的父进程号。

    当前进程的会话号和终端的会话号相同。

    进程用户属性

    真实用户号(RUID)创建该进程的用户的UID

    真实用户组号(RGID):RUID的用户所在的组号

    有效用户号(EUID):多数情况下EUID与UID相同,如果可执行文件的setuid位有效,则其他用户运行该程序时EUID和UID不同,EUID为该文件的拥有者,UID为文件的使用者。表示实际拥有的权限

    有效用户组号(EGID):有效用户所在的用户组号

    setuid位:当该位有效时,在执行该程序时,该执行用户所拥有的权限等同于文件的拥有者的权限。执行用户可以通过该权限处理其他的文件。

               设置setuid位有效  chmod u+s 可执行文件名

               设置setuid位无效  chmod u-s 可执行文件名

               当设置成有效时,可执行位上显示s,否则显示x.

                         

        举例:修改密码时,需要修改/etc/passwd 文件,该文件权限如下。root用户可以读写,但普通用户只能读。

                         

               普通用户通过执行 /usr/bin/passwd 文件来修改密码。其权限如下,setuid位是有效的

                        

               这样,普通用户执行/usr/bin/passwd时,就有了相当于root的权限,而root可以写/etc/passwd,这样普通用户就可以修改密码了。如果把setuid位设为无效,则普通用户就无法修改密码。

               

    getresuid() :分别获取真实的,有效的和保存过的用户标识号

    #include<stdio.h>
    #include<stdlib.h>
    #include<unistd.h>
    int main()
    {
        int uid, euid, suid;
        getresuid(&uid, &euid, &suid);
        printf("uid = %d, euid = %d, suid = %d
    ", uid, euid, suid);
        return 0;
    }

    可以看到,当root执行该文件时,使用id为0(root使用),有效用户id为0(有相当于root的权限),保存过的用户标识号为0(root)

                  当game执行该文件时,使用id为1000(game使用,这也是真实用户号),有效用户id为0(有相当于root的权限),保存过的用户标识号为0(root)

    __uid_t getuid (void):获取进程真实用户号

    __uid_t geteuid (void):获取有效用户号

    __uid_t getgid(void):获取真实用户组号

    __uid_t getegid(void):获取有效进程用户组号

    linux命令:

    id :查看当前用户的id信息

    whoami :显示当前用户

    进程管理及控制

    1.创建进程

    __pid_t fork (void)

            成功,则父进程返回子进程的pid,子进程返回0. 子进程会复制父进程的几乎所有的信息。子进程在fork返回的位置继续向下执行。

            失败,返回-1.

    i.子进程对父进程文件流缓冲区的处理

    #include<stdio.h>
    #include<unistd.h>
    #include<stdlib.h>
    
    int main(int argc, char *argv[])
    {
        pid_t pid;
        printf("before fork, have enter
    ");  //有回车,先输出
        printf("before fork, no enter: pid = %d	", getpid());  //没有回车,缓存到输出流缓冲区
        pid = fork();
        if(pid == 0)
            printf("
    child, after fork: pid = %d
    ", getpid());
        else
            printf("
    parent, after fork: pid = %d
    ", getpid());
    }

    注意:没有回车时,内容先保存在了缓冲区里!

    看到,子进程中复制了父进程缓冲区的内容!

    ii.子进程对父进程打开的文件描述符的处理

    共享

    #include<unistd.h>
    #include<fcntl.h>
    #include<string.h>
    #include<stdlib.h>
    
    int main(int argc, char *argv[])
    {
        pid_t pid;
        int fd;
        int i = 1;
        int status;
        char *ch1 = "hello";
        char *ch2 = "world";
        char *ch3 = "IN";
        if((fd = open("test.txt",O_RDWR|O_CREAT,0644)) == -1)
        {
            perror("parent open");
            exit(EXIT_FAILURE);
        }
        if(write(fd,ch1,strlen(ch1)) == -1)
        {
            perror("parent write");
            exit(EXIT_FAILURE);
        }
        if((pid = fork()) == -1)
        {
            perror("fork");
            exit(EXIT_FAILURE);
        }
        else if(pid == 0)
        {
            i = 2;
            printf("in child
    ");
            printf("i = %d
    ", i);
            if(write(fd, ch2, strlen(ch2)) == -1)
                perror("child write");
            return 0;
        }
        else    //父进程
        {
            sleep(1);  //等子进程先执行
            printf("in parent
    ");
            printf("i = %d
    ", i);
            if(write(fd, ch3, strlen(ch3)) == -1)
                perror("parent write");
            wait(&status); //等待子进程结束
            return 0;
            
        }
    }

    可以看到,当文件打开后,父子进程写文件时共享了文件的偏移量等信息。不会出现覆盖。

    iii.结合vfork测试全局数据段与BSS段使用策略

    vfork():与fork基本相同,但是vfork()不复制父进程的地址空间,而是跟父进程共享所有资源

    #include<unistd.h>
    #include<error.h>
    #include<sys/types.h>
    #include<stdio.h>
    #include<stdlib.h>
    int glob = 6;  //全局已经初始化变量,位于数据段中
    int main()
    {
        int var;
        pid_t pid;
        var = 88;        //局部变量,位于栈空间中
        printf("in beginning:	glob=%d	var=%d
    ",glob, var);
        
        if((pid = vfork()) < 0)
        {
            perror("vfork");
            exit(EXIT_FAILURE);
        }
        else if(pid == 0)
        {
            printf("in child, modify the var: glob++, var++
    ");
            glob++;
            var++;
            printf("in child:	glob=%d	var=%d
    ",glob, var);
            _exit(0);        //退出
        }
        else
        {
            printf("in parent:	glob=%d	var=%d
    ",glob,var);
            return 0;
        }
    }

    可以看到,父子进程打印的内容相同,说明他们共享资源。

    如果把vfork改为fork,则,父子进程打印的内容不同。fork的子进程复制了父进程的内容。

    iv.子函数调用vfork创建子进程

    会出错!

    #include<stdlib.h>
    void test(void)  //test函数,在此函数中调用vfork
    {
        pid_t pid;
        pid = vfork();
        if(pid == -1)
        {
            perror("vfork");
            exit(EXIT_FAILURE);
        }
        else if(pid == 0)
        {
            printf("1:child pid=%d,ppid=%d
    ",getpid(),getppid());
            return;
        }
        else
            printf("2:parent pid=%d,ppid=%d
    ",getpid(),getppid());
    }
    
    void fun(void)
    {
        int i;
        int buf[100];
        for(i = 0; i < 100; i++)
            buf[i]=0;
        printf("3:child pid=%d,ppid=%d
    ",getpid(),getppid());
    }
    int main(void)
    {
        pid_t pid;
        test();
        fun();
        return 0;
    }

    出错原因:

    调用main,申请栈空间,没有问题。

    调用test函数,里面用vfork创建新进程,共享栈空间。子进程先运行,没有问题。子进程执行完test后返回,清理栈空间

    子进程调用fun(),覆盖原来的test函数的栈空间。子进程执行完毕

    子进程退出后,父进程从vfork返回处执行代码,没有问题。但返回时该栈空间已经不存在,出现栈错误。

    把代码里的vfork改为fork则代码可以正常运行!

  • 相关阅读:
    数据挖掘-基本流程
    ArcGIS GP应用-GP模型服务发布
    ArcGIS GP应用-GP模型创建-缓冲区分析
    Hadoop2的Yarn和MapReduce2相关
    hadoop学习WordCount+Block+Split+Shuffle+Map+Reduce技术详解
    WordCount示例深度学习MapReduce过程
    数组的几种排序算法的实现
    hBase官方文档以及HBase基础操作封装类
    Hive SQL执行流程分析
    Hive SQL的编译过程
  • 原文地址:https://www.cnblogs.com/dplearning/p/4678175.html
Copyright © 2011-2022 走看看