嵌入式linux(基于飞腾派)
首先看一下phytiumpi OS 的目录结构
ren@phytiumpi:/$ ls
bin boot data dev etc home lib lost+found media mnt opt proc root run sbin srv sys tmp usr var
这一节与前面跨度较大,学习一下关于编写嵌入式应用程序的内容
RTOS任务->linux线程
八.进程和线程
进程(process):一个内存地址空间和执行线程的组合
线程(thread):进程的执行线程
main thread: 主线程
pthread_create: 创建线程
地址空间是进程私有的,thread无法访问
8.1 进程
‘拥有’:内存映射,文件描述符,信号处理程序,当前工作目录,用户ID,组ID
8.1.1 进程的创建(POSIX)
pid_t fork(void);//pid_t为int类型,进行了重载
pid_t getpid();// 获取当前进程的 pid 值
pid_t getppid(); //获取当前进程的父进程 pid 值
创建的子进程是父进程的副本
区分方式:fork返回值:子进程为0;父进程为子进程的pid;返回值为-1表示创建失败
8.1.2 终止进程
#include <stdlib.h>
void exit(int status);
#include <unistd.h>
void _exit(int status);
exit() 函数与 _exit() 的区别就在于 exit() 函数在终止当前进程之前要检查该进程打开了哪些文件,并把文件缓冲区中的内容写回文件。
linux终止信号
SIGKIL(信号9): 强制终止进程
kill -9 PID # 终止单个进程
kill -9 $(pidof 进程名) # 终止同名所有进程
SIGTERM(信号15):优雅终止进程
kill -15 PID # 发送 SIGTERM 到指定进程
kill -TERM PID # 等效写法(-TERM 是信号名称)
demo
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(void)
{
int pid;
int status;
pid = fork();
if (pid == 0) {
printf("I am the child, PID %d\n", getpid());
sleep(10);
exit(42);
} else if (pid > 0) {
printf("I am the parent, PID %d\n", getpid());
wait(&status);
printf("Child terminated, status %d\n", WEXITSTATUS(status));
} else
perror("fork:");
return 0;
}
运行结果
ren@phytiumpi:~/demo$ ./fork_demo
I am the parent, PID 7159
I am the child, PID 7160
Child terminated, status 42
8.1.3 进程重载execl
函数族
execl execle execlp
execv execve execvp
execl:为进程重载0-3G的用户空间,可与fork函数搭配使用
int execl(const char *path, const char *arg0, ...,NULL);
demo:简易shell
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(int argc, char *argv[])
{
char command_str[128];
int pid;
int child_status;
int wait_for = 1;
while (1) {
printf("sh> ");
scanf("%s", command_str);
pid = fork();
if (pid == 0) {
/* child */
printf("cmd '%s'\n", command_str);
execl(command_str, command_str, (char *)NULL);
/* We should not return from execl, so only get
to this line if it failed */
perror("exec");
exit(1);
}
if (wait_for) {
waitpid(pid, &child_status, 0);
printf("Done, status %d\n", child_status);
}
}
return 0;
}
运行结果
sh> /bin/ls
cmd '/bin/ls'
exex-demo exex-demo.c Makefile
Done, status 0
新建一个终端线程,查看这个进程的fd文件描述符
Linux内核对所有打开的文件有一个文件描述符表格,里面存储了每个文件描述符作为索引与这个打开文件相对应的关系
0:标准输入
1:标准输出
2:标准错误
关于文件描述符的更多内容,可以上网查询
ren@phytiumpi:~/demo/exex-demo$ pidof exex-demo
16223
ren@phytiumpi:~/demo/exex-demo$ ls -l /proc/16223/fd
total 0
lrwx------ 1 ren ren 64 Cax 8 12:19 0 -> /dev/pts/0
lrwx------ 1 ren ren 64 Cax 8 12:19 1 -> /dev/pts/0
lrwx------ 1 ren ren 64 Cax 8 12:19 2 -> /dev/pts/0
8.2 进程间通信
两种通信方式:
1.将信息从一个地址空间复制到另一个地址空间
2.创建一个既可以访问又可以复制数据的内存区域
进程间通信(Inter-Process Communication IPC)是多进程协作的基础,在进程间传递数据
同步IPC和异步IPC:同步IPC指它的IPC操作(如Send)会阻塞进程直到该操作完成;而异步IPC则通常是非阻塞的,进程只要发起一次操作即可返回,而不需要等待其完成
超时机制:扩展了通信双方的接口,允许发送者/接收者指定它们发送/接收请求的等待时间
进程间通信机制:管道(Pipe)、信号(Signal)、套接字(Socket)、共享内存(Shared Memory)、消息队列(Message Queue)、信号量(Semaphore)
8.2.1 套接字(简略介绍)
套接字类型:
1.SOCK_STREAM:流式套接字,用于TCP协议
2.SOCK_DGRAM:数据报套接字,用于UDP协议
既可用于本地,又可跨网络使用。如 ROS 使用套接字作为进程间通信方案
进程间通信可以使用不同的协议,如 TCP 和 UDP
8.2.2 管道(pipe)
(1)父进程创建管道,得到两个⽂件描述符指向管道的两端
(2)父进程fork出子进程,⼦进程也有两个⽂件描述符指向同⼀管道。
(3)父进程关闭fd[0],子进程关闭fd[1],即⽗进程关闭管道读端,⼦进程关闭管道写端(因为管道只支持单向通信)。⽗进程可以往管道⾥写,⼦进程可以从管道⾥读,管道是⽤环形队列实现的,数据从写端流⼊从读端流出,这样就实现了进程间通信
原文链接:https://blog.csdn.net/skyroben/article/details/71513385
共享内存(Shared Memory)、消息队列(Message Queue)、信号量(Semaphore)会在下一期详细介绍