linux系统编程笔记

linux系统编程

基础命令

  • 根目录下的一些目录

bin目录

  • 存放可执行文件

dev目录

  • 存放设备文件 : 字符设备,块设备

boot目录

  • 存放开机启动相关

etc目录

  • 存放用户当前的配置信息文件 passwd group

lib目录

  • 库文件 libc.so.6

usr目录

  • 用户资源管理目录 (第三方库)

  • cd ~ 返回家目录

  • cd - 返回上一次目录

  • ls -d 查看目录信息

linux系统文件类型

  • 普通文件: -
  • 目录文件 : d
  • 字符设备文件: c
  • 块设备文件: b
  • 软连接: l
  • 管道文件: p
  • 套接字: s

cp 拷贝文件命令

  • 拷贝目录

  • cp  dir1  dir2 -r
    cp  dir1 ~/ -r
    

软硬链接

  • 软连接 类似windows下的快捷方式

  • 软连接创建,为保证软连接任意搬移,务必保证用绝对路径

  • 硬链接特征,对任意一个修改,都变化

  • 硬链接通过inode(编号)连接在一起

  • 操作系统给每一个文件赋予唯一的 inode,当有相同inode的文件存在时,彼此同步。删除时,只将硬链接计数减一。减为0时, inode被释放。

创建用户

  • sudo adduser 用户名

设置密码

  • sudo passwd 用户名

创建用户组

  • sudo addgroup 新组名 //修改所属组 sudo chgrp 新用户组名 待修改文件
修改文件所属用户组和用户组名
  • sudo chown nobody:nogroup a.c
    

删除用户

  • sudo deluser 用户名

find 命令

  • -type 按文件类型搜索
  • -name 按文件名搜索
  • -maxdepth 指定搜索深度

注意:find命令先写文件名在写指令

find /home/itcast -size +20M -size -50M  //单位 k  M  G

grep命令

  • 找文件内容
grep  文件内容
ps aux | grep 内容
grep -r 'copy'  ./  -n   -n//显示行号

安装软件包

  • 更新软件列表到本地 sudo apt-get update

  • 卸载 sudo apt-get remove

  • deb包安装 (类似window中的exe文件)

  • sudo  dpkg -i xxx.deb  //安装deb软件包命令
    
  • aptitude类似于apt-get 不过多了show命令

