mxxhcm's blog

  • 首页

  • 标签

  • 分类

  • 归档

UNIX system function

发表于 2020-02-20 | 分类于 UNIX

system

ISO C定义了system函数,它用来执行一个shell命令,对系统的依赖性很强。system库函数调用fork创建一个子进程使用execl执行参数system指定的shell命令。相当于:

1
execl("/bin/sh", "sh", "-c", command, (char *) 0);

POSIX.1包括了system接口,扩展了ISO C定义,描述了system在POSIX.1环境中的运行行为。

1
2
3
#include <stdlib.h>

int system(const char *command);

属性

system在其实现中调用了fork, exec和waitpid,可能有以下的返回值:

  1. 如果command是一个空指针,当前系统是有可用的shell时,system返回非0值,否则返回0。在UNIX的各个实现中,一定提供了shell,当command是空指针时,总是返回非零值。
  2. fork失败或者waitpid返回处EINTR之外的出错,返回-1,设置errno。
  3. 如果exec失败,表示不能执行shell,返回值如同shell执行了exit(127)一样
  4. 所有三个函数都成功,system的返回值和在shell中执行相应命令的的termination status一样。

可能实现

其中一种system的可能实现如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
int system(const char *cmdstring)
{
pid_t pid;
int status;

if(cmdstring == NULL)
return 1;

if ((pid = fork()) < 0)
{
status = -1;
}
else if(pid == 0)
{
execl("/bin/sh", "sh", "-c", cmdstring, (char*)0);
_exit(127);
}
else
{
while(waitpid(pid, &status, 0) < 0)
{

if(errno != EINTR)
{
status = -1;
break;
}
}
}

return status;
}

使用system而不是直接使用fork和exec的好处是:system进行了各种所需要的各种出错处理和信号处理。但是早期的系统中,没有waitpid函数,于是父进程使用下列语句等待子进程结束:

