zoukankan      html  css  js  c++  java
  • nohup、&、setsid、fork和fg、bg究竟有啥区别?

    目录

    目录 1

    1. 名词解释 1

    2. 什么是守护进程? 2

    2.1. 后台运行 2

    2.2. 解释关系 2

    3. 以“&”方式运行有何问题? 2

    4. fork 2

    5. setsid&setuid 3

    5.1. setuid应用场景 3

    5.2. 案例 3

    5.3. 用户ID 4

    5.4. chmod 5

    6. fg&bg 6

    7. abc.cpp 7

    1. 名词解释

    名词(中文)

    名词(英文)

    解释

    会话

    Session

    每打开一次终端(本地或远程)登录Linux,都会生成一个新的会话;除此之外,程序中也可以调用函数setsid创建一个新的会话;脚本也可以调用命令setsid创建一个新的会话。新建的会话无控制终端。

    会话ID

    SessionID

    用来标识一个会话,同一时刻不同会话的ID不会相同,是一个int类型的整数值。

    用户

    User

    指能登录Linux的用户帐号

    用户ID

    UserID

    用来唯一标识一个用户

    伪终端或叫虚拟终端

    pty

    (Pseudo Terminal)

    控制终端

    tty

    (Teletypewriter或Teletype)

    字符型设备

    pts

    (pseudo-terminal slave)

    pty的实现方法,分两端:一端叫master,另一端叫slave。可打开的最多pts数为/proc/sys/kernel/pty/max,当前已打开的pts数为/proc/sys/kernel/pty/nr。

    2. 什么是守护进程?

    在后台运行的进程不一定是守护进程!一个进程要成为守护进程,必须做到以下两点:

    1) 在后台运行

    2) 脱离了终端

    2.1. 后台运行

    要使一个进程在后台运行,代码中可以通过fork子进程来实现,而命令行或脚本中可以通过使用“&”来实现。

    fork之后,子进程会继承父进程的SessionID,调用execve()后的进程的SessionID不会改变。

    2.2. 解释关系

    子进程从父进程继承了:SessionID、进程组ID和打开的终端。子进程如果要脱离这些,代码中可通过调用setsid来实现。,而命令行或脚本中可以通过使用命令setsid来运行程序实现。setsid帮助一个进程脱离从父进程继承而来的已打开的终端、隶属进程组和隶属的会话。

    需要注意,代码中调用setsid是有条件的:即调用进程自己不能是进程组长。因此,调用setsid之前需要先fork,然后由产生的子进程调用setsid

    3. 以“&”方式运行有何问题?

    以“&”方式可以将一个前台进程以后台方式运行,但是如果它是一个终端的job,则如果向终端收到SIGHUP信号,终端也会向它的所有job发送SIGHUP,这样以“&”方式运行的进程则会因为收到SIGHUP则退出。

    当用户注销(logout)或者网络断开时,终端会收到 SIGHUPhangup)信号从而关闭其所有子进程。

    早期的Unix,终端通过Modem和系统通讯,当用户注销(logout)时,Modem就会挂断(hangup)电话;当Modem断开时,会给终端发SIGHUP来通知终端关闭所有子进程。

    4. fork

    fork只是使得进程可以以后台方式运行,但不能使进程完全独立,因为fork出来的进程仍然继承了父进程已打开的终端、会话和进程组。

    5. setsid&setuid

    setsid命令或函数

    setuid函数(相对应的命令为chmod)

    1) 创建一个会话

    2) 调用进程将成为会话的组长

    3) 调用进程将成为进程组的组长

    4) 调用进程将脱离控制终端(tty)

    5) 会话ID和进程组ID都和调用进程的PID相同

    1) 设置调用进程的真实用户ID、有效用户ID和保存用户ID

    按下“ctrl+z”会触发SIGTSTP,注意不是SIGSTOP,这两个信号的区别是前者可以捕获,而后者不可以。进程收到这两个信号后,都进入STOP状态,使用ps aux看到的状态值为“T”,可以通过发送信号SIGCONT重新回到运行状态。

    5.1. setuid应用场景

    1) 以root启动,但以其它用户身份运行,常见于Webserver,因为需要监听80端口,不得不以root启动,但root权限过大,因此运行经常会从root切换到nobody等用户;

    2) 开发中经常需要让非root用户能够去gdb一些root启动的进程,这个时候只开放gdb等少数一些命令给非root用户。

    5.2. 案例

    使用ls命令查看下passwd

    ~> ls -l /usr/bin/passwd

    -rwsr-xr-x 1 root shadow 73300 2007-05-04 19:30 /usr/bin/passwd

    ~> ls -l /etc/passwd

    -rw-r--r-- 1 root root 1138 2015-03-31 17:04 /etc/passwd

    从上可以看到,虽然所有用户对/usr/bin/passwd都有可执行权限,但只有root/etc/passwd有可修改权限。使用/usr/bin/passwd修改密码时,/usr/bin/passwd会修改/etc/passwd,为何普通用户也可以执行passwd修改密码了?

    这里的奥秘就在/usr/bin/passwd的权限属性,不难看到/usr/bin/passwd隶属用户为root,对root的权限为“rws”,这里不是“rwx”而是“s”。

    这个“s”表示,当其它用户执行时,用效用户将变成root,因此普通用户才可以修改密码。如果将“s”变成“x”,则普通用户将不能修改密码。

    请观察:

    # ls -l /usr/bin/passwd

    -rwsr-xr-x 1 root shadow 73300 May  4  2007 /usr/bin/passwd

    # chmod u-s /usr/bin/passwd

    # ls -l /usr/bin/passwd

    -rwxr-xr-x 1 root shadow 73300 May  4  2007 /usr/bin/passwd

    从上面的变化可以看到:权限“s”退化成了“x”。

    > whoami 

    zhangsan

    > passwd

    Changing password for zhangsan.

    Old Password: 

    Old Password: 

    passwd: Authentication failure

    s”总是要建立在“x”之上,如果要为一个没有“x”权限的文件添加“s”,则小写“s”会变成大写“S”:

    # ls -l abc.log

    -rw-r--r-- 1 root root 7 2015-04-22 18:34 abc.log

    # chmod u+s abc.log

    # ls -l abc.log

    -rwSr--r-- 1 root root 7 2015-04-22 18:34 abc.log

    # ls -l abc

    -rwxr-xr-x 1 root root 35893 2015-04-23 09:40 abc

    # chmod u+s abc

    # ls -l abc

    -rwsr-xr-x 1 root root 35893 2015-04-23 09:40 abc

    5.3. 用户ID

    真实用户ID

    登录(login)时的用户ID

    用效用户ID

    运行时产生作用的用户ID,比如对于“passwd”命令,以非root用户运行它,但有效用户ID是root的ID。为弄明白,先查看下passwd的文件权限:

    # ls -l `which passwd`

    -rwsr-xr-x 1 root shadow 79336 2007-05-04 19:29 /usr/bin/passwd

    不难看出,passwd有“rwx”之外的另一个权限属性“s”。可借助chmod命令为需要的文件添加“s”权限,如:chmod u+s abc.cpp。

    保存的用户ID

    看一小段代码:

    // filename: uid.cpp

    // g++ -g -o uid uid.cpp

    #include <stdio.h>

    #include <unistd.h>

    int main()

    {

            printf("uid = %d ", getuid());

            printf("effective uid = %d ", geteuid());

            return 0;

    }

    root用户编译生成可执行程序uid

    # g++ -g -o uid uid.cpp

    # ls -l uid

    -rwxr-xr-x 1 root root 10498 2015-04-23 10:43 uid

    以用户zhangsan运行uid

    > ./uid

    uid = 30038

    effective uid = 30038

    30038为用户zhangsan的用户ID,以及有效用户ID。以root用户为uid增加“s”权限:

    # chmod u+s uid

    # ls -l uid

    -rwsr-xr-x 1 root root 10498 2015-04-23 10:43 uid

    再以用户zhangsan运行uid

    > ./uid

    uid = 30038

    effective uid = 0

    这个时候,有效用户ID不再是30038了,而是变成了00root的用户IDzhangsan虽然是普通用户,但已获得了root权限。

    5.4. chmod

    chmod用来修改文件的权限。如果只修改当前用户对文件的权限,可用:chmod u+xrw,这里的“u”表示当前用户自己,如果改成“g”则表示同组,改成“o”则表示其它用户。 示例:

    为文件abc添加当前用户可执行权限

    chmod u+x abc

    为文件abc添加同组用户可读权限

    chmod g+r abc

    为文件abc添加其它用户可写权限

    chmod o+w abc

    为文件abc添加当前用户可读可写权限

    chmod u+rw abc

    为文件abc添加当前用户和同组用户可读可写权限

    chmod ug+rw abc

    6. fg&bg

    看下面一段代码:

    // filename: x.cpp

    // g++ -g -o x x.cpp

    #include <stdio.h>

    #include <unistd.h>

    int main()

    {

            while (true)

            {

                    sleep(1);

            }

            return 0;

    }

    编译生成可执行程序文件x,然后在终端上执行下列操作:

    # jobs

    jobs无任何输出,表示没有任何作业

    # ./x

    从上面的代码可以看出,执行x时,终端会卡住。这个时候按“ctrl+z”,也就是发送SIGTTSTP信号给程序x的进程,进程会被stop,并且进入后台方式:

    [1]+  Stopped                 ./x

    再执行jobs可以看到:

    # jobs

    [1]+  Stopped                 ./x

    注意,x的父进程必须是终端进程,否则jobs是看不到的。

    接下来,执行命令fg:

    # fg

    ./x

    会发生x又变成前台进程了,再按“ctrl+z”让它变成后台进程:

    [1]+  Stopped                 ./x

    执行bg和jobs命令:

    # bg

    [1]+ ./x &

    # jobs

    [1]+  Running                 ./x &

    这个时候可以看到x变成了一个非stop的,而是running的后台进程。只要x的父进程仍然是终端进程,则仍然可以用命令fg把它调到前台运行。

    7. abc.cpp

    // filename: abc.cpp

    // g++ -g -o abc abc.cpp

    #include <fcntl.h>

    #include <libgen.h> // basename

    #include <pty.h> // openpty and forkpty

    #include <signal.h>

    #include <stdio.h>

    #include <stdlib.h>

    #include <string>

    #include <string.h>

    #include <sys/stat.h>

    #include <sys/types.h>

    #include <unistd.h>

    // 取得进程程序文件名

    static std::string get_process_name(pid_t pid)

    {

    char process_path[PATH_MAX];

    char proc_path[PATH_MAX];

    snprintf(proc_path, sizeof(proc_path), "/proc/%d/exe", pid);

    readlink(proc_path, process_path, sizeof(process_path)-1);

    return basename(process_path);

    }

    static void info()

    {

    printf("PID: %d ", getpid());

    printf("ParentPID: %d ", getppid());

    printf("ParentName: %s ", get_process_name(getppid()).c_str());

    printf("GroupID: %d ", getpgrp());

    printf("SessionID: %d ", getsid(getpid()));

    printf("TerminalName: %s ", ttyname(STDIN_FILENO));

    printf("PseudoTerminalName: %s ", ptsname(STDIN_FILENO));

    printf(" ");

    }

    static void set_session()

    {

    printf("setsid ... ");

    if (-1 == setsid())

    {

    perror("setsid");

    }

    else

    {

    printf("SessionID: %d ", getsid(getpid()));

    }

    }

    int main()

    {

    info();

    printf("fork ... ");

    pid_t pid = fork();

    if (pid > 0)

    exit(0);

    sleep(1);

    info();

    set_session();

    info();

    printf("fork ... ");

    pid = fork();

    if (pid > 0)

    exit(0);

    info();

    while (true)

    {

    sleep(1);

    }

    return 0;

    }

  • 相关阅读:
    二阶注入
    ACCESS延时注入
    宽字节注入源码
    Sqli-LABS通关笔录-14
    Sqli-LABS通关笔录-13
    Sqli-LABS通关笔录-12
    PHP学习路线
    华科机考:二叉排序树
    华科机考:打印日期
    华科机考:A+B
  • 原文地址:https://www.cnblogs.com/aquester/p/9891553.html
Copyright © 2011-2022 走看看