cha40.登录记账

40-1 40-4

  • 实现getlogin0。在40.5节中曾提到过当进程运行在一些软件终端模拟器下时getlogin0可能无法正确工作,在那种情况下就在虚拟控制台中进行测试。

  • 实现一个简单的who(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
//
// Created by root on 8/23/23.
//
#define _GNU_SOURCE
#include <stdio.h>
#include <unistd.h>
#include <utmpx.h>
#include <string.h>
#include <time.h>

char *__getlogin() {
static char login[__UT_NAMESIZE];
char *tty = ttyname(0);
if(tty) tty++;
tty=strchr(tty, '/');
if(tty) tty++;
printf("tty=%s\n", tty);
size_t ttylen = strlen(tty);
struct utmpx *ut;
setutxent();
while((ut = getutxent()) != NULL) {
printf("type:\t%s\nline:\t%s\nuser:\t%s\nhost:\t%s\ntime:\t%s\n\n",
ut->ut_type == EMPTY ? "EMPTY" :
ut->ut_type == RUN_LVL ? "RUN_LVL" :
ut->ut_type == BOOT_TIME ? "BOOT_TIME" :
ut->ut_type == NEW_TIME ? "NEW_TIME" :
ut->ut_type == OLD_TIME ? "OLD_TIME" :
ut->ut_type == INIT_PROCESS ? "INIT_PROCESS" :
ut->ut_type == USER_PROCESS ? "USER_PROCESS" :
ut->ut_type == DEAD_PROCESS ? "DEAD_PROCESS" :
ut->ut_type == LOGIN_PROCESS ? "LOGIN_PROCESS" : "unknown",
ut->ut_line, ut->ut_user, ut->ut_host, ctime((time_t *)&ut->ut_tv.tv_sec)
);
if(!strncmp(tty, ut->ut_line, ttylen) &&
(ut->ut_type == INIT_PROCESS ||
ut->ut_type == USER_PROCESS ||
ut->ut_type == LOGIN_PROCESS)) {
strcpy(login, ut->ut_user);
}
}
endutxent();
return login;
}

int main() {
printf("getlogin=%s\n", getlogin());
printf("getlogin=%s\n", __getlogin());
}

40-2 40-3

  • 修改程序清单40-3中的程序(utmpx loginc)使它除了更新utmp和wtmp文件之外还更新lastlog文件。
  • 阅读login(3)、logout(3)以及logwtmp(3)的手册。实现这些函数
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
//
// Created by root on 8/24/23.
//
#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <utmpx.h>
#include <stdarg.h>
#include <time.h>
#include <paths.h>
#include <wait.h>
#include <lastlog.h>
#include <pwd.h>
#include <fcntl.h>
#include <pwd.h>
#include <crypt.h>
#include <shadow.h>

void error(const char *file, int line,const char *str, ...) {
va_list fmt;
va_start(fmt, str);
fprintf(stderr, "error:%s %s:%d\n", strerror(errno), file, line);
vfprintf(stderr, str, fmt);
fprintf(stderr, "\n");
va_end(fmt);
}

#define ERROR(...) do { error(__FILE__, __LINE__, __VA_ARGS__); exit(1); } while(0)
#define FAIL(...) do { error(__FILE__, __LINE__, __VA_ARGS__); return -1; } while(0)

char *alloc_sprintf(const char * __format, ...) {
va_list fmt;
va_start(fmt, __format);
int len = vsnprintf(NULL, 0, __format, fmt);
va_end(fmt);
char *str = malloc(len+1);
if(str == NULL) ERROR("");
va_start(fmt, __format);
vsnprintf(str, 0, __format, fmt);
va_end(fmt);
return str;
}

int tmpxlog(struct utmpx *ut, const char *user, const char *ttyName) {
memset(ut, 0, sizeof(struct utmpx));
ut->ut_type = USER_PROCESS;
strncpy(ut->ut_user, user, sizeof(ut->ut_user));
if(time((time_t *)&ut->ut_tv.tv_sec) == -1) {
ERROR("");
}
ut->ut_pid = getpid();
strcpy(ut->ut_line, ttyName + 5);
strcpy(ut->ut_id, ttyName + 8);
strcpy(ut->ut_host, "meow-bash");
setutxent();
if(pututxline(ut) == NULL) ERROR("");
updwtmpx(_PATH_WTMP, ut);
return 0;
}

int lastlog(uid_t uid, const char *ttyName) {
struct lastlog lastlog = {
.ll_host="meow-bash"
};
strcpy(lastlog.ll_line, ttyName + 5);
if(time((time_t *)&lastlog.ll_time) == -1) {
ERROR("");
}
int lstlogfd = open(_PATH_LASTLOG, O_RDWR);
if(lseek(lstlogfd, uid * sizeof(struct lastlog), SEEK_SET) == -1) ERROR("");
if(write(lstlogfd, &lastlog, sizeof(struct lastlog)) != sizeof(struct lastlog)) ERROR("");
close(lstlogfd);
return 0;
}

int tmpxlogout(struct utmpx *ut) {
ut->ut_type = DEAD_PROCESS;
if(time((time_t *)&ut->ut_tv.tv_sec) == -1) {
ERROR("");
}
strncpy(ut->ut_user, "", sizeof(ut->ut_user));
setutxent();
if(pututxline(ut) == NULL) ERROR("");
updwtmpx(_PATH_WTMP, ut);
endutxent();
return 0;
}

int checkpwd(const char *username) {
char *shadow = NULL;
struct passwd *usrpwd;
if((usrpwd = getpwnam(username)) == NULL) FAIL("username:%s not found", username);
shadow = usrpwd->pw_passwd;
char *pass = getpass("Password: ");
if(!strcmp(shadow, "x")) {
struct spwd *shadowpwd;
if((shadowpwd = getspnam(username)) == NULL) FAIL("shadowpwd not found");
shadow = shadowpwd->sp_pwdp;
pass = crypt(pass, shadow);
}
if(strcmp(shadow, pass) == 0) FAIL("password not match!");
return 0;
}

char *user = NULL;

int init_check(int argc, char *argv[]) {
if(argc < 2) {
user = malloc(2048);
char hostname[2048];
gethostname(hostname, 2048);
printf("%s login: ", hostname);
scanf("%s", user);
} else {
user = strdup(argv[1]);
}
if(getuid() != 0) {
ERROR("uid must be 0\n");
}
}

int main(int argc, char *argv[]) {
struct utmpx ut;
char *homedir = NULL;
uid_t uid;
struct passwd *passwd = NULL;
char *ttyName = ttyname(0);
if(ttyName == NULL) {
ERROR("");
}

init_check(argc, argv);

if(checkpwd(user) == -1) ERROR("");

passwd = getpwnam(user);
uid = passwd->pw_uid;
homedir = alloc_sprintf("%s/.bashrc", passwd->pw_dir);

tmpxlog(&ut, user, ttyName);
lastlog(uid, ttyName);
printf("meow-meow-bash login\n");

fflush(NULL);
switch (fork()) {
case -1:
ERROR("");
break;
case 0:
setuid(uid);
if(execlp("/bin/bash", "/bin/bash", "--init-file", homedir, NULL) == -1) {
ERROR("");
}
break;
default:
wait(NULL);
break;
}
tmpxlogout(&ut);
printf("meow-meow-bash logout\n");
free(homedir);
free(user);
}

cha39.能力

读书笔记

进程能力分为:

类型 解释
许可集 进程可使用的能力,删除一个能力是不可逆的
有效集 进程当前能使用的能力
可继承集 exec之后,可以继承、进入许可集的能力集(规定被exec的文件可以继承哪些能力)

文件能力分为:

类型 解释
许可集 exec时添加到进程的许可集
有效集 1位,关闭,则exec后进程有效集为空;开启,exec后有效集为许可集
可继承集 文件可继承集与进程可继承集相交后,作为exec后可被继承、进入许可集的能力集合(规定被exec的文件可以继承哪些能力)

exec前后计算公式

阅读更多

cha38.编写安全的特权程序

38.1

用一个普通的非特权用户登录系统,创建一个可执行文件(或复制一个既有文件如/bin/sleep),然后启用该文件的set-user-ID权限位(chmod u+s)。尝试修改这个文件(如cat >>fle)。当使用(ls -l)时文件的权限会发生什么情况呢?为何会发生这种情况?

现象:saved-user-id不见了
原因:通过下面的例子,猜测open时会清除set-usr-id标志位

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
//
// Created by root on 8/1/23.
//

#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>

static int exitcode = 0;

#define ERR(str...) do { fprintf(stderr, "%s:%d\n%s:", __FILE__, __LINE__, strerror(errno)); fprintf(stderr, str); fprintf(stderr, "\n"); exit(exitcode++); } while(0)

int main(int argc, char **argv) {
if(argc < 3) {
ERR("Usage: %s src dst [content..]", argv[0]);
}
char *file1 = argv[1];
char *file2 = argv[2];
int fd1 = open(file1, O_RDONLY);
if(fd1 == -1) {
ERR("file to open %s", file1);
}
struct stat stat1;
if(fstat(fd1, &stat1) == -1) {
ERR("fstat(fd1, &stat1)");
}
fprintf(stderr, "F_GETFL:%o\n", stat1.st_mode);
int fd2 = open(file2, O_WRONLY | O_CREAT, stat1.st_mode);
if(fd2 == -1) {
ERR("fail to open %s", file2);
}

char buffer[4096] = {0};
ssize_t readsize = 0;
while ((readsize = read(fd1, buffer, 4096)) > 0) {
ssize_t writesize = write(fd2, buffer, readsize);
if(writesize != readsize) {
ERR("fail to write %s", file2);
}
}
if(readsize < 0) {
ERR("fail to read %s", file1);
}
for(int i = 3; i < argc; i++) {
size_t len = strlen(argv[i]);
if(write(fd2, argv[i], len) != len) {
ERR("fail to write %s", file2);
}
}
return 0;
}

38.2

编写一个与 sudo(8)程序类似的 set-user-ID-root 程序。这个程序应该像下面这样接收命令行选项和参数:

阅读更多

cha37.DAEMON

37.1

编写一个使用syslog(3)的程序(与logger(1)类似)来将任意的消息写入到系统日志
文件中。程序应该接收包含如记录到日志中的消息的命令行参数,同时应该允许指
定消息的level。

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
//
// Created by root on 7/24/23.
//

#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <sys/stat.h>
#include <limits.h>
#include <sys/resource.h>
#include <syslog.h>

volatile int init = 0;

void restart(int sig) {
if(init) {
closelog();
printf("restart\n");
}
openlog("meow", LOG_CONS|LOG_NDELAY|LOG_PID, LOG_USER);
init = 1;
}

#define min(x, y) ((x) < (y) ? (x) : (y))

#define CHECK_WITH_RET(x, ret, format, ...) \
do { \
if(!(x)) { \
fprintf(stderr, format, __VA_ARGS__); \
ret \
} \
} while(0)

#define CHECK(x) CHECK_WITH_RET(x, return -1;, "%s:%d\nerror: %s\nunmet condition:\"%s\"\n", __FILE__, __LINE__,strerror(errno), #x)
#define CONDITION(x) CHECK_WITH_RET(x, exit(2);, "%s:%d\nunmet condition:\"%s\"\n", __FILE__, __LINE__, #x)
#define CHECKMAIN(x) CHECK_WITH_RET(x, exit(1);, "%s:%d\nerror: %s\nunmet condition:\"%s\"\n", __FILE__, __LINE__,strerror(errno), #x)

int makeDaemon() {
pid_t pid;
switch ((pid = fork())) {
case -1:
CHECK(0);
case 0:
break;
default:
printf("pid:%ld\n", (long)pid);
_exit(0);
}
CHECK(setsid() != -1);
CHECK(umask(0) != -1);
CHECK(chroot("/") != -1);
int nullfd = open("/dev/null", O_RDWR);
CHECK(nullfd != -1);
CHECK(dup2(nullfd, STDIN_FILENO) != -1);
CHECK(dup2(nullfd, STDOUT_FILENO) != -1);
CHECK(dup2(nullfd, STDERR_FILENO) != -1);
CHECK(close(nullfd) != -1);
long fdmax = sysconf(_SC_OPEN_MAX);
struct rlimit rlimit;
CHECK(getrlimit(RLIMIT_NOFILE, &rlimit) != -1);
for(int fdi = 0; fdi < min(fdmax, rlimit.rlim_cur); fdi++) {
close(fdi);
}
CHECK(signal(SIGHUP, restart) != SIG_ERR);
return 0;
}

struct logmesg {
int level;
char *str;
};

int main(int argc, char **argv) {
printf("pid:%ld\n", (long)getpid());
CHECKMAIN(makeDaemon() != -1);
printf("pid:%ld\n", (long)getpid());
restart(SIGHUP);
CHECKMAIN(signal(SIGHUP, restart) != SIG_ERR);
CONDITION(argc >= 3);
struct logmesg *data = malloc(sizeof(struct logmesg));
data->level = atoi(argv[1]);
CONDITION(data->level >= 0 && data->level <= 7);
int strlen = 0;
for(char **argvi = &argv[2]; *argvi; argvi++) {
strlen += snprintf(NULL, 0, "%s %s", (char *)NULL, *argvi);
}
char *str = malloc(strlen + 1);

for(char **argvi = &argv[2]; *argvi; argvi++) {
strlen += sprintf(str, "%s %s",str, *argvi);
}
data->str = str+1;
syslog(data->level, "%s", data->str);
printf("syslog: %s\n", data->str);
closelog();
}

不及丢为什么,syslog.conf中配置的东西打印不出来

2023年8月23日更新

wsl下syslog确实不行,换一个环境,安装syslog后,重新允许,可以正常打印log

阅读更多

cha36.进程资源

36.1

编写一个程序使用getrusage0 RUSAGE CHILDREN标记获取wait调用所等待的
子进程相关的信息。(让程序创建一个子进程并使子进程消耗一些 CPU 时间,接着
让父进程在调用wait0前后都调用getrusage0。

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
//
// Created by root on 7/18/23.
//

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/wait.h>
#include <sys/times.h>
#include <unistd.h>
#include <limits.h>
#include <sys/resource.h>

#define CHECK(x) \
do { \
if(!(x)) { \
fprintf(stderr, "%s:%d\nerror: %s\n", __FILE__, __LINE__,strerror(errno)); \
exit(0); \
} \
} while(0)



#define rcputime(rusage, xtime) rusage.xtime.tv_sec + rusage.xtime.tv_usec/1000000.0
int main(int argc, char **argv) {
long tic = sysconf(_SC_CLK_TCK);
long tottm = 0;
for(int i = 1; i < argc; i++) {
char *end = NULL;
long sec = strtol(argv[i], &end, 10);
tottm += sec;
CHECK(end != argv[i] && errno == 0);
if(!fork()) {
struct tms tms, start;
CHECK(times(&start) != (clock_t) -1);
CHECK(times(&tms) != (clock_t) -1);
for(; (tms.tms_utime + tms.tms_stime)/tic - (start.tms_utime + start.tms_stime)/tic <= sec;) {
CHECK(times(&tms) != (clock_t) -1);
}
exit(0);
}
}
printf("total time:%ld\n", tottm);
struct rusage rusage;
CHECK(getrusage(RUSAGE_CHILDREN, &rusage) != -1);
printf("child use:\n\tcpu time:%.2lfs (user)\n\tcpu time:%.2lfs (system)\ntotal:%.2lfs\n", rcputime(rusage, ru_utime), rcputime(rusage, ru_stime), rcputime(rusage, ru_utime) + rcputime(rusage, ru_stime));
for(;;)
if(waitpid(-1, NULL, 0) == -1 && errno == 10) break;
CHECK(getrusage(RUSAGE_CHILDREN, &rusage) != -1);
printf(""
"child use:\n"
"\tcpu time:\n"
"\t\tcpu time:%.2lfs (user)\n"
"\t\tcpu time:%.2lfs (system)\n"
"\ttotal:%.2lfs\n"
"\tMax resident set:%ldKB\n"
"\tIntegral shared text mem:%ldkB/s\n"
"\tIntegral shared data mem:%ldkB/s\n"
"\tIntegral shared stack mem:%ldkB/s\n"
"\tsoft page fault:%ld\n"
"\thard page fault:%ld\n"
"\tswaps out of physical mem:%ld\n"
"\tfile input block:%ld\n"
"\tfile output block:%ld\n"
"\tIPC msg send:%ld\n"
"\tIPC msg recv:%ld\n"
"\tsignal recv:%ld\n"
"\tvoluntary context switch:%ld\n"
"\tinvoluntary context switch:%ld\n",
rcputime(rusage, ru_utime),
rcputime(rusage, ru_stime),
rcputime(rusage, ru_utime) + rcputime(rusage, ru_stime),
rusage.ru_maxrss,
rusage.ru_ixrss,
rusage.ru_idrss,
rusage.ru_isrss,
rusage.ru_minflt,
rusage.ru_majflt,
rusage.ru_nswap,
rusage.ru_inblock,
rusage.ru_oublock,
rusage.ru_msgsnd,
rusage.ru_msgrcv,
rusage.ru_nsignals,
rusage.ru_nvcsw,
rusage.ru_nivcsw);
}

36.2

编写一个程序来执行一个命令,接着显示其当前的资源使用。这个程序与 time(1)
命令的功能类似,因此可以像下面这样使用这个程序:

1
$ ./rusage command arg...
阅读更多

cha35.进程优先级和调度

读书笔记

nice

nice值形象的说是一个进程的友好(nice)程度,越nice(nice值数值越大的)的进程越礼让,多个任务争用cpu的时候越礼让。(本书中高nice值表示nice值数值更低,更不nice

  • 取值范围为(-20(不友好,高优先级)-19(友好,低优先级))
  • fork、exec时继承nice值

RLIMIT_NICE

资源限制,特权进程最高可以将nice值提升到20-RLIMIT_NICE

阅读更多

cha34.进程组、会话和作业控制

读书笔记

34.1

假设一个父进程执行了下面的步骤。
这个应用程序设计可能会碰到什么问题?考虑 shell 管道。
如何避免此类问题的发生?

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
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>

void handler(int sig){
printf("%d, received %d(%s)\n", getpid(), sig, strsignal(sig));
}

int main() {
sigset_t set;
sigemptyset(&set);
sigaddset(&set, SIGUSR1);
signal(SIGUSR1, handler);
for(int i = 0; i < 10; i++) {
pid_t pid;
if((pid = fork()) == 0) {
pause();
return 0;
} else {
printf("child-%d: %d\n", i, pid);
}
}
signal(SIGUSR1, SIG_IGN);
killpg(getpgrp(), SIGUSR1);
waitpid(-1, NULL, 0);
}
  • 假设不发送SIGUSER时
    • 当不使管道时,可以看到有小于等于10个子进程显示收到SIGUSER1
    • 如果使用管道,会发现child-%d: %d被重复输出了多次,这是由于stdout被重定向后变成了全缓冲,fork的子进程退出后fflush()时会将fork前未flush的数据打印出来。
  • 假设执行该命令./practice34.1 | cat,输出会是User defined signal 1,即cat与程序同属一个进程组,cat也会收到该信号,导致无法正常执行(非预期内)

避免

阅读更多

cha33.线程:更多细节

33.1

编写程序以便证明:作为函数sigpending()的返回值,同一个进程中的的不同线程可以拥有不同的 pending信号。可以使用函数pthread_kill(分别发送不同的信号给阻塞这些信号的两个不同的线程,接着调用sigpending()方法并显示这些pending信号的信息。(可能会发现程序清单20-4中函数的作用。)

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
//
// Created by root on 7/2/23.
//
#define _GNU_SOURCE
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>

#define CHECK(x) do { if(!(x)) { fprintf(stderr, "ERROR: %s\nfile=%s, line=%d\n", strerror(errno), __FILE__, __LINE__); exit(1); } } while(0)
#define THREAD_CNT 5
pthread_mutex_t sig_mutex;
pthread_cond_t sig_cond;
int thread_ready_count;

void handler(int sig, siginfo_t *info, void *context) {
printf("recv sig = %d: %s, from = %d\n", sig, strsignal(sig), info->si_value.sival_int);
}

pthread_t tid2pthread[THREAD_CNT];

int tid2sig(int tid) {
return SIGRTMAX - 1 - tid;
}

void *fun_thread(void *arg) {
int tid = *(int *)arg;
free(arg);
sigset_t set;
CHECK(sigemptyset(&set) == 0);
CHECK(sigaddset(&set, tid2sig(tid)) == 0);
CHECK(pthread_sigmask(SIG_SETMASK, &set, NULL) == 0);
CHECK(sigaction(tid2sig(tid), &(struct sigaction) {
.sa_sigaction =handler,
.sa_flags = SA_SIGINFO,
.sa_mask = 0
}, NULL) == 0);
printf("Thread-%d, sigaction %d\n", tid, tid2sig(tid));
CHECK(pthread_mutex_lock(&sig_mutex) == 0);
thread_ready_count++;
tid2pthread[tid] = pthread_self();
CHECK(pthread_mutex_unlock(&sig_mutex) == 0);
CHECK(pthread_cond_broadcast(&sig_cond) == 0);

CHECK(pthread_mutex_lock(&sig_mutex) == 0);
while(thread_ready_count < THREAD_CNT){
CHECK(pthread_cond_wait(&sig_cond, &sig_mutex) == 0);
}
CHECK(pthread_mutex_unlock(&sig_mutex) == 0);
int sig = tid2sig((tid + 1) % THREAD_CNT);
printf("Thread-%d send %d(%s) to %d\n", tid, sig, strsignal(sig), (tid + 1) % THREAD_CNT);
CHECK(pthread_sigqueue(tid2pthread[(tid + 1) % THREAD_CNT], sig, (union sigval) {
.sival_int=tid
}) == 0);
do {
printf("Thread-%d sig not pending %d\n", tid, tid2sig(tid));
sleep(1);
CHECK(sigemptyset(&set) == 0);
CHECK(sigpending(&set) == 0);
} while(sigismember(&set, tid2sig(tid)) == 0);
printf("Thread-%d sig pending %d\n", tid, tid2sig(tid));
CHECK(sigemptyset(&set) == 0);
CHECK(pthread_sigmask(SIG_SETMASK, &set, NULL) == 0);
CHECK(sigpending(&set) == 0);
sig = -1;
while(sig != tid2sig(tid)) CHECK(sigwait(&set, &sig) == 0);
printf("Thread-%d exit\n", tid);
return NULL;
}

int main(int argc, char *argv[]) {
CHECK(pthread_mutex_init(&sig_mutex, NULL) == 0);
CHECK(pthread_cond_init(&sig_cond, NULL) == 0);
thread_ready_count = 0;
for(int i = 0; i < THREAD_CNT; i++) {
pthread_t thread;
int *data = malloc(sizeof(int));
*data = i;
CHECK(pthread_create(&thread, NULL, fun_thread, data) == 0);
}
pthread_exit(NULL);
}

33.2

假设一个线程使用fork()创建了一个子进程。当子进程终止时,可以保证由此产生的SIGCHLD信号一定会发送给调用fork()的线程吗(可以用进程中的其他线程做对比)?

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
//
// Created by root on 7/2/23.
//
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>

#define CHECK(x) do { if(!(x)) { fprintf(stderr, "ERROR: %s\nfile=%s, line=%d\n", strerror(errno), __FILE__, __LINE__); exit(1); } } while(0)

sigset_t set;

void handler(int sig, siginfo_t *info, void *context) {
printf("recv sig = %d: %s, my tid is %ld\n", sig, strsignal(sig), pthread_self());
}

void *fn(void *arg) {
CHECK(sigaction(SIGCHLD, &(struct sigaction) {
.sa_mask = 0,
.sa_flags = SA_SIGINFO,
.sa_sigaction=handler
}, NULL) == 0);
if(*(int *)arg) {
printf("I am %ld, I fork\n", pthread_self());
if(!fork()) {
exit(0);
}
// wait(NULL);
} else {
printf("I am %ld, I sleep\n", pthread_self());
sleep(10);
}
free(arg);
return NULL;
}

int main() {
CHECK(sigemptyset(&set) == 0);
CHECK(sigaddset(&set, SIGCHLD) == 0);
CHECK(sigaction(SIGCHLD, &(struct sigaction) {
.sa_mask = 0,
.sa_flags = SA_SIGINFO,
.sa_sigaction=handler
}, NULL) == 0);
pthread_t tid;
int ok = 0;
CHECK(pthread_create(&tid, NULL, fn, memcpy(malloc(sizeof(int)), &ok, sizeof(int))) == 0);
ok = 1;
CHECK(pthread_create(&tid, NULL, fn, memcpy(malloc(sizeof(int)), &ok, sizeof(int))) == 0);
pthread_exit(NULL);
}
阅读更多

cha32.线程取消

读书笔记

带参宏

从作用域的角度上看,带参宏和函数的区别:

  • 函数有新的函数栈,而带参宏没有,因而带参宏在不同作用域调用时,会受到作用域的影响。如在宏A中的定义的变量,也会影响后续。
  • 如果一对作用相反的带参宏,如本节的pthread_cleanup_pushpthread_cleanup_pop,很多实现都是使用带参宏,那么这两个宏调用时必须属于同一个代码块

    ?,保证push的作用域包含pop不就好了吗

线程取消

线程取消状态可设置为启用和禁用。

阅读更多

cha31.线程安全和每线程存储

读书笔记

  • pthread提供了一种所有线程只执行一次(用于所有线程只初始化一次)的方法pthread_once(pthread_once_t*, void (*)(void));
  • pthread提供了每线程存储,即每个线程有独立与其他线程的存储,使用k-v存储
    • int pthread_key_create(pthread_key_t *, void (*)(void *));每个线程都存储一份,第一个参数为一个全局变量的指针,第二个参数为线程终止时自动调用的析构函数
    • int pthread_setspecific(pthread_key_t, const void *)指定key所对应的内存区域,进程终止时会将第二个参数送入析构函数
    • void * pthread_getspecific(pthread_key_t)获取key所对应的内存区域

31.1

实现pthread_once

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
//
// Created by root on 6/29/23.
//

#include <pthread.h>
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>

void init() {
printf("init\n");
}
typedef struct {
pthread_mutex_t mutex;
bool call;
} _pthread_once_t;
_pthread_once_t once = {
.mutex=PTHREAD_MUTEX_DEFAULT,
.call=false
};
int _pthread_once(_pthread_once_t *once_ctrl, void (*init)(void)) {
bool ok;
if(pthread_mutex_lock(&once_ctrl->mutex) == 0) {
ok = !once_ctrl->call;
once_ctrl->call = true;
pthread_mutex_unlock(&once_ctrl->mutex);
} else {
return -1;
}
if(ok) {
init();
}
return 0;
}

void *fun(void *arg) {
printf("Thread-%d start\n", *(int*)arg);
if(_pthread_once(&once, init) == 0){
printf("Thread-%d _pthread_once Success\n", *(int*)arg);
}
free(arg);
return NULL;
}

int main() {
pthread_t t;
for(int i = 1; i <= 100; i++)
pthread_create(&t, NULL, fun, memcpy(malloc(sizeof(int)), &i, sizeof(int)));
pthread_exit(NULL);
}

31.2

阅读更多