binder

binder

1. binder demo

c++实现

1
2
3
4
5
6
7
8
9
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE := binder
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := binder.cpp
LOCAL_SHARED_LIBRARIES := libutils libcutils libbinder liblog
LOCAL_C_INCLUDES += frameworks/native/include system/core/include
include $(BUILD_EXECUTABLE)
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
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*-
vi:ai:tabstop=8:shiftwidth=4:softtabstop=4:expandtab
*/

/*
* Author: Gabriel Burca <gburca dash binder at ebixio dot com>
*
* Sample code for using binders in Android from C++
*
* The Demo service provides 3 operations: push(), alert(), add(). See
* the IDemo class documentation to see what they do.
*
* Both the server and client code are included below.
*
* To view the log output:
* adb logcat -v time binder_demo:* *:S
*
* To run, create 2 adb shell sessions. In the first one run "binder" with no
* arguments to start the service. In the second one run "binder N" where N is
* an integer, to start a client that connects to the service and calls push(N),
* alert(), and add(N, 5).
*/

#define LOG_TAG "binder_demo"

/* For relevant code see:
frameworks/native/{include,libs}/binder/{IInterface,Parcel}.{h,cpp}
system/core/include/utils/{Errors,RefBase}.h
*/

#include <stdlib.h>

#include <utils/RefBase.h>
#include <utils/Log.h>
#include <binder/TextOutput.h>

#include <binder/IInterface.h>
#include <binder/IBinder.h>
#include <binder/ProcessState.h>
#include <binder/IServiceManager.h>
#include <binder/IPCThreadState.h>

using namespace android;


#define INFO(...) \
do { \
printf(__VA_ARGS__); \
printf("\n"); \
ALOGD(__VA_ARGS__); \
} while(0)

void assert_fail(const char *file, int line, const char *func, const char *expr) {
INFO("assertion failed at file %s, line %d, function %s:",
file, line, func);
INFO("%s", expr);
abort();
}

#define ASSERT(e) \
do { \
if (!(e)) \
assert_fail(__FILE__, __LINE__, __func__, #e); \
} while(0)


// Where to print the parcel contents: aout, alog, aerr. alog doesn't seem to work.
#define PLOG aout



// Interface (our AIDL) - Shared by server and client
class IDemo : public IInterface {
public:
enum {
ALERT = IBinder::FIRST_CALL_TRANSACTION,
PUSH,
ADD
};
// Sends a user-provided value to the service
virtual void push(int32_t data) = 0;
// Sends a fixed alert string to the service
virtual void alert() = 0;
// Requests the service to perform an addition and return the result
virtual int32_t add(int32_t v1, int32_t v2) = 0;

DECLARE_META_INTERFACE(Demo); // Expands to 5 lines below:
//static const android::String16 descriptor;
//static android::sp<IDemo> asInterface(const android::sp<android::IBinder>& obj);
//virtual const android::String16& getInterfaceDescriptor() const;
//IDemo();
//virtual ~IDemo();
};

// Client
class BpDemo : public BpInterface<IDemo> {
public:
BpDemo(const sp<IBinder>& impl) : BpInterface<IDemo>(impl) {
ALOGD("BpDemo::BpDemo()");
}

virtual void push(int32_t push_data) {
Parcel data, reply;
data.writeInterfaceToken(IDemo::getInterfaceDescriptor());
data.writeInt32(push_data);

aout << "BpDemo::push parcel to be sent:\n";
data.print(PLOG); endl(PLOG);

remote()->transact(PUSH, data, &reply);

aout << "BpDemo::push parcel reply:\n";
reply.print(PLOG); endl(PLOG);

ALOGD("BpDemo::push(%i)", push_data);
}

virtual void alert() {
Parcel data, reply;
data.writeInterfaceToken(IDemo::getInterfaceDescriptor());
data.writeString16(String16("The alert string"));
remote()->transact(ALERT, data, &reply, IBinder::FLAG_ONEWAY); // asynchronous call
ALOGD("BpDemo::alert()");
}

virtual int32_t add(int32_t v1, int32_t v2) {
Parcel data, reply;
data.writeInterfaceToken(IDemo::getInterfaceDescriptor());
data.writeInt32(v1);
data.writeInt32(v2);
aout << "BpDemo::add parcel to be sent:\n";
data.print(PLOG); endl(PLOG);
remote()->transact(ADD, data, &reply);
ALOGD("BpDemo::add transact reply");
reply.print(PLOG); endl(PLOG);

int32_t res;
status_t status = reply.readInt32(&res);
ALOGD("BpDemo::add(%i, %i) = %i (status: %i)", v1, v2, res, status);
return res;
}
};

//IMPLEMENT_META_INTERFACE(Demo, "Demo");
// Macro above expands to code below. Doing it by hand so we can log ctor and destructor calls.
const android::String16 IDemo::descriptor("Demo");
const android::String16& IDemo::getInterfaceDescriptor() const {
return IDemo::descriptor;
}
android::sp<IDemo> IDemo::asInterface(const android::sp<android::IBinder>& obj) {
android::sp<IDemo> intr;
if (obj != NULL) {
intr = static_cast<IDemo*>(obj->queryLocalInterface(IDemo::descriptor).get());
if (intr == NULL) {
intr = new BpDemo(obj);
}
}
return intr;
}
IDemo::IDemo() { ALOGD("IDemo::IDemo()"); }
IDemo::~IDemo() { ALOGD("IDemo::~IDemo()"); }
// End of macro expansion

// Server
class BnDemo : public BnInterface<IDemo> {
virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0);
};

status_t BnDemo::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
ALOGD("BnDemo::onTransact(%i) %i", code, flags);
data.checkInterface(this);
data.print(PLOG); endl(PLOG);

switch(code) {
case ALERT: {
alert(); // Ignoring the fixed alert string
return NO_ERROR;
} break;
case PUSH: {
int32_t inData = data.readInt32();
ALOGD("BnDemo::onTransact got %i", inData);
push(inData);
ASSERT(reply != 0);
reply->print(PLOG); endl(PLOG);
return NO_ERROR;
} break;
case ADD: {
int32_t inV1 = data.readInt32();
int32_t inV2 = data.readInt32();
int32_t sum = add(inV1, inV2);
ALOGD("BnDemo::onTransact add(%i, %i) = %i", inV1, inV2, sum);
ASSERT(reply != 0);
reply->print(PLOG); endl(PLOG);
reply->writeInt32(sum);
return NO_ERROR;
} break;
default:
return BBinder::onTransact(code, data, reply, flags);
}
}

class Demo : public BnDemo {
virtual void push(int32_t data) {
INFO("Demo::push(%i)", data);
}
virtual void alert() {
INFO("Demo::alert()");
}
virtual int32_t add(int32_t v1, int32_t v2) {
INFO("Demo::add(%i, %i)", v1, v2);
return v1 + v2;
}
};


// Helper function to get a hold of the "Demo" service.
sp<IDemo> getDemoServ() {
sp<IServiceManager> sm = defaultServiceManager();
ASSERT(sm != 0);
sp<IBinder> binder = sm->getService(String16("Demo"));
// TODO: If the "Demo" service is not running, getService times out and binder == 0.
ASSERT(binder != 0);
sp<IDemo> demo = interface_cast<IDemo>(binder);
ASSERT(demo != 0);
return demo;
}


