linux系统编程
# 线程安全,对文件加锁
# 进程体系与进程管理
https://blog.csdn.net/qq_37233070/article/details/122519360
https://blog.51cto.com/dlican/3744943
https://xiaolong.blog.csdn.net/category_3278435.html
https://www.zhihu.com/question/30101368/answer/2427069276
PCB进程控制块,fork创建,exec调度,同步,通信
# 进程控制块PCB
task_struct结构体: 进程id 进程的状态,有运行、挂起、停止、僵尸等状态。 进程切换时需要保存和恢复的一些CPU寄存器 描述虚拟地址空间的信息 描述控制终端的信息(记录终端,因为会给终端打印消息) 当前工作目录 umask掩码 文件描述符表,包含很多指向file结构体的指针 和信号相关的信息 用户id和组id Session和进程组 进程可以使用的资源上限
各个进程的地址空间中的数据是完全独立的。
# 进程控制fork 复制
fork的作用是根据一个现有的进程复制出一个新进程,原来的进程称为父进程(Parent Process),新进程称为子进程(Child Process)。系统中同时运行着很多进程,这些进程都是从最初只有一个进程开始一个一个复制出来的。 pstree命令可以看到进程间的关系。 在shell下输入命令可以运行一个进程,是因为shell进程在读取用户输入的命令之后会调用fork复制出一个新的shell进程。
- fork函数原型
pid_t fork(void);
//成功后子进程返回0,父进程返回子进程id,出错返回-1
fork/getpid/getppid
#include <stdio.h>
#include <unistd.h>
int main()
{
pid_t pid = fork();
if(pid)
{
printf("self process id: %d\n", getpid());
printf("father process id: %d\n", getppid());
printf("child process id: %d\n", pid);
}else{
printf("create child process ^_^\n");
}
return 0;
}
# exec函数族 李代桃僵
fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支),子进程往往要调用一种exec函数以执行另一个程序。当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行。调用exec并不创建新进程,所以调用exec前后该进程的id并未改变。
取代当前进程
#include <unistd.h>
extern char **environ;
//pathname: 要执行的程序的绝对路径
//第一个arg: 占位
//后边的arg: 命令的参数
//参数写完之后要加NULL
//例 execl("/bin/ls", "ls", "-lah", NULL);
int execl(const char *pathname, const char *arg, .../* (char *) NULL */);
//执行PATH环境变量能够搜索到的程序
int execlp(const char *file, const char *arg, .../* (char *) NULL */);
int execle(const char *pathname, const char *arg, .../*, (char *) NULL, char *const envp[] */);
//char* args = ["ps", "aux", NULL];
//execv("/bin/ps", args);
int execv(const char *pathname, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[],char *const envp[]);
这些函数如果调用成功则加载新的程序从启动代码开始执行,不再返回,如果调用出错则返回-1,所以exec函数只有出错的返回值而没有成功的返回值。
环境变量 extern char **environ; exec系统调用执行新程序时会把命令行参数和环境变量表传递给main函数。和命令行参数argv类似,环境变量表也是一个字符串数组 name=value
可以用getenv函数,查找对应的name的value
#include <stdlib.h>
char *getenv(const char *name);
可以用setenv函数,将环境变量的name的值设置为value
#include <stdlib.h>
int setenv(const char *name, const char *value, int rewrite);
void unsetenv(const char *name);
gdb调试多进程
跟着父进程走 set follow-fork-mode parent 跟着子进程走 set follow-fork-mode child
通过在gdb中设置follow-fork-mode和detach-on-fork的值来调试子程序, 在gdb中分别默认的值是: parent和on:
follow-fork-mode detach-on-fork 效果 parent on 只调试主进程(GDB默认) child on 只调试子进程 parent off 同时调试两个进程, gdb跟主进程, 子进程block在fork位置 child off 同时调试两个进程,gdb跟子进程,主进程block在fork位置
# 进程回收
# 孤儿进程
父进程先于子进程结束,则子进程成为孤儿进程,子进程的父进程成为init进程,称为init进程领养孤儿进程 为了释放子进程的系统资源,PCB必须由父进程释放
# 僵尸进程
子进程终止,父进程尚未回收,子进程残留资源(PCB)存放于内核中,变成僵尸(Zombie)进程。
# wait和waitpid函数 收拾残局
一个进程在终止时会关闭所有文件描述符,释放在用户空间分配到内存,但它的PCB还保留着,内核在其中保存了一些信息:如果时正常终止则保存着退出状态,如果是异常终止则保存着导致该进程终止的信号是哪个。
父进程可以调用wait或waitpid获取这些信息,然后彻底清除掉这个进程
#include <sys/types.h>
#include <sys/wait.h>
//status:判断子进程是如何死的 - 正常退出/被某个信号杀死了
//调用一次只能回收一个子进程, 返回值-1表示没有子进程
//WIFEXITED(status) 为非0 → 进程正常结束
// WEXITSTATUS(status) 如上宏为真,使用此宏 → 获取进程退出状态 (exit的参数)
//WIFSIGNALED(status) 为非0 → 进程异常终止
// WTERMSIG(status) 如上宏为真,使用此宏 → 取得使进程终止的那个信号的编号。
pid_t wait(int* status);//等待所有子进程中的任何一个死亡,阻塞等待
//option: WNOHANG非阻塞,0阻塞
pid_t waitpid(pid_t pid, int* status, int option);//指定可以收谁的尸体
例如,一个进程的退出状态可以在shell中用特殊变量$?查看,因为shell是它的父进程,当它终止时shell调用wait或waitpid得到它的退出状态同时彻底 注意:一次wait或waitpid调用只能清理一个子进程,清理多个子进程应使用循环。
int status;
pid_t wpid;
while((wpid = wait(&status)) != -1){
printf("child died pid = %d\n", wpid);
if(WIFEXITED(status)){
printf("return value %d\n", WEXITSTATUS(status));
}else if(WIFSIGNALED(status)){
printf("return value %d\n", WTERMSIG(status));
}
}
ps aux | grep "xxx" x 显示不依赖终端的进程 ps ajx | grep "xxx" kill -l 查看所有信号 kill发信号 杀死某个进程 kill -9 pid
=====段错误===
- 访问了非法内存
- 访问了不可写的区域进行写操作
- 栈空间溢出
# 实现简单的shell
识别和处理以下符号: 简单的标准输入输出重定向(<和>) 管道(|):shell进程先调用pipe创建一对管道描述符,然后fork出两个子进程,一个子进程关闭读端,调用dup2把写端赋给标准输出,另一个子进程关闭写端,调用dup2把读端赋给标准输入,两个子进程分别调用exec执行程序,而shell进程把管道的两端都关闭,调用wait等待两个子进程终止。
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <ctype.h>
#include <sys/stat.h>
#include <fcntl.h>
#define LEN 1024
char* trim(char* str){
int head = 0;
int tail = strlen(str)-1;
while(isspace(str[head++]));
while(isspace(str[tail])) str[tail--] = 0;
return str+head;
}
int main()
{
char buf[LEN];
while(1){
printf("kkb$");
fgets(buff, LEN, stdin);
buff[strlen(buff)-1] = 0;
if(!strcmp(buff,"exit"))
{
printf("exit~\n");
break;
}
//创建子进程
pid_t pid = fork();
if(pid<0)
{
perror("fork");
exit(1);
}
//父进程 等待子进程结束,继续下一轮
if(pid){
wait(NULL);
continue;
}
//子进程 redirect fd重定向
int redfd = -1;
if(strstr(buff,"<")){
redfd = 0;
}
if(strstr(buff,">")){
redfd = 1;
}
char* cmd=NULL;
if(-1 != redfd){
char* token = NULL;
token = strtok (str,"<>");
cmd = token;
while (token != NULL)
{
printf ("%s\n",token);
token = strtok (NULL, "<>");
}
token = trim(token);
int fd;
if(redfd)
fd = open(token, O_RDWR | O_CREAT, 0644);
else
fd = open(token, O_RDWR);
if(fd<0){
perror(token);
exit(1);
}
printf("open %s successfully~\n",token);
dup2(fd, redfd);//将前面的重定向到后边
}else{
cmd = buff;
}
int i = 0;
char* argarr[20];
char* tk=strtok(cmd," ");
while(tk){
argarr[i++] = tk;
tk=strtok(NULL, "");
}
argarr[i]=NULL;
execv(argarr[0],argarr);
perror("exec");
exit(1);
}
}
# 网络IO模型
https://baijiahao.baidu.com/s?id=1702203870868277915&wfr=spider&for=pc 5种Linux网络IO模型包括:同步阻塞IO、同步非阻塞IO、多路复用IO、信号驱动IO和异步IO。
实践:https://zhuanlan.zhihu.com/p/150972878
Linux编程 基本使用 不同系统下的安装 vscode的使用 cmake详解 基础命令 常用命令 man ls引申硬/软链接 gdb 系统编程 文件io 文件系统 进程 线程 网络编程
C语言 编译过程 ESc 基本数据结构 枚举 https://www.bilibili.com/read/cv9652772 数组+指针+结构体 链表 链表相关操作 插入(头插法/尾插法) 队列、栈的实现 常用头文件 io相关操作 C string time stdlib
CPlusPlus 面向对象特性 友元 继承 多态 设计模式 Container allocator vector/queue/stack/... 多线程 thread 常用库 std::chrono 日期时间库 iostream C++11特性(程序喵大人) auto和decltype 右值 智能指针 std::ref std::bind和std::thread传递引用参数需要用到 lambda ... 其他库的使用 json libcurl log4cplus drogon
PHP
项目实战 高并发服务器 QT PyQt5 数据
工具 Git使用 Git结构 指令
数据结构 算法
# 智能指针
详讲:https://zhuanlan.zhihu.com/p/436290273
实践:https://blog.csdn.net/albertsh/article/details/82286999
# C++并发编程
http://shouce.jb51.net/cpp_concurrency_in_action/