Linux进程相关

显示进程(pid,user,all)ps aux
显示进程(pid,ppid,进程组,会话)ps ajx
查看信号kill -l
九号信号的作用kill -9(SIGKILL) pid

每个进程地址空间都是从0开始(虚拟地址空间)

创建进程

函数fork()

*问题:fork之后,进程间的数据共享是怎么样的

答:fork之后进程之间的数据完全一样但是完全独立,互不影响

*问题:数据在物理内存中(需要对数据操作的时候)时,存储几份

答:读时一份,写时两份(读时共享,写时复制)
写的时候,无论父子,都复制一个数据去自己操作

*问题:父子进程之间可以通过全局变量通信吗

答:不可以,因为相互之间数据是独立的(在函数fork的操作下)

exec函数族

让父子进程执行不相干的操作
能够替换进程地址空间中的.text源代码段
需要指定可执行程序"例如:exec(“a.out”)

实现了换核不换壳的作用
执行另一个程序不许要创建另外的地址空间
应用场景:在一个运行的程序a里调用另外的应用程序b
exec之前需要fork

返回值:成功不返回,失败就需要打印错误信息perror,退出子进程
perror(s):用来将上一个函数发生错误的原因输出到标准设备(stderr)。参数 s 所指的字符串会先打印出,后面再加上错误原因字符串。此错误原因依照全局变量errno的值来决定要输出的字符串

函数execl

一般执行自定义的程序
int execl(const char *path, const char *arg, ...)
第一个参数写路径,可以是绝对路径(最好),可以是相对路径
第二个参数写占位,可以写程序名
后面参数可以写函数执行需要的参数
最后的参数一定是NULL

函数execlp

int execlp(const char *file, const char *arg, ...)
执行path环境变量能够搜索到的程序
参数基本上同上,其中file直接写程序名

孤儿进程、僵尸进程、进程回收

孤儿进程

父进程先结束,子进程还存活
孤儿被init领养,init进程变为孤儿进程的父进程

目的:释放系统资源
进程结束之后可以释放用户区空间,但释放不了pcb(进程控制块)
pcb必须由父进程释放

僵尸进程

子进程死了,父进程还存活
此时需要父进程释放pcb,但是父进程没有进行释放
此时,子进程即为僵尸进程

僵尸进程不可以被kill杀死

当父进程死后,僵尸进程会被init进程领养,然后释放

进程回收

函数wait()

wait函数是一个阻塞函数,等待条件是子进程的死亡
pid_t wait(int *status)
返回值 :

  • - 1:回收失败,已经没有子进程了
  • > 0:回收的子进程对应的pid

参数:status 判断子进程如何死的
正常退出/被某信号杀死

调用一次,只能回收一个子进程

子进程退出状态:

  • WIFEXITED(status):函数返回非零,为正常结束
  • WEXITSTATUS(status):如果上宏为真,获取进程退出状态参数
  • WIFSIGNALED(status):非零,正常退出
  • WTERMSIG(status):如果上宏为真,则获取终止进程的信号
函数wait_pid()

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

  • pid == -1:回收任一子进程,循环方法(wpid = waitpid(-1, &status, WNOHANG) != -1)

  • pid > 0:回收其进程ID与PID相等的子进程

  • pid == 0:回收其组ID与调用进程的组ID一致的任一子进程

  • pid < -1:回收其组ID等于PID绝对值的任一子进程
    2.status

  • 子进程的退出状态,用法和wait一致

3.options:设置为WNOHANG,函数非阻塞,设置为0,函数阻塞
返回值:

  • 0 返回回收的子进程的PID

  • = -1 无子进程
  • = 0 子进程正在运行

进程通信(IPC)

管道 - 简单
信号 - 开销小
共享映射区 - 有无血缘关系都可以
本地套接字 - 稳定

pipe(匿名管道)

  • pipe只能在有血缘关系的进程间通信
  • 不占用磁盘空间,只是一个内存缓冲区,是伪文件
    特点:
  • 两部分:读端和写端,对应两个文件描述符
  • 数据写端流入,读端流出
  • 操作管道的进程被销毁之后,管道被自动释放
  • 管道默认读写都阻塞
    原理:
  • 内部实现方式:环形队列(先进先出)
  • 缓冲区大小默认4k
  • 大小会根据实际情况适当调整
    局限性:
  • 队列:数据只能读取一次
  • 采用半双工的方式实现
  • 匿名管道只能在有血缘关系的进程中
创建匿名管道int pipe(int fd[2])

fd[0]读端 fd[1]写端
返回值:

  • 0:成功
  • -1:失败
  • 父子进程间通信是否需要sleep

答:不需要,因为管道的读写会互相阻塞
文件描述符重定向:dup2(oldfd, newfd);
对管道操作时,单个管道应当只在一个进程内读,在另一个进程写,多余的应当关闭

读操作:

  • 有数据:read(fd),正常读,返回读出的字节数
  • 无数据:
    • 写端全部关闭,返回0,相当于读文件读到了尾部
    • 没有全部关闭,阻塞

写操作:

  • 读端全部关闭,管道破裂,进程终止
    • 内核给当前进程发信号SIGPIPE(13)
  • 没有被全部关闭
    • 缓冲区写满了
      • write阻塞
    • 缓冲区没满,继续写
  • 如何设置非阻塞
  • 设置读端非阻塞
    • fcntl - 变参函数
      • 复制文件描述符 - dup
      • 修改文件属性 - open的时候对应的flag属性
    • 设置方法:
      • 获取原来的flag
int flags = fcntl(fd[0], F_GETFL);
flags |= O_NONBLOCK;

fcntl(fd[0], F_SETFL, flags);//设置flag

fifo(有名管道)

