Linux下C语言教程-李慧芹老师-第十五章

配套代码笔记仓库

目录

进程基本知识

已经进入多进程阶段

进程标识符pid

类型pid_t,传统意义上是一个16位有符号整型数。

命令ps

常用命令:ps axfps auxps axmps ax -L

进程号是顺次向下使用

1
2
3
4
5
// 返回当前进程号
pid_t getpid(void);

// 返回父进程的进程号
pid_t getppid(void);

进程的产生

pid_t fork();

  • 复制(duplicating)当前进程的方式创建一个新进程
  • setjmp一样,执行一次,返回两次
  • fork处复制,不会从头运行

fork后父子进程的不同之处:

  1. fork的返回值不一样
  2. pid不同
  3. ppid也不同
  4. 未决信号和文件锁不继承
  5. 资源利用量清0

init进程:1号,是所有进程的祖先进程

调度器的调度策略来决定哪个进程先执行

fflush()的重要性

1
2
3
4
5
/*
* vfork创建的子进程只能做exec或者exit
* ! 基本废弃
*/
pid_t vfork(void);

进程的消亡及释放资源

1
2
3
4
5
6
7
8
9
10
// 等待进程状态发生变化
pid_t wait(int *status); // 阻塞

pid_t waitpid(pid_t pid, int *status, int options);

int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options);


wait3();
wait4();

分配法和交叉分配法,90%优先选择交叉分配法。

池类算法:
上游往池子里放任务,下游三个线程从池子里取任务。

exec函数族

eg. bash进程创建primer进程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// exec函数族:替换当前进程的映像

extern char **environ;

int execl(const char *path, const char *arg, ...);

int execlp(const char *file, const char *arg, ...);

int execle(const char *path, const char *arg, ..., char * const envp[]);

int execv(const char *path, char *const argv[]);

int execvp(const char *file, char *const argv[]);

用户权限及组权限

u+s:如果文件是可执行的,则执行文件时,是以文件的拥有者的权限执行的。

1
-rwsr-xr-x 1 root root 68248 Mar 23  2023 /usr/bin/passwd

所以普通用户执行passwd时,是以root的权限执行的。

g+s:如果文件是可执行的,则执行文件时,是以文件的所在组的权限执行的。

uidgid都有三种类型:

  1. real uid:进程的实际所有者
  2. effective uid:进程的有效所有者
  3. saved uid:进程的保存的有效所有者
1
2
3
4
5
shell获取身份的流程
fork exec fork
init -->--> getty -->--> login -->--> shell
exec exec
root root root user
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// 获取当前用户的real uid
uid_t getuid(void);

// 获取当前用户的effective uid
uid_t geteuid(void);

// 获取当前进程的real gid
pid_t getegid(void);

// 获取当前进程的effective gid
pid_t getgid(void);

// 设置当前进程的real uid
int setuid(uid_t uid);

// 设置当前进程的effective uid
int seteuid(uid_t uid);

// 设置当前进程的real gid
int setgid(gid_t gid);

// 设置当前进程的effective gid
int setegid(gid_t gid);

// 交换uid和gid (原子操作)
int setreuid(uid_t ruid, uid_t euid);

// 交换gid和egid (原子操作)
int setregid(gid_t rgid, gid_t egid);

观摩课:解释器文件

unix讲究机制而非策略

脚本,后缀名是什么都可以,一般用sh, exec

1
2
3
#!/bin/cat

# some shell

#!是一种约定俗成的标记,告诉系统这个脚本应该用什么解释器来执行。

system()函数

1
2
3
4
5
/*
* 运行一个shell命令
* 调用/bin/sh
*/
int system(const char *command);

相当于fork+exec+wait的封装

进程会计

1
2
//! freeBSD系统的方言
int acct(const char *filename);

进程时间

1
2
3
4
5
6
7
8
9
10
11
clock_t times(struct tms *buf);

// clock_t 滴答数

struct tms{
clock_t tms_utime; /* user time */
clock_t tms_stime; /* system time */
clock_t tms_cutime; /* user time of children */
clock_t tms_cstime; /* system time of children */
}

守护进程

  1. 守护进程PPID为1
  2. 守护进程没有控制终端,TTY为?
  3. PID, PGID, SID相同
1
2
3
4
5
pid_t setpgid(pid_t pid, pid_t pgid);
pid_t getpgid(pid_t pid);

pid_t getpgrp(void); //! 方言
pid_t getpgrp(psid_t pid); //! 方言
  • 会话(session):一个或多个进程组的集合,以sid为标识
    pid_t setsid(void);
    setsid必须由非leader进程调用,从而创建一个新的会话。

    • 前台进程组:正在与终端交互的进程组
    • 后台进程组:正在运行,但不与终端交互的进程组
  • 终端:
    我们接触的都是虚拟终端

单实例守护进程:锁文件/var/run/name.pid

启动脚本文件:/etc/rc*...

系统日志

syslogd服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <syslog.h>

/**
* 打开系统日志
*
* @prarm: ident 标识符
* @prarm: option 选项 LOG_CONS, LOG_NDELAY, LOG_NOWAIT, LOG_PERROR ...
* @prarm: facility 来源 LOG_USER, LOG_DAEMON, LOG_KERN, LOG_LOCAL0~7 ...
*/
void openlog(const char *ident, int option, int facility);

/**
* 记录系统日志
*
* @prarm: priority 优先级 以 ERR 与 WARNING 为分界点
* @prarm: format 格式化字符串
* @prarm: ... 格式化参数
*/
void syslog(int priority, const char *format, ...);

/**
* 关闭系统日志
*/
void closelog(void);
1
2
sudo tail /var/log/messages  # 老师
journalctl -r # 我的debian

Linux下C语言教程-李慧芹老师-第十五章
http://sinlatansen.github.io/2024/05/16/Linux下C语言教程-李慧芹老师-第十五章/
作者
fugu
发布于
2024年5月16日
许可协议