int main(int argc, char **argv) {

if (argc == 1) {
ALOGD("We're the service");

defaultServiceManager()->addService(String16("Demo"), new Demo());
android::ProcessState::self()->startThreadPool();
ALOGD("Demo service is now ready");
IPCThreadState::self()->joinThreadPool();
ALOGD("Demo service thread joined");
} else if (argc == 2) {
INFO("We're the client: %s", argv[1]);

int v = atoi(argv[1]);

sp<IDemo> demo = getDemoServ();
demo->alert();
demo->push(v);
const int32_t adder = 5;
int32_t sum = demo->add(v, adder);
ALOGD("Addition result: %i + %i = %i", v, adder, sum);
}

return 0;
}

/*
Single-threaded service, single-threaded client.
*/

java实现

1
2
3
4
5
// ICompute.aidl
package com.example.test.app;
interface ICompute {
int add(int a, int b);
}
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
package com.example.test.app;

public interface ICompute extends android.os.IInterface {
/**
* Local-side IPC implementation stub class.
*/
public static abstract class Stub extends android.os.Binder implements com.example.test.app.ICompute {
private static final java.lang.String DESCRIPTOR = "com.example.test.app.ICompute";

/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}

/**
* Cast an IBinder object into an com.example.test.app.ICompute interface,
* generating a proxy if needed.
*/
public static com.example.test.app.ICompute asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.example.test.app.ICompute))) {
return ((com.example.test.app.ICompute) iin);
}
return new com.example.test.app.ICompute.Stub.Proxy(obj);
}

@Override
public android.os.IBinder asBinder() {
return this;
}

@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_add: {
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
int _result = this.add(_arg0, _arg1);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}

private static class Proxy implements com.example.test.app.ICompute {
private android.os.IBinder mRemote;

Proxy(android.os.IBinder remote) {
mRemote = remote;
}

@Override
public android.os.IBinder asBinder() {
return mRemote;
}

public java.lang.String getInterfaceDescriptor() {
return DESCRIPTOR;
}

/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
@Override
public int add(int a, int b) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(a);
_data.writeInt(b);
mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}

static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}

/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
public int add(int a, int b) throws android.os.RemoteException;
}

Makefile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
objects = main.o Board.o Node.o Stone.o Utils.o

game : $(objects)
g++ -o game $(objects)

main.o : main.cpp Board.h
Board.o : Board.cpp Board.h Node.h Stone.h Utils.h
Node.o : Node.cpp Node.h Stone.h Utils.h
utils.o : Utils.cpp Utils.h
Stone.o : Stone.cpp Stone.h

.PHONY : clean
clean :
rm game $(objects)

linux-unix系统编程手册

0. 前置

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
0. 因为想了解linux的系统调用,所以查到这本书
1. 下载这本书的pdf
2. 快速扫过前三章,基本无感,当看到第4章,看到有代码,于是然后看到#include "tlpi_hdr.h", 所以google到了官网
https://www.man7.org/tlpi/index.html
3. 下载源码并编译
1. 在根目录直接敲make,提示没../libtlpi.a, 官网说要先在lib目录下make
2. 在lib目录下make,提示没有头文件userns_functions.c:25:10: fatal error: sys/capability.h: No such file or directory, 解决办法在FAQ中,apt install libcap-dev
3. 再次在根目录make
4. 再次回到,./fileio/copy.c的代码,很奇怪,为什么#include "tlpi_hdr.h"
没带全路径,因为它不在当前目录,这他妈是怎么找到的?
看了Makefile,实在看不懂,于是找了个教程
https://seisman.github.io/how-to-write-makefile/Makefile.pdf
5. 苦逼的翻过80页的教程,再去看项目里的Makefile。才知道,CFLAGS中,包含查找头文件的目录 -I${TLPI_INCL_DIR}, 由于隐式规则,copy这个目标的命令应该用的cc –c $(CFLAGS) copy.c
6. 终于可以愉快的开始了,fucking!!
7. 又有问题,在我自己的demo目录,提示链接失败,因此又配置了
CFLAGS = -std=c99 -D_XOPEN_SOURCE=600 \
-D_DEFAULT_SOURCE \
-g -I${TLPI_INCL_DIR} \
-L${TLPI_LIB} \
-pedantic \
-Wall \
-W \
-Wmissing-prototypes \
-Wno-sign-compare \
-Wimplicit-fallthrough \
-Wno-unused-parameter
LDLIBS = -ltlpi

4. 文件I/O:通用的I/O模型

1
2
3
// open, read, write, close
// lseek
// ioctl
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
// 文件拷贝
// open, read, write, close
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "tlpi_hdr.h"

int main(int argc, char* argv[]) {
if (argc != 3) {
usageErr("error:%d\n", argc);
}

if (strcmp(argv[1], "--help") == 0) {
usageErr("error: No support '--help'\n");
}

int ifd;
int ofd;

ifd = open(argv[1], O_RDONLY);
if (ifd < 0) {
errExit("error: open fd=%d fail!", ifd);
}

int filePerms = S_IRUSR | S_IWUSR;
ofd = open(argv[2], O_WRONLY | O_CREAT | O_APPEND, filePerms);
if (ofd < 0) {
errExit("error: open fd=%d fail!", ofd);
}

int readcount = 0;
char buf[1024];
while ((readcount = read(ifd, buf, 1024)) > 0) {
printf("readcount=%d\n", readcount);
if (write(ofd, buf, readcount) != readcount) {
fatal("error: fatal");
}
}

if (readcount == -1) {
errExit("read");
}

if (close(ifd) != 0) {
errExit("error: close error fd=%d\n", ifd);
}

if (close(ofd) != 0) {
errExit("error: close error fd=%d\n", ofd);
}

return 0;
}
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
// tee test
// test -a test
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include "tlpi_hdr.h"

#define BUF_SIZE 1024

int main(int argc, char* argv[]) {
if (argc > 3) {
usageErr("argv too long");
}

int ifd = 0;
int ofd = 1;
if (argc >= 2) {
char* filename = argv[1];
int mode = O_CREAT | O_WRONLY;
if (argc == 3 && strcmp(argv[1], "-a") == 0) {
filename = argv[2];
mode |= O_APPEND;
}

int perms = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP;
if ((ofd = open(filename, mode, perms)) == -1) {
errExit("open file %s failed!", filename);
}
}

ssize_t numRead;
char buf[BUF_SIZE];
while ((numRead = read(ifd, buf, BUF_SIZE)) > 0) {
if (write(ofd, buf, numRead) != numRead) {
fatal("write");
}
}

if (numRead == -1) {
errExit("read");
}

if (close(ifd) == -1) {
errExit("close ifd");
}

if (close(ofd) == -1) {
errExit("close ofd");
}

return 0;
}
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
// 文件空洞
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include "tlpi_hdr.h"

void writeText(int, char*);

int main(int argc, char* argv[]) {
int fd = open("hole", O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR);
if (fd == -1) {
errExit("open");
}

writeText(fd, "hello");
if (lseek(fd, 10000, SEEK_CUR) == -1) {
errExit("lseek");
}
writeText(fd, "world");

if (close(fd) == -1) {
errExit("close");
}

return 0;
}

void writeText(int fd, char* buf) {
int size = strlen(buf);
if (write(fd, buf, size) != size) {
errExit("write");
}
}