特点:

  • 磁盘上有文件,属性为p,查看时大小永远为零
  • 伪文件,数据存在内核里对应的缓冲区
  • 半双工
    使用场景:
  • 无血缘关系的进程间通信
    创建方式:
  • 命令:mkfifo 管道名
  • 函数:mkfifo(文件名,模式)

mmap(内存映射)

可以有血缘,也可以没血缘
匿名
mmap-创建内存映射区(动态库加载区)
可以用来修改磁盘文件,也可以文件间通信
函数原型:

void *mmap()
(
	void *adrr,//映射区首地址,传NULL
	size_t length, //映射区大小 实际上是4k的整数倍 不能为零一般按文件设定
	int prot,//映射区权限
		PROT_READ
		PROT_WRITE
		PROT_READ | PROT_WRITE
	int flags,//标志位参数
		MAP_SHARED//会同步到磁盘
		MAP_PRIVATE//不会同步到磁盘
	int fd,//文件描述符  要映射的文件对应的fd 
		//如何得到 open
	off_t offset//映射文件偏移量
		//映射的时候文件指针的偏移量,必须是4k的整数倍
		//一般指定0
)

返回值:

  • 调用成功,映射区首地址
  • 调用失败,返回MAP_FAILED((viod *) -1)

调用完之后需要释放

munmap — 释放内存映射区

int munmap(void *addr, size_t length);
//addr mmap的返回值,映射区的首地址
//length mmap的第二个参数
  • 对mmap的ptr做++等操作后,可以释放吗

答:不能,会变成无效参数,如果想操作,要创建新指针

  • 如果open的时候指定的是只读,mmap的时候权限是读写,会怎么样

答:在mmap的时候会出错,权限不够;open指定的权限应该大于等于mmap指定的权限

  • 如果文件偏移量指定为1000,会怎么样

答:会mmap失败,必须为4096的整数倍

  • 如果不检测mmap的返回值会怎样

答:无法对其进行操作

  • mmap什么情况下会调用失败

答:第二个参数为0,第三个参数必须有read,fd权限问题,偏移量必须为4k整数倍

  • 可以open的时候O_CREAT一个新文件来创建映射区吗

答:可以,需要做文件拓展,lseek,truncate(path, length),ftruncate(fd, length)

  • mmap后关闭文件描述符,对映射区有没有影响

答:没有影响

  • 对ptr越界操作会怎样

答:段错误,如果未被占用,就不报错,越界操作的内容也无法同步

  • 父子进程间永远共享的东西

答:文件描述符,内存映射区

创建匿名映射区

不需要指定文件描述符,需要在flags的位置传MAP_ANON

无血缘关系的进程通信–通过文件(无法使用匿名)

信号

特点:

  • 简单
  • 携带的信息量少
  • 使用在特定场景中

信号的状态:

  • 产生
  • 未决状态–没有被处理的状态
  • 递达–信号被处理

产生:

  • 键盘:ctrl + c
  • 命令:kill
  • 系统函数kill
  • 软条件:定时器
  • 硬条件:段错误,除0错误
    递达:
  • 忽略
  • 被捕捉,执行自定义操作
  • 执行了默认动作
    未决:
  • 被拦截
    信号的优先级比较高,一般先处理信号
    阻塞信号集,未决信号集:
    不能直接操作
    储存在pcb里
  • 阻塞信号集:放的是要屏蔽的信号
    未决信号集:
  • 没有被处理的信号的集合

相关函数

kill–给指定进程发信号
  • 函数原型:int kill(pid_t pid, int sig)
  • pid > 0:发送给指定进程
  • pid = 0:给调用kill同一进程组的进程(所有)
  • pid < -1:取|pid|发给指定进程组(如何指定??)
  • pid = -1:发送给进程有权限发送的所有进程
raise–自己给自己发信号
  • 函数原型:int raise(int sig)
abort–给自己发送异常终止信号SIGABRT
  • 函数原型void abort(void)
闹钟(定时器)

每个进程只有一个定时器

  • 函数原型unsigned int alarm(unsigned int seconds)
  • 计时完成发送SIGALARM信号
  • 参数为零,就是取消
  • 返回值:上一个定时器还剩几秒,没有就为0
  • 使用的是自然定时法,不受当前进程影响
  • 练习:看看1s计多少数
  • real = 用户 + 内核 + 损耗
  • 损耗主要在于文件I/O操作
setitimer–定时器,并实现周期性定时
  • 函数原型int setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value)
信号集

类型:sigset_t

  • sigemptyset:将set集设空
  • sigfillset:将所有信号加入set集合
  • sigaddset:添加信号
  • sigdelset:删除信号
  • sigismember:判断信号是否存在
  • sigprocmask:将自定义信号集设置到内核中
  • sigpenging:读取当前未决信号集
信号捕捉
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)

守护进程

特点:

  • 后台服务进程
  • 独立于控制终端
  • 周期性的执行某个任务
  • 等待某事件发生
  • 不受用户注销登陆的影响
  • 一般采用d结尾的名字(服务)
进程组的id就是组长的pid
会话
  • 不能是组长创建
  • 创建了一个会话就相当于创建了一个守护进程
    创建会话:pid_t setsid(void)
    获取当前会话:pid_t getsid(pid_t pid)

正规流程:

  • 创建子进程,父进程退出(必须)
  • 子进程创建会话(必须)
  • 改变当前工作目录(非必须)//增加健壮性chdir(“path”)
  • 重设文件掩码,因为子进程会继承父进程的掩码,设置为0umask(0)(非必须)//增加灵活性
  • 关闭文件描述符0, 1, 2标准输入输出错误流close(0/1/2)释放资源(非必须)
  • 执行核心工作(必须)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值