1. Users and Groups


真实用户ID和真实组ID

真实用户ID和组ID表示运行进程的真实用户 ID 和 组ID。

有效用户ID和有效组IDp

有效 ID 是进程进行相关操作(比如系统调用)的凭证。

为什么需要有效用户ID和组ID?

通常有效ID和真实ID一致,但是当执行set-user-id 和 set-group-id程序时,有效用户ID被改为程序的拥有者的ID。

set-user id 和 set-group-id

执行set-user id 和 set-group-id程序时将设置进程的有效ID为程序的拥有者的ID,使得其他用户可以通过该程序执行相关操作。

File System User ID and File System Group ID

在Linux操作系统中,是文件系统用户ID和组ID决定文件系统的操作权限而不是有效用户ID和组ID。通常情况下文件系统ID和有效用户ID一致。

为甚要设置文件系统用户ID和组ID呢,也就是说什么情况下有效用户ID和文件系统用户ID不一样呢?

文件系统ID在Linux1.2中开始使用,在Linux1.2的内核中进程如果要发送信号给目标进程,必须与目标进程有相同的有效用户ID。像NFS那样的程序将收到影响,NFS Server 为了能够代表客户端访问文件系统必须将自己的有效用户ID设置为客户端有效用户ID,这样NFS Server将能够接受其他进程的信号。设置文件系统ID可以防止出现这样的情况。

2. File System

Linux文件系统采用索引文件系统,索引文件系统的好处是文件大小可以动态的变化而且支持随机读写,连续文件分配文件块或者串联文件系统都不支持。

Linux文件系统中i结点的作用是用来表示树形文件系统中文件结点(包括目录和数据文件),i 结点存储了文件的属性信息(文件大小,用户者和组,文件权限等,链接计数等)以及数据块索引。12个直接索引,1个间接索引,一个二级索引,一个三级索引。

Linux文件数据文件并不存储文件名,而是通过路径名找到文件对应的i结点,目录文件中存储的是子文件的i node号。

IO Buffer

Kernel Buffer: 当读写磁盘文件时,readwrite系统调用都不直接访问磁盘,而是通过读或者写缓存(kernel buffer),内核负责从磁盘读数据到缓存或者将数据flush到磁盘。Kernel buffer减少了系统调用的次数和访问磁盘的次数。

Buffer in stdio:

The standard I/O library provides a simple and efficient buffered stream I/O interface.

stdio stream buffer 设置
#include <stdio.h>
int setvbuf(FILE * stream , char * buf , int mode , size_t size );
Returns 0 on success, or nonzero on error
  • _IONBF 无缓冲
  • _NOLBF 行缓冲
  • _IOFBF 完全缓冲,即缓冲满才调用系统调用read或者write,磁盘文件的默认缓冲方式
#include <stdio.h>
void setbuf(FILE * stream , char * buf);

等价于:setvbuf(fp, buf, (buf != NULL) ? _IOFBF: _IONBF, BUFSIZ);,BUFSIZE 定义在stdio.h头文件中

flush stdio buffer
#include <stdio.h>
int fflush(FILE* stream);
Returns 0 on success, EOF on error
结合使用stdio和fd
#include <stdio.h>
int fileno(FILE* stream);
Returns file descriptor on success, or –1 on error
FILE* fdopen(int fd, const char* mode);
Returns (new) file pointer on success, or NULL on error

fdopen在读写非磁盘文件时非常有用,因为可以直接使用标准IO库函数读写非磁盘文件,比如socket。

3. Signal

信号是Linux进程间通信中的异步IO通信方式,每个进程维护一个信号集,如果收到信号则将对应的信号位置为1,当重新调度时会进程会执行信号处理函数。每个进程同样维护一个信号掩码集(mask),如果所有在Mask中的信号都将被阻塞,直到信号从mask集中删除。由于信号集并不记录信号的发生的次数,所以信号并不会排队,如果在执行信号处理程序之前收到多个信号也只调用一次信号处理函数。

信号默认处理方式:

  • ignore
  • terminate process
  • stop running process
  • restart process

