【linux高级程序设计】(第八章)进程管理与程序开发 1

概念:

进程号(PID):系统标识一个进程的正整数

父进程号(PPID):父进程的进程号

进程组号(PGID):进程组是一个或多个进程的集合。

会话:一个或多个进程组的集合。

 

控制终端的含义我不是很理解?

 

函数/命令:

ps aux:查看当前系统所有进程的基本属性

getpid():获取当前进程PID

getppid():获取父进程的PID

__pid_t getpgid (__pid_t __pid):获取指定进程的进程组号,输入为0表示获取当前进程组号

pid_t getpgrp(void):获取当前进程进程组号

int setpgid(pid_t pid, pid_t pgid):将某个进程加入到某个进程组

          参数1:待修改进程组号的进程PID,0表示当前进程

          参数2:新的进程组号,0表示进程组号与参数1相同

__pid_t getsid (__pid_t __pid):获取指定进程的会话号,0表示调用进程

__pid_t setsid (void):创建新会话。如果调用进程是一个进程组组长,函数返回错误。

pid_t tcgetpgrp(int filedes):filedes是打开的终端,获取与该终端相关联的前台进程组的进程组号

pid_t tcsetpgrp(int filedes, pid_t pgrpid):将控制终端filedes的前台进程组ID设置为pgrpid。pgrpid应该是同一会话中的进程组ID

pid_t tcgetsid(int filedes):获取控制终端会话首进程的会话ID

 

#include<sdtio.h>
#include<unistd.h>
#include<fcntl.h>
#include<stdlib.h>

int main()
{
    int fd;
    pid_t pid;
    pid = fork();
    if(pid == -1)
        perror("fork");
    else if(pid > 0)
    {
        wait(NULL);
        exit(EXIT_FAILURE);
    }
    else
    {
        if((fd = open("/dev/tty1",O_RDWR)) == -1)        //注意 tty1是在ps 查看进程中能够找到的 换成ps中找不到的会打不开
            perror("open");
        
        printf("pid=%d,ppid=%d\n", getpid(), getppid());                 //获取进程号 父进程号
        printf("sid=%d,tcgetsid=%d\n",getsid(getpid()), tcgetsid(fd));   //获取会话号 终端会话号
        printf("tcgetpgrp=%d\n", tcgetpgrp(fd));                         //获取前台进程进程组号
        printf("pigd=%d\n",getpgid(getpid()));                           //获取当前进程进程组id
    }
}

可以看到,进程组号,终端的前台进程组号都是当前进程的父进程号。

当前进程的会话号和终端的会话号相同。

 

进程用户属性

真实用户号(RUID)创建该进程的用户的UID

真实用户组号(RGID):RUID的用户所在的组号

有效用户号(EUID):多数情况下EUID与UID相同,如果可执行文件的setuid位有效,则其他用户运行该程序时EUID和UID不同,EUID为该文件的拥有者,UID为文件的使用者。表示实际拥有的权限

有效用户组号(EGID):有效用户所在的用户组号

 

setuid位:当该位有效时,在执行该程序时,该执行用户所拥有的权限等同于文件的拥有者的权限。执行用户可以通过该权限处理其他的文件。

           设置setuid位有效  chmod u+s 可执行文件名

           设置setuid位无效  chmod u-s 可执行文件名

           当设置成有效时,可执行位上显示s,否则显示x.

                     

    举例:修改密码时,需要修改/etc/passwd 文件,该文件权限如下。root用户可以读写,但普通用户只能读。

                     

           普通用户通过执行 /usr/bin/passwd 文件来修改密码。其权限如下,setuid位是有效的

                    

           这样,普通用户执行/usr/bin/passwd时,就有了相当于root的权限,而root可以写/etc/passwd,这样普通用户就可以修改密码了。如果把setuid位设为无效,则普通用户就无法修改密码。

           

 

getresuid() :分别获取真实的,有效的和保存过的用户标识号

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
int main()
{
    int uid, euid, suid;
    getresuid(&uid, &euid, &suid);
    printf("uid = %d, euid = %d, suid = %d\n", uid, euid, suid);
    return 0;
}

可以看到,当root执行该文件时,使用id为0(root使用),有效用户id为0(有相当于root的权限),保存过的用户标识号为0(root)

              当game执行该文件时,使用id为1000(game使用,这也是真实用户号),有效用户id为0(有相当于root的权限),保存过的用户标识号为0(root)

 

__uid_t getuid (void):获取进程真实用户号

__uid_t geteuid (void):获取有效用户号

__uid_t getgid(void):获取真实用户组号

__uid_t getegid(void):获取有效进程用户组号

 

linux命令:

id :查看当前用户的id信息

whoami :显示当前用户

 

 

进程管理及控制