tar压缩

  • tar -zcvf要生成的压缩包名

  • gzip file 只能压缩一个一个文件

  • tar命令就是进行打包的 上面zgzip c表示创建 v指显示压缩过程 f表示生成文件

  • bzip2也是用于压缩文件,类似于``gzip`

  • tar jcvf test.tar.gz file1 dir2

  • 解压缩

  • tar zxvf test.tar.gz//用gzip进行解压缩

rar压缩

rar a -r newdir dir//压缩
unrar x  newdir.rar  //解压缩

zip压缩

zip -r dir.zip dir  //压缩
unzip  dir.zip  //解压缩

其他命令

who

  • 查看当前在线上的用户情况

ps

  • 用于监控后台进程的工作情况

jobs

  • 显示当前shell下正在运行哪些作业

  • fg``bg前后台切换

kill

  • 杀死进程

env

  • 显示当前进程的环境变量

ifconfig

  • 查看网卡信息 //ip地址

alias

  • 起别名 alias ls='ls --color=auto'

umask

  • 指定用户创建文件时的掩码

file命令

  • file命令用于确定文件类型。它会读取文件的内容或者元数据,然后根据这些信息判断文件的类型。file命令通常用于识别未知文件的类型,以便正确地处理它们。

gcc 参数

  • gcc -I指定头文件所在目录位置

  • gcc -c只做预处理,编译,汇编操作

  • gcc -g编译时添加调试语句

  • -On优化级

  • -Wall提示更多警告信息

  • -D向当前程序中注册一个宏

静态库和共享库

静态库制作

ar rcs libmylib.a file12.o
gcc  test.c libmymath.a -o test  //使用静态库
  • 没有函数定义和声明时 ,编译器会进行隐式声明
    • 不过格式为int 函数(int, int)

动态库的制作和使用

  • 注意:普通函数:链接之前main函数地址为0;
  • 链接之后会地址回填 main函数地址改变
  • 动态库生成与位置无关的代码 //延迟绑定
//动态库的制作
将.c文件生成.o文件(生成与位置无关的代码  -fPIC)
  gcc -c add.c  -o  add.o  -fPIC
  
使用gcc -shared 制作动态库
	gcc -shared lib库名.so add.o sub.o div.o
	
编译可执行程序时,指定所使用的动态库。 -l: 指定库名(去掉lib前缀和.so后缀  -L:指定路径。 )
	gcc test.c -o a.out -l mymath -L ./lib

注意:需要提供工作动态库所在目录位置。
通过环境变量: export LD_LIBRARY_PATH= 动态库路径
./aout 成功

ldd 命令

​ 用于列出程序运行所需的共享库依赖关系,当你对一个可执行文件运行ldd命令时,它显示该文件所依赖的共享库及其路径。

页面大小

  • linux常见页面大小为4KB

命名空间

  • 就是把单纯的把一堆名字放到一个盒子里

  • using namespace std 就相当于把这个盒子里所有名字倒出来

  • :: 查找全局这个盒子里的名字,std:: 查找 std 命名空间这个盒子里名字

gdb调试

  • -g :使用该参数编译可执行文件,得到调试表
  • list: 列出源码
  • d : d 20 在第20行位置断点
  • p : 查看变量的值
  • continue: 继续执行断点后续指令
  • quit : 退出gdb当前调试
  • run : 使用run查找段错误出现位置
  • finish : 结束当前函数调用
  • set args :设置main函数命令行参数
  • info b: 查看断点信息表
  • ptype : 查看变量类型

栈帧

  • 随着函数调用而在stack上开辟的一片内存空间。用于存放函数调用时产生的局部变量和临时值。

makefile

  • 两个函数:

    • src = $(wildcard ./*.c):匹配当前工作目录下的所有.c文件 将文件名组合成列表,赋值给变量  src
      
      obj = $(patsubst %.c, %.o, $(src)):将参数3中,包含参数1的部分,替换为参数2.
      
    • clean

  • -rm rf $(obj) a.out ( -表示失败,还会继续操作)

模式规则

  • %.o : %.c

​ gcc -c $< -o %@

静态模式规则

  • $ (obj) : %.o :% .c
    • gcc -c $< -o %@

伪目标

  • .PHONY: clean ALL

系统调用

  • 系统调用是操作系统提供给外部程序的编程接口:
    • 用户调用printf函数 调用 write系统函数 调用sys_write 系统调用

open函数

int open (char* pathname ,int flags,mode_t mode)
参数:
	pathname 打开文件路径名
	flags  文件打开方式  O_RDONLY|O_WRONLY|O_RDWR   O_CREAT...
	mode  使用前提 flags是O_CREAT  取值为八进制,表示访问权限 rwx
		创建文件最终权限  = mode & ~umask
		
返回值:
	成功,返回所对应的文件描述符
	失败 返回-1 设置errno (系统保存的变量)
int close (int fid);


错误处理:
	strerror(errno)会返回对应errno的错误信息,字符串类型

read函数

  • ssize_t read ( int fd ,void* buf ,size_t count);
    
    参数:
    	fd:文件描述符
    	buf: 存储数据的缓冲区
    	count: 缓冲区大小
    	
    返回值: 
    	0:读到文件末尾
    	成功 :  读到的字节数
    	失败:  -1,设置 errno
    	-1 并且 errno = EAGIN或EWOULDBLOCK  说明不是read失败,而是read以一种非阻塞的方式读一个设备文件(网络文件),并且文件无数据
    	
    

write函数

  • ssize_t(int fd,const void* buf,size_t count)
    
    返回值:
    	成功:写入的字节数
    	失败: -1 ,设置errno
    

文件描述符:

​ PCB控制块:本质 结构体

​ 成员 : 文件描述符表。

​ 文件描述符: 0/1/2/3… /1023 表中可用的最小

​ 0-STDIN_FILEEND

​ 1-STDOUT_FILEEND

​ 2-STDERR_FILEEND

阻塞和非阻塞

  • 是设备文件和网络文件的属性

产生阻塞的场景。读设备文件,网络文件。(读常规文件无阻塞概念)

/dev/tty —终端文件

open(“/dev/tty ”,O_RDWR|O_NONBLOCK) —-设置 终端文件为非阻塞状态(默认为阻塞状态)

fcntl函数

  • 改变文件性质

  • int fcntl (int fd, int cmd ….)

int flags = fcntl (fd, F_GETFL)

flags |= O_NONBLOCK

fcntl(fd, F_SETFL, flags)

改变文件性质为非阻塞文件

lseek函数

  • 改变文件的读写位置

  • off_t lseek( int fd, off_t offset, int whence);
    whence参考位置:
    	SEEK_SET 文件开头
        SEEK_CUR  当前位置
        SEEL_END 末尾
        
    成功返回新的读写位置(相对于文件开头的偏移量)
    失败时返回-1,并设置errno
    
  • 使用lseek拓展文件大小,要想真正拓展,必须引起IO操作

truncate函数

  • 截断文件长度或指定长度,常用来拓展文件大小

  • int truncate (const char* path, off_t length)
    

    可以直接拓展文件, int ret =trucate (‘’…‘’,250);

传入参数:

​ 1.指针作为函数参数。

​ 2.同常有const关键字修饰。
​ 3.指针指向有效区域,在函数内部做读操作。

传出参数;

​ 1.指针作为函数参数。
​ 2.在函数调用之前,指针指向的空间可以无意义,但必须有效。

​ 3.在函数内部,做写操作。
​ 4。函数调用结束后,充当函数返回值。

传入传出参数;

​ 1.指针作为函数参数。l
​ 2.在函数调用之前,指针指向的空间有实际意义。

​ 3.在函数内部,先做读操作,后做写操作。
​ 4函数调用结束后,充当函数返回值。

stat 函数

  • 获取文件属性(从inode机构体中获取)
int stat (const char* path ,struct stat *buf)成功返回0;失败返回-1,设置errno
参数一:文件名
2:inode结构体指针(传出参数)
  • istat函数不同之处 istat函数不会穿透符号链接
  • 文件类型判断发方法:
    • st_mode 取高四位 使用对应的宏

access 函数

  • 测定指定文件是否存在或拥有某种权限

  • int access (const char* pathname, int mode)
    参数2: F_OK  判断文件是否存在
    

chmod函数

  • 修改文件的访问权限

  • int chmod (const char * path,mode_t mode)
        
    

进程

getcwd函数

  • 获取进程当前工作目录

  • char * getcwd(char*buf,size_t size)buf 中保存当前进程工作目录
    
  • 失败返回 NULL;

chdir函数

  • 改变当前进程工作目录

目录操作函数

DIR * opendir(char *name) ;
int c1osedir(DIR *dp);
struct dirent *readdir(DIR * dp ) ;


struct dirent {
	inode
	char dname [256];
}
  • linux中 ls -R表示递归列出当前目录下的文件和子目录
  • sprintf函数 功能是将格式化的数据输出到字符串走

dup和dup2函数

int dup (int oldfd)     文件描述符复制
    返回:新的文件描述符
int  dup2 (int oldfd,int newfd)  重定向

进程控制块PCB

  • 每个进程在内核都有一个进程控制块(PCB),linux内核的进程控制块是task_struct结构体
  • 该结构体内部成员
    • 进程id。系统中每个进程有唯一的id,在c语言中用pid_t类型表示,其实就是一个非负整数。
    • 进程的状态,就绪,运行,挂起,停止等
    • 进程切换时需要保存和恢复的一些CPU寄存器。
    • 描述虚拟地址空间的信息。
    • 描述控制终端的信息。
    • 当前工作目录(Current Working Directory)。
    • umask掩码。
    • 文件描述符表,包含很多指向file结构体的指针。
    • 和信号相关的信息。
    • 用户 id和组id
    • 会话(Session)和进程组。

fork函数

# include<unistd.h>
pid_t fork(void);
  • 创建子进程

  • 父子进程各自返回,父进程返回子进程pid,子进程返回0

getpid() getppid()函数

  • #include<unistd.h>
    #include<sys/types.h>
    
    pid_t getpid(void);
    pid_t getppid (void);
    

进程共享

  • fork()后
    • 父子相同处: 全局变量、.data、.text、栈、堆、环境变量、用户ID、宿主目录、进程工作目录、信号处理方式…
    • 父子不同处: 1.进程ID 2.fork返回值 3.父进程ID 4.进程运行时间 5.闹钟(定时器) 6.未决信号集·

​ 似乎,子进程复制了父进程0-3G 用户空间内容,以及父进程的 PCB,但 pid 不同。真的每.fork一个子进程都要将父进程的0-3G地址空间完全拷贝一份,然后在映射至物理内存吗?,

  • 父子进程遵循写时共享读时复制——一般是 全局变量
  • 还共享 文件描述符 mmap建立的映射区

gdb调试

  • gdb调试只会跟踪一个进程,可以在fork之前设置跟踪父进程还是子进程

  • 设置父进程调试路径: set fol1ow-fork-mode parent (默认)
    
  • 设置子进程调试路径:  set fol1ow-fork-mode child
    

exec函数族

  • 使进程执行某一程序。成功无返回值,失败返回-1

  • int execlp(const char* file,const char* arg,...)       借助PATH环境变量寻找执行程序
        参1  程序名
        参2  argv 0
        参3  agrv 1
        
        哨兵  NULL
    
  • int execl(const char*path ,const char* arg,...)       自己指定待执行程序路径
    

孤儿进程

  • 父进程先于子进终止,子进程沦为“孤儿进程”,会被init进程领养。

僵尸进程

  • 子进程终止,父进程尚未对子进程进行回收,在此期间,子进程为“僵尸进程”。

ps指令

wait函数

  • 回收子进程退出的资源

  • pid_t wait  (int *status)
    
  • 参数(传出) 回收进程的状态

  • 返回值: 成功 返回回收进程pid 失败 -1 errno

  • 函数作用1:

    • 阻塞等待子进程退出
  • 函数作用2

    • 清理子进程残留在内核的 pcb资源
  • 函数作用3

    • 通过传出参数,得到子进程结束状态
  • 获取子进程正常终止值:

    • 宏函数WIFEXITED(status) 为真 调用WEXITSTATUS(status) 得到 子进程的退出值
  • 获取导致子进程异常终止的信号:

    • IFSIGNALED(status) 为真 调用VTERMSIG(status) 得到导致子进程异常终止的信号编号。

waitpid函数

  • 指定某一进程进行回收

  • pid_t  waitpid(pid_t  pid,  int *status ,int options)
    
  • 参数:

    • pid : 指定回收的子进程pid

    • 大于 0 待回收的子进程pid

    • -1。任意子进程

    • 0 。同组的子进程。

    • status: 传出 回收进程的状态。

    • options : VNOHANG指定回收方式为 , 非阻塞。

    • 返回值:

    • 大于0∶表成功回收的子进程pid

    • 0:函数调用时,参3指定了WNOHANG,并且,没有子进程结束。

    • -1:失败。errno

  • 注意:wait和waitpid函数:一次调用,回收一个子进程

管道

概念

  • 进程间通信

    • ①管道(使用最简单)
    • ②信号(开销最小)
    • ③共享映射区(无血缘关系)
    • ④本地套接字(最稳定)
  • 管道是一种最基本的IPC机制,作用于有血缘关系的进程之间,完成数据传递。调用pipe系统函数即可创建一个管道。

  • 实现原理:内核借助环形队列机制,使用内核缓冲区实现。

  • 特质:

    • 1.伪文件(实为内核缓冲区)
    • 2.管道中的数据只能一次读取。
    • 3.数据在管道中,只能单向流动。
  • 局限性:

    • 1.不能自己写,自己读。
    • 2.数据不可以反复读。一旦读走就不存在了
    • 3.半双工通信。
    • 4.血缘关系进程间可用。

pipe函数

  • int pipe(int pipefd[2]);  
    成功: 0
    失败 :-1  errno
    
  • 函数调用成功返回r/w两个文件描述符 不用open 需要手动close 规定 fd[0]->r读端;fd[1]->w 写端.

  • 父子进程,兄虎进程间通信

fifo管道

  • 无血缘关系进程间通信
  • 命名管道: mkfifo
  • 无血缘关系进程间通信:
    • 读端: open fifo O_RDNLY
    • 写端 open fifo O_WRONLY

读管道

1,管道有数据 read返回实际读到的字节数

2,管道中无数据 管道写端被全部关闭 返回0;

​ 管道写端没有全部关闭 read阻塞等待

写管道

1,管道读端全部关闭,进程异常终止

2,管道读端没有全部关闭

​ 管道未满 返回实际写入的字节数

​ 管道已满 write函数阻塞

文件实现进程间通信

  • 打开的文件是内核的一块缓冲区,多个无血缘关系的进程,可以同时访问该文件。

共享内存映射

存储映射IO

​ 使一个磁盘文件与存储空间中的一个缓冲区相映射。于是当从缓冲区中取数据,就相当于读文件中的相应字节。于此类似,将数据存入缓冲区,则相应的字节就自动写入文件。这样,就可在不适用readwrite函数的情况下,使用地址(指针)完成IO操作。

​ 这个映射工作可以通过mmap函数完成

mmap函数

void * mmap (void* addr ,size_t length,int prot,int flags,int fd , off_t offset);   //创建共享内存映射
参数: 
    addr  :指定映射区的首地址。通常传NULL,表示让系统自动分配
	length: 共享内存映射区的大小。(<=文件的实际大小)
	prot :  共享内存映射区的读写属性。 PROT_READ、 PROT_WRITE、PROT_READ|PROT_WRITE
	falgs : 标注共享内存的共享属性。MAP_SHARED、MAP_PRIVATE
	fd : 用于创建共享内存映射区的那个文件的文件描述符。
	offset: 默认0,表示映射文件全部。偏移位置。需是4的整数倍。

返回值:
        成功: 映射区的首地址
        失败: MAP_FAILED  。errno
        

mmap 父子进程通信

  • 要在创建映射区的时候指定对应的标志位参数flags;

    • MAP_PRIVATE(私有映射) 父子进程各自独占映射区
    • MAP_SHARED(共享映射) 父子进程共享映射区
  • 父进程先创建映射区: open(O_RDWR) mmap(MAP_SHARED)

  • fork()创建子进程

  • 注意:mmap数据可以重复读取

  • fifo 数据只能一次读取

匿名映射区

p = (int*)mmap (NULL,40,PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS ,-1,0);
其中:fd =-1;

信号

  • 信号共性:简单,不能携带大量信息,满足条件才能发送

  • 信号是软件层面上的“中断”,程序立即终止处理信号,所有信号的产生及处理全部都是有内核完成。

  • 信号相关的概念:

    • 产生信号:

    • 1,按键产生

    • 2.系统调用产生

    • 3.软件条件产生

    • 4.硬件异常产生

    • 5.命令产生

      概念。

    • 未决。产生与递达之间状态。

    • 递达。产生并且送达到进程。直接被内核处理掉。

    • 信号处理方式 : 执行默认处理动作、忽略、捕捉(自定义)

    • 阻塞信号集(信号屏蔽字)﹔本质。位图。用来记录信号的屏蔽状态。一旦被屏蔽的信号,在解除屏蔽前,一直处于未决态。未决信号集:本质。位图。用来记录信号的处理状态。该信号集中的信号,表示,已经产生,但尚未被处理。

默认动作

  • Term 终止进程
  • ign 忽略信号(默认不处理该信号)
  • core 终止进程,生成core文件, (查验进程死亡原因,用于 gdb 调试)
  • Stop 停止进程
  • Cont 继续运行进程

信号四要素

  • 信号编号,信号名称,信号对应事件,信号默认处理动作

常用信号

  • 1 SIGHUP 当用户退出 shell时,由该shell启动的所有进程将收到这个信号默认动作为终止进程
  • 2 SIGINT 当用户按下了<Ctrl+C>组合键时,用户终端向正在运行中的由该终端启动的程序发出此信号。默认动作为终止进程。
  • 3 SIGQUIT 当用户按下<ctrl+\>组合键时产生该信号,用户终端向正在运行中的由该终端启动的程序发出信号。默认动作为终止进程。
    • 注:Ctrl+\Ctrl+C都是用于终止正在运行的程序,但Ctrl+\发送的是退出信号,而Ctrl+C发送的是中断信号。Ctrl+C更常用于正常中断程序的运行,而Ctrl+\更为强制,可能会导致程序生成core文件。
  • 9 SIGKILL 无条件终止进程。本信号不能被忽略,处理和阻塞。默认动作为终止进程。它向系统管理员提供了可以杀死任何进程的方法。
  • 14 SIGALRM 定时器超时,超时的时间由系统调用alarm设置。默认动作为终止进程。
  • 15 SIGTERM 程序结束信号,与SIGKILL不同的是,该信号可以被阻塞和终止。通常用来要示程序正常退出。执行shell命令Kill时,缺省产生这个信号。默认动作为终止进程。
  • 17 SIGCHLD 子进程状态发生变化时,父进程会收到这个信号。默认动作为忽略这个信号。
  • 19 SIGSTOP 停止进程的执行。信号不能被忽略,处理和阻塞。默认动作为暂停进程。

kill函数

  • kill函数给指定的进程发送信号,不一定杀死

    • int kill (pid_t pid, int sig);  成功:返回0, 失败: -1,errno
      参数:
          sig:不推荐使用数字, 用宏名
          
      	pid>0  发送信号给指定进程
          pid=0  发送信号给调用kill函数的那个进程组的全部进程
          pid<-1  发送信号给|pid|进程组的全部进程
          pid =-1  发送信号给有权限发送的所有进程
          
      
      

alarm函数

  • 设置定时器(闹钟)。在指定seconds后,内核会给当前进程发送 14 SIGALRM 信号。进程收到该信号,默认动作终止。

  • 每个进程都有且只有唯一空间

  • unsigned int alarm(unsigned int seconds);返回o或剩余的秒数,无失败。

  • 常用:取消定时器 alarm(0) ,返回旧闹钟余下秒数

setitimer函数

int setitimer(int which , const struct itimerva1 *new_value , struct itimerva1* o1d_va1ue);
参数:
    which:
	ITIMER_REAL:采用自然计时。——>SIGALRMI
	ITIMER_VIRTUAL:采用用户空间计时―---> SIGVTALRM
    ITIMER_PROF:采用内核+用户空间计时--->SIGPROF
old_value  :传出参数,上次定时剩余时间。

信号集操作函数

  • 信号集操作函数

  • sigset_t set  ;自定义信号集。  本质是位图
    sigemptyset(sigset_t *set);		清空信号集
    sigfi11set(sigset_t *set);     全部置1
    sigaddset(sigset_t *set,int signum);  将一个信号添加到集合中
    sigdelset(sigset_t *set,int signum);   将一个信号从集合中移除
    sigismember (const sigset_t *set,int signum);判断一个信号是否在集合中。在--》1,不在--》0
    
    
  • 设置信号屏蔽字和解除屏蔽

  • 注意:屏蔽信号,只是将信号处理延后执行(延迟至解除屏蔽),忽略表示将信号丢处理

  • int sigprocmask(int how,const sigset_t *set,sigset_t *oldset)
    how:
    	SIG_BLOCK:    设置阻塞
    	SIG_UNBLOCK :   取消阻塞
    	sIG_SETMASK :    用自定义set替换mask 。
    set:  自定义的set
    oldset :  旧有的mask
    
  • 查看未决信号集:

    • int sigpending(sigset_t*set)
    • set 传出的未决信号集

信号捕捉

signal函数
  • 注册一个信号捕捉函数
typedef void (*sighandler t)(int);
sighandler_t signal(int signum, sighandler_t handler);

sigaction函数

更常用

  • 修改信号处理动作
int sigaction (int signum, const struct sigaction *act ,struct sigaction *oldact )
    成功:0
    失败:-1 errno
    
参数:
    act  传入参数,新的处理方式
    oldact  传出参数,旧的处理方式
    
    
struct sigaction 结构体
struct sigaction{
	void (*sa_handle)(int);
    void(*sa_sigaction)(int, siginfo t*,void*)
    sigset_t   sa_mask;
    int sa_flags;
    void (*sa_restorer)(void);
}

捕捉特性:

  • 1.捕捉函数执行期间,信号屏蔽字由mask --> sa_mask ,捕捉函数执行结束。恢复回mask.1.捕捉函数执行期间,信号屏蔽字由口罩->SA_MASE,捕捉函数执行结束。恢复回面具。
  • 2.捕捉函数执行期间,本信号自动被屏蔽(sa_f1gs = 0).
  • 3.捕捉函数执行期间,被屏蔽信号多次发送,解除屏蔽后只执行一次

进程组和会话

  • 进程组是一组相关进程的集合,共享一个PGID 这个进程组的首进 进程PID和PGID 相同 他负责管理和协调进程组中其他进程
  • 会话:当用户登录到系统后,会启动一或多个进程,这些进程以及所属进程组同属于一个会话。,每个会话有个会话idSID
  • 会话提供了用户与系统交互的框架。

守护进程

  • daemon进程。通常运行与操作系统后台,脱离控制终端。一般不与用户直接交互。周期性的等待某个事件发生或周期性执行某一动作。不受用户登录注销影响。通常采用以a结尾的命名方式。

守护进程创建步骤:

  • fork子进程,让父进程终止
  • 子进程调用setsid()创建新会话
  • 通常根据需要,改变工作目录位置chdir()
  • 根据需要,设置umask文件权限掩码
  • 根据需要,关闭/重定向 文件描述符
  • 守护进程,业务逻辑

线程

  • 轻量级进程(LWP),创建底层函数clone

  • 线程可以看做寄存器的集合

    • 注: 三级映射 进程PCB–>页目录(可看出数组,首地址在PCB中)—>页表—>物理页面—>内存单元
  • 进程:有独立的进程地址空间。有独立的PCB 分配资源的最小单元

  • 线程: 有独立的PCB。没有独立的进程地址空间 最小单位的执行

  • ps -Lf pid 获得线程号 LWP

线程共享的资源

  • 共享:./text ./data ./rodata ./bsss heap –>全局变量 文件描述符表
  • 独享: errno变量 信号屏蔽字 调度优先级 栈空间(内核栈 和 用户栈)

线程控制原语

  • pthread_t pthread_self (void) 获取线程id,

    • 返回,本线程id
  • 检查出错返回:

    • fprintf( stderr ,"...%s..", strerror())

    • 在线程中使用 perror 函数可能会导致错误信息不准确或者无法获取到正确的错误信息。这是因为 perror 函数是基于全局变量 errno 来输出错误信息的,而在多线程环境中,每个线程都有自己独立的 errno 变量,因此在多线程环境下使用 perror 函数可能会导致错误信息混乱或者无法获取到正确的错误信息。

      为了避免这种情况,可以使用 strerror 函数来获取特定错误码对应的错误信息,或者使用线程安全的 strerror_r 函数来获取线程特定的错误信息。

    • fprintf函数
      int fprintf(FILE*stream , const char* format, [argument]..) 函数没有缓冲
      
      
  • int pthread_creat(pthread_t *tid , const pthread_attr_t *attr , void *(*start_rountn)(void*) , void *arg)

    • 参数:
    • 参数1,返回参数,表示新创建的子线程id
    • 参数2,线程属性,传 NULL 表示默认属性
    • 参数3,子线程回调函数,创建成功,pthread_creat函数返回时,该函数会被自动调用
    • 参数4,参数3的参数,没有传NULL
  • void pthread_exit (void * retval)    //退出当前线程
        retval  :退出值,无退出值时为NULL
    
        
        
    int pthread_join (pthread_t thread,void** retval)   //回收线程
        thread  :待回收的线程id
        retval : 传出参数,回收的线程的退出值
        返回值:成功返回0 失败 -1 errno
    
            
            
    int pthread_detach(pthread_t thread)      //设置线程分离  线程终止,会自动清理pcb 无需线程回收 
          thread   带分离线程id
            成功返回 0
    int pthread_cancel(pthread_t thread)      //杀死一个线程,需要到达取消点(保存点)
            thread  待杀死的线程
            
       如果,子线程没有到达取消点,那么函数失效
       我们可以在程序中手动添加一个取消点   使用  pthread_testcancel()
       成功被 函数杀死  返回-1  被pthread_join()回收
            
    

线程和进程对比

  • 线程控制源于
线程控制原语进程控制原语
pthread_creat()fork()
pthread_self()getpid()
pthread_exit()exit()
pthread_join()wait()/waitpid()
pthread_cancel()kill()
pthread_detach()

线程属性

  • 例如设置分离属性
  • pthread_attr_t attr 创建一个线程属性结构体变量
  • pthread_attr_init (&attr) 初始化线程属性
  • pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED) 设置线程属性为分离态
  • pthread_create(&tid , %attr, tfn, NULL) 创建分离的线程
  • pthread_attr_init(&attr) 销毁线程属性

线程使用注意事项

  • 主线程退出其他线程还没有退出 主线程调用pthread_exit()
  • 避免僵尸线程

线程同步

  • 锁,是建议锁, 应先拿锁再访问

使用 mutex(互斥锁,互斥量):

  • pthread_mutex_t lock()创建锁
  • pthread_mutex_init(& mutex,NULL)动态初始化 静态初始化pthread_mutex_t mutex= PTHREAD_MUTEX_INITIALIZER
  • pthread_mutex_lock()加锁
  • 访问共享数据(stdout)
  • pthread_mutex_unlock() 解锁
  • pthread_mustex_destory 销毁锁

restrict 关键字

  • 用来限定指针变量。被该关键字限定的指针变量所指向的内存操作,必须由本指针完成。

读写锁

  • 只有一把
  • 读共享,写独占。
  • 写锁优先级高于读锁
pthread_rwlock_t  rwlock;
pthread_rwlock_init(&rwlock ,NULL);
pthread_rwlock_rdlock(&rwlock);
pthread_rwlock_wrlock(&rwlock);
pthread_rwlock_unlock(&rwlock);
pthread_rwlock_destory(&rwlock);

条件变量

  • 本身不是锁
  • pthread_cond_init (&cond, NULL); //动态初始化
  • pthread_cond_t cond = PIHREAD_COND_INITIALIZER

阻塞等待条件:

pthread_cond_wait(&cond,&mutex):

作用: 1)阻塞等待条件变量满足
2)解锁已经加锁成功的信号量(相当于pthread_mutex_un1ock(&mutex))
3)当条件满足,函数返回时,重新加锁信号量(相当于,pthread_mutex_1ock(&mutex); )

pthread_cond_signal() 唤醒阻塞在条件变量上的至少一个线程

pthread_cond_broadcast()唤醒所有线程

信号量

  • 相当于初始化N个互斥量,N表示可以同时访问共享数据区的线程数

  • sem_t sem 定义类型;
    int sem_init(sem_t *sem,int pshared, unsigned int value);
    
    参数:
        sem  信号量
        pshared  0:用于线程间同步
        		 1:用于进程间同步
        value:N值
    
  • sem_destroy()

  • sem_wait()一次调用,进行一次 - - 操作,

  • sem_post()进行++操作

  • 23
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值