0
点赞
收藏
分享

微信扫一扫

Unix/Linux-06


文章目录

  • ​​回顾​​
  • ​​今天​​
  • ​​目录相关函数​​
  • ​​读目录的内容:(相当于ls)​​
  • ​​进程​​
  • ​​进程 用ps(process show)可以查看​​
  • ​​ps​​
  • ​​ps -aux​​
  • ​​ps -ef​​
  • ​​杀进程​​
  • ​​进程状态​​
  • ​​进程的分类​​
  • ​​进程常见状态​​
  • ​​父进程和子进程的关系(父子关系)​​
  • ​​fork / vfork​​
  • ​​进程​​
  • ​​fork​​
  • ​​验证fork之后各区段是否是复制关系​​
  • ​​文件描述符与fork​​

回顾

文件的相关操作

  • fcntl 函数 (复制文件描述符、取文件表的状态、文件锁)
  • access 函数
  • stat 函数

今天

文件的周边函数、内存mmap、目录的函数
进程的创建(fork/vfork)

umask 是 设置权限的屏蔽字,对 创建文件有效。
chmod 是 对已经存在的文件 ,修改 权限。
truncate/ftruncate 可以 指定文件的长度(可大可小)

内存 映射 文件 mmap

目录相关函数

rename - 重命名
mkdir - 创建新目录
rmdir - 删除目录, 只能删除空目录
chdir - 切换工作目录/当前目录(相当于cd)
fchdir
getcwd - 取当前目录(相当于pwd)

{
chdir(filename); // 切换目录

char* path = getcwd(NULL, 0); // 利用返回值获取文件当前目录
printf("path: %s\n", path);

char buf[100] = {};
getcwd(buf, sizeof(buf)); // 利用数组获取文件当前目录
printf("path: %s\n", buf);
}

读目录的内容:(相当于ls)

  • 1 opendir 返回DIR*,错误返回NULL
  • 2 readdir(DIR*) 一次只返回一个文件,返回 结构体,结构体中存储了 子目录/子文件 信息。如果读完,返回NULL
  • 3 rewinddir 回到文件初始位置
  • 4 telldir 返回文件当前指针位置
  • 5 seekdir 直接设置文件指针当前位置,用telldir得到设置之后的位置

示例代码

  struct dirent {
ino_t d_ino; /* inode number */
off_t d_off; /* not an offset; see NOTES */
unsigned short d_reclen; /* length of this record */
unsigned char d_type; /* type of file; not supported
by all filesystem types */
char d_name[256]; /* filename */
};

// --------------------------------------------------
// 使用
DIR* dir = opendir("."); // 打开目录
if(dir == NULL) perror("open"),exit(-1);

struct dirent* ent;
while(ent = readdir(dir)){ // 读取某个文件或目录
printf("%d,%s,%d,%d\n",ent->d_type,
ent->d_name,ent->d_off,telldir(dir));
}

seekdir(dir,881236067); // 指定到某一offset的下一位置
rewinddir(dir); //回到开始

进程

操作系统都是支持多进程的,每个进程都有 自己的进程ID。进程号 可以延迟重用(进程结束后,进程ID过段时间后可以继续使用)。

进程 用ps(process show)可以查看

ps

直接ps只显示当前终端相关进程

Unix/Linux-06_父子进程

ps -aux

ps -aux(Linux)—Unix变相支持ps -aux,不直接支持

Unix/Linux-06_linux_02

ps -ef

ps -ef(Unix/Linux)

Unix/Linux-06_父子进程_03

但 Unix 也 可以用特殊的方式 支持ps -aux。

whereis ps

/usr/ucb/ps -aux 可以的

Unix/Linux-06_linux_04

杀进程

kill -9 进程ID(必杀)

Unix/Linux-06_父进程_05

进程状态

Unix/Linux-06_C_06

进程的分类

Unix/Linux-06_父子进程_07

进程常见状态

字母

含义

D

不可中断睡眠

S

休眠状态

O

可运行状态

R

运行状态

T

挂起状态

Z

僵尸进程(本应关闭,但资源没有释放的进程,不是由父进程中止的)

<

高优先级

N

低优先级

L

在内存中有内存页被锁住

s

其下面有子进程

l

是多线程

+

前台进程组

如果一个进程A 启动了进程B,则A进程叫B进程的父进程,B进程叫A进程的子进程

$ ps
PID TTY TIME CMD
56370 pts/4 00:00:01 bash
125930 pts/4 00:00:00 ps
// 我们查找 bash 56370
ps -ef
liujing 56370 56363 0 Aug19 pts/4 00:00:01 bash
liujing 56363 1576 0 Aug19 ? 00:01:37 /usr/lib/gnome-terminal/gnome-terminal-server
liujing 1576 1382 0 Aug17 ? 00:00:00 /sbin/upstart --user
root 1382 982 0 Aug17 ? 00:00:00 lightdm --session-child 12 19
root 982 1 0 Aug17 ? 00:00:02 /usr/sbin/lightdm
root 1 0 0 Aug17 ? 00:00:11 /sbin/init auto noprompt

