查看进程信息命令:ps aux
查看环境变量命令:env
pid_t fork(void) 调用一次创建一个进程,在父进程中返回子进程的pid,在子进程中返回0
getpid() 返回调用进程的pid
execl进程重载:execl(const char* path,argv[0],argv[1],....,"NULL");
path:你要重载的程序/命令的路径
argv: 程序/命令的参数 例如:ps aux ----> execl("/bin/ps","ps",argv[1],"aux","NULL");
NULL:最后一个参数一定要传NULL表示传参结束,不写函数失败。
pid_t zpid = wait(NULL(有一个int*类型的status传出参数用于获取子进程退出信息用于验尸));
pid_t zpid = waitpid(pid_t pid,int* status,int opt);
参数:
pid:指定回收的方式
>0:指定一个子进程pid,回收这个子进程
-1:回收任意子进程
=0:同组回收,只能回收掉与父进程同组的所有子进程
<0(组id gpid):指定组id,实现夸组回收
status:暂时传NULL,用于验尸,传出参数,里面是子进程的退出信息
opt:工作模式
WNOHANG:非阻塞关键字,可以让waitpid进行非阻塞回收,非阻塞回收可以让父进 程在回收过程中穿插执行自身的工作,非阻塞回收也是经典的主动回收方式。
返回值:
三种返回值 >0:表示回收成功,-1:表示回收失败,0:表示非阻塞返回
进程的退出和status参数,验尸
WIFEXITED(status): 判断进程是否是正常退出
WEXITSTATUS(status):返回的是退出码或返回值
WIFSIGNALED(status):判断进程是否是正常退出
WTERMSIG(status): 获取杀死子进程的信号编号
匿名管道:pipe(fds) int fds[2]:fds[0]读端, fds[1]写端
创建有名管道文件命令:mkfifo 管道名
文件共享映射mmap:
头文件:#include<sys/mman.h>
void* ptr = mmap(NULL(映射内存地址传) , 映射大小 , 映射内存权限 , 映射方式 , 文件描述符 , 0(映射偏移量))
返回值:成功返回映射内存地址,失败返回MAP_FAILED关键字
参数1: 映射内存地址,传NULL让系统自行分配映射内存,也可以malloc自己分配
参数2: 映射大小,一般是文件大小
参数3: 映射内存权限,一般是PROT_READ|PROT_WRITE|PROT_EXEC|PROT_NONE
参数4: 映射方式,有私有映射MAP_PRIVATE,共享映射MAP_SHARED
私有映射:把映射文件内容拷贝到映射内存,称之为拷贝映射,一方改变对另一方没有影响
共享映射:建立了一种sync同步机制,将文件数据映射给进程,一方改变会同步给另一方
参数5: 映射文件的描述符,映射时使用访问磁盘数据
参数6: 映射偏移量,mmap支持偏移映射,也就是一次映射文件一部分,用来处理大文件
munmap(void* ptr,映射大小) 释放映射内存
ftruncate(fd , 扩展大小) 扩展文件大小
getpgrp() 返回进程组id
setpgid(pid_t pid , pid_t gid) 创建新组/组员转移
getsid(pid_t pid) 获取会话id
setsid() 创建新会话
ps ajx 查看进程间关系
kill( 1000 , 0 ) 可以检查1000这个进程是否存活
kill(-1000 , 9) 杀死1000这个组里的进程
kill -l 获取信号列表
信号头文件:#include<signal.h>
组合键:
ctl+c(SIGINT=2,杀死进程)
ctl+\(SIGQUIT=3,杀死进程)
ctl+z(SIGTSTP=20,挂起进程)
命令:
kill - signo pid 向任意进程发送任意信号
函数:
kill(pid_t pid,int signo) 向任意进程发送任意信号
raise(int signo) 向调用进程发送任意信号,谁调给谁发
abort(void) 向调用进程发送SIGABRT信号
unsigned int alarm(unsigned int 秒数) 定时到时系统发送SIGALARM信号
信号行为结构体
struct sigaction act;
act.sa_handler类型:void(*act.handler)(int)
信号行为选择
act.sa_handler = SIG_DFL(默认) | SIG_IGN(忽略) | sigdo(捕捉函数地址)
选项
act.sa_flags = 0; 默认选0
临时屏蔽字
act.sa_mask 不使用但是要初始化
sigemptyset(&act.sa_mask); 初始化临时屏蔽字
捕捉函数的接口还有,两个,其中sa_restorer没有参数属于比较老的不怎么用了,sa_sigaction是有三个参数,可以用来进程间通信,使用这个接口函数注意sa_flags要设置成SA_SIGINFO
sa_sigaction(int n, siginfo_t* info, void* arg)
进程传递的数据被保存在info结构体中
整形被保存在info->si_int
地址数据被保存在info->si_ptr。
sigaction(SIGINT,&newact,&oldact);
sigaction(SIGINT(对那个信号操作),&act(新的信号行为结构体),&oact(传出参数,传出旧的信号行为结构体,不要就传NULL))
通过屏蔽字屏蔽信号可以实现让信号临时失效,但是信号并没有消失卡在那里,只要解除屏蔽,立即处置进程
信号集的类型 sigset_t set
初始化信号集所有位为0 sigemptyset(&set)
初始化信号集所有位为1 sigfillset(&set)
将特定信号的位码置为1 sigaddset(&set,信号宏名或信号编号)
将特定信号的位码置为0 sigdelset(&set,信号宏名或信号编号)
查看特定信号的位码置是0是1并返回 int code = sigisnumber(&set,信号宏名或信号编号)
采用替换方式更改屏蔽字
SETMASK直接覆盖
sigprocmask(SIG_SETMASK,&newset,&oldset(传出以前的信号集就传,不要就传NULL))
sigpending(sigset_t* pset) 调用函数后,系统将进程的未决信集传出到pset
pause() 执行立即挂起,当察觉到一个信号就唤醒,任意信号都能唤醒,能察觉的信号必须已递达并有处理动作
sigsuspend(sigset_t*) 挂起的同时设置临时屏蔽字
sigqueue(pid_t pid, int signo, union sigval val) 发送信号的同时发送数据
union sigval
sigqueue 用来传递数据用的联合体参数
联合体 union sigval val 有两个成员,一个传整形数据,一个传地址数据
val.sival_int (int)
val.sival_ptr (void*)
ps -eLf 查看系统中所有线程
ps -Lf pid 查看某一个进程的所有线程
pid 进程id
LWP 轻量级进程编号(不是tid , 而是调度编号)
NLWP 轻量级进程数量
pthread_t tid 用于存储线程id
返回调用线程tid:pthread_self()
线程创建函数 pthread_create
int err = pthread_create(pthread_t* tid , pthread_attr_t* attr , void* (*tjob)(void*arg),void* arg)
返回值:
线程创建函数,成功返回0,失败返回整形错误号需要用户自行接收,而后使用strerror进行错误处理
参数:
tid : 线程创建成功, 将此线程的id,传出到变量中
attr: 线程属性参数, 传出NULL表示使用默认属性
tjob : 线程工作地址, 此参数为函数指针void* (*tjob)(void*arg)
arg : 线程函数参数, 系统创建线程后调用twk而后把arg传入twk中
pthread_join(pthread_t tid , void** reval)线程回收函数
返回值:
成功返回0,失败返回错误码
参数:
tid :要等待结束的线程的标识符。
retval:用于存储线程返回值的指针。如果该线程没有返回值,则该参数可以设置为NULL。
线程回收,接受线程返回值,如果不回收会引发僵尸线程(TCB)残留,默认情况下,线程均是回收态线程(PTHREAD_JOINABLE),这类线程退出后,需要进行join回收操作,否则导致内存泄露引发僵尸线程(TCB)残留。
阻塞函数,线程未退出等待,退出后立即回收,调用pthread_join函数会阻塞当前线程,直到指定的线程结束为止。
pthread_exit((void *)9) 线程退出函数
线程退出并返回特定值,无论那种线程使用,都只结束当前线程与进程无关。
pthread_cancel(pthread_t tid) 线程取消函数
线程取消,参数为目标线程的tid,可以将目标线程杀死。
如果需要对线程的返回值进行验证,需要使用回收线程,否则分离线程即可。
如果线程取消,那么回收线程得到的返回值为-1,线程开发时不允许使用-1作为返回值,保留给cancel,这样回收线程时可以判断线程是正常return还是被其他线程cancel取消杀死。
信号是100%会其作用,cancel不是,cancel需要有系统调用才会处理。
pthread_testcancel() 触发一次系统调用
调用后触发一次系统调用以解决cancel后线程无法杀死的情况,配合cancel使用
pthread_detach(pthread_t tid);线程分离设置
可以讲一个线程设置为分离态。
默认线程退出状态为回收态,但是可以变为分离态,分离态线程结束后系统自动回收线程资源。
将自己设置为分离态:pthread_detach(pthread_self());
线程属性结构体 pthread_attr_t attr
int err = pthread_create(pthread_t tid , pthread_attr_t* attr , void* (*twk)(void*arg),void* arg)
pthread_create第二个参数要的就是这个结构体的地址,传NULL表示使用默认结构体,默认属性
用户可以自定义线程属性结构体,而后创建线程,决定线程形态
线程属性结构体成员有:
线程优先级指针 默认=dfl
线程警戒缓冲区大小 默认=4096Byte 防止栈溢出机制,溢出会污染进程用户空间
线程退出状态 默认=PTHREAD_JOINABLE
线程栈地址 默认=null
线程栈大小 默认=0
int err = pthread_create(pthread_t tid , pthread_attr_t* attr , void* (*twk)(void*arg),void* arg)
自定义线程属性变量
pthread_attr_t newattr;
初始化线程属性
pthread_attr_init(&newattr); 初始化线程属性,初始完毕为默认属性
销毁释放属性
pthread_attr_destroy(&newattr); 使用完释放属性
获取属性中的退出状态
pthread_attr_getdetachstate(&newattr,int* detachstate); 传出属性中的退出状态到变量detachstate中
设置属性中的退出状态
pthread_attr_setdetachstate(&newattr,PTHREAD_CREATE_DETACHED / PTHREAD_CREATE_JOINABLE); 向属性中设置状态
获取线程属性中的栈信息
pthread_attr_getstack(&newattr, void ** stackaddr, size t*stacksize); 参数是传出参数,获取属性中的栈地址与大小,并传出
设置线程属性的栈信息
pthread_attr_setstack(&newattr, void * stackaddr, size t stacksize); 设置属性中的栈地址与大小
互斥锁类型
pthread_mutex_t lock;
动态初始化互斥锁
pthread_mutex_init(&lock,NULL);
释放锁内存
动态初始化对应的销毁
pthread_mutex_destroy(&lock);
静态初始化互斥锁
lock = PTHREAD_MUTEX_INITIALIZER;
不需要释放
阻塞上锁
pthread_mutex_lock(&lock);
解锁
pthread_mutex_unlock(&lock);
非阻塞上锁
支持非阻塞上锁,如果获取不到锁,无需阻塞等待,函数立即返回
pthread_mutex_trylock(&lock);
互斥锁属性变量
pthread_mutexattr_t attr;
初始化属性
初始化后的锁属性为线程互斥
pthread_mutexattr_init(&attr);
设置互斥锁属性为进程锁
pthread_mutexattr_setpshared(&attr,PTHREAD_PROCESS_SHARED);
PTHREAD_PROCESS_SHARED 进程锁
PTHREAD_PROCESS_PRIVATE 线程锁
初始化时使用自定义锁属性
初始化互斥锁时,加上属性
pthread_mutex_init(&lock,&attr);
静态初始化的互斥锁必然是线程锁,想要使用进程锁必须动态初始化
释放锁属性
pthread_mutexattr_destroy(&attr);
读写锁类型
pthread_rwlock_t lock;
动态初始化读写锁
pthread_rwlock_init(&lock,NULL);
释放锁内存
动态初始化对应的销毁
pthread_rwlock_destroy(&lock);
静态初始化读写锁
lock = PTHREAD_RWLOCK_INITIALIZER;
阻塞上读锁
pthread_rwlock_rdlock(&lock);
阻塞上写锁
pthread_rwlock_wrlock(&lock);
解锁
两个锁都能解除
pthread_rwlock_unlock(&lock);
条件变量类型
pthread_cond_t cd
挂起线程在其中,也可以唤醒这些线程(线程的挂起/唤醒点)
初始化条件变量
静态初始化
cd = PTHREAD_COND_INITIALIZER;
动态初始化
pthread_cond_init(&cd,NULL);
销毁条件变量
pthread_cond_destroy(&cd);
挂起到条件变量
pthread_cond_wait(&cd,&lock);
参数1:条件变量地址,参数2:要操作的锁的地址
谁调谁挂起,线程调用函数后将线程挂起在指定条件变量中挂起的同时解锁
此函数有两次执行,第一次是挂起并解锁,第二次被唤醒时继续执行wait自动再上锁
将条件变量中挂起的线程唤醒一个
pthread_cond_signal(&cd);
唤醒条件变量中所有挂起的线程
pthread_cond_broadcast(&cd);
死锁产生的四个必要条件
1.保持与请求 (占用资源的情况下还有申请新资源)
2.互斥条件 (请求占用的资源,会产生阻塞挂起)
3.不可剥夺条件 (占用资源的线程只要不主动释放,其他人无法剥夺资源)
4.循环等待条件 (每个单元都在等待相邻单元的资源)
select(maxfd+1,&set,NULL,NULL,struct timeval* tim);
参数一
监听文件描述符数量,从0开始 可以写maxfd(变量)+1即可覆盖所有的文件描述符 如上图serfd为3但一共有四个文件描述符所以+1
参数二三四
要监听的事件,分别为 读,写,异常,&set为fd_set类型
全监听则为 select(maxfd+1,&set,&set,&set,NULL);
只监听读 select(maxfd+1,&set,NULL,NULL,NULL);
最后一个参数
struct timeval* tim结构体指针,tim参数表示select工作模式
传NULL表示阻塞监听
struct timeval* tim; tim->second = 0; tim->us = 0; 这样为非阻塞监听
struct timeval* tim; tim->second = 20; tim->us = 0; 这样为定时阻塞监听,比如传20就是阻塞20秒就返回
返回值
ready就绪数量(int),返回就绪数量,不返回是谁就绪
fd_set set; 监听集合类型大小1024
FD_ZERO(&set); 初始化为0
FD_SET(int sockfd,&set); 设置监听,将某一个Socket的位置为1
FD_CLR(int sockfd,&set); 取消监听,将某一个Socket的位置为0
FD_ISSET(int sockfd,&set); 返回集合中对应位置的监听位码,遍历Socket数组判断哪个Socket就绪时可用
struct pollfd类型
监听集合为结构体数组
struct pollfd listen_array[1000];
成员:
listen_array[0].fd = sockfd; 监听的 socket 设置 socket表示监听,设置-1表示取消监听
listen_array[0].events = POLLIN|POLLOUT|POLLERR; 设置监听什么类型的事件
listen_array[0].revents 如果就绪传出就绪事件,例如写就绪则 = POLLIN
int ready = poll( listen_array , array_size , int timeout);
参数1: 监听数组的首地址
参数2: 最大监听数---监听数组的大小
参数3: -1阻塞模式工作 0非阻塞模式工作 >0定时阻塞单位是毫秒传几就是几毫秒
处理完就绪将对应socket在数组中的revents归0 listenarray[i].revents = 0;
返回值:就绪数量
节点类型: struct epoll_event
struct epoll_event node;
成员:
node.data.fd = sock; 监听的Socket
node.events = EPOLLIN|EPOLLOUT|EPOLLERR 监听的事件
还有一个隐藏的callback()回调函数
监听树
监听集合使用红黑树
创建监听树
int epfd = epoll_create(int size);
参数:int size
创建监听树的大小
返回值:int epfd
监听树的文件描述符
操纵监听树(添加,删除,修改)epoll_ctl
epoll_ctl( epfd,cmd ,int socketfd,&node);
参数1:
操作的监听树
参数2:
操作模式
EPOLL_CTL_ADD添加节点
EPOLL_CTL_DEL删除节点
EPOLL_CTL_MOD修改节点 只能修改监听事件无法修改socket本身(会导致Socket与索引不一致)
参数3:
socket,与节点中的一致,也是节点的索引编号
参数4:
节点的地址
返回值:
成功:返回0
失败:返回-1
监听函数epoll_wait,阻塞监听socket事件
epoll_wait(epfd , struct epoll_event* array , int size , -1);
参数1:
监听树的文件描述符,表示对哪个树进行监听
参数2:
就绪队列,是节点结构体的数组 struct epoll_event*
参数3:
最大就绪数,一般和最大监听数相等
如果最大监听数为1000,最大就绪数为500表示对1000个sock监听但最多只会让500个socket就绪
参数4:
-1表示阻塞监听
返回值:
小于0:出错 等于0:超时 大于0:返回就绪事件个数
边缘模式关键字 EPOLLET
默认情况下是水平模式,要想使用边缘模式监听需要添加节点的时候加关键字EPOLLET
node.data.fd = sock;
node.events = EPOLLIN|EPOLLET;
pthread_kill(tid,0) 返回ESRCH表示线程已死返回0否则存活返回1