cha27.进程执行

exec()

  • 带e的可以指定环境变量,否则继承
  • 带p的允许只提供文件名,允许提供不带"/"的路径,在path中寻找
    • 若无env PATH默认为.:/usr/bin/:/bin
    • 从左往右搜索,直到找到为止
  • 带l的用不定长参数(参数列表),以NULL结尾
    • execle在NULL后面接envp数组

exec执行脚本

当exec第一个参数文件以"#!"开始,则会读取该行进行解析

1
#!<interpreter-path> [arg] <script> <script-args...> 

阅读更多

cha26.监控子进程

孤儿与僵尸

  • 孤儿: 子进程结束前父进程未wait结束的进程。其父进程会变成1由init接管进行wait
  • 僵尸: 父进程未结束,子进程已经结束,且父进程未执行wait。系统保留僵尸的进程表记录,以备未来父进程需要wait获取其结束状态
    • 无法被kill,只能kill其父进程

1

编写一程序以验证当一子进程的父进程终止时,调用getppid()将返回1(进程 init的进程ID)。

无聊,不弄

2

阅读更多

cha24.进程的创建

1

执行下面代码后会产生多少新进程

1
2
3
fork();
fork();
fork();

$ 2^3 -1 = 7 $

1
2
3
fork(); // A,产生B, A+B
fork(); // A产生C,B产生D, A+B+C+D
fork(); // ABCD产生EFGH,A+B+C+D+E+F+G+H

2

阅读更多

cha22.信号:高级特性

读书笔记

核心转储文件

  • 特定信号会引发进程创建核心转储文件(工作目录)并终止。

  • 核心转储文件(core)是内存映像的一个文件,利用调试器可以查看到退出时的代码、数据状态。

  • P371展示了不会产生核心转储文件的情况,大致为

    • 没有写权限
    • 存在硬链接大于1的同名文件
    • 目录不存在
    • ulimit等限制为0
    • 二进制程序没有读权限
    • 工作目录的挂载方式为只读
    • set-user/group-ID程序运行者为非文件属主(组)
  • /proc/sys/kenel/core_pattern中存储了核心转储文件的命名格式化字符串

信号处理、传递特殊情况

  • SIGKILL和SIGSTOP的默认行为无法改变,无法阻塞。总是可以使用该信号处理失控进程。

    • 前面读书不认真
    • 信号阻塞即对该信号的传递延后,直到该信号从掩码中移除。
    • 除非是实时信号,否则不对阻塞信号排队,恢复信号后只传递该信号一次
  • SIGCONT恢复停止的进程

    • SIGCONT总会恢复运行,不论该信号是否被阻塞或忽略
    • 在停止的进程恢复之前,若有其他进程传递其他信号,则该信号并未被真实传递。(除了sigkill)
    • 收到SIGCONT时,处于等待状态的停止信号将会被丢弃。反过来,收到停止信号后,等待状态的SIGCONT也会被丢弃
  • 若由终端产生的信号(SIGHUP SIGINT SIGQUIT SIGTTIN SIGTTOU SIGTSTP)被忽略,则不应该改变其信号处置(处理函数)

    • 这个很难懂,后面34章会讲

sigkill的力所不能及

阅读更多

LeetCode-26

[Medium] 1110. 删点成林

分析

  1. 使用什么样的数据结构
    1. 直接用数组
    2. 用孩子兄弟表示法
  2. 使用什么样的遍历方法?

代码

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
33
34
35
36
37
38
class Solution {
public:
vector<TreeNode*> forest;
vector<TreeNode*> delNodes(TreeNode* root, vector<int>& to_delete) {
if(root){
if(del(root, to_delete)) {
push_forest(root);
} else {
forest.push_back(root);
}
}
return forest;
}
bool del(TreeNode* root, vector<int>& to_delete) {
if(root->left && del(root->left, to_delete)) {
push_forest(root->left);
root->left = nullptr;
}
if(root->right && del(root->right, to_delete)) {
push_forest(root->right);
root->right = nullptr;
}
for(int d : to_delete) {
if(d == root->val) {
return true;
}
}
return false;
}
void push_forest(TreeNode *root) {
if(root->left) {
forest.push_back(root->left);
}
if(root->right) {
forest.push_back(root->right);
}
}
};

结果

阅读更多

cha21.信号:信号处理器函数

21.1

实现abort

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void __abort(void) {
fflush(NULL);
// 随便输出点什么吧
void *buff = malloc(BUFSIZ);
int cd = open("coredump", O_RDWR | O_CREAT, 0644);
int mem = open("/proc/self/stack", O_RDONLY);
size_t readsize;
while((readsize = read(mem, buff, BUFSIZ)) > 0) {
write(cd, buff, readsize);
}
close(cd);
close(mem);
// 后面这三行+fflush就够了吧
printf("raise SIGABRT\n");
raise(SIGABRT);
printf("signal SIG_DFL\n");
signal(SIGABRT, SIG_DFL);
printf("raise SIGABRT\n");
raise(SIGABRT);
printf("__abort return\n");
}