1.创建进程

__pid_t fork (void)

        成功,则父进程返回子进程的pid,子进程返回0. 子进程会复制父进程的几乎所有的信息。子进程在fork返回的位置继续向下执行。

        失败,返回-1.

i.子进程对父进程文件流缓冲区的处理

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>

int main(int argc, char *argv[])
{
    pid_t pid;
    printf("before fork, have enter\n");  //有回车,先输出
    printf("before fork, no enter: pid = %d\t", getpid());  //没有回车,缓存到输出流缓冲区
    pid = fork();
    if(pid == 0)
        printf("\nchild, after fork: pid = %d\n", getpid());
    else
        printf("\nparent, after fork: pid = %d\n", getpid());
}

注意:没有回车时,内容先保存在了缓冲区里!

看到,子进程中复制了父进程缓冲区的内容!

 

ii.子进程对父进程打开的文件描述符的处理

共享

#include<unistd.h>
#include<fcntl.h>
#include<string.h>
#include<stdlib.h>

int main(int argc, char *argv[])
{
    pid_t pid;
    int fd;
    int i = 1;
    int status;
    char *ch1 = "hello";
    char *ch2 = "world";
    char *ch3 = "IN";
    if((fd = open("test.txt",O_RDWR|O_CREAT,0644)) == -1)
    {
        perror("parent open");
        exit(EXIT_FAILURE);
    }
    if(write(fd,ch1,strlen(ch1)) == -1)
    {
        perror("parent write");
        exit(EXIT_FAILURE);
    }
    if((pid = fork()) == -1)
    {
        perror("fork");
        exit(EXIT_FAILURE);
    }
    else if(pid == 0)
    {
        i = 2;
        printf("in child\n");
        printf("i = %d\n", i);
        if(write(fd, ch2, strlen(ch2)) == -1)
            perror("child write");
        return 0;
    }
    else    //父进程
    {
        sleep(1);  //等子进程先执行
        printf("in parent\n");
        printf("i = %d\n", i);
        if(write(fd, ch3, strlen(ch3)) == -1)
            perror("parent write");
        wait(&status); //等待子进程结束
        return 0;
        
    }
}

可以看到,当文件打开后,父子进程写文件时共享了文件的偏移量等信息。不会出现覆盖。

 

iii.结合vfork测试全局数据段与BSS段使用策略

vfork():与fork基本相同,但是vfork()不复制父进程的地址空间,而是跟父进程共享所有资源

#include<unistd.h>
#include<error.h>
#include<sys/types.h>
#include<stdio.h>
#include<stdlib.h>
int glob = 6;  //全局已经初始化变量,位于数据段中
int main()
{
    int var;
    pid_t pid;
    var = 88;        //局部变量,位于栈空间中
    printf("in beginning:\tglob=%d\tvar=%d\n",glob, var);
    
    if((pid = vfork()) < 0)
    {
        perror("vfork");
        exit(EXIT_FAILURE);
    }
    else if(pid == 0)
    {
        printf("in child, modify the var: glob++, var++\n");
        glob++;
        var++;
        printf("in child:\tglob=%d\tvar=%d\n",glob, var);
        _exit(0);        //退出
    }
    else
    {
        printf("in parent:\tglob=%d\tvar=%d\n",glob,var);
        return 0;
    }
}

可以看到,父子进程打印的内容相同,说明他们共享资源。

如果把vfork改为fork,则,父子进程打印的内容不同。fork的子进程复制了父进程的内容。

 

iv.子函数调用vfork创建子进程

会出错!

#include<stdlib.h>
void test(void)  //test函数,在此函数中调用vfork
{
    pid_t pid;
    pid = vfork();
    if(pid == -1)
    {
        perror("vfork");
        exit(EXIT_FAILURE);
    }
    else if(pid == 0)
    {
        printf("1:child pid=%d,ppid=%d\n",getpid(),getppid());
        return;
    }
    else
        printf("2:parent pid=%d,ppid=%d\n",getpid(),getppid());
}

void fun(void)
{
    int i;
    int buf[100];
    for(i = 0; i < 100; i++)
        buf[i]=0;
    printf("3:child pid=%d,ppid=%d\n",getpid(),getppid());
}
int main(void)
{
    pid_t pid;
    test();
    fun();
    return 0;
}

出错原因:

调用main,申请栈空间,没有问题。

调用test函数,里面用vfork创建新进程,共享栈空间。子进程先运行,没有问题。子进程执行完test后返回,清理栈空间

子进程调用fun(),覆盖原来的test函数的栈空间。子进程执行完毕

子进程退出后,父进程从vfork返回处执行代码,没有问题。但返回时该栈空间已经不存在,出现栈错误。

把代码里的vfork改为fork则代码可以正常运行!

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值