父进程和子进程的关系(父子关系)

  • 父进程启动子进程后,父子进程 同时运行。
  • 如果子进程先结束,子进程 给父进程发信号,父进程 负责回收子进程的资源。
  • 父进程启动子进程后,父子进程 同时运行。
  • 如果父进程先结束,子进程成 孤儿进程,
  • 然后子进程 认 init进程(进程1)做新的父进程,init进程 也叫 孤儿院。
  • 父进程启动子进程后,父子进程 同时运行。
  • 如果子进程先结束,子进程 给父进程发信号,
  • 但父进程有可能没收到信号,或者收到了没及时处理,子进程虽然结束,但资源没有被回收,就会变成僵尸进程。

系统用pid管理进程,每个进程都有当时唯一的进程ID

  • getpid 取当前进程id
  • getppid 取父进程的id
  • getuid 取真实用户id,其实返回的就是有效用户id
  • geteuid 取有效用户id
  • getgid 取用户组id

程序中起作用的是 有效用户,因此,getuid和geteuid返回的都是 有效用户。

Linux/Unix 切换用户 su , 但导致的问题是这个机器本身是谁的账号登录的,所以这个第一个机器启动时登录的账号就是真实用户
切换到其它用户, 如 root , 也可以是其它普通用户, 它们就是有效用户

示例代码

printf("pid=%d, ppid=%d, uid=%d, euid=%d\n",
getpid(), getppid(), getuid(), geteuid());
// pid=126640, ppid=126639, uid=1000, euid=1000

fork / vfork

Unix/Linux-06_子进程_08

进程

  • 代码段
  • 全局区(数据段)
  • BSS段
  • 堆区
  • 栈区

fork()/vfork() 都可以创建子进程。
fork是通过复制父进程来创建子进程,复制 父进程的内存区域(堆、栈,全局等)。代码区不复制(代码区只读,共用不会有冲突),其它区都复制。子进程会申请新的内存空间,值和父进程对应内存空间一样。子进程不执行 fork之前的代码。但 fork之后的代码 父子进程 各 执行一遍。

fork

  • 父子进程都会执行
  • 父进程返回子进程id
  • 子进程返回0。
  • -1 代表错误。

fork之后,谁先执行,谁后执行,没有固定顺序,由操作系统调度算法决定

    printf("begin, pid=%d\n", getpid());
pid_t pid = fork();
if (pid == -1)
perror("fork"), exit(-1);
if (pid == 0){ // 子进程中
printf("我是子进程, 我的父进程ppid=%d, 我的pid=%d\n", getppid(), getpid());
}else{ // 父进程中
printf("我是=父进程, 我的子进程pid=%d, 我的pid=%d\n", pid, getpid());
}
printf("end, getpid=%d\n", getpid());


/* 注意:有时并不能确保所有顺序都是这样
begin, pid=127438
我是=父进程, 我的子进程pid=127439, 我的pid=127438
end, getpid=127438
我是子进程, 我的父进程ppid=127438, 我的pid=127439
end, getpid=127439
*/

exit(0) 可以退出当前进程,例如在子进程中写exit(0), 退出子进程,并不会影响父进程

验证fork之后各区段是否是复制关系

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
using namespace std;

int i1 = 100;
int b;
int main(int argc, char** argv) {
printf("begin, pid=%d\n", getpid());

int i2 = 200;
char* str = (char*)malloc(20);
strcpy(str, "abcdef");
pid_t pid = fork();
if (pid == -1)
perror("fork"), exit(-1);

int i3 = 300;
if (pid == 0){
// 子进程中
printf("我是子进程, 我的父进程ppid=%d, 我的pid=%d\n", getppid(), getpid());
i1 = 101;
i2 = 201;
i3 = 301;
b = 501;
int i4 = 401;
str[0]= 'A';
printf("i1=%d,i2=%d,i3=%d,i4=%d,b=%d,str=%s\n", i1, i2, i3, i4,b,str);
}else{
// 父进程中
sleep(2);
printf("我是=父进程, 我的子进程pid=%d, 我的pid=%d\n", pid, getpid());
//i1 = 102;
//i2 = 202;
//i3 = 302;
int i4 = 402; // 父进程中并不能直接访问子进程中的局部变量
//str[1] = 'B';
printf("i1=%d,i2=%d,i3=%d,i4=%d,b=%d,str=%s\n", i1, i2, i3, i4,b, str);
}

printf("end, getpid=%d\n", getpid());

return 0;
}
/*
begin, pid=127977
我是子进程, 我的父进程ppid=127977, 我的pid=127978
i1=101,i2=201,i3=301,i4=401,b=501,str=Abcdef
end, getpid=127978
我是=父进程, 我的子进程pid=127978, 我的pid=127977
i1=100,i2=200,i3=300,i4=402,b=0,str=abcdef
end, getpid=127977
*/

文件描述符与fork

在fork时,打开文件,在父子进程中同时写文件,一个写入26个大写字母,一个写入26个小写字母

  • 情况一:fork之前,有文件描述符,是只复制文件描述符,有没有复制文件表?
    看文件偏移变量,52个,不复制文件表,测试使用fork之后,对同一个文件写入,看其大小和内容,可以确认只用了一个文件偏移量
  • 情况二:fork之后,父子进程同时都打开同一文件,
    会复制文件描述符,也会复制文件表,如果文件不加读写锁,会产生覆盖情况


举报

相关推荐

0 条评论