What is common between the Linux kernel and iOS?
Naturally, in two large systems there will always be common concepts. For example, it is difficult to avoid using basic algorithms and data structures. Such as lists, arrays, trees. But this is not what I want to tell.
iOS is an object oriented thing. Somewhere below Objective-C (and soon we will be able to talk about Swift), huge layers of non-object code lie, and under them is a Unix (or rather BSD) system. At that level, Linux and iOS have a lot in common. But I'm not talking about that.
Let's compare the basic structures of the Linux kernel with the object-oriented part of iOS.
Both systems have a certain number of fundamental structures. For example, in iOS it will be:
Lines (NSSrting);
Arrays (NSArray);
Collections (NSSet);
Dictionaries (NSDictionary);
Representation of numerical primitives (NSNumber);
Scalars (NSRange).
I leave out brackets various variations of all these types (NSSet / NSMutableSet / NSCountedSet and so on).
All these data types are implemented as classes. It is easy to notice that there are no several fundamental structures: linked lists and binary trees. They are not for the simple reason that they are already encapsulated in other data types. So, NSArray can be used instead of a linked list, and NSDictionary instead of a binary tree, without particularly worrying about their internal implementation.
Good. And what are the main types we can find in the Linux kernel? Here the situation looks exactly the opposite. It is difficult to distinguish any standard data types for the kernel. The largest contenders for this title:
A double linked list defined in the include / linux / list.h file;
Red-black trees defined in include / linux / rbtree.h;
Radix trees defined in the include / linux / radix-tree.h file;
Bit arrays defined in the include / linux / bitmap.h file;
Well, as well as semaphores and spinlocks that are not explicitly present in iOS - that is, they do not need to be allocated, they are hidden in the methods.
There is no requirement to use these particular implementations. If you are unhappy with something, then you can write your implementation of anything. True, moderators may not accept it if you try to upload your patch. In any case, moderators will not accept it, unless there is a very good reason to implement it yourself instead of using already written structures.
So, at first glance, we have two orthogonal approaches to the construction of the system. iOS offers a fairly clean object-oriented approach, and Apple is trying as soon as possible to hide the insides of objects from the end programmer. The Linux kernel, by contrast, defines very basic primitives, leaving the programmer to deal with them. Roughly speaking, iOS is a block building, and the Linux kernel sometimes puts bricks at your disposal, and sometimes just clay and a kiln. However, the programming goals in the two systems are completely different: no one expects the kernel developer to create a user interface, no one expects the iOS programmer to write support for the chip and data bus.
References Counting references to objects.
Until recently, iOS required the programmer to manually decrease the count of objects. This was done by calling the standard retain method to increase it, release to reduce it. In the latest versions of Objective-C, this is no longer necessary, the system does this automatically.
An object counter is a completely object-oriented thing: you have several processes sharing an object. For simplicity, imagine that this is a read-only object, that is, none of the processes can change it. But the question is: when should this object be freed? And by whom?
On systems with automatic memory management, each object has an internal counter, which increases every time someone receives a link to this object. When this someone stops using this link (for example, assigning Nil), the internal counter of the object decreases.
I apologize for the tedium, but here's how it goes. From my code, I create a new line in iOS:
After executing this code, an object of type NSString was created. The variable s holds a reference to the object, and this object has an internal counter equal to 1.
After executing this line of code, the s2 variable also refers to the same object. And the internal reference counter of this object will be 2.
Now the variable s no longer refers to the object - and the internal reference counter in the object is 1.
Now the s2 variable no longer holds an object reference. The object is not available, its address is lost. The internal counter of the object is 0. The object will be automatically destroyed by the garbage collection system.
Now back to the Linux kernel. And immediately open the file incluce / linux / kref.h
In this file we can see the generic implementation of this particular mechanism - the reference counter. The file is not too big, but necessary.
The Linux kernel in terms of parallel processes is a very interesting thing. If you forget about synchronization, your code will crash very quickly. Together with the rest of the core, by the way. Most often, this requires a split second. Sometimes seconds. If you forget that the kernel is reinterant, and your code can be interrupted at any time, your code will crash. There is no way to prevent this — even banning interrupts will not help solve synchronization problems. Big Lock, which once existed in the kernel and allowed you to stop everything except your process, was removed from the kernel years ago, as it was used. No, seriously, this is a rather strange situation: you have a great way to solve all your problems with synchronization, but they tell you: do not use it, it's bad karma. But you never have time and Big Lock wonderfully prevents your code from crashing. So at some point, Linus willedly removed this mechanism.
So, if you do a search in the kernel, for example, in the kref_init function, you will see that it is used in more than two hundred places. Which is quite a lot for the kernel. How does kref work and why is it needed?
Answering the second question: it is needed for the same purpose, which also needs a reference counter in iOS objects - this is a synchronization mechanism for managing objects from the point of view of memory management. Naturally, in the kernel you have to implement all the methods of removing an object yourself, there is no garbage collector here, as in iOS (more precisely, it exists, but in a different place, it does completely different things, and it has nothing to do with kref).
- If we summarize the logic of kref, then it is as follows:
- Created an object? Immediately increase him kref.
- Got a link to an object? Increase the kref object by one (using kref_get ())
- Stop using the object link? Reduce the object kref by one (kref_put ())
- did the object kref reach zero? The object is destroyed - a reference to the destructor function is passed when kref_put () is called, and it is used from kref_put () automatically.
There is no morality in this article.
It’s not news to anyone that the Linux kernel has adapted an object-oriented paradigm in many ways. However, kernel moderators do not allow developers to complicate it (the kernel), and maintain the infrastructure at the level of simple primitives. The philosophy here is to “keep it simple” - let the developer cook his own soup from raw brisket, do not offer him a soup set or chicken powder.
iOS, in turn, follows the path of hiding implementation details from developers. At some point, iOS introduced ARC, Automatic Reference Counting, a system that monitors the reference count of objects and destroys them automatically. In the near future, it seems that objects will not only automatically be destroyed, but also created, the latest trends indicate this - in some cases you can omit the alloc call today (for example, [NSSting stringWithFormat] works the same as [[NSString alloc] initWithFormat :]).
For those who are interested:
Referencing the reference system in iOS.
One of the first references to kref in the Linux kernel. Linux kernel
document on kref
Have a nice day. Thanks for attention.
iOS is an object oriented thing. Somewhere below Objective-C (and soon we will be able to talk about Swift), huge layers of non-object code lie, and under them is a Unix (or rather BSD) system. At that level, Linux and iOS have a lot in common. But I'm not talking about that.
Let's compare the basic structures of the Linux kernel with the object-oriented part of iOS.
1. Basic structures
Both systems have a certain number of fundamental structures. For example, in iOS it will be:
Lines (NSSrting);
Arrays (NSArray);
Collections (NSSet);
Dictionaries (NSDictionary);
Representation of numerical primitives (NSNumber);
Scalars (NSRange).
I leave out brackets various variations of all these types (NSSet / NSMutableSet / NSCountedSet and so on).
All these data types are implemented as classes. It is easy to notice that there are no several fundamental structures: linked lists and binary trees. They are not for the simple reason that they are already encapsulated in other data types. So, NSArray can be used instead of a linked list, and NSDictionary instead of a binary tree, without particularly worrying about their internal implementation.
Good. And what are the main types we can find in the Linux kernel? Here the situation looks exactly the opposite. It is difficult to distinguish any standard data types for the kernel. The largest contenders for this title:
A double linked list defined in the include / linux / list.h file;
Red-black trees defined in include / linux / rbtree.h;
Radix trees defined in the include / linux / radix-tree.h file;
Bit arrays defined in the include / linux / bitmap.h file;
Well, as well as semaphores and spinlocks that are not explicitly present in iOS - that is, they do not need to be allocated, they are hidden in the methods.
There is no requirement to use these particular implementations. If you are unhappy with something, then you can write your implementation of anything. True, moderators may not accept it if you try to upload your patch. In any case, moderators will not accept it, unless there is a very good reason to implement it yourself instead of using already written structures.
So, at first glance, we have two orthogonal approaches to the construction of the system. iOS offers a fairly clean object-oriented approach, and Apple is trying as soon as possible to hide the insides of objects from the end programmer. The Linux kernel, by contrast, defines very basic primitives, leaving the programmer to deal with them. Roughly speaking, iOS is a block building, and the Linux kernel sometimes puts bricks at your disposal, and sometimes just clay and a kiln. However, the programming goals in the two systems are completely different: no one expects the kernel developer to create a user interface, no one expects the iOS programmer to write support for the chip and data bus.
2. So what is common between these two systems?
References Counting references to objects.
Until recently, iOS required the programmer to manually decrease the count of objects. This was done by calling the standard retain method to increase it, release to reduce it. In the latest versions of Objective-C, this is no longer necessary, the system does this automatically.
An object counter is a completely object-oriented thing: you have several processes sharing an object. For simplicity, imagine that this is a read-only object, that is, none of the processes can change it. But the question is: when should this object be freed? And by whom?
On systems with automatic memory management, each object has an internal counter, which increases every time someone receives a link to this object. When this someone stops using this link (for example, assigning Nil), the internal counter of the object decreases.
I apologize for the tedium, but here's how it goes. From my code, I create a new line in iOS:
NSString *s = [[NSString alloc] init ];
After executing this code, an object of type NSString was created. The variable s holds a reference to the object, and this object has an internal counter equal to 1.
NSString *s2 = s;
After executing this line of code, the s2 variable also refers to the same object. And the internal reference counter of this object will be 2.
s = Nil;
Now the variable s no longer refers to the object - and the internal reference counter in the object is 1.
s2 = Nil;
Now the s2 variable no longer holds an object reference. The object is not available, its address is lost. The internal counter of the object is 0. The object will be automatically destroyed by the garbage collection system.
Now back to the Linux kernel. And immediately open the file incluce / linux / kref.h
In this file we can see the generic implementation of this particular mechanism - the reference counter. The file is not too big, but necessary.
The Linux kernel in terms of parallel processes is a very interesting thing. If you forget about synchronization, your code will crash very quickly. Together with the rest of the core, by the way. Most often, this requires a split second. Sometimes seconds. If you forget that the kernel is reinterant, and your code can be interrupted at any time, your code will crash. There is no way to prevent this — even banning interrupts will not help solve synchronization problems. Big Lock, which once existed in the kernel and allowed you to stop everything except your process, was removed from the kernel years ago, as it was used. No, seriously, this is a rather strange situation: you have a great way to solve all your problems with synchronization, but they tell you: do not use it, it's bad karma. But you never have time and Big Lock wonderfully prevents your code from crashing. So at some point, Linus willedly removed this mechanism.
So, if you do a search in the kernel, for example, in the kref_init function, you will see that it is used in more than two hundred places. Which is quite a lot for the kernel. How does kref work and why is it needed?
Answering the second question: it is needed for the same purpose, which also needs a reference counter in iOS objects - this is a synchronization mechanism for managing objects from the point of view of memory management. Naturally, in the kernel you have to implement all the methods of removing an object yourself, there is no garbage collector here, as in iOS (more precisely, it exists, but in a different place, it does completely different things, and it has nothing to do with kref).
- If we summarize the logic of kref, then it is as follows:
- Created an object? Immediately increase him kref.
- Got a link to an object? Increase the kref object by one (using kref_get ())
- Stop using the object link? Reduce the object kref by one (kref_put ())
- did the object kref reach zero? The object is destroyed - a reference to the destructor function is passed when kref_put () is called, and it is used from kref_put () automatically.
3. Conclusion
There is no morality in this article.
It’s not news to anyone that the Linux kernel has adapted an object-oriented paradigm in many ways. However, kernel moderators do not allow developers to complicate it (the kernel), and maintain the infrastructure at the level of simple primitives. The philosophy here is to “keep it simple” - let the developer cook his own soup from raw brisket, do not offer him a soup set or chicken powder.
iOS, in turn, follows the path of hiding implementation details from developers. At some point, iOS introduced ARC, Automatic Reference Counting, a system that monitors the reference count of objects and destroys them automatically. In the near future, it seems that objects will not only automatically be destroyed, but also created, the latest trends indicate this - in some cases you can omit the alloc call today (for example, [NSSting stringWithFormat] works the same as [[NSString alloc] initWithFormat :]).
For those who are interested:
Referencing the reference system in iOS.
One of the first references to kref in the Linux kernel. Linux kernel
document on kref
Have a nice day. Thanks for attention.