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只显示当前终端相关进程
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZR23ciGy-1598237696991)(index_files/4d9770ad-5056-4f03-ab21-8c12d827977e.png)]

ps -aux

ps -aux(Linux)—Unix变相支持ps -aux,不直接支持
Unix/Linux-06_第1张图片

ps -ef

ps -ef(Unix/Linux)
Unix/Linux-06_第2张图片

但 Unix 也 可以用特殊的方式 支持ps -aux。
whereis ps
/usr/ucb/ps -aux 可以的
Unix/Linux-06_第3张图片

杀进程

kill -9 进程ID(必杀)

Unix/Linux-06_第4张图片

进程状态

Unix/Linux-06_第5张图片

进程的分类

Unix/Linux-06_第6张图片

进程常见状态

字母 含义
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_第7张图片

进程

  • 代码段
  • 全局区(数据段)
  • 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 
#include 
#include 
#include 
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之后,父子进程同时都打开同一文件,
    会复制文件描述符,也会复制文件表,如果文件不加读写锁,会产生覆盖情况

你可能感兴趣的:(Linux,C)