1
2
while((lastpid = wait(&status) != pid && lastpid !=-1)
;

如果在调用system之前产生了其它子进程,如果这些子进程在system产生的子进程之前结束,那么上面的代码会将这些提前结束的子进程的进程ID和termination都给丢弃。
system函数还有可能会出现漏洞。如果设置了set UID或者set GID位的程序执行system,那么这个进程的高级别权限可能会保持下来(现代的系统都解决了这个问题)。如果一个进程正在以特殊的权限(set UID和set GID)运行,它又想生成另一个进程执行另一个程序,它应该直接使用fork和exec,而且在fork之后,exec之前要改回普通权限,set UID和set GID程序绝不应该调用system函数。

参考文献

1.《APUE》第三版

UNIX interpreter file

发表于 2020-02-20 | 分类于 UNIX

解释器文件

所有的UNIX系统都支持解释器文件。这种文件是文本文件,起始形式是:

1
#! pathname [optional-argument]

在感叹号和pathname之间的空格是可以选的。常见的解释器文件以下列行开始:

1
#! /bin/bash

pathname通常是绝对路径名,识别这种文件是exec系统调用处理的一部分。内核使调用exec函数的进程实际执行的并不是该解释器文件,而是在该解释器文件第一行中pathname指定的文件,即解释器。
解释器文件是文本文件,它以!#开头,而解释器是二进制文件,由解释器中的文件第一行的pathname指定。

解释器文件的好处

是否一定需要解释器文件呢?不完全如此,但是它们确实使得用户得到效率方面的好处,代价是内核的额外开销,因为识别解释器文件的是内核。由于下列原因,解释器文件是有用的:

  1. 有些程序是用某种脚本语言写的脚本,解释器文件可以将这一事实隐藏起来。
  2. 解释器脚本在效率方面提供了好处。
  3. 解释器脚本使我们可以使用除了/bin/sh以外的其他shell编写shell脚本。

参考文献

  1. 《APUE》第三版

UNIX id

发表于 2020-02-20 | 更新于 2020-02-21 | 分类于 UNIX

和进程相关的ID

  1. 每个进程都有一个进程ID(pid),还有一个父进程ID(ppid)。
    除此之外,每个进程还有以下ID:
  2. 实际用户ID,实际组ID。实际用户ID和实际组ID就是当前用户的UID和GID。
  3. 有效用户ID,有效组ID,附属组ID。通常情况下,实际用户ID和有效用户ID是一样的,实际组ID和有效组ID也是一样的。
  4. 保存的设置用户ID,保存的设置组ID,它们包含了执行一个程序时有效U(G)ID的副本。可以在st_mode中设置set-user-ID和set-group-ID位(这个和保存的设置U(G)ID不是一个东西),表示,当执此文件时,将进程的有效用户ID设置成文件所有者的UID,或者将进程的有效组ID设置称为文件所有组的GID,通过exec实现。
  5. 进程组ID。每一个进程都属于一个进程组。一个进程只能为自己或者它的子进程设置进程组ID,在它的子进程调用了exec之后,它就不能再更改子进程的进程组ID了。
  6. 会话ID。一个session可以有多个进程组,一个session可以有一个控制控制(终端设备或者伪终端设备)。如果进程调用setsid()创建一个新的session,那么这个进程没有控制终端。

访问进程的各种ID

1
2
3
4
5
6
7
8
pid_t getpid(void);
pid_t getppid(void);

uid_t getuid(void);
uid_t geteuid(void);

gid_t getgid(void);
gid_t getegid(void);

修改进程相关的ID

同时设置实际,有效和保存的设置ID

  1. 使用setxid设置进程的实际U(G)ID,有效U(G)ID,保存的设置U(G)ID:
    1
    2
    int setuid(uid_t uid);
    int setgid(gid_t gid);

关于修改的规则,有三条:
1. 如果是root用户,可以将三个ID都设置为u(g)id。
2. 如果不是root用户,但是uid和实际用户id一样,可以将有效uid改为uid,其他不变。
3. 否则,出错。

  1. 使用此外,调用exec也可能会更改有效U(G)ID和保存的设置U(G)ID。无论是否设置set-user(group)-id,实际U(G)ID都不变;当设置U(G)ID位打开时,将有效U(G)ID设置为程序文件的UID,否则有效U(G)ID不变,保存的set-user(group)-id都是从有效U(G)ID复制。

总结得到以下的表格:

ID exec setuid
set-user-ID位关闭 set-user-ID位开启 超级用户 非超级用户
real UID 不变 不变 设置为uid 不变
effective UID 不变 程序文件的UID 设置为uid 设置为uid
saved set-UID 从effective ID复制 从effective ID复制 设置为uid 不变

设置有效ID

设置进程的有效U(G)ID。如果是普通用户,可以将有效U(G)ID设置为实际U(G)ID或者保存的设置U(G)ID,如果是特权用户,可以将有效U(G)ID设置为u(g)id:

1
2
int seteuid(uid_t uid);
int setegid(gid_t gid);

交换实际ID和有效ID

交换进程的实际U(G)ID和有效U(G)ID。对于普通用户,可以交换实际U(G)ID和有效U(G)ID;还可以允许普通用户将有效U(G)ID设置为保存的设置U(G)ID。
某个参数设置为1,表示相应的ID不变。

1
2
int setreuid(uid_t ruid, uid_t euid);
int setregid(uid_t rgid, uid_t egid);

参考文献

1.《APUE》

C++ regex

发表于 2020-02-18 | 更新于 2020-02-19 | 分类于 C/C++

正则表达式

regex定义在regex包中。

regex类

regex是一个正则表达式类。
输入是string,使用smatch保存匹配的相关信息,使用sregex_iterator获得某个string中所有匹配的子串。
输入是const char *,使用regex, cmatch,和cregex_iterator。

C++ 正则使用流程

给定一个输入(待搜索序列)string。
创建一个regex对象,调用regex_search(是否存在一个子串和regex匹配)或者regex_match(整个字符串是否和regex匹配)进行匹配,如果匹配成功,将匹配到的内容写入一个smatch对象,并且返回一个true的布尔变量,否则返回false。
或者可以使用sregex_iterator查找所有的匹配子串,返回一个smatch对象的引用,或者一个指向smatch对象的指针。

smatch操作

参考文献

1.《C++ Primer》

C++ tuple

发表于 2020-02-18 | 分类于 C/C++

tuple

在头文件tuple中,是一个类模板。和pair很像,pair是具有两个不同成员的类模板,而tuple是具有不定个数的成员类型也不定的类模板。

创建tuple

  1. 直接调用构造函数
  2. 使用make_tuple函数模板。

访问tuple的成员

  1. get<i>(t)获得tuple t的第i个元素。
  2. tuple_size<tupleType>类模板,使用它的public成员value获得某个tuple的数据成员的个数。
  3. tuple_element<i, tupleType>类模板,使用它的public成员type获得某个tupleType类型变量的第i个元素的类型。

tuple的用处

  1. 返回多个值。

参考文献

1.《C++ Primer》

C RTTI

发表于 2020-02-13 | 分类于 C/C++

运行时类型识别

运行时类型识别的功能由两个运算符实现:

  • typeid返回表达式的类型。
  • dynamic_cast将基类的指针或引用转换成派生类的指针或者引用。

dynamic_cast

指针类型的dynamic_cast

引用类型的dynamic_cast

type_id

使用RTTI

typeinfo类

参考文献

1.《C++ Primer》第五版

network questions

发表于 2020-02-04 | 分类于 计算机网络

三次握手的过程

为什么不是两次或者四次挥手

第三次没有收到

半开连接状态

SYN洪泛攻击

四次挥手的过程

参考文献

ml regression lasso and ridge

发表于 2020-01-26 | 分类于 机器学习

Lasso

ridge

参考文献

network linked layer

发表于 2020-01-13

链路层

差错检验和纠错

链路层编址

以太网

链路层交换机

参考文献

1.《计算机网络自顶向下》

network internet protocol

发表于 2020-01-13 | 更新于 2020-02-03 | 分类于 计算机网络

网络层

IP协议

参考文献

1.《计算机网络自顶向下》

1234…34
马晓鑫爱马荟荟

马晓鑫爱马荟荟

记录硕士三年自己的积累

337 日志
26 分类
77 标签
RSS
GitHub E-Mail
© 2022 马晓鑫爱马荟荟
由 Hexo 强力驱动 v3.8.0
|
主题 – NexT.Pisces v6.6.0