定义信号处理函数API:

#include <signal.h>
void signal(int sig, void (*handler)(int))
int sigaction(int sig, const struct sigaction *act, struct sigaction* oldact)
return 0 on Success, -1 on Error
struct sigaction{
void (* sa_handler)(int);
sigset_t sa_mask;
int sa_flags;
void (* sa_restore)(void);
}

4. Timer

Software Clock,软时钟单位定义在内核源代码中,是内核分配给进程的时间片单位,2.4版本的代码中为100Hz,2.6版本中为1000Hz。软时钟单位决定了timer的精确度。

  • struct itimerval
  • setitimer(int which, const struct itimerval* newtimer, struct itimerval* oldtimer)
  • alarm(int sec)

5. Process

Process Creation

调用fork后子进程父进程的拷贝,子进程的拷贝了父进程的代码段,数据段,堆,堆栈断,父进程和子进程在fork返回后分别继续运行,根据fork返回值区分父进程和子进程。

调用fork后子进程可以调用exec加载新的程序,加载新的程序意味着代码端和数据段替换,堆和栈被重置。

如果每次fork都需要通过拷贝生成父进程的内存镜像,fork的效率会很低,fork使用了copy on write技术,fork只是拷贝了虚拟内存的页表,父进程和子进程共享相同的物理内存也,后面每次写内存都会拷贝到子进程的物理页面。

Process Termination

非正常结束:收到Signal,该Signal的默认处理方式结束进程。

正常结束:调用_exit或者exit

#include <unistd.h>
void _exit(int status);
void exit(int status):

进程一般不使用_exit而使用exit结束进程,exit执行过程:

  1. 执行handler(function registered with atexit() and on_exit())
  2. flush stdio buffers
  3. 执行_exit

从main函数返回等价于exit(main())

进程结束都干了啥?

  • 关闭所有进程已经打开的文件描述符,包括普通文件描述符,目录文件描述符,套接字描述符,消息描述符等。
  • 关闭文件后,加在该文件上的锁被释放
  • 共享内存detached
  • 所有打开的POSIX信号量都被关闭?
  • POSIX消息队列被关闭
  • 内存锁被释放(mlock or mlockall)
  • 内存映射(mmap)解除
exit handler

标准c库提供了两种注册exit handler的方方式:

方式一:

#include <stdlib.h>
int atexit(void (*func)(void))
Returns 0 on success, or nonzero on error

可以注册多个handler,执行顺序与注册顺序相反。

执行fork后子进程继承父进程所有的exit handler,如果子进程执行exec,将删除所有继承的exit handler。

atexit的限制,首先,exit handler不知道exit status,其次,不能传参数给exit handler。

方式二:

#define _BSD_SOURCE /* Or: #define _SVID_SOURCE */
#include <stdlin.h>
int on_exit(void (*func)(int, void*), void* arg);
Returns 0 on success, or nonzero on error

6. pthread

下图是线程虚拟内存地址

每个线程都有自己的堆栈,线程共享虚拟地址空间,因此线程间共享数据非常方便。

6.1 线程创建:
数据类型 作用
pthread_t Thread identifier
pthread_mutex_t Mutex
pthread_mutexattr_t Mutex attributes object
pthread_cond_t Condition variable
pthread_condattr_t Condition variable attributes object
pthread_key_t Key for thread-specific data
pthread_once_t One-time initialization control context
pthread_attr_t Thread attributes object
#include <pthread.h>
nt pthread_create(pthread_t * thread , const pthread_attr_t * attr ,void *(* start )(void *), void * arg );
Returns 0 on success, or a positive error number on error
6.2 线程退出
#include <pthread.h>
void pthread_exit(void * retval );

传给pthread_exit的参数是返回值,该返回值不应该存储在线程栈上,使用pthread_join可以获取线程的返回值。

6.3线程ID
#include <pthread.h>
pthread_t pthread_self(void);
Returns the thread ID of the calling thread

返回线程自己的ID。

#include <pthread.h>
int pthread_equal(pthread_t t1 , pthread_t t2 );
Returns nonzero value if t1 and t2 are equal, otherwise 0

