UC高级编程第三天
1.int fcntl(int fd, int cmd, … /* arg */ );
功能:
操作/控制文件描述符
参数:
fd: 要操作的文件描述符
cmd: 指定了对文件描述符操作的命令
文件的状态信息:
F_GETFL
F_SETFL
建议锁的类型:
F_GETLK 测试锁, 并不实际加锁, 如果可以加锁, 会在l_type中返回F_UNLCK
如果不能加锁, 在l_pid返回持有互斥锁进程PID
F_SETLK 设置锁, 如果锁是互斥锁(写锁), 返回-1, errno被设置 EAGAIN EACCES
F_SETLKW 设置锁, 如果锁是互斥锁, 等待其他进程释放锁
返回值:
成功 0
错误 -1 errno被设置
一.对文件夹进行操作
1.使用库函数对文件夹进行操作
1.opendir(3)
DIR *opendir(const char *name);
功能:打开一个文件夹
参数: name, 指定了要打开的文件夹的名字
返回值:成功 文件夹流
错误 NULL errno被设置
2.closedir(3)
int closedir(DIR *dirp);
功能:关闭文件夹流
参数: dirp 文件夹流
返回值:成功 0
错误 -1 errno被设置
3.readdir(3)
struct dirent *readdir(DIR *dirp);
功能:遍历文件夹流
参数: 文件夹流
返回值:
成功 返回一个指向struct dirent指针
NULL 读到了文件夹流的末尾
错误 NULL
4.glob(3)
int glob(const char *pattern,int flags,int(*errfunc)(const char *epath,int eerrno),glob_t *pglob);
功能:
解析给定的模式, 模式的规则符合shell
参数:
pattern:指定的模式 shell bash
flags: 要求
errfunnc: 错误处理函数
pglob: 保存查找的/匹配的结果
返回值:
成功:0 结果保存到pglob
错误:没匹配到 指定errfunc
二,linux中密码校验过程
1.密码存储在这里
/etc/shadow
2.使用crypt来加密密码
读出来的是加密之后的字符串, 和用户输入的密码加密之后的字段相匹配
3.密码的键入
getpass(3)
4.glob(3)
是一个用于匹配文件名模式(pattern)的函数,它遵循 shell 的规则来扩展文件名模式。
shell的规则:
1. ```
1. “/etc/" 表示/etc/下的所有文件
2. “/etc/a?” 表示/etc/所有以a开头的文件 ?通配
3. "/etc/.c” 表示/etc下的所有.c文件
```
```
strsep(3)
char *strsep(char **stringp, const char *delim);
功能:
分割字符串
参数:
**stringp 就是指向要分割的字符串
*delim 由切割字符组成的字符串
返回值:
成功 返回切割出来的字符串
错误 NULL
```
三,并发
进程与程序的区别
程序存放在硬盘中, 是计算机指令的集合 是静态的
进程是运行着的程序, 程序运行的实例 是动态的
进程的基本状态
a. 就绪态
b.运行态
c. 阻塞态
d. 终止态
linux是采用时间片轮训
进程的状态
ps -aux//显示当前运行的进程列表
ps -axj//显示进程的关系
PPID:父进程的,PID 进程的,PGID 进程组的ID 会话的ID
ps axm -L///显示进程中的线程的状态
linux中c语言是如何终止的
1.从main函数中return //return到C启动例程中
调用exit(3)
调用__exit(3) 或_Exit(3)
调用pthread_exit(3)
最后一个线程从启动例程返回
调用abort
最后一个对取消请求做出响应
接受到signal终止进程
2.c程序的启动
内核调用C启动例程, C启动例程是系统已经写好的代码
C启动例程调用main函数, 从而整个进程就运行起来了
3.C程序的终止
main中return到C启动例程,
C启动例程调用exit(3)终止进程
main中调用exit(3)
main中调用_exit(2)
用户函数中调用exit(3)
用户函数中调用_exit(2)
exit(3)和_exit(2)的区别
exit(3)最终是要调用_exit(2)终止进程, _exit是系统调用 exit是库函数
exit(3)在调用_exit(2)之前, 先调用进程终止处理函数和标准IO清理程序 fclose
_exit(2) 什么都不做直接终止进程
4.进程的环境
用户级的进程
所有的用户级进程都是以树状的形式进行组织的
如何查看进程树:
pstree
进程只有两种关系:
父子关系 兄弟关系
创建子进程:
进程只有两种关系:
父子关系 兄弟关系
pid_t fork(void);
功能:
创建一个子进程
返回值:
成功:
在父进程中返回子进程的pid 子进程中返回0
错误:
在父进程中返回-1 errno被设置
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main() {
pid_t pid = fork(); // 创建子进程
if (pid < 0) {
// fork失败
perror("fork failed");
return 1;
} else if (pid == 0) {
// 子进程
printf("I am the child process. PID: %d\n", getpid());
} else {
// 父进程
printf("I am the parent process. PID: %d, Child PID: %d\n", getpid(), pid);
}
return 0;
}
-
子进程和父进程分别运行在自己独立的4G虚拟地址空间
-
fork并不是返回两次, 而是向父进程返回子进程的pid 向子进程返回0
在执行过程中父进程和子进程谁先执行不确定,跟内核的调度策略有关系
遗言函数:
int atexit(void (*function)(void))
返回值: 成功 0
错误 非0值
int on_exit(void (*function)(int, void *), void *arg)
参数:function 函数指针
arg 要传递给遗言函数的第二个参数, 第一个参数来自exit(status)
返回值: 成功 0
错误 非0值
活着的时候不执行,死了才执行
获取环境变量
getenv (3)
char *getenv(const char *name);
功能:
获取环境变量
参数:
要查找的环境变量的名字
返回值:
环境变量的value
设置环境变量
int setenv(const char *name, const char *value, int overwrite);
overwrite: 非0,可以修改成功环境变量的值
0
name: 环境变量列表中不存在, 添加,环境变量列表中存在, 修改为value
unsetenv(3) //从环境变量列表中移除一个环境变量
进程的并发和进程资源的限制:
ulimit -a
进程的优先级:40级
-20-19
fork创建新进程几乎完全复制父进程的资源,包括但不限于内存、文件描述符、环境变量等。
vfork 完全继承父进程的内存空间,可能会造成内存冲突,因为子进程跟父进程会造成内存共享
进程的并发:
pid_t getpid(void); //如何获取进程的pid
pid_t getppid(void);//如何获取父进程的pid
进程的资源回收
父进程通过fork创建子进程
子进程在内核中也有一个PCB来维护其状态
子进程的时间片耗尽了, 就需要有人回收它的资源
父进程负责回收子进程的资源
如果父进程跟子进程两个进程,
父进程的时间片耗尽,子进程时间片没有耗尽,这时候父进程会被init进程接受,这时候子进程称为“孤儿进程”
子进程时间片耗尽,父进程没有耗尽,但是父进程也没有回收子进程,这时候子进程称为“僵尸进程”
wait(2)//给进程收拾
pid_t wait(int *wstatus);
WIFEXITED(wstatus) true, 子进程被正常终止
WEXITSTATUS(wstatus) 只有在WIFEXITED 返回真的情况下被使用, 返回子进程退出的状态码
WIFSIGNALED(wstatus) true, 子进程是被信号终止
WTERMSIG(wstatus) 返回终止的子进程的信号的编号, 只有在WIFSIGNALED返回真的时候才能被使用
返回值:
成功:被终止的子进程的PID
错误:-1 errno被设置
waitpid;等待指定的子进程 pid 的状态改变,或者等待满足特定条件的一组子进程。
pid_t waitpid(pid_t pid, int *wstatus, int options); //用于等待子进程状态改变的标准库函数
参数:
pid: < -1 等待当前进程组中|pid| 相等的pid的进程的终止
-1 等待任意子进程的终止
0 pid==gpid 的子进程的终止
> 0 与pid相等的子进程的终止
wstatus:
options:
WNOHANG 子进程被终止,立即返回
0:阻塞
返回值:成功 收到了子进程的资源, 立即返回子进程的pid
错误 -1
进程关系:
1. 会话 sid
每打开一个终端都是创建了一个会话 session
2. 进程组 gpid
进程组中第一个进程就是进程组的组长, 同时又是会话组的组长
3 可以更改进程所在进程组, 但是永远无法脱离会话 用setpgid//用于设置进程组 ID 的系统调用
int setpgid(pid_t pid, pid_t pgid);
pid: 要设置进程组 ID 的进程的 PID。如果 pid 等于 0,表示使用调用进程的 PID。
pgid: 要设置的新进程组 ID。如果 pgid 等于 pid,表示创建一个新的进程组,并将 pid 指 定的进程作为该组的组长
四.更新进程的映像
为了更新进程的映像,采用exec家族函数
execve(2)//更新进程的映像,它替换当前进程的映像为一个新的程序,新程序从其 main 函数开始执行,并且拥有调用 execve 的进程的 PID 和一些其他属性
int execve(const char *pathname,char *const argv[],char *const ebvp[])
参数:pathname:指定要执行二进制的路径
argv:传递给新的映像的命令行参数 是一个字符串列表
argv[0] 指定了要执行的新的文件名
envp: 传递给新的映像的环境变量列表
返回值:
为什么成功不返回
使用新的映像替换了旧的映像, 旧的映像的代码段 数据段 堆 栈全被覆盖了
错误 -1 errno被设置
execve 替换当前进程的映像,而不是创建一个新进程。
进程 ID (PID) 保持不变,但是进程的执行内容完全改变。
所有打开的文件描述符、内存段、当前工作目录、根目录、umask 等属性都会被新程序继承。
进程的终止状态和信号处理设置也会被新程序继承
exec 家族的函数
execl
execlp p:路径
execv v:环境变量
execvp
ps -o pid,ppid,pgrp,session,comm
五,守护进程
a.out & // 加& 将a.out进程放在后台运行
可以改变进程所在的进程组, 但是无法改变其所在的会话组
setsid (2)
怎默写守护进程流程
1.首先创建子进程,然后让子进程终止pid = fork();
if (pid > 0) {
exit (0);}
2.使用setsid创建新的会话,让子进程成为会话领导
3.使用 umask(0) 重设文件权限掩码,以确定守护进程创建的文件的权限。
kill -l
一共64个信号 32和33信号不存在 所以一共有62个
可靠信号 又叫实时信号 34~64
不可靠信号 又叫标准信号 1~31
信号就是软中断
中断信号一定由硬件产生!
软中断指的就是软件模拟硬件产生的中断信号
信号的产生 信号阻塞 信号抵达进程 信号的处理
信号的产生到处理之间的状态称为未决状态 pendding
未决信号
信号处理:
Ign 忽略信号
signal (3, SIG_IGN);//忽略三号信号
Core 接受到信号的进程终止并产生dump文件
Stop 接受到信号的进程暂停
Cont 接受到信号的进程如果暂停了就继续
大多数信号的默认行为都是终止进程
如何产生信号:
kill 信号编号 pid//1-64个信号量,pid//进程pid
rasie(2);
raise(sig)//信号编号
等待信号
pause (2)//会挂起调用进程的执行,直到收到一个信号。
sigsuspend (2)//它允许进程在指定的信号集上挂起直到某个信号到达。
改变信号的行为
sigaction(2)//
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
signum 信号的编号, 也可以是信号的名字
handler 函数指针类型的变量
SIG_DFL SIG_IGN 用户自定义的处理函数
alarm(2)
unsigned int alarm(unsigned int seconds);
功能:
产生一个闹钟信号
参数:
seconds, 0, 取消所有的未决闹钟信号
定义的闹钟时间, seconds秒之后发送给进程
返回值:
0 表示没有任何调度的闹钟 所有的闹钟都被执行完毕了
闹钟还没有被执行的秒数
系统在中断时的操作
1.cpu节后中断信号是首先保存当前寄存器的状态
2.保存下一条指令的状态
3.修改模式,从用户模式到中断模式
4.跳转到异常处理函数
5.处理完恢复到异常处理前的状态
6.执行下一条语句
信号的应用
1.漏桶模型
漏桶的速率是恒定的, 不会随着访问量的激增或骤减而变化速率
2.令牌桶模型
令牌三要素
1. 令牌 token
2. 上限 burst
3. 速率 cps
名字
handler 函数指针类型的变量
SIG_DFL SIG_IGN 用户自定义的处理函数
alarm(2)
unsigned int alarm(unsigned int seconds);
功能:
产生一个闹钟信号
参数:
seconds, 0, 取消所有的未决闹钟信号
定义的闹钟时间, seconds秒之后发送给进程
返回值:
0 表示没有任何调度的闹钟 所有的闹钟都被执行完毕了
闹钟还没有被执行的秒数
系统在中断时的操作
1.cpu节后中断信号是首先保存当前寄存器的状态
2.保存下一条指令的状态
3.修改模式,从用户模式到中断模式
4.跳转到异常处理函数
5.处理完恢复到异常处理前的状态
6.执行下一条语句
信号的应用
1.漏桶模型
漏桶的速率是恒定的, 不会随着访问量的激增或骤减而变化速率
2.令牌桶模型
令牌三要素
1. 令牌 token
2. 上限 burst
3. 速率 cps