虚拟地址、内存、进程管理、状态模式(tcp三次握手,进程的状态变迁,switch语句90%以上都是状态、)阻塞非阻塞 并发并行 同步异步 、系统调用、写实拷贝?、
命令
sed
利用脚本来处理文本文件,sed 可依照脚本的指令来处理、编辑文本文件。
Sed 主要用来自动编辑一个或多个文件、简化对文件的反复操作、编写转换程序等
-
a :新增, a 的后面可以接字串,而这些字串会在新的一行出现(目前的下一行)~ -
c :取代, c 的后面可以接字串,这些字串可以取代 n1,n2 之间的行! -
d :删除,因为是删除啊,所以 d 后面通常不接任何咚咚; -
i :插入, i 的后面可以接字串,而这些字串会在新的一行出现(目前的上一行); -
p :打印,亦即将某个选择的数据印出。通常 p 会与参数 sed -n 一起运行~ -
s :取代,可以直接进行取代的工作哩!通常这个 s 的动作可以搭配正规表示法!例如 1,20s/old/new/g 就是啦
sed -e 4a\newLine testfile
sed -e '4 a newline\nnewline2' testfile
[root@www ~]# nl /etc/passwd | sed '2,5d'
nl /etc/passwd | sed '2d'
nl /etc/passwd | sed '3,$d'
[root@www ~]# nl /etc/passwd | sed '2a drink tea'
nl /etc/passwd | sed '2i drink tea'
nl /etc/passwd | sed '/root/d'
进程
1. 编译连接过程、功能
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-R28BBOex-1636470780563)(C:\Users\Lee Wen\AppData\Roaming\Typora\typora-user-images\image-20211025185629954.png)]
- 预编译:gcc -E main.c -o main.i
功能:
- 删除所有的"#define",展开所有的宏定义
- 处理所有预编译指令,"#if","#ifdef","#endif"
- 处理"#include"指令,将被包含的文件插入到该预编译指令的位置
- 删除所有注释
- 添加行号和文件名标识,方便编译器产生调试用的符号信息以及有错误和警告时显示行号
- 保留所有的#pragma编译器指令
- 编译 :gcc -S main.i -o main.s
功能:
语法、词法、语义分析,代码优化,汇总符号
- 汇编:gcc -c main.s -o main.o
功能:
将汇编指令翻译成二进制格式,生成各个section,生成符号表
- 链接:gcc main.o -o main
功能:
(1)合并各个section,合并符号表,进行符号解析,给符号分配虚拟地址
(2)符号重定位
2. makefile gdb
make
实现自动化编译
gcc 前面必须是table键缩进的
make命令根据makefile文件的规则生成可执行程序
all: main
main:main.o add.o max.o
gcc -o main main.o add.o max.o
main.o:main.c
gcc -c main.c
add.o:add.c
gcc -c add.c
max.o:max.c
gcc -c max.c
clear:
rm -rf *.o main
#include<stdio.h>
int add(int x,int y);
int max(int x,int y);
int main()
{
int a = 1;
int b = 3;
int c = add(a,b);
int d = max(a,b);
printf("a+b=%d\n",c);
printf("a,b中最大的是:%d\n",d);
return 0;
}
#include<stdio.h>
int add(int x,int y)
{
return x+y;
}
#include<stdio.h>
int max(int x,int y)
{
return x>y ? x : y;
}
gdb
gcc -o main main.c add.c max.c -g 包含调试信息
all : main
GDB = -g
main:main.o add.o max.o
gcc -o main main.o add.o max.o
main.o:main.c
gcc -c main.c $(GDB)
add.o:add.c
gcc -c add.c $(GDB)
max.o:max.c
gcc -c max.c $(GDB)
clear:
rm -rf *.o main
gdb main
l | 显示代码 |
---|
b 12 | 加断点 | info break | 显示所有断点 | delete 2 | 删除编号为2的断点 | r | 启动程序 | n | 单步执行 | p i | 输出i的值 | p &i | 输出i的地址 | q | 退出 | s | 进入函数 |
3. 库文件
-
定义:预先编译好的方法(函数)的集合 -
两种形式:静态库 libxxx.a 共享库 libxxx.so -
< >说明是在标准库的include里面 #include<stdio.h> 自己做的foo库是在./usr/lib里面
静态库
第一步:先将.c编译成.o目标文件
foo.h 里面包含函数的声明
add.c max.c 里面包含函数的定义
gcc -c add.c
gcc -c max.c
第二步:使用ar命令将第一步所有编译出的.o文件生成静态库
ar crv libfoo.a add.o max.o
c:创建库 r:将方法添加到库中 v: 显示过程
测试静态库:
vi main.c
gcc -o main main.c -L. -lfoo
-L :指定库的存储路径 -l:指定库的名称(不需要前面的’lib’和扩展名’.a’)
. :表示库的存储路径在当前路径
共享库
第一步:先将.c编译成.o目标文件
foo.h 里面包含函数的声明
add.c max.c 里面包含函数的定义
gcc -c add.c
gcc -c max.c
第二步:使用gcc命令将第一步编译的.o文件生成共享库
gcc -shared -fPIC -o libfoo.so add.o max.o
测试共享库:
vi main.c
链接出main gcc -o main main.c -L. -lfoo
执行不成功 将libfoo.so移动到./usr/lib路径下
- 通过ldd命令查看可执行程序使用了那些共享库 ldd main
静态库和共享库的区别
静态库:在链接时将用到的方法包含到最终生成的可执行程序中,执行过一次之后,即使删除了静态库也可以进行链接,因为已经将使用 的函数已经拷贝过来了
共享库:不进行复制,只做标记,在运行程序时,才动态加载,找不到则无法执行,如果删掉动态库,则函数将不能执行
4. 计算机基础
五大部件
- 运算器:算术逻辑单元
- 控制器:指挥计算机各部分协调的工作,保证计算机按照预先规定的目标和步骤有条不紊的进行操作
- 存储器
- 输入设备
- 输出设备
进程
1.概念:正在运行的程序
2.PCB:进程控制块,进程存在的唯一标志
3.进程状态:就绪、运行、阻塞
4.并发、并行
前台 sleep 10
? 结束前台进程 CTRL C
? 停止进程 CTRL Z
后台 sleep 10 & 同时执行程序
kill -9 3665 强制结束进程
kill -stop 挂起进程
pkill 结束一组同名的进程
jobs 显示当前终端的任务
fg
? fg %任务号 将后台运行的或挂起的进程挪到前台执行
bg
bg %任务号 将挂起的进程唤醒到后台执行
5. 进程的复制与替换
printf
不会直接将数据输出到屏幕上,而是先放到缓冲区,在满足以下三种情况时,才会输出到屏幕
1.缓冲区满
2.强制刷新缓冲区 fflush
3.程序结束时
printf("hello");
sleep(3);
exit(0); exit()->fflush->_exit(0)
_exit(0);
-
没有\n会在缓冲区中等待情况满足在输出 -
有\n在会直接输出
fork复制进程的过程
-
pid_t pid = fork(); //pid_t是int类型 #include<unistd.h>
#include<stdlib.h>
pid_t pid = fork();
getpid()
getppid()
-
首先先给予一个新的进程号,在复制一份PCB,再复制父类的进程 生成的子进程,与父进程的大小相同 -
fork在父进程中返回的是子进程的pid,在子进程中成功返回0,失败返回-1;
注:在父进程执行完fork()之后才出现子进程
? 子进程不是从最开始执行,而是从fork()之后开始执行
? 父子进程中的n不是同一块地址空间
? main程序的父进程是3280 bash
? 0号进程没有父进程
僵死进程概念及处理方法
-
概念:子进程先于父进程结束,父进程没有调用wait获取子进程退出码 -
处理方法:父进程通过调用wait()完成
wait
WIFEXITED(val)
WEXITSTATUS(val)
子进程结束后并没有消失,仍然可以在系统中观察到,但此时的子进程已经结束了,此时子进程的状态称为僵死状态,把处于该类状态的进程称为僵死进程
如果是父进程先结束,子进程是不会变成僵死进程的
子进程结束后,父进程会获得他的退出码
- 父子进程会共享父进程fork之前打开的所有文件描述符
6. file
1. 文件描述符
是一个非负的索引值(一般从3开始,0,1,2已经被使用),指向内核中的“文件记录表”,
-
当打开一个现存文件或创建一个新文件时,内核就向进程返回一个文件描述符(内核记录表某一栏的索引); -
当需要读写文件时,也需要把文件描述符作为参数传递给相应的函数。 -
Linux 下所有对设备和文件的操作都使用文件描述符来进行。
- 常见的文件描述符类型
-
” 0 “:表示标准输入,对应宏为:STDIN_FILENO,函数 scanf() 使用的是标准输入; -
“ 1 ”:表示标准输出,对应宏为:STDOUT_FILENO, 函数 printf() 使用的是标准输出; -
“ 2 ”:表示标准出错处理,对应的宏为:STDERR_NO; 你也可以使用函数 fscanf() 和 fprintf() 使用不同的 文件描述符 重定向进程的 I/O 到不同的文件。
- 若要访问文件,而且调用的函数又是
write 、read 、open 和close 时,就必须用到文件描述符(一般从3开始)。 - 若调用的函数为
fwrite 、fread 、fopen 和fclose 时,就可以绕过直接控制文件描述符,使用的则是与文件描述符对应的文件流。
int open(const char* pathname,int flags);
int open(const char* pathname,int flags,mode_t mode);
ssize_t read(int fd,void* buf,size_t count);
ssize_t write(int fd,const void* buf,size_t count);
int close(int fd);
2. 系统调用和库函数的区别
2.1 系统调用
-
系统调用指运行在用户空间的程序向操作系统内核请求需要更高权限运行的服务。它通过软中断向内核态发出一个明确的请求。系统调用实现了用户态进程和硬件设备之间的大部分接口。 -
常见系统调用 open, close, read, write, ioctl,fork,clone,exit,getpid,access,chdir,chmod,stat,brk,mmap等,需要包含unistd.h等头文件。 -
2.2 库函数
-
库函数用于提供用户态服务。它可能调用封装了一个或几个不同的系统调用(printf调用write),也可能直接提供用户态服务(atoi不调用任何系统调用)。 -
常见库函数 printf,scanf,fopen,fclose,fgetc,fgets,fprintf,fsacnf,fputc,calloc,free,malloc,realloc,strcat,strchr,strcmp,strcpy,strlen,strstr等,需要包含stdio.h,string.h,malloc.h,stdlib.h等头文件。
2.3 区别
1.系统调用运行在内核空间,库函数运行在用户空间
如果当用户态进程调用一个系统调用时,CPU需要将其切换到内核态,并执行一个内核函数。
2.内核调用返回的是一个整数值,库函数不一定
在内核中,整数或0表示系统调用成功结束,而负数表示一个出错条件。而出错时,内核不会将其设置在errno,而是由库函数从系统调用返回后对其进行设置或使用。
3.系统调用运行时间属于系统时间,库函数运行时间属于用户时间
4.调用系统调用开销比库函数更大
5.系统调用不可替换,库函数通常可以替换
普通的库函数调用由函数库或用户自己提供,因此库函数是可以替换的
2.4 内核态和用户态的区别
1.操作系统需要两种CPU状态
内核态:运行操作系统程序,操作硬件
用户态:运行用户程序
用户态到内核态:程序运行中断、异常、系统调用
2.区别
-
运行在用户态下的程序不能直接访问操作系统内核数据结构和程序。当我们在系统中执行一个程序时,大部分时间是运行在用户态下的,在其需要操作系统帮助完成某些它没有权力和能力完成的工作时就会切换到内核态 -
处于用户态执行时,进程所能访问的内存空间和对象受到限制,其所处于占有的处理器是可被抢占的 -
处于内核态执行时,则能访问所有的内存空间和对象,且所占有的处理器是不允许被抢占的。
7. 信号
#include<signal.h>
名称 | 说明 | 值 |
---|
SIGINT | 终端中断 | 2 | SIGKILL | 终止终端 | 9 | SIGPIPE | 向无读进程的管道写数据 读端关闭 写端写入时,该信号会终止程序 | 13 | SIGTERM | 命令kill默认发送的信号 | 15 | SIGCHLD | 子进程结束后 默认给父进程发送该信号 | 17 |
1. signal()
改变信号响应方式
ctrl+c 会给当前终端执行的进程发送SIGINT信号
void fun(int sign)
{
printf("fun was called,sign = %d\n",sign);
}
int main()
{
signal(SIGINT,fun);
for(int i = 0;i< 10; ++i)
{
sleep(1);
printf("main running\n");
}
exit(0);
}
2. kill()
发送信号
向指定的进程发送指定的信号
int kill(pid_t pid,int sig);
pid > 0 指定将信号发送给那个进程
pid == 0 发送到和当前进程在同一个进程组的进程
pid == -1 发送给系统上有权限发送的所有进程
pid < -1 发送给进程组id等于pid绝对值,并且有权限发送大所有进程
sig 指定发送信号的类型
文件传输
查看服务、删除、下载文件、上传
|