系统操作
函数 | 说明 |
---|---|
getuid() | 获取UID(10000 * userId + AppId,默认用户userId = 0) |
getpid | 获取进程ID |
getppid | 获取父进程ID |
gettid | 获取线程ID |
fork | 创建子进程 |
execl | 在当前进程执行其他程序 |
sleep | 线程休眠,单位:秒 |
usleep | 线程休眠,单位:微秒 |
pipe | 管道 |
syscall | 系统调用 |
fork
fork被调用一次,能够返回两次,它可能有三种不同的返回值:
- 在父进程中,fork返回新创建子进程的进程ID
- 在子进程中,fork返回0
- 如果出现错误,fork返回一个负值
void main() {
pid_t pid = fork();
LOGD("fork pid = %d", pid);
if (pid == 0) {
//子进程
} else if (pid > 0) {
//父进程
}
}
pipe
参考linux 管道pipe使用,用管道进行通信的时候主要由如下两种局限性:
- 管道是半双工的,及数据只能在一个方向上流动;
- 管道只能在具有公共祖先的两个进程之间使用。通常一个管道由一个进程创建,在进程调用fork之后,这个管道就能在父进程和子进程之间使用了。
#define PIPE_READ 0
#define PIPE_WRITE 1
void main(){
//数据流向:父->子
int pipe2Child[2];
//数据流向:子->父
int pipe2Parent[2];
if (-1 == pipe(pipe2Child)) {
LOGE("fail create pipe2Child");
return;
}
if (-1 == pipe(pipe2Parent)) {
LOGE("fail create pipe2Parent");
return;
}
int pid = fork();
if (pid == -1) {
LOGE("fail fork");
return;
}
if (pid == 0) {
//子进程,关闭不需要的FD
close(pipe2Parent[PIPE_READ]);
close(pipe2Child[PIPE_WRITE]);
//向父进程写入数据
const char *writeBuffer = "hello, i am child";
int writeResult = write(pipe2Parent[PIPE_WRITE], writeBuffer, strlen(writeBuffer));
LOGD("write to parent success %d", writeResult);
//从父进程读取数据
char readBuffer[1024];
memset(readBuffer, 0, 1024);
read(pipe2Child[PIPE_READ], readBuffer, 1024);
LOGD("read from parent = %s", readBuffer);
} else {
//父进程,关闭不需要的FD
close(pipe2Parent[PIPE_WRITE]);
close(pipe2Child[PIPE_READ]);
//从子进程读取数据
char readBuffer[1024];
memset(readBuffer, 0, 1024);
read(pipe2Parent[PIPE_READ], readBuffer, 1024);
LOGD("read from child = %s", readBuffer);
//向子进程写入数据
const char *writeBuffer = "i am parent,i receive";
int writeResult = write(pipe2Child[PIPE_WRITE], writeBuffer, strlen(writeBuffer));
LOGD("write to child success %d", writeResult);
}
}
execl
当用fork函数创建新的子进程后,子进程往往要调用一种exec函数以执行另一个程序。当程序调用一种exec函数时,该进程执行的程序完全替换为新程序,而新程序则从其main函数开始执行。因为调用exec并不创建新进程,所以前后的进程ID并未改变。exec只是用磁盘上的一个新程序替换了当前进程的正文段、数据段、堆段和栈段。
- 参数1:文件路径
- 参数2:文件标识符,例如文件名称
- 参数3:执行程序需要的参数
int execl(const char *path, const char *arg, ...);
//例如执行 ls -l 命令
execl("/bin/ls", "ls", "-l", NULL);
同族还有其他相关函数,并且其命名有一定规律
- 第5位 l:参数传递为逐个列举
- 第5位 v:参数传递为构造指针数组方式
- 第6位 e:可传递新进程环境变量
- 第6位 p:可执行文件查找方式为文件名
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg,..., char * const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[],char *const envp[]);
文件操作
函数 | 说明 |
---|---|
read | 从文件描述符对应的文件中读取数据 |
write | 向文件描述符对应的文件中写入数据 |
lseek | 控制文件读写位置 |
dup | 复制文件描述符,指向同一个文件 |
truncate | 修改文件大小(参数为文件路径) |
ftruncate | 修改文件大小(参数为文件描述符) |
chown | 修改文件所有者 |
close | 关闭fd |
read、write、lseek
int fd = open("/sdcard/tencent/test", O_RDWR);
const char *writeBuffer = "Happy NewYear 2023!";
//向fd中写入数据,返回值为写入数据的字节数
int writeResult = write(fd, writeBuffer, strlen(writeBuffer));
/*
* 移动读写位置为文件开头
* 参数2:偏移量
* 参数3:偏移的起点,有如下三个
* SEEK_SET 将读写位置指向文件头后再增加offset个位移量。
* SEEK_CUR 以目前的读写位置往后增加offset个位移量。
* SEEK_END 将读写位置指向文件尾后再增加offset个位移量。
*/
lseek(fd, 0, SEEK_SET);
char readBuffer[1024];
memset(readBuffer, 0, 1024);
//从fd中读取数据,返回值为读取的字节数
int readResult = read(fd, readBuffer, 1024);
truncate、ftruncate
在进行mmap
内存映射之前就需要修改文件大小,否则可能导致映射失败(新建的文件大小为0,无法映射内存数据)
truncate("/sdcard/tencent/test_mmap", 1024);
int fd = open("/sdcard/tencent/test_mmap", O_RDWR);
ftruncate(fd, 1024);
dup
void main() {
int fd = open("/sdcard/tencent/test", O_RDONLY);
char buffer[1024];
memset(buffer, 0, 1024);
read(fd, buffer, 1024);
LOGD("read old fd = %s", buffer);
//复制fd,指向同一个文件
int newFd = dup(fd);
memset(buffer, 0, 1024);
lseek(newFd, 0, SEEK_SET);
read(newFd, buffer, 1024);
LOGD("read new fd = %s", buffer);
}