读后感

可重入问题

这一章首先讲了信号处理器函数的可重入问题。这是由于执行信号处理器函数时,有可能再次触发信号,调用该函数。

阅读更多

LeetCode-25

[hard] 1377. T 秒后青蛙的位置

题目分析

题目强调为一颗无向树,每次访问未访问过的节点。也就是说,每秒若有子节点,则跳到子节点,否则呆在原地不动。

也就是根据题目构造一棵根节点为1的树,并按照层次遍历该树即可。但是题目输入的边并不一定以1为根节点。

代码

阅读更多

cha20.信号:基本概念

20.2

展示SIG_IGN一定不会收到信号

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//
// Created by root on 5/23/23.
//

#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

int main() {
signal(SIGINT, SIG_IGN);
printf("set SIGINT(%s) as SIG_IGN. always ignore ctrl-c\n", strsignal(SIGINT));
for(int i = 16; i >= 0; i--) {
sleep(1);
printf("sleep %ds, try press ctrl-c\n", i);
}
signal(SIGINT, SIG_DFL);
printf("set SIGINT(%s) as SIG_DFL. always take default action for ctrl-c\n", strsignal(SIGINT));
for(;;) {
usleep(500000);
printf("try press ctrl-c\n");
}
return 0;
}

20.3

展示sigaction时,sa_nodefersa_resethand的作用

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
33
34
35
36
37
38
39
40
41
42
//
// Created by root on 5/23/23.
//

#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

void sigint_handler(int sig) {
if(sig != SIGINT) {
return;
}
printf("喵!\n");
sleep(1);
// sa_nodefer处理过程中,不阻塞sigint
// 此时连续按ctrl-c,可以喵很多次
printf("汪!\n");
}


int main() {
sigaction(SIGINT, &(struct sigaction){
.sa_handler = sigint_handler,
.sa_flags = SA_NODEFER,
}, NULL);
printf("set SIGINT(%s) as SA_NODEFER\n", strsignal(SIGINT));
for(int i = 16; i >= 0; i--) {
sleep(1);
printf("sleep %ds, try press ctrl-c\n", i);
}
sigaction(SIGINT, &(struct sigaction){
.sa_handler = sigint_handler,
.sa_flags = SA_RESETHAND,
}, NULL);
printf("set SIGINT(%s) as SA_RESETHAND\n", strsignal(SIGINT));
for(;;) {
usleep(500000);
printf("try press ctrl-c\n");
}
return 0;
}
阅读更多

cha19.监控文件事件

19.1

监控某个目录及其所有子目录的,创建,删除改名

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
//
// Created by root on 5/22/23.
//
#define _XOPEN_SOURCE 600
#include <stdio.h>
#include <unistd.h>
#include <limits.h>
#include <sys/inotify.h>
#include <stddef.h>
#include <sys/stat.h>
#include <ftw.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <alloca.h>
#include <stdbool.h>
// 对root及其子目录下所有文件的创建、删除改名操作监控,并支持监控新建的子目录

struct listNode {
int wd;
char *name;
struct listNode *next;
} *head;

int len() {
int l = 0;
struct listNode *next = head->next;
while (next) {
l++;
next = next->next;
}
return l;
}

struct listNode *newListNode(int wd, const char *name, struct listNode *next) {
struct listNode *node = malloc(sizeof(struct listNode));
node->wd = wd;
node->name = strdup(name);
node->next = next;
return node;
}
struct listNode *searchWD(const char *name) {
struct listNode *next = head->next;
while (next) {
if(strcmp(next->name, name) == 0) {
return next;
}
next = next->next;
}
return NULL;
}
struct listNode *search(int wd) {
struct listNode *next = head->next;
while (next) {
if(next->wd == wd) {
return next;
}
next = next->next;
}
return NULL;
}

bool delete(int wd) {
struct listNode *next = head;
while (next->next) {
if(next->next->wd == wd) {
struct listNode *del = next->next;
next->next = next->next->next;
free(del);
return true;
}
next = next->next;
}
return false;
}

const uint32_t watch_mask = IN_CREATE|IN_DELETE|IN_DELETE_SELF|IN_MOVED_TO|IN_MOVED_FROM;
int fd = -1;
size_t read_event(void *ievent) {
size_t numRead = read(fd, ievent, 10 * (sizeof(struct inotify_event) + NAME_MAX + 1));
if(numRead == -1) {
fprintf(stderr, "read1: %s\n", strerror(errno));
return -1;
}
printf("readNum = %lu\n", numRead);
return numRead;
}

int addwatch(const char *path) {
int wd;
if((wd = inotify_add_watch(fd, path, watch_mask)) != -1) {
struct listNode *e = NULL;
if((e = search(wd)) == NULL) {
head->next = newListNode(wd, path, head->next);
} else {
free(e->name);
e->name = strdup(path);
}
fprintf(stderr, "watching: %s\n", path);
return 0;
} else {
fprintf(stderr, "fail to watch: %s, %s\n", path, strerror(errno));
return -1;
}
}