判断两个线程id是否相等,之所以需要这个函数是因为pthread_t是opaque data(不透明数据类型)

6.4 join and detach

有的线程需要等待其他线程结束才能继续运行,可以使用pthread_join等待其他线程结束,并获取它们的返回值。如果线程既没有被detach也没有被join,那么该线程将变为僵尸(zombie)线程,僵尸线程积累过多将无法创建新的线程。

#include <pthread.h>
int pthread_join(pthread_t thread , void ** retval );
Returns 0 on success, or a positive error number on error

使用注意:重复join可能导致不可预料的结果

与waitpid()的区别:

  1. 线程是对等的,任意两个线程可以join,而进程是等级关系( hierarchical relationship ),只有父进程才能等待子进程。
  2. 进程可以通过waitpid(–1, &status, options))等待任意的子进程,而join必须指定线程id

有的时候我们并不用等待线程结束,也不需要线程的返回状态,可以使用pthread_detach标记线程为detached。

#include <pthread.h>
int pthread_detach(pthread_t thread );
Returns 0 on success, or a positive error number on error

线程一旦被detach,就不能变回joinable状态,而且exit()任然能使detached线程终止。

6.5 threads and errno

在传统的Unix API中errno是一个全局变量,但是在多线程程序中如果errno还是一个全局变量将会出现条件竞争,因此多线程程序中每个线程都有自己的errno(具体如何实现的有待研究)。

thread specific data

thread specific data 可以避免函数使用静态数据或者全局数据,可以用来实现thread safe函数

#include <iostream>
#include <pthread.h>
#include <stdio.h> using namespace std; const int MAX_LEN = 100;
pthread_key_t key;//a key index the specific data
pthread_once_t once = PTHREAD_ONCE_INIT; void destructor(void* arg){
delete (char*)arg;
} void create_key(void){
pthread_key_create(&key, destructor);
} void* thread_func(void* arg){
//allocate specific data key
pthread_once(&once,create_key);//pthread_key_create called only once
char* buf;
buf = (char*)pthread_getspecific(key);
if(buf == NULL){
buf = new char[MAX_LEN];
pthread_setspecific(key, buf);
}
printf("thread:%ld \t specific data pointer:%p\n", pthread_self(), buf);
return NULL;
} int main(){
pthread_t t1, t2;
pthread_create(&t1,NULL,thread_func,NULL);
pthread_create(&t2,NULL,thread_func,NULL);
pthread_join(t1,NULL);
pthread_join(t2,NULL);
return 0;
}

