今日思维导图:
一,linux系统中的库
1> linux系统中的库本质上是一个二进制文件,由.c文件(不含main)编译而来,其他程序可以使用该库调用相关函数
2 > 分类:静态库 动态库
3 > 不同的操作系统对应的库的格式有所不同
windows:XXX.lib(静态库)
XXX.dll(动态库)
linux:libXXX.a(静态库)
libxxx.so(动态库)
4 >由于库是二进制文件,.c文件编译生成库,函数的实现过程是看不到的,所以使用库比较安全
1.1静态库
将XXX.c文件编译生成的一个库名为.a的二进制文件,当你需要使用该库中的某个函数时,直接调用该函数即可,前提是编译时链接了该库,静态体现在:在使用gcc编译时,会将你的文件和库文件一起生成一个可执行文件,在最终的可执行程序中,包含了静态库,程序的体积一般比较大,在执行程序的过程中,就无须取找该库所在的位置,效率较高
编译生成静态库
1 gcc -c add.c -o add.o //只编译不链接 将源文件生成2进制文件
2 ar -cr libadd.a add.o //ar说明这是做静态库的指令
//如果由多个.o文件生成一个静态库 ar -crs 库名.a add1.o add2.o ...
库名命名规范:以lib开头,后面是库名,最后以.a结尾
例如:xxx.c生成的库名:libxxx.a
编译静态库:
使用静态库:
1 gcc main.c -L ./ -ladd -I ./
2 格式:gcc 主程序.c -L库所在的路径 -l库名 -I 头文件所在路径
3 -L:指定库的路径
4 -l 所需库名
5 -I(大写i):指定头文件所在路径
1.2动态库
将XXX.c文件编译生成的一个库名为.a的二进制文件,当你需要使用该库中的某个函数时,直接调用该函数即可,前提是编译时链接了该库,静态体现在:在使用gcc编译时,会将你的文件和库文件的索引表一起生成一个可执行文件,程序的体积一般比较大,在执行程序的过程中,就须取找该库所在的位置,效率较低。但是由多个程序共享一个库,动态库又称共享库。
解决办法1,2:
二,多进程
2.1 进程的概念
进程:程序的一次执行过程
进程就是一个动态的过程,是有生命周期的,随着进程的创建而出现,随着进程的UAN的消亡而销毁
进程是分配资源的最小单位 每个进程都会拥有自己的0-3G 的用户空间 但是共享3-4G的内核空间
这0-3G 的用户空间又被分为三个部分 栈区 堆区 静态区
进程在内核空间有一个task_struct表示
2.2进程的内存管理(重点!!!)
1>每个进程都会被分配4G的空间
2> 每个进程都拥有自己的0-3G的用户空间,3--4G的内核空间是共享的
2.3程序和进程的区别
进程:进程是动态的,有生命周期,是程序的一次执行过程,每个进程拥有0-3G的用户空间,并且共享3-4G的内核空间
程序:程序是静态的,没有生命周期可言,它是在磁盘上的一个二进制文件
2.4 进程的组成
进程有三部分组成:进程控制段,数据段,程序段
进程控制段:是内核空间分配的一个task_struct的结构体,包含了一个进程的所有信息:PID,进程的状态,相关寄存器
数据段:在进程执行过程中,中间数据存放的位置:.data .bss
程序段:存放程序允许的代码:.text
2.5 进程的种类
进程一共有三种:交互进程,批处理进程,守护进程
交互进程:它是由shell脚本控制,可以直接和用户进行交互的进程,例如文本编辑器
批处理进程:维护了一个队列,被放到该队列中的进程会被统一处理,例如终端执行./a.out
守护进程:脱离终端而存在,例如服务进程
2.6进程的PID概念
进程号:PID 计算机系统识别进程的唯一标识
父进程号:PPID
无论是PID还是PPID都是一个大于等于0的数字
2.7特殊的进程(PID:0,1,2)
1> 0号进程:它是linux系统启动后的第一个进程,这个进程也叫作空闲进程
2> 1号进程:它是在0号进程产生,这个进程主要用于硬件的初始化工作,也是其他进程的父进程,可以完成对一些进程的收尸工作
3> 2号进程:也称为调度进程,这个进程也是0号进程产生,他的任务是完成任务调度工作
2.8 进程的相关命令
1> 查看进程的指令:ps
ps -ef :可以显示进程之间的关系PPID
UID:用户id
PID:当前进程id号
PPID:当前进程父进程的PID
STIME:运行日期
ps-ajx 查看进程的运行状态
ps-aux 查看计算机分配资源的占比
2> kill指令:给进程发信号
kill-l:可以查看系统提供的信号都有哪些
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP
6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR前30个是稳定信号 ,下面是不稳定信号
31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3
38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7
58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
63) SIGRTMAX-1 64) SIGRTMAX
2) SIGINT:中断进程,相当于ctrl+c
19) SIGSTOP :暂停进程ctrl+z
9) SIGKILL:杀死进程
8) SIGFPE :继续一个进程执行
kill -信号号 pid(给进程发射信号)
killall 进程名:杀死所有的该进程名对应的进程
pidof 文件 显示进程号
5> 查看能够创建多少个进程: cat/proc/sys/kernel/pid_max
6> pstree:显示进程树的关系
三,进程的状态
man ps 可以得到相关状态描述
D uninterruptible sleep (usually IO) 不可中断的睡眠状态
R running or runnable (on run queue) 运行态
S interruptible sleep (waiting for an event to complete)可中断的睡眠状态
T stopped, either by a job control signal or because it is being traced 停止态
//弃用W paging (not valid since the 2.6.xx kernel)
X dead (should never be seen)死亡态:不能被看到
Z defunct ("zombie") process, terminated but but not reaped by its parent 僵尸态:子进程结束,父进程没有为其收尸
< high-priority (not nice to other users)高优先级进程
N low-priority (nice to other users)低优先级进程
L has pages locked into memory (for real-time and custom IO)加锁的过程,不可用放入swap分区内
s is a session leader会话组组长进程
l is multi-threaded (using CLONE_THREAD, like NPTL pthreads do)多线程进程
+ is in the foreground process group 前台进程
休眠进程的切换
四,进程的创建
进程的创建过程,是通过拷贝父进程得到的,进程在内核空间是通过task_struct结构体表述的,新进程在创建过程中,直接拷贝父进程的该结构体得到,只需要稍作修改即可,保留了父进程大部分的信息,这个拷贝过程,被封装在fork函数中。
4.1 进程创建函数fork
#include <unistd.h>
pid_t fork(void);
功能:拷贝一个父进程得到一个子进程
返回值:成功
父进程会得到子进程的pid,子进程会得到0
失败:给父进程返回-1,子进程创建失败
注意:1.父进程会拷贝一份资源给子进程,父进程和子进程的资源是一致的,但是子进程不会执行fork之前的语句
2.父子进程先执行哪一个是不确定,由cpu调度机制来决定
3.子进程会拷贝父进程的虚拟空间,父子进程0-3G的用户空间内容完全一致,3-4G内核空间共享
#include <myhead.h>
int main(int argc,const char *argv[])
{
pid_t pid;
pid=fork(); //生成一个子进程
//pid判断
if(pid<0)
{
perror("fork error");
return -1;
}else if(pid ==0)// 说明这段是子进程的内容
{
printf("这是子进程\n");//子进程单独执行
}
else{
printf("这是父进程\n");//父进程单独执行
}
puts("woshishuaige");//父子进程同时执行
while(1);
return 0;
}
4.2 fork出来的进程与原进程的执行先后顺序
子进程和父进程执行没有先后顺序,原因是遵循时间片轮询机制,上下文切换原则,调度到那个就执行那个
4.3 父子进程的进程号获取
#include <sys/types.h>
#include <unistd.h>
pid_t getpid(void);
pid_t getppid(void);
功能:获取自己的进程号或父进程号
参数:无
返回值:返回自己的进程号和父进程号
#include <myhead.h>
int main(int argc,const char *argv[])
{
pid_t pid;
pid=fork(); //生成一个子进程
//pid判断
if(pid<0)
{
perror("fork error");
return -1;
}else if(pid ==0)// 说明这段是子进程的内容
{
printf("这是子进程:pid=%d,ppid=%d\n",getpid(),getppid());//子进程单独执行
}
else{
printf("这是父进程:pid=%d,ppid=%d,childpid=%d\n",getpid(),getppid(),pid);//父进程单独执行
}
puts("woshishuaige");//父子进程同时执行
while(1);
return 0;
}
4.4 进程退出
在main函数中,使用return也可以结束一个进程
可以调用相关函数,结束进程
exit函数是库函数,会刷新缓存区
_exit()是系统调用,也是结束进程但是不会刷新缓冲区
#include <stdlib.h>
void exit(int status);
功能:结束进程,并刷新缓冲区
参数:退出时的状态
返回值:无
#include <unistd.h>
void _exit(int status);
功能:结束进程,不刷新缓冲区
4.5 进程资源回收(wait/waitpid)
1> 僵尸进程:子进程已经结束,但是,父进程没有为其收尸,那么子进程就是僵尸进程
2> 孤儿进程:父进程已经结束,子进程就变成了孤儿进程,就会被init进程收养
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status);
功能:阻塞等待回收子进程资源
参数:子进程使用exit/_exit退出时的状态。一般不用接受,直接:传进去NULL就行
返回值:成功返回子进程的pid,失败返回错误码
pid_t waitpid(pid_t pid, int *status, int options);
功能:可以阻塞也可以不阻塞等待回收子进程号为pid的进程资源
参数1:要回收的自进程的pid号
<-1:回收绝对值与pid相等的同一组id下的所有子进程
=-1:回收任意一个子进程(常用) waitpid(-1,NULL,0)<===>wait(NULL)
=0:回收多个pid中的一个,与当前进程所在的同一组的任意一个进程(常用)
>0: 回收值为pid的进程(回收指定的进程)
参数2:子进程使用exit/_exit退出时的状态。一般不用接受,直接传进去NULL就行
参数3:0表示阻塞等待回收资源,WNOHANG则表示非阻塞等待回收资源
返回值:成功返回回收了资源的子进程的pid,如果是非阻塞等待,并且没有子进程结束
练习:一个父进程创建两个子进程(一共三个进程),然后在父进程中为两个子进程进行收尸
#include <myhead.h>
int main(int argc,const char *argv[])
{
pid_t pid1,pid2;
pid1=fork();
if(pid1==0)
{
printf("子进程1开始\n");
puts("子进程1结束");
exit(EXIT_SUCCESS);
}else
{
pid2=fork();
if(pid2==0)
{ printf("子进程2开始\n");
puts("子进程2结束");
exit(EXIT_SUCCESS);
}else
{
printf("父进程开始\n");
puts("父进程结束");
wait(NULL);
exit(EXIT_SUCCESS);
}
}
return 0;
}
五,守护进程
所谓守护进程,其实就是一个服务,随着系统的启动而启动,随着系统的结束而结束,不依赖于终端而存在,因为在终端上运行的进程,随着终端被关闭,进程也随之关闭
而且一般不会被打断,需要将其执行放在根目录下
5.1守护进程创建流程
1>创建一个孤儿进程
2>重新设置孤儿进程的SID(会话id)和组id(让进程独立出来)
3>修改守护进程的目录为根目录,当然也可以不修改,但是一旦所在的目标被删除,该守护进程也被删除了
4>修改创建文件的umask给umask(0)
5>将标准输入,标准输出,标准出错重定向某个文件(日志文件 dup2)
6> 对应的文件就是日志文件
5.2守护进程的API
#include <unistd.h>
pid_t setsid(void);
功能:创建一个会话,将当前的进程的id设置为pgid和sid
参数:无
返回值:成功返回会话的id 失败返回-1并置位错误码
#include <unistd.h>
int chdir(const char *path);
功能:切换目录
参数:要切换的目录,是一个字符串
返回值:成功返回0,失败返回-1
#include <sys/types.h>
#include <sys/stat.h>
mode_t umask(mode_t mask);
功能:更改掩码
参数:要修改的值
返回:不会失败
#include <unistd.h>
int getdtablesize(void);
功能:获取程序中能打开的最大文件描述符的值(1024)
参数:无
返回值:成功返回当前进程 文件描述符的最大个数,不会失败
六,多线程
6.1多线程概念
多线程:线程是粒度更小的处理单位
进程是资源分配的最小单位,线程是调度器进行调度的最小单位
线程共享进程的资源,多线程拥有自己独立的资源
线程几乎不占用资源,只占用的很小的有关执行状态的资源,大概在8k左右
线程由于共用线程的资源,所以多线程没有多进程安全,使用多线程是因为开销较小
在一个进程内,至少有一个线程
因为线程操作函数,需要依赖第三方库,所以想使用线程处理函数,需要安装对应的库
6.2多线程创建
#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
Compile and link with -pthread.
功能:创建线程
参数1:要创建的线程的线程号(以参数的形式返回)
参数2: 线程的属性,一般填NULL即可
参数3:线程处理函数,是一个参数为void*类型,返回值为void*类型的函数指针,需要传递进来的是线程处理函数的函数名
参数4:向线程处理函数中传递的参数
#include <myhead.h>
//定义一个全局变量
int money=5000;
//定义线程处理函数
void *task1(void *arg)
{
money-=50;
printf("这是线程1 我拿了50元,还有%d\n",money);
}
void *task2(void *arg)
{
money-=100;
printf("这是线程2 我拿了100元,还有%d\n",money);
}
int main(int argc,const char *argv[])
{
pthread_t tid1,tid2;
//创建两个线程
if(pthread_create(&tid1,NULL,task1,NULL))
{
perror("create tid1 error");
return -1;
}
if(pthread_create(&tid2,NULL,task2,NULL))
{
perror("create tid2 error");
return -1;
}
while(1);
return 0;
}
6.3多线程内存问题
多线程共享所在进程的资源
6.4多线程获取线程ID
#include <pthread.h>
pthread_t pthread_self(void);
功能:获取当前线程的id
返回:永无失败
6.5多线程之间执行顺序
没有要求,遵循时间片轮询
6.6多线程退出(pthread_exit)
#include <pthread.h>
void pthread_exit(void *retval);
功能:退出所在的线程
参数:退出线程状态
返回:无返回值
6.7多线程资源回收(pthread_join)
#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);
功能:阻塞等待回收线程的资源并为线程收尸
参数1:线程号
参数2:线程退出时的状态,一般填NULL
返回:成功返回0,失败返回错误码
6.8多线程发信号(pthread_cancel)
#include <pthread.h>
int pthread_cancel(pthread_t thread);
功能:一个线程给另一个线程发送一个取消的信号
参数:别人的线程号
返回值:成功返回0,失败返回非0的错误码
#include <pthread.h>
int pthread_setcancelstate(int state, int *oldstate)
参数1:状态
PTHREAD_CANCEL_ENABLE
PTHREAD_CANCEL_DISABLE
参数2:旧的状态
返回值:成功返回0,失败返回错误码
6.9多线程分离态(pthread_detach)
1> 对于线程而言,有两个状态,分别是结合态和分离态,默认是结合态,结合态的线程,结束后需要使用pthread_join回收资源
2> 分离态的线程,不使用pthread_join回收资源
#include <pthread.h>
int pthread_detach(pthread_t thread);
功能:将线程记为分离态
参数:线程号
返回值:成功返回0,失败返回错误码
作业:
使用父子进程拷贝文件
include <myhead.h>
int get_file_len(const char *srcfile, const char *destfile)//获取文件长度
{
int sfd,dfd;//定义文件描述符
int len;//定义字符个数
if((sfd=open(srcfile,O_RDONLY))==-1)//以只读的方式打开
{
perror("open file");
return -1;
}
if((dfd=open(destfile,O_RDWR|O_CREAT|O_TRUNC,0664))==-1)//以读写的方式打开
{
perror("open file");
return -1;
}
len=lseek(sfd,0,SEEK_END);//计算长度
close(sfd);//关闭sfd
close(dfd);
return len;
}
int mycopy(const char *srcfile,const char *destfile,int start,int len)
{
int sfd,dfd;//定义文件描述符
char buf[10]="";//准备容器
int i;
if((sfd=open(srcfile,O_RDONLY))==-1)//以只读的方式打开
{
perror("open file");
return -1;
}
if((dfd=open(destfile,O_RDWR))==-1)//以读写的方式打开
{
perror("open file");
return -1;
}
lseek(sfd,start,SEEK_SET);//把光标移动到要复制的位置
lseek(dfd,start,SEEK_SET);
while(1)
{
int ret=read(sfd,buf,sizeof(buf));//读取文件
i=+ret;
if(ret==0|i>len)//表示读超了或读到结尾
{
write(dfd,buf,ret-i+len);
break;
}
write(dfd,buf,ret);//见文件写入
return 0;
}
}
int main(int argc,const char *argv[])
{
if(argc!=3)//判断参数是否大于3
{
printf("usage:./a.out srcfile destfile\n");
return -1;
}
pid_t pid=fork();//创建一个子进程
int len=get_file_len(argv[1],argv[2]);//获取长度
if(pid==0)
{
//前半部分由子进程拷贝
printf("It's time to copy by childpid\n");
mycopy(argv[1],argv[2],0,len/2);
exit(EXIT_SUCCESS);
}else
{//后半部分由父进程拷贝
printf("It's time to copy by fatherpid\n");
mycopy(argv[1],argv[2],len/2,len-len/2);
wait(NULL);//回收子进程的资源
exit(EXIT_SUCCESS);
}
return 0;
}