5. 文件操作,更多特性

  1. O_EXCL | O_CREAT, 保证了创建和打开的原子性,如果打开的文件以经存在,则会报错. 场景:当两个进程同时打开一个fd时

  2. O_APPEND, 保证了seek和write的原子性

  3. 获取/设置一个已经打开的fd的标记位

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
#include <sys/stat.h>
#include <fcntl.h>
#include "tlpi_hdr.h"

void lookup(int);

int main(int argc, char* argv[]) {
int fd = open("fcntl.c", O_WRONLY);
if (fd == -1) {
errExit("open");
}

int flags;
flags = fcntl(fd, F_GETFL);
if (flags == -1) {
errExit("fnctl");
}
lookup(flags);

flags |= O_APPEND;
if (fcntl(fd, F_SETFL, flags) == -1) {
errExit("fnctl");
}

flags = fcntl(fd, F_GETFL);
if (flags == -1) {
errExit("fnctl");
}
lookup(flags);

return 0;
}

void lookup(int flags) {

if (flags & O_SYNC) {
printf("O_SYNC is open\n");
}

if (flags & O_APPEND) {
printf("O_APPEND is open\n");
}

int accmode = flags & O_ACCMODE;
if (accmode == O_RDONLY) {
printf("O_RDONLY is open\n");
}

if (accmode == O_WRONLY) {
printf("O_WRONLY is open\n");
}

if (accmode == O_RDWR) {
printf("O_RDWR is open\n");
}

printf("----finish\n");
}

4. 复制fd(dup, dup2, dup3)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 把标准输出重定向到日志文件
#include <sys/stat.h>
#include <fcntl.h>
#include "tlpi_hdr.h"

int main(int argc, char* argv[]) {
int perms = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP;
int ofd = open(argv[1], O_CREAT | O_WRONLY | O_APPEND, perms);
if (ofd == -1) {
errExit("open");
}

if (close(1) == -1) {
errExit("close");
}

if (dup2(ofd, 1) == -1) {
errExit("dup");
}

printf("hello world dup2\n");
return 0;
}

5. 带偏移的原子读写, pread/pwrite

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
// 
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include "tlpi_hdr.h"

int main(int argc, char* argv[]) {
int flags = O_CREAT | O_EXCL | O_RDWR;
int perms = S_IRUSR | S_IWUSR | S_IRGRP | S_IRGRP;
int fd = open(argv[1], flags, perms);
if (fd == -1) {
errExit("open");
}

char* buf = "hello";
if (pwrite(fd, buf, strlen(buf), 100) == -1) {
errExit("pwrite");
}

char* buf1 = malloc(10);
if (pread(fd, buf1, 10, 101) == -1) {
errExit("pread");
}
printf("pread:%s\n", buf1);
return 0;
}

6. 分散缓冲区的集中原子读写,readv/writev

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
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/uio.h>
#include "tlpi_hdr.h"

struct Cat {
char name[10];
int age;
};

void try_write(int fd);
void try_read(int fd);

int main(int argc, char* argv[]) {
if (argc != 3) {
usageErr("Error: argc is not 3\n");
}

int flags = O_CREAT | O_RDWR;
int perms = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP;
int fd = open(argv[2], flags, perms);
if (fd == -1) {
errExit("open");
}

if (strcmp(argv[1], "r") == 0) {
try_read(fd);
} else {
try_write(fd);
}

if (close(fd) == -1) {
errExit("close");
}
return 0;
}

void try_write(int fd) {
struct iovec data[2];

struct Cat cat = { "mimi", 10 };
data[0].iov_base = &cat;
data[0].iov_len = sizeof(struct Cat);

int x = 99;
data[1].iov_base = &x;
data[1].iov_len = sizeof(int);

if (writev(fd, data, 2) == -1) {
errExit("writev");
}
}

void try_read(int fd) {
struct iovec data[2];

struct Cat cat;
data[0].iov_base = &cat;
data[0].iov_len = sizeof(struct Cat);

int x = 0;
data[1].iov_base = &x;
data[1].iov_len = sizeof(int);

if (readv(fd, data, 2) == -1) {
errExit("readv");
}

printf("cat:%s, %d\n", cat.name, cat.age);
printf("x:%d\n", x);
}

7. 设置文件大小,多退少补,truncate/ftruncate

1
2
3
4
5
6
7
8
9
#include <unistd.h>
#include <stdio.h>

int main(int argc, char* argv[]) {
if (truncate(argv[1], 1024) == -1) {
printf("error");
}
return 0;
}

8. 非阻塞IO标记,O_NONBLOCK

1
// 如果陷入阻塞,则直接返回错误EAGAIN

9. 大文件标记,O_LARGEFILE

1
// 如果open的文件大于2GB,且没有此标记,则直接报错

10. 临时文件,mkstemp/tmpfile

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
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include "tlpi_hdr.h"

void try_mkstemp();
void try_tmpfile();

int main(int argc, char* argv[]) {
try_tmpfile();
return 0;
}

void try_mkstemp() {
// XXXXXX必须大写
char name[] = "/tmp/tmpfileXXXXXX";
int fd;
if ((fd = mkstemp(name)) == -1) {
errExit("error mkstemp");
}

printf("tmp name:%s\n", name);
unlink(name);

if (close(fd) == -1) {
errExit("error mkstemp");
}
}

void try_tmpfile() {
FILE* f = tmpfile();
int fd = f->_fileno;
printf("fd:%d\n", fd);


char* buf = "helloabcdefghijklmn";
if (write(fd, buf, strlen(buf)) == -1) {
errExit("write");
}

int pos;
if ((pos = lseek(fd, 0, SEEK_SET)) == -1) {
errExit("lseek");
}

int numread;
char* rbuf = malloc(10);
if ((numread = read(fd, rbuf, 10)) == -1) {
errExit("read");
}

printf("rbuf:%d %s\n", numread, rbuf);

if (close(fd) == -1) {
errExit("close");
}
}

11. getpid/getppid

1
如果一个进程的父进程被杀,则子进程归属到init进程

12. getenv/setenv/clearenv/

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include "tlpi_hdr.h"

extern char** environ;

int main(int argc, char* argv[], char* envp[]) {
// 清除所有环境变量
clearenv();
for (char** one=environ; one&&*one; one++) {
printf("%s\n", *one);
}

if (setenv("ming", "hello", 1) == -1) {
errExit("setenv");
}
printf("----->%s\n", getenv("ming"));
if (unsetenv("ming") == -1) {
errExit("unsetenv");
}
printf("----->%s\n", getenv("ming"));

return 0;
}

13. setjmp/longjmp

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
#include "tlpi_hdr.h"
#include <setjmp.h>

jmp_buf env;

void f2(void) {
longjmp(env, 2);
printf("f2 finish\n");// 不会打印
}

void f1(int argc) {
if (argc == 1) {
longjmp(env, 1);
}
f2();
printf("f1 finish\n");// 不会打印
}

int main(int argc, char* argv[]) {
printf("setjmp\n");
switch(setjmp(env)) {
case 0:
printf("first jmp\n");
f1(argc);
break;
case 1:
printf("second jmp from f1\n");
break;
case 2:
printf("third jmp from f2\n");
break;
}
printf("finish\n");
return 0;
}

14. brk/sbrk

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
#include <unistd.h>

int main(int argc, char* argv[]) {
void* p1 = sbrk(0);
printf("cur=%p\n", p1);

//char* buf = malloc(0x10);
brk(p1+1024);

void* p2 = sbrk(0);
printf("cur=%p\n", p2);

printf("dx=%ld\n", (p2-p1));
return 0;
}

//cur=0x55e333f69000
//cur=0x55e333f69400
//dx=1024

15. malloc/free

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, char* argv[]) {
printf("cur=%p\n", sbrk(0));

void* buf = malloc(100);
printf("cur=%p\n", sbrk(0));

void* buf1 = malloc(200);
printf("cur=%p\n", sbrk(0));

return 0;
}

//cur=0x55608a112000
//cur=0x55608a133000
//cur=0x55608a133000

16. calloc/realloc

17. getpwnam/getpwuid/getgrnam/getgrgid

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <stdio.h>
#include <pwd.h>

int main(int argc, char* argv[]) {
struct passwd* pwd = getpwnam("ming");
printf("uid:%d\n", pwd->pw_uid);
printf("name:%s\n", pwd->pw_name);
printf("dir:%s\n", pwd->pw_dir);
printf("shell:%s\n", pwd->pw_shell);

struct passwd* pwd1 = getpwuid(pwd->pw_uid);
printf("name:%s\n", pwd1->pw_name);
printf("dir:%s\n", pwd->pw_dir);
printf("shell:%s\n", pwd->pw_shell);
return 0;
}

18. 登陆校验,crypt/getpass

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
#include <stdio.h>
#include "tlpi_hdr.h"
#include <pwd.h>
#include <string.h>
#include <shadow.h>
#include <unistd.h>
#include <crypt.h>

int main(int argc, char* argv[]) {

printf("username:");

char* username = malloc(8);
memset(username, 0, 8);
if (fgets(username, 8, stdin) == NULL) {
errExit("fgets");
}

int len = strlen(username);
if (username[len-1] == '\n') {
username[len-1] = '\0';
}

struct passwd* pwd = getpwnam(username);
if (pwd == NULL) {
errExit("getpwnam");
}
struct spwd* sp = getspnam(pwd->pw_name);
if (sp == NULL) {
errExit("getpwnam");
}
printf("sp=%s\n", sp->sp_pwdp);

char* password = getpass("password:");
char* encrypted = crypt(password, sp->sp_pwdp);
if (0 == strcmp(encrypted, sp->sp_pwdp)) {
printf("welcome %s\n", username);
} else {
printf("username or password error!\n");
}
return 0;
}

19. 实现getpwnam()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <stdio.h>
#include <pwd.h>
#include <string.h>

struct passwd *getpwnam(const char *name);

int main(int argc, char* argv[]) {
struct passwd *pwd = getpwnam("ming");
printf("dir=%s\n", pwd->pw_dir);
return 0;
}

struct passwd *getpwnam(const char *name) {
struct passwd *pwd = NULL;
while ((pwd = getpwent()) != NULL) {
if (strcmp(name, pwd->pw_name) == 0) {
break;
}
}
endpwent();
return pwd;
}

20. setresuid/setfsuid

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
// 1. 实际用户ID
// 2. 有效用户ID
// 3. 保存用户ID
// 4. 文件系统用户ID


// aaa文件,只对1000用户有读写权限
// bbb文件,只对1001用户有读写权限
// 如果有效用户ID是1001,则无法open aaa

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include "tlpi_hdr.h"

void show_resuid();
void try_read_file(const char* name);

int main(int argc, char* argv[]) {
show_resuid();

try_read_file("aaa");
try_read_file("bbb");

// 修改有效用户id
if (setresuid(-1, 1001, -1) == -1) {
errExit("setresuid");
}
show_resuid();
try_read_file("aaa");
try_read_file("bbb");

// 尝试改回root, 如果ruid/euid/suid中有一个是0,则可用改回root,否则会失败
if (setresuid(-1, 1001, -1) == -1) {
errExit("setresuid");
}
show_resuid();
return 0;
}

void try_read_file(const char* name) {
printf("read %s\n", name);
int fd = open(name, O_RDONLY);
if (fd == -1) {
errMsg("open");
return;
}

char buf[10];
if (read(fd, buf, 10) == -1) {
errMsg("read");
}
printf("read file=%s\n", buf);

if (close(fd) == -1) {
errExit("close");
}
}

void show_resuid() {
// 获取实际用户id,有效用户id,保存用户id
uid_t ruid, euid, suid;
if (getresuid(&ruid, &euid, &suid) == -1) {
errExit("getresuid");
}
printf("%d, %d, %d\n", ruid, euid, suid);
}

21. 日历时间转换

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
#include <stdio.h>
#include <time.h>
#include <sys/time.h>
#include "tlpi_hdr.h"

#define TIME_LEN 100

int main(int argc, char* argv[]) {
struct timeval tv;
int r = gettimeofday(&tv, NULL);
if (r == -1) {
errMsg("gettimeofday");
}
printf("cal=%ld, %ld\n", tv.tv_sec, tv.tv_usec);

// 获取当前UTC秒数
time_t t = time(NULL);
printf("t=%ld\n", t);

// Sun May 9 10:11:32 2021
char* timestr = ctime(&t);
printf("%s\n", timestr);

// 以GMT时区分解时间2:18
struct tm *tmp = gmtime(&t);
printf("hour=%d:%d\n", tmp->tm_hour, tmp->tm_min);

// 以本地时区分解时间10:18
struct tm *ltmp = localtime(&t);
printf("hour=%d:%d\n", ltmp->tm_hour, ltmp->tm_min);

// 合并分解时间
// 增加10秒
ltmp->tm_sec += 10;
time_t mkt = mktime(ltmp);
printf("mkt=%ld\n", mkt);

// 把分解时间转成字符串
char* tstr = asctime(ltmp);
printf("tstr=%s\n", tstr);

// 格式化时间
char tbuf[TIME_LEN];
strftime(tbuf, TIME_LEN, "%Z %Y %X", ltmp);
printf("%s\n", tbuf);

return 0;
}

21. 软件时钟(jiffies)

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
```

### 22. 进程时间, times/clock
```c
// 12-1
#include <stdio.h>
#include <pwd.h>
#include <dirent.h>
#include <ctype.h>
#include "tlpi_hdr.h"
#include "ps.h"

int main(int argc, char* argv[]) {
if (argc < 2) {
errExit("Not found username");
}

char* username = argv[1];
int uid = get_uid_by_name(username);
if (uid == -1) {
errExit("get uid failed");
}

DIR *rootdir = opendir(ROOT_DIR);
if (rootdir == NULL) {
errExit("opendir");
}

struct dirent *dir;
while ((dir = readdir(rootdir)) != NULL) {
if (dir->d_type != DT_DIR) continue;
if (!digit(dir->d_name)) continue;

char* pid = dir->d_name;
char* status_path = gen_status_path(pid);
// printf("path=%s\n", status_path);
struct Entry *entry = readentry(uid, status_path);
if (entry != NULL) {
printf(" %s %ld\n", entry->name, entry->pid);
}
}
// printf("finish\n");

return 0;
}
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
// 12-2 生成进程树
#include <stdio.h>
#include "ps.h"