int nftw_read(const char *path, const struct stat *sbuf, int type, struct FTW *ftwb) {
switch (sbuf->st_mode & S_IFMT) {
case S_IFDIR:
break;
default:
return 0;
}
addwatch(path);
return 0;
}

void update_monitor(struct inotify_event *ievent) {

struct listNode *node = search(ievent->wd);
char *new_path = malloc(strlen(node->name) + strlen(ievent->name) + 1 + 1);
sprintf(new_path, "%s/%s", node->name, ievent->name);
struct stat stat1;
if(stat(new_path, &stat1) != -1) {
if((stat1.st_mode & S_IFMT) == S_IFDIR) {
// addwatch(new_path);
if(nftw(new_path, nftw_read, 10, FTW_PHYS) == -1) {
fprintf(stderr, "fail to traverse: %s, %s\n", new_path, strerror(errno));
}
}
}
free(new_path);
}
int nftw_del(const char *path, const struct stat *sbuf, int type, struct FTW *ftwb) {
switch (sbuf->st_mode & S_IFMT) {
case S_IFDIR:
break;
default:
return 0;
}
// addwatch(path);
int wd = searchWD(path)->wd;
inotify_rm_watch(fd,wd);
delete(wd);
return 0;
}
void rm_monitor(struct inotify_event *ievent, bool recursive) {
if(recursive) {
struct listNode *node = search(ievent->wd);
char *new_path = malloc(strlen(node->name) + strlen(ievent->name) + 1 + 1);
sprintf(new_path, "%s/%s", node->name, ievent->name);
if (nftw(new_path, nftw_del, 10, FTW_PHYS) == -1) {
fprintf(stderr, "fail to traverse: %s, %s\n", new_path, strerror(errno));
}
free(new_path);
} else {
// int wd = searchWD(new_path)->wd;
inotify_rm_watch(fd,ievent->wd);
delete(ievent->wd);
}
}

void process_event(struct inotify_event *ievent) {
// IN_CREATE|IN_DELETE|IN_DELETE_SELF|IN_MOVE

printf("mask = %x\n", ievent->mask);
if(ievent->mask & IN_CREATE) {
printf("Monitor: File Creation: %s/%s\n", search(ievent->wd)->name, ievent->name);
update_monitor(ievent);
}
if(ievent->mask & IN_DELETE) {
printf("Monitor: File Deletion: %s, wd = %s\n", ievent->name, search(ievent->wd)->name);
}
if(ievent->mask & IN_DELETE_SELF) {
printf("Monitor: File Deletion: %s, stop monitoring, wd = %s\n", search(ievent->wd)->name, search(ievent->wd)->name);
rm_monitor(ievent, false);
}
if(ievent->mask & IN_MOVED_FROM) {
printf("Monitor: File Move in, from: %s/%s\n", search(ievent->wd)->name, ievent->name);
rm_monitor(ievent, true);

}
if(ievent->mask & IN_MOVED_TO) {
printf("Monitor: File Move out, to: %s/%s\n", search(ievent->wd)->name, ievent->name);
update_monitor(ievent);
}
}

int main(int argc, char *argv[]) {
char *monitor_root = (argc > 1) ? argv[1] : ".";
fd = inotify_init();
if(fd == -1) {
fprintf(stderr, "fail to init inotify: %s\n", strerror(errno));
return 1;
}

head = newListNode(0, "", NULL);
if(head == NULL) {
fprintf(stderr, "fail to malloc head, %s\n", strerror(errno));
return 2;
}

if(nftw(monitor_root, nftw_read, 10, FTW_PHYS) == -1) {
fprintf(stderr, "fail to traverse: %s, %s\n", monitor_root, strerror(errno));
}

void *ievent = malloc(10 * (sizeof(struct inotify_event) + NAME_MAX + 1));
for(;len()>0;) {
size_t numread = -1;
if((numread = read_event(ievent)) == -1) {
fprintf(stderr, "read fail, sleep\n");
usleep(500000);
continue;
}
for(void *p = ievent; p < ievent + numread;) {
struct inotify_event* e = (struct inotify_event *)p;
p += e->len + sizeof(struct inotify_event);
process_event(e);
}
}
free(ievent);
}

todo:

如果read了半个event怎么办

cha18.目录与链接

18.1

4.3.2节曾指出,如果一个文件正处于执行状态,那么要将其打开以执行写操作是不可能的(open)调用返回-1,且将errno置为ETXTBSY。然而,在 shell 中执行如下操作却是可能的:

1
2
3
4
5
gcc -o longrunner longrunner.c$ ./longrunner &
# Leave running in background
vi longrunner.c
# Make some changes to the source code
gcc -o longrunner longrunner.c

最后一条命令覆盖了现有的同名可执行文件。原因何在?(提示:在每次编译后调用ls -li命令来查看可执行文件的i-node编号。)

解释

变异前后使用ls -li,inode确实变了。猜测-o参数会令编译程序将临时文件rename为对应名称,rename若newpath存在,则会覆盖。

阅读更多