Unix 编程的更多相关文章

  1. 学习linux/unix编程方法的建议(转)

    假设你是计算机科班出身,计算机系的基本课程如数据结构.操作系统.体系结构.编译原理.计算机网络你全修过 我想大概可以分为4个阶段,水平从低到高从安装使用=>linux常用命令=>linux ...

  2. 转《UNIX编程艺术》读书心得

    花了一段时间看完了<UNIX编程艺术>,但不是看得特别仔细,尤其是后面作者通过对工具的讲解来阐述其设计思想,因为很多工具能未曾接触过,难免就会产生一些乏味的感觉.其实就像译者姜宏在译序里说 ...

  3. 读《UNIX编程艺术》一感

    我记得早在2006年的时候就开始频繁使用awk做文本处理方面的工作,07年的时候周围有人用perl,我还感到很不解,觉得写得很复杂,没有awk one liner 那么方便和神奇.一直在了解awk的具 ...

  4. 《UNIX编程艺术》读书笔记

    最近这段时间比较忙,利用业余时间看完了这本书.虽然书中讲到的很多例子都是上古文物,我没有用过,不过原理都是相通的,对我的启发很大.比如无所不在的KISS原则,实践中慢慢体会到的SPOT原则,无不产生共 ...

  5. 《Unix编程艺术》读书笔记(1)

    <Unix编程艺术>读书笔记(1) 这两天開始阅读该书,以下是自己的体会,以及原文的摘录,尽管有些东西还无法全然吃透. 写优雅的代码来提高软件系统的透明性:(P134) Elegance ...

  6. Linux/UNIX编程:使用C语言实现简单的 ls 命令

    刚好把 Linux/UNIX 编程中的文件和IO部分学完了,就想编写个 ls 命令练习一下,本以为很简单,调用个 stat 就完事了,没想到前前后后弄了七八个小时,90%的时间都用在格式化(像 ls ...

  7. 学习linux/unix编程方法的建议,学习Linux的四个步骤(转)

    解答:学习Linux的四个步骤假设你是计算机科班出身,计算机系的基本课程如数据结构.操作系统.体系结构.编译原理.计算机网络你全修过我想大概可以分为4个阶段,水平从低到高从安装使用=>linux ...

  8. UNIX编程艺术

    本文主要是 <UNIX编程艺术>的摘录,摘录的主要是我觉得对从事软件开发有用的一些原则. 对于程序员和开发人员来说,如果完成某项任务所需要付出的努力对他们是个挑战却又恰好还在力所能及的范围 ...

  9. Linux/UNIX编程如何保证文件落盘

    本文转载自Linux/UNIX编程如何保证文件落盘 导语 我们编写程序write数据到文件中时,其实数据不会立马写入磁盘,而是会经过层层缓存.每层缓存都有自己的刷新时机,每层缓存都刷新后才会写入磁盘. ...

  10. Linux/Unix编程中的线程安全问题【转】

    转自:http://blog.csdn.net/zhengzhoudaxue2/article/details/6432984 在目前的计算机科学中,线程是操作系统调度的最小单元,进程是资源分配的最小 ...

随机推荐

  1. selenium借助AutoIt识别上传文件Java篇

    官方网站:https://www.autoitscript.com/site/ 从网站上下载AutoIt并安装,安装完成在菜单中会看到图4.13的目录: 图4.13  AutoIt菜单 1.首先打开A ...

  2. 怎样对CODESOFT中的条形码进行黑白转换

      CODESOFT 2015标签设计软件能 够提供无与伦比的灵活性.功能和支持,其面对的用户也是极其的广泛.对于一些需要打印黑白反转条形码的特殊用户,例如使用黑色标签纸的用 户,CODESOFT 2 ...

  3. 酷我音乐API

    今天把酷我音乐API分享给大家: 歌曲搜索API:http://search.kuwo.cn/r.s?all={0}&ft=music& itemset=web_2013&cl ...

  4. DoTween使用

    官网:http://dotween.demigiant.com/ 1.step 这里使用lamda表达式,通过dotween的to方法将其移动到 Vector3(348, 196, 0)的值返回到Ve ...

  5. 多线程——GCD

    一. GCD的基本概念 GCD:强大的中枢调度,纯C语言,提供了非常多强大的函数. 任务(block):执行什么操作. 队列(queue):用来存放任务. 同步函数dispatch_sync():不创 ...

  6. POI实现word文档转html文件

    POI word文件转html package com.feiruo.officeConvert; import java.io.BufferedWriter; import java.io.File ...

  7. 火箭18号秀光膀为父母割草(FW)

    火箭18号秀光膀为父母割草 一夜致富不改本色 来源:新浪 2015年06月30日 分享到: 更多 收藏 分享 被浏览5次   <ignore_js_op id="r_article_i ...

  8. Android添加权限大讲解

    对于新手来说,最烦恼的不是如何从网上下载到安卓项目,而是下载到的安卓项目不知道如何添加权限和要添加哪些权限. 现在就针对安卓的权限来讲解这些权限应该具体用在什么地方 首先在项目下找到 AndroidM ...

  9. MongoDB 2: 安装和使用

    导读:上篇博客中简单介绍了MongoDB,本篇文章主要是介绍Mongo的安装和使用(环境为win8).(PS:这是一篇没什么技术含量的文章,仅是个人的笔记式文档)下一篇博客,将介绍Mongo使用过程中 ...

  10. kickstart bonding安装

    bonding用的是最简单的负载均衡模式,交换机不需要做配置. https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Lin ...