int main(int argc, char* argv[]) {
struct node *head = NULL;
struct node *tail = NULL;

DIR *rootdir = opendir(ROOT_DIR);
if (rootdir == NULL) {
errExit("opendir");
}

struct dirent *dir;
while ((dir = readdir(rootdir)) != NULL) {
if (dir->d_type != DT_DIR) continue;
if (!digit(dir->d_name)) continue;

char* pid = dir->d_name;
char* status_path = gen_status_path(pid);
// printf("path=%s\n", status_path);
// 1.create node
struct node *np = createnode(status_path);

// 2. sort
if (head == NULL) {
head = np;
tail = np;
} else {
struct node *pre = NULL;
struct node *p = head;
while (p != NULL && p->entry->pid < np->entry->pid) {
pre = p;
p = p->next;
}
np->next = p;
pre->next = np;
if (tail->next != NULL) {
tail = tail->next;
}
}
}

print_queue(head);

// 3.gen tree
struct node *root = createtree(head);

// 4.print tree
print_tree(head, root);
return 0;
}
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
#include <stdio.h>
#include <pwd.h>
#include <dirent.h>
#include <ctype.h>
#include "tlpi_hdr.h"

#define ROOT_DIR "/proc"
#define STATUS "status"
#define READ_BUFFER_SIZE 1024

int get_uid_by_name(char*);
int digit(char* s);
char* gen_status_path(char* pid);
char* getval(char* line, char* name);

struct Entry {
char name[100];
long pid;
long ppid;
};

struct node {
struct Entry *entry;
struct node *child; // for tree
struct node *next; // for queue
};

void print_queue(struct node *head);
struct node *createnode(char* path);
struct node *createtree(struct node *head);
struct node *clonenode(struct node *p);
struct node *searchnode(struct node *root, long pid);
void print_tree(struct node *head, struct node *root);

struct Entry *readentry(int uid, char* path);
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
#include "ps.h"

int get_uid_by_name(char* name) {
struct passwd *pwd = getpwnam(name);
if (pwd == NULL) {
return -1;
}
return pwd->pw_uid;
}

int digit(char* s) {
for (char* p=s; (*p)!='\0'; p++) {
if ((*p) < '0' || (*p) > '9') return 0;
}
return 1;
}

char* gen_status_path(char* pid) {
int len = strlen(ROOT_DIR) + 1 + strlen(pid) + 1 + strlen(STATUS);
char *buf = malloc(len);
memset(buf, 0, len);
strcat(buf, ROOT_DIR);
strcat(buf, "/");
strcat(buf, pid);
strcat(buf, "/");
strcat(buf, STATUS);
return buf;
}

struct Entry *readentry(int uid, char* path) {
struct Entry *pentry = malloc(sizeof(struct Entry));

FILE* f = fopen(path, "r");

char* linebuf = malloc(READ_BUFFER_SIZE);
memset(linebuf, 0, READ_BUFFER_SIZE);

// Name: sshd
// Pid: 1
// Uid: 1000 1000 1000 1000

// uid is match
while ((linebuf = fgets(linebuf, READ_BUFFER_SIZE, f)) != NULL) {
char* uidstr = getval(linebuf, "Uid:");
if (uidstr == NULL) continue;
int target = atoi(uidstr);
if (target == uid) {
break;
} else {
return NULL;
}
}

if (fseek(f, 0, SEEK_SET) == -1) {
errMsg("fseek");
return NULL;
}

while ((linebuf = fgets(linebuf, READ_BUFFER_SIZE, f)) != NULL) {
char* name = getval(linebuf, "Name:");
if (name != NULL) {
strcpy(pentry->name, name);
}

char* pid = getval(linebuf, "Pid:");
if (pid != NULL) {
pentry->pid = atol(pid);
}
}
return pentry;
}

char* getval(char* line, char* name) {
char* p1 = line;
char* p2 = name;
while ((*p1) == (*p2)) {
p1++;
p2++;
}
if (p2 - name != strlen(name)) {
return NULL;
}

int size = strlen(line);
char* buf = malloc(size);
memset(buf, 0, size);
int i = 0;
for (char* p=p1+1; (*p)!='\0'; p++) {
if (isspace(*p)) {
if (i > 0) {
break;
} else {
continue;
}
}
buf[i] = *p;
i++;
}
return buf;
}

struct node *createnode(char* path) {
struct Entry *pentry = malloc(sizeof(struct Entry));

FILE *f = fopen(path, "r");

char *linebuf = malloc(READ_BUFFER_SIZE);
memset(linebuf, 0, READ_BUFFER_SIZE);

while ((linebuf = fgets(linebuf, READ_BUFFER_SIZE, f)) != NULL) {
char* name = getval(linebuf, "Name:");
if (name != NULL) {
strcpy(pentry->name, name);
}

char* pid = getval(linebuf, "Pid:");
if (pid != NULL) {
pentry->pid = atol(pid);
}

char* ppid = getval(linebuf, "PPid:");
if (ppid != NULL) {
pentry->ppid = atol(ppid);
}
}

struct node *p = malloc(sizeof(struct node));
p->entry = pentry;
return p;
}

void print_queue(struct node *head) {
if (head == NULL) return;
struct node *p = head;
while (p->next != NULL) {
printf("%s(%ld)-->", p->entry->name, p->entry->pid);
p = p->next;
}
printf("\n");
}

struct node *createtree(struct node *head) {
struct node *root = NULL;
struct node *p = head;
while (p != NULL) {
// 从队列里找child节点, 如果找到就clone,然后加入child队列
struct node *q = head;
while (q != NULL) {
if (p->entry->pid == q->entry->ppid) {
struct node *x = clonenode(q);
if (root == NULL) {
root = x;
}
x->next = p->child;
p->child = x;
}
q = q->next;
}
p = p->next;
}
return root;
}

struct node *clonenode(struct node *p) {
struct node *r = malloc(sizeof(struct node));
r->next = NULL;
r->child = NULL;
r->entry = malloc(sizeof(struct Entry));
strcpy(r->entry->name, p->entry->name);
r->entry->pid = p->entry->pid;
r->entry->ppid = p->entry->ppid;
return r;
}

struct node *searchnode(struct node *root, long pid) {
if (root == NULL) return NULL;
if (root->entry->pid == pid) return root;

struct node *p = root->child;
while (p != NULL) {
struct node *x = searchnode(p, pid);
if (x != NULL) return x;
p = p->next;
}
return NULL;
}

void print_tree(struct node *head, struct node *root) {
if (root == NULL) return;

struct node *p = head;
while (p != NULL) {
printf("%s(%ld)-->>", p->entry->name, p->entry->pid);
struct node *x = p->child;
while (x != NULL) {
printf("%s(%ld)-->", x->entry->name, x->entry->pid);
x = x->next;
}
printf("\n");
p = p->next;
}
}
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
// 12-3 打开某个文件fd的进程id列表
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include "ps.h"

struct pidnode {
long pid;
struct pidnode *next;
};

struct item {
char* path;
struct pidnode *node;
};

char* gen_fd_dir_path(char* pid);
struct item *createitem(char* path, long pid);
void print_list(struct item *list[]);

int main(int argc, char* argv[]) {
struct item *list[2000];
int i = 0;

DIR *rootdir = opendir(ROOT_DIR);
struct dirent *dir;
while ((dir = readdir(rootdir)) != NULL) {
if (dir->d_type != DT_DIR) continue;
if (!digit(dir->d_name)) continue;

char* pid = dir->d_name;
char* fdpath = gen_fd_dir_path(pid);

DIR *fddir = opendir(fdpath);
if (fddir == NULL) {
errExit("opendir");
}

struct dirent *fdd;
while ((fdd = readdir(fddir)) != NULL) {
if (fdd->d_type != DT_LNK) continue;
if (!digit(fdd->d_name)) continue;

char* filepath = malloc(PATH_MAX);
strcpy(filepath, fdpath);
strcat(filepath, fdd->d_name);

char* buf = malloc(PATH_MAX);
if (readlink(filepath, buf, PATH_MAX) == -1) {
errMsg("readlink");
}

// printf("%d %s\n", i, buf);
list[i++] = createitem(buf, atol(pid));
}

}

print_list(list);
return 0;
}
void print_list(struct item *list[]) {
for (struct item **p=list; (*p) != NULL; p++) {
//for (int i=0; list[i]!=NULL; i++) {
//struct item *p = list[i];
printf("%s ", (*p)->path);
struct pidnode *x = (*p)->node;
while (x != NULL) {
printf("%ld-->", x->pid);
x = x->next;
}
printf("\n");
}
}

struct item *createitem(char* path, long pid) {
int size = sizeof(struct item);
struct item *one = malloc(size);
memset(one, 0, size);
one->path = path;
one->node = malloc(sizeof(struct pidnode));
one->node->pid = pid;
return one;
}

char* gen_fd_dir_path(char* pid) {
char* path = malloc(PATH_MAX);
memset(path, 0, PATH_MAX);
strcat(path, "/proc/");
strcat(path, pid);
strcat(path, "/fd/");
return path;
}

23. I/O的两层缓冲(内核缓冲和libc的缓冲)

1
2
3
4
5
6
7
8
9
10
11
// 控制用户空间缓冲
// setbuf/setvbuf/setbuffer/fflush

// 控制内核空间的缓冲
// fsync/fdatasync/O_SYNC

// 绕过用户空间和内核空间的缓冲
// O_DIRECT

// fileno
// fdopen
1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int
main(int argc, char *argv[])
{
// setbuf(stdout, NULL);
printf("If I had more time, \n");
write(STDOUT_FILENO, "I would have written you a shorter letter.\n", 43);
exit(EXIT_SUCCESS);
}

24. 文件系统

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
# 如何创建一个文件系统, 使用文件模拟的方式
# 1. 使用dd拷贝zero中的字节到myfs,每次拷贝256字节,拷贝4K次
dd if=/dev/zero of=myfs count=256 bs=4K
256+0 records in
256+0 records out
1048576 bytes (1.0 MB, 1.0 MiB) copied, 0.0012152 s, 863 MB/s

# 2. 在myfs上格式化, 每个block 1024字节
mke2fs -b 1024 myfs
mke2fs 1.45.5 (07-Jan-2020)
Discarding device blocks: done
Creating filesystem with 256 4k blocks and 128 inodes

Allocating group tables: done
Writing inode tables: done
Writing superblocks and filesystem accounting information: done

# 3. dump文件系统
dumpe2fs myfs
dumpe2fs 1.45.5 (07-Jan-2020)
Filesystem volume name: <none>
Last mounted on: <not available>
Filesystem UUID: 4eaa38d0-0996-465f-876f-62651858305f
Filesystem magic number: 0xEF53
Filesystem revision #: 1 (dynamic)
Filesystem features: ext_attr resize_inode dir_index filetype sparse_super large_file
Filesystem flags: signed_directory_hash
Default mount options: user_xattr acl
Filesystem state: clean
Errors behavior: Continue
Filesystem OS type: Linux
Inode count: 128
Block count: 1024
Reserved block count: 51
Free blocks: 986
Free inodes: 117
First block: 1
Block size: 1024
Fragment size: 1024
Reserved GDT blocks: 3
Blocks per group: 8192
Fragments per group: 8192
Inodes per group: 128
Inode blocks per group: 16
Filesystem created: Wed May 12 17:12:29 2021
Last mount time: n/a
Last write time: Wed May 12 17:12:29 2021
Mount count: 0
Maximum mount count: -1
Last checked: Wed May 12 17:12:29 2021
Check interval: 0 (<none>)
Reserved blocks uid: 0 (user root)
Reserved blocks gid: 0 (group root)
First inode: 11
Inode size: 128
Default directory hash: half_md4
Directory Hash Seed: 422d2f7b-6f06-41cb-866e-469150bf95be


Group 0: (Blocks 1-1023)
Primary superblock at 1, Group descriptors at 2-2
Reserved GDT blocks at 3-5
Block bitmap at 6 (+5)
Inode bitmap at 7 (+6)
Inode table at 8-23 (+7)
986 free blocks, 117 free inodes, 2 directories
Free blocks: 38-1023
Free inodes: 12-128

# 4. mount文件系统
sudo mount -o loop /mnt

# 5. 在文件系统上创建文件
touch ming
vim ming
文件内容:123456

# 6. umount /mnt
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
# 7. od -tx1 -Ax myfs 
000000~000400, 启动块(block 0),
000000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
*
(上边是64行00, 1024字节, 刚好1个block)

000400~0007ff, superblock(block 1)
000400 80 00 00 00 00 04 00 00 33 00 00 00 d9 03 00 00
000410 74 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00
000420 00 20 00 00 00 20 00 00 80 00 00 00 27 9c 9b 60
000430 5e 9c 9b 60 01 00 ff ff 53 ef 01 00 01 00 00 00
000440 fd 9b 9b 60 00 00 00 00 00 00 00 00 01 00 00 00
000450 00 00 00 00 0b 00 00 00 80 00 00 00 38 00 00 00
000460 02 00 00 00 03 00 00 00 4e aa 38 d0 09 96 46 5f
000470 87 6f 62 65 18 58 30 5f 00 00 00 00 00 00 00 00
000480 00 00 00 00 00 00 00 00 2f 6d 6e 74 00 00 00 00
000490 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
*
0004c0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 03 00
0004d0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0004e0 00 00 00 00 00 00 00 00 00 00 00 00 42 2d 2f 7b
0004f0 6f 06 41 cb 86 6e 46 91 50 bf 95 be 01 00 00 00
000500 0c 00 00 00 00 00 00 00 fd 9b 9b 60 00 00 00 00
000510 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
*
000560 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
000570 00 00 00 00 00 00 00 00 09 00 00 00 00 00 00 00
000580 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
*

000800~000bff, Group descriptors (block 2-5)
000800 06 00 00 00 07 00 00 00 08 00 00 00 d9 03 74 00
000810 02 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00
000820 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
*

001800~001c00, block bitmap (block 6)
001800 ff ff ff ff 3f 00 00 00 00 00 00 00 00 00 00 00
001810 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
*
001870 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 80
001880 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
*

001c00~002000, inode bitmap (block 7)
001c00 ff 17 00 00 00 00 00 00 00 00 00 00 00 00 00 00
001c10 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
*




inode-------------begin
inode 1
002000 00 00 00 00 00 00 00 00 fd 9b 9b 60 fd 9b 9b 60
002010 fd 9b 9b 60 00 00 00 00 00 00 00 00 00 00 00 00
002020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
*

inode 2
002080 ed 41 00 00 00 04 00 00 62 89 9c 60 5e 89 9c 60
002090 5e 89 9c 60 00 00 00 00 00 00 04 00 02 00 00 00
0020a0 00 00 00 00 0a 00 00 00 18 00 00 00 00 00 00 00
0020b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
*
002300 80 81 00 00 00 30 04 04 fd 9b 9b 60 fd 9b 9b 60
002310 fd 9b 9b 60 00 00 00 00 00 00 01 00 08 00 00 00
002320 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
*
002350 00 00 00 00 00 00 00 00 00 00 00 00 25 00 00 00
002360 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
*
002500 c0 41 00 00 00 30 00 00 fd 9b 9b 60 fd 9b 9b 60
002510 fd 9b 9b 60 00 00 00 00 00 00 02 00 18 00 00 00
002520 00 00 00 00 00 00 00 00 19 00 00 00 1a 00 00 00
002530 1b 00 00 00 1c 00 00 00 1d 00 00 00 1e 00 00 00
002540 1f 00 00 00 20 00 00 00 21 00 00 00 22 00 00 00
002550 23 00 00 00 24 00 00 00 00 00 00 00 00 00 00 00
002560 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
*

inode 12 0xc mings
002580 ff a1 00 00 04 00 00 00 4b 89 9c 60 49 89 9c 60
002590 49 89 9c 60 00 00 00 00 00 00 01 00 00 00 00 00
0025a0 00 00 00 00 01 00 00 00 6d 69 6e 67 00 00 00 00
0025b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
*
0025e0 00 00 00 00 6b 2d f6 bf 00 00 00 00 00 00 00 00
0025f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

inode 13 0xd ming1
002600 a4 81 00 00 07 00 00 00 41 9c 9b 60 2c 89 9c 60
002610 41 9c 9b 60 00 00 00 00 00 00 02 00 02 00 00 00
002620 00 00 00 00 01 00 00 00 26 00 00 00 00 00 00 00
002630 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
*
002660 00 00 00 00 38 35 13 02 00 00 00 00 00 00 00 00
002670 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

inode 14 0xe kk
002680 ed 41 00 00 00 04 00 00 5e 89 9c 60 5e 89 9c 60
002690 5e 89 9c 60 00 00 00 00 00 00 02 00 02 00 00 00
0026a0 00 00 00 00 01 00 00 00 27 00 00 00 00 00 00 00
0026b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
*
0026e0 00 00 00 00 d3 24 b1 7e 00 00 00 00 00 00 00 00
0026f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
*
inode------------end

block 1
006000 02 00 00 00 0c 00 01 02 2e 00 00 00 02 00 00 00
006010 0c 00 02 02 2e 2e 00 00 0b 00 00 00 14 00 0a 02
006020 6c 6f 73 74 2b 66 6f 75 6e 64 00 00 0d 00 00 00
006030 0c 00 04 01 6d 69 6e 67 0d 00 00 00 10 00 05 01
006040 6d 69 6e 67 31 00 00 00 0c 00 00 00 10 00 05 07
006050 6d 69 6e 67 73 00 00 00 0e 00 00 00 a8 03 02 02
006060 6b 6b 00 00 00 00 00 00 00 00 00 00 00 00 00 00
006070 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
*
006400 0b 00 00 00 0c 00 01 02 2e 00 00 00 02 00 00 00
006410 f4 03 02 02 2e 2e 00 00 00 00 00 00 00 00 00 00
006420 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
*
006800 00 00 00 00 00 04 00 00 00 00 00 00 00 00 00 00
006810 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
*
006c00 00 00 00 00 00 04 00 00 00 00 00 00 00 00 00 00
006c10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
*
007000 00 00 00 00 00 04 00 00 00 00 00 00 00 00 00 00
007010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
*
007400 00 00 00 00 00 04 00 00 00 00 00 00 00 00 00 00
007410 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
*
007800 00 00 00 00 00 04 00 00 00 00 00 00 00 00 00 00
007810 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
*
007c00 00 00 00 00 00 04 00 00 00 00 00 00 00 00 00 00
007c10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
*
008000 00 00 00 00 00 04 00 00 00 00 00 00 00 00 00 00
008010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
*
008400 00 00 00 00 00 04 00 00 00 00 00 00 00 00 00 00
008410 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
*
008800 00 00 00 00 00 04 00 00 00 00 00 00 00 00 00 00
008810 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
*
008c00 00 00 00 00 00 04 00 00 00 00 00 00 00 00 00 00
008c10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
*
009000 00 00 00 00 00 04 00 00 00 00 00 00 00 00 00 00
009010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
*
009400 00 00 00 00 03 00 00 00 04 00 00 00 05 00 00 00
009410 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
*

block 38 ming的数据
009800 31 32 33 34 35 36 0a 00 00 00 00 00 00 00 00 00
009810 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
*

block 39 kk的数据
009c00 0e 00 00 00 0c 00 01 02 2e 00 00 00 02 00 00 00
009c10 f4 03 02 02 2e 2e 00 00 00 00 00 00 00 00 00 00
009c20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
*
100000

25. 获取/修改inode节点的信息

1
2
3
4
5
// 1. stat/fstat/lstat

// 2. utime/utimes

// 3. chmod/fchmod/lchmod

怎样用ndk编译一个可执行程序

怎样用ndk编译一个可执行程序

1. 下载NDK,配置环境变量

1
2
3
4
5
6
7
8
vim ~/.bash_profile

# 追加下面两行
export ANDROID_NDK=/Users/liuming/Library/Android/sdk/ndk-bundle
export PATH=$ANDROID_NDK:$PATH

# 试一下是否可用
ndk-build

2. 准备3个文件

1
2
3
4
5
6
7
// hello.c
#include <stdio.h>

int main() {
printf("hello world!!\n");
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
# Android.mk

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_SRC_FILES := hello.c
LOCAL_MODULE := hello

# 生成可执行文件
include $(BUILD_EXECUTABLE)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# Application.mk
# 可用通过adb shell进入手机后,cat /proc/cpuinfo查看cpu架构,
# walleye:/system/bin # cat /proc/cpuinfo
# Processor : AArch64 Processor rev 4 (aarch64)
# processor : 0
# BogoMIPS : 38.00
# Features : fp asimd evtstrm aes pmull sha1 sha2 crc32
# CPU implementer : 0x51
# CPU architecture: 8 -------表示armv8a
# CPU variant : 0xa
# CPU part : 0x801
# CPU revision : 4
#
# processor : 1
# BogoMIPS : 38.00
# Features : fp asimd evtstrm aes pmull sha1 sha2 crc32
# CPU implementer : 0x51
# CPU architecture: 8
# CPU variant : 0xa
# CPU part : 0x801
# CPU revision : 4

APP_ABI :=arm64-v8a

4. 编译

1
2
# 指定mk文件的位置
ndk-build NDK_PROJECT_PATH=. NDK_APPLICATION_MK=Application.mk APP_BUILD_SCRIPT=Android.mk

5. 上传到手机 & 执行

1
2
3
4
5
adb push hello /data/local/tmp

cd /data/local/tmp
./hello
hello world!!

编译aosp

环境

  • ubuntu 20.04.2
  • SamSung SSD T7 1T
  • 内存16GB

下载aosp源码

  • 下面这两个源,任选一个就行,如果中断了,也可以换另外一个镜像继续下载,因为两个tar的md5都是一样的
  • 这个tar包大概95GB,我下载了两天,可以中断,wget -c有断点续传的功能
1
2
3
4
# 科大镜像
wget -c https://mirrors.ustc.edu.cn/aosp-monthly/aosp-latest.tar &
# 清华镜像
wget -c https://mirrors.tuna.tsinghua.edu.cn/aosp-monthly/aosp-latest.tar

校验md5

1
2
3
# 超级慢,因为包太大
md5sum aosp-latest.tar
803b418c730ab10db3c44df3ae8e7b1f aosp-latest.tar

解压

1
tar xf aosp-latest.tar
  • 问题: 如果遇到解压错误,tar Cannot create symlink to Operation not permitted,
  • 原因: 一般是磁盘文件系统不一致导致的,我的ssd是exfat,不支持软链接
  • 解决:
    1. lsblk -f 先查看磁盘文件系统
    2. 把文件系统格式化成ext4

同步代码

1
2
3
4
5
6
7
8
9
# 因为使用清华的镜像做同步时,总提示error: RPC failed; curl 56 GnuTLS recv error (-9): Error decoding the received TLS packet. 所以配置了vpn,改成google的源
repo init -u https://android.googlesource.com/platform/manifest -b android-11.0.0_r4
repo init -u https://mirrors.tuna.tsinghua.edu.cn/git/AOSP/platform/manifest -b android-11.0.0_r4

# 可以不断重复,同步命令, 直到出现这个提示
# Checking out projects: 100% (781/781), done.
# repo sync has finished successfully.
repo sync -c -j8

repo操作

1
2
# 创建dev分支
repo start dev --all

编译

1
2
3
4
5
6
7
8
9
10

source build/envsetup.sh

# 对应Pixel 2
# https://source.android.com/setup/build/running#selecting-device-build
lunch aosp_walleye-userdebug

# 如果中断,可以重复执行m
# 直到#### build completed successfully (24:07 (mm:ss)) ####
m

刷机

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
ming@ming-Thurley:/media/ming/mingssd/android/aosp$ which adb
/media/ming/mingssd/android/aosp/out/soong/host/linux-x86/bin/adb
ming@ming-Thurley:/media/ming/mingssd/android/aosp$ adb reboot bootloader
ming@ming-Thurley:/media/ming/mingssd/android/aosp$ fastboot flashall -w
--------------------------------------------
Bootloader Version...: mw8998-002.0083.00
Baseband Version.....: g8998-00023-2004070200
Serial Number........: HT8591A01651
--------------------------------------------
Checking 'product' OKAY [ 0.001s]
Setting current slot to 'b' OKAY [ 0.009s]
Sending 'boot_b' (32768 KB) OKAY [ 1.309s]
Writing 'boot_b' OKAY [ 0.199s]
Sending 'dtbo_b' (8192 KB) OKAY [ 0.349s]
Writing 'dtbo_b' OKAY [ 0.047s]
Sending 'vbmeta_b' (8 KB) OKAY [ 0.011s]
Writing 'vbmeta_b' OKAY [ 0.001s]
Sending sparse 'system_b' 1/3 (458752 KB) OKAY [ 17.126s]
Writing 'system_b' OKAY [ 0.000s]
Sending sparse 'system_b' 2/3 (458752 KB) OKAY [ 18.620s]
Writing 'system_b' OKAY [ 0.000s]
Sending sparse 'system_b' 3/3 (200392 KB) OKAY [ 9.960s]
Writing 'system_b' OKAY [ 0.000s]
Sending 'system_a' (78268 KB) OKAY [ 2.959s]
Writing 'system_a' OKAY [ 0.000s]
Sending 'vendor_b' (468008 KB) OKAY [ 17.787s]
Writing 'vendor_b' OKAY [ 0.000s]
Erasing 'userdata' OKAY [ 2.659s]
mke2fs 1.45.4 (23-Sep-2019)
Creating filesystem with 13933563 4k blocks and 3489792 inodes
Filesystem UUID: 0e6436b0-06fc-496e-9aaf-a352dea25ede
Superblock backups stored on blocks:
32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208,
4096000, 7962624, 11239424

Allocating group tables: done
Writing inode tables: done
Creating journal (65536 blocks): done
Writing superblocks and filesystem accounting information: done

Sending 'userdata' (4669 KB) OKAY [ 0.189s]
Writing 'userdata' OKAY [ 0.001s]
Rebooting OKAY [ 0.000s]
Finished. Total time: 76.247s

gcc

gcc使用指北

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
#----------------
# 预处理, 生成.i文件
gcc -E hello.c -o hello.i

# 编译, 编译生成hello.s
gcc -S hello.i

# 汇编,生成.o文件
gcc -c hello.s

# 链接, 生成可执行文件
gcc hello.o -o hello


#----------------
# -Wall 使gcc对源文件的代码有问题的地方发出警告

# -Idir 将dir目录加入搜索头文件的目录路径
# ./what 是目录
gcc -I./what -c hello.c

# -Ldir 将dir目录加入搜索库的目录路径
gcc -L./what -c hello.c

# -llib 连接lib库
# -g 在目标文件中嵌入调试信息,以便gdb之类的调试程序调试


#----------------
# 编译静态库
ar rcs libmyh.a myh.o
# 链接hello.o和libmyh.a
gcc hello.o ./what/libmyh.a -o hello
# 在./what目录下搜索头文件和库文件,编译hello.c,再链接libmyh
gcc -I./what -L./what hello.c -o hello -lmyh


#----------------
# 编译共享库
gcc -shared -fPIC *.c -o libwhat.so
# 使用共享库, 如果./what目录下存在同名的静态库和共享库,则使用共享库
gcc -I./what -L./what hello.c -o hello3 -lwhat

# 执行时报错
#./hello3: error while loading shared libraries: libwhat.so: cannot open shared object file: No such file or directory
./hello3

有三种办法解决:
# 0. gcc -o main main.c -L. -lwhat -Wl,-rpath=.
# 1. 拷贝.so文件到系统共享库路径下,一般指/usr/lib
# 2. 在~/.bash_profile文件中,配置LD_LIBRARY_PATH变量
# 3. 配置/etc/ld.so.conf,配置完成后调用ldconfig更新ld.so.cache

# 库的搜索路径遵循几个搜索原则:从左到右搜索-I -l指定的目录,如果在这些目录中找不到,那么gcc会从由环境 变量指定的目录进行查找。头文件的环境变量是C_INCLUDE_PATH,库的环境变量是LIBRARY_PATH.如果还是找不到,那么会从系统指定指定的目录进行搜索。

# 查看依赖的库
ldd dynamic hello3

# 查看库中,包含的函数名
nm libwhat.a
nm libwhat.so

gdb

gdb

1. 启动gdb

1
gdb ./myps

2. 设置命令行 参数

1
set args ming

3. 查看代码行号

1
list

4. 设置断点

1
2
3
4
5
6
7
8
9
10
11
# 在第18行设置断点
b 18

# 查看设置的断点
i b

# disable 编号是1的断点
disable b 1

# enable 编号是1的断点
enable b 1

5. 查看变量的值

1
p linebuf

6. 下一步

1
2
3
4
5
6
7
8
9
10
11
# 在当前函数中执行下一行
n

# 进入本行的函数中
s

# 跳出当前函数
fin

# 继续执行到下一个断点
c

6. 调试汇编

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 查看反汇编
objdump -d -M intel a.out

(gdb) set disassembly-flavor intel

# starti 停在最开始,再查真实地址
(gdb) starti
(gdb) info proc mappings # 找到程序加载的基地址
(gdb) b *0x555555555060 # 基地址 + 0x1060

(gdb) r
(gdb) layout asm
(gdb) layout regs
# 之后用 ni/si 单步,观察寄存器变化
(gdb) x/8xg $rsp